Skip to content

feat(dashboard): add WYSIWYG Discord markdown editor component#422

Merged
BillChirico merged 12 commits intomainfrom
feat/discord-markdown-editor
Apr 3, 2026
Merged

feat(dashboard): add WYSIWYG Discord markdown editor component#422
BillChirico merged 12 commits intomainfrom
feat/discord-markdown-editor

Conversation

@BillChirico
Copy link
Copy Markdown
Collaborator

@BillChirico BillChirico commented Apr 2, 2026

Summary

Implements a WYSIWYG Discord Markdown Editor component for use in dashboard template editing.

Also includes the supporting config-editor / autosave test stabilization needed to integrate the new editor into the dashboard settings flow without introducing mount-time save regressions.

Closes #370

Changes

web/src/lib/discord-markdown.ts — Parser utility

  • parseDiscordMarkdown() — converts Discord-flavored markdown to HTML
  • Supports: bold, italic, underline, strikethrough, code, code blocks, ||spoilers||, > quotes, # headings (H1-H3), bullet lists, numbered lists
  • Template variable {{var}} rendering as styled badges
  • HTML escaping for XSS prevention
  • Helper functions: wrapSelection(), insertAtCursor(), wrapLine()

web/src/components/ui/discord-markdown-editor.tsx — Editor component

  • Toolbar — 13 formatting buttons with tooltips (Bold, Italic, Underline, Strikethrough, Code, Code Block, Spoiler, Quote, H1-H3, Bullet List, Numbered List)
  • Variable inserter — dropdown to insert template variables ({{username}}, etc.) at cursor position
  • Split view — raw markdown editor (left) ↔ Discord-style rendered preview (right)
  • Character counter — shows current length vs configurable limit with color coding (green → yellow → red)
  • Keyboard shortcuts — Ctrl+B (bold), Ctrl+I (italic), Ctrl+U (underline)
  • Follows existing Radix UI + Tailwind + Lucide patterns
  • Fully accessible with ARIA labels and keyboard navigation

Dashboard integration / supporting fixes

  • Stabilizes config-editor autosave-related tests and Next navigation mocks used by the dashboard settings flow
  • Keeps the editor integration covered without triggering mount-time PATCH/save regressions during tests

Component API

<DiscordMarkdownEditor
  value={template}
  onChange={setTemplate}
  variables={["username", "mention", "level"]}
  maxLength={2000}
  placeholder="Enter your message..."
/>

web/tests/components/ui/discord-markdown-editor.test.tsx — 29 tests

  • Parser: bold, italic, underline, strikethrough, code, code blocks, spoilers, headings, quotes, lists, variables, XSS prevention
  • Utilities: wrapSelection, insertAtCursor, wrapLine
  • Component: toolbar rendering, textarea, preview, character counter, variable dropdown, disabled state, onChange

Testing

✓ tests/components/ui/discord-markdown-editor.test.tsx (29 tests) 512ms
Test Files  1 passed (1)
Tests       29 passed (29)

Copilot AI review requested due to automatic review settings April 2, 2026 16:46
@github-project-automation github-project-automation bot moved this to Backlog in Volvox.Bot Apr 2, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 2, 2026

⚠️ No Changeset found

Latest commit: dd8a279

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@railway-app
Copy link
Copy Markdown

railway-app bot commented Apr 2, 2026

🚅 Deployed to the volvox-bot-pr-422 environment in volvox-bot

Service Status Web Updated (UTC)
web ✅ Success (View Logs) Apr 3, 2026 at 12:07 am
bot ❌ Build Failed (View Logs) Web Apr 3, 2026 at 12:07 am

@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-422 April 2, 2026 16:46 Destroyed
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f59be68ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

Warning

Rate limit exceeded

@BillChirico has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 2 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 0 minutes and 2 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d0122dde-cfed-4100-bfd9-0fb8540d000e

📥 Commits

Reviewing files that changed from the base of the PR and between f6220b7 and dd8a279.

📒 Files selected for processing (7)
  • tests/modules/actions/sendDm.test.js
  • tests/modules/ai.test.js
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
  • web/tests/components/dashboard/config-editor-autosave.test.tsx
📝 Walkthrough

Walkthrough

Adds a Discord-flavored markdown editor component with parser and editor helpers, test coverage, a new dashboard ConfigEditor wrapper and related ReputationSection changes, plus assorted test updates and minor formatting/refactor tweaks across backend modules and CI/config files.

Changes

Cohort / File(s) Summary
Discord Markdown Editor & Parser
web/src/components/ui/discord-markdown-editor.tsx, web/src/lib/discord-markdown.ts
New client-only DiscordMarkdownEditor component and exported DiscordMarkdownEditorProps; new parseDiscordMarkdown, wrapSelection, insertAtCursor, and wrapLine helpers. Features: toolbar actions, variable inserter, keyboard shortcuts, live preview (HTML from parser), and character counter.
Editor Tests
web/tests/components/ui/discord-markdown-editor.test.tsx
New Vitest/RTL tests for parser, fenced code blocks, headings/quotes/lists, XSS escaping, template-variable rendering, helper behaviors (selection/cursor), toolbar buttons, variable dropdown, keyboard shortcuts, preview, and character-counter states.
Dashboard Config UI
web/src/components/dashboard/config-editor.tsx, web/src/components/dashboard/config-sections/ReputationSection.tsx
Added ConfigEditor wrapper using ConfigProvider + useConfigContext; ReputationSection now accepts updateDraftConfig prop, reads xp?.levelThresholds with fallbacks, and removes the announce-channel control.
Config Editor Autosave Test Mock
web/tests/components/dashboard/config-editor-autosave.test.tsx
Added next/navigation mock exposing usePathname() and useRouter() (push) with per-test reset of mocked pathname and push calls.
AI history timestamping & tests
src/modules/ai.js, tests/modules/ai.test.js
DB hydration now selects created_at and populates message timestamp (epoch ms, fallback Date.now()); tests adjusted to assert presence/type of timestamp and use partial matches for {role, content}.
Misc. backend & tests formatting/guards
src/modules/*, tests/modules/actions/*, tests/api/utils/configValidation.test.js
Minor refactors: import reorderings, SQL call formatting, tightened optional-chaining guards, and small test formatting changes (no behavioral changes).
CI & tooling
.github/workflows/ci.yml, biome.json
Playwright cache key updated to root pnpm-lock.yaml; Biome schema version bumped to 2.4.10. No behavior changes.
Small JS edits
src/modules/actions/buildPayload.js, src/modules/actions/xpBonus.js, src/modules/levelUpActions.js, src/modules/triage-parse.js, src/modules/triage.js, src/modules/welcomeOnboarding.js
Formatting and minor optional-chaining/conditional refactors; no API or behavioral changes.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(dashboard): add WYSIWYG Discord markdown editor component' clearly and concisely summarizes the main change—adding a new WYSIWYG editor component to the dashboard.
Description check ✅ Passed The PR description is well-structured and comprehensively related to the changeset, detailing the parser utility, editor component, dashboard integration, API, and comprehensive test coverage.
Linked Issues check ✅ Passed All primary objectives from issue #370 are met: toolbar with 13 Discord markdown buttons [#370], variable inserter dropdown [#370], split-view editor with preview [#370], character counter with configurable limits [#370], reusable component API [#370], accessibility with keyboard shortcuts [#370], and comprehensive test suite [#370].
Out of Scope Changes check ✅ Passed All code changes are directly aligned with #370 objectives: new markdown parser/editor components, dashboard config-editor stabilization, import reordering, schema/formatting updates, and timestamp enrichment in conversation history for potential future feature support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #370

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/discord-markdown-editor
  • 🛠️ Publish Changes: Commit on current branch
  • 🛠️ Publish Changes: Create PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 2, 2026

Greptile Summary

This PR introduces a WYSIWYG Discord markdown editor component (DiscordMarkdownEditor) with a full parser, toolbar, split preview, character counter, and variable insertion, along with backend cleanups (optional-chaining simplifications, import ordering, and hydrateHistory now selects created_at).

  • initConversationHistory timestamp bug (src/modules/ai.js): The outer SELECT projects only channel_id, role, content; created_at is used in the subquery for ordering but is not returned to the application. Every message loaded at startup will receive Date.now() as its timestamp rather than the actual stored time — this should be fixed before merging.

Confidence Score: 4/5

Safe to merge after fixing the missing created_at column in the initConversationHistory outer SELECT.

One confirmed P1 defect: startup conversation history hydration always assigns Date.now() as the timestamp because created_at is not projected by the outer SQL query. The new editor component and all other backend cleanups look correct.

src/modules/ai.js — initConversationHistory outer SELECT is missing created_at.

Important Files Changed

Filename Overview
src/modules/ai.js Adds created_at to the hydrateHistory SELECT and maps it to a timestamp field. However, initConversationHistory's outer SELECT still only projects channel_id/role/content — row.created_at is always undefined there, so every startup-loaded message gets a timestamp of Date.now() instead of the actual stored time.
web/src/lib/discord-markdown.ts New Discord markdown parser. HTML escaping is applied before any inline parsing, keeping XSS prevention intact. Block and inline formatting, code-block splitting, template variable rendering, and utility helpers all look correct.
web/src/components/ui/discord-markdown-editor.tsx New WYSIWYG editor component. Uses DOMParser + recursive React.createElement for the preview, with correct SSR guard and RAF cleanup. Toolbar actions correctly pass selectionStart. Attribute forwarding in renderPreviewNode is safe given the constrained attribute set produced by the parser.
web/src/components/dashboard/config-sections/ReputationSection.tsx Reads levelThresholds from draftConfig.xp?.levelThresholds (was reputation?.levelThresholds). Write path correctly targets xp.levelThresholds. Backend migration status remains an open dependency.
web/tests/components/ui/discord-markdown-editor.test.tsx 29 tests covering parser cases, utility helpers, and component behaviour. Coverage looks thorough for the introduced functionality.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User types in textarea] --> B[onChange updates value]
    B --> C{Toolbar / Shortcut action?}
    C -- Yes --> D[applyAction\nwrapSelection / wrapLine / insertAtCursor]
    D --> B
    C -- No --> E[parseDiscordMarkdown]
    B --> E
    E --> F[splitCodeBlockSegments]
    F --> G{Segment type}
    G -- codeblock --> H[escapeHtml → pre/code HTML]
    G -- text --> I[parseInlineAndBlocks]
    I --> J[parseHeading / parseBlockQuote / parseList / parseParagraph]
    J --> K[parseInline → escapeHtml → formatInlineText]
    K --> L[Bold / Italic / Underline / Strike / Spoiler / Variables]
    H --> M[Combined HTML string]
    L --> M
    M --> N[renderPreviewContent\nDOMParser → React.createElement tree]
    N --> O[Preview pane renders]
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/modules/ai.js
Line: 374-378

Comment:
**`created_at` missing from outer SELECT — timestamp always falls back to `Date.now()`**

The outer query at line 353 selects only `channel_id, role, content`, so `row.created_at` is always `undefined` here. The ternary on line 377 will therefore always take the `Date.now()` branch, meaning every message loaded at startup will appear to have been created at the current moment rather than its actual stored time.

The inner subquery does project `created_at` (for the `ORDER BY` and `ROW_NUMBER`), but PostgreSQL does not pass it through to the outer result set unless it is also listed in the outer `SELECT`.

Fix: add `created_at` to the outer projection:

```suggestion
      `SELECT channel_id, role, content, created_at
       FROM (
         SELECT channel_id, role, content, created_at,
                ROW_NUMBER() OVER (PARTITION BY channel_id ORDER BY created_at DESC) AS rn
         FROM conversations
         WHERE created_at >= NOW() - INTERVAL '1 day' * $2
       ) sub
       WHERE rn <= $1
       ORDER BY channel_id, created_at ASC`,
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (8): Last reviewed commit: "fix: address final review feedback on ma..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a reusable Discord-flavored markdown editor for the web dashboard, including a preview renderer and editing utilities, intended for use in template/message editing flows (closes #370).

Changes:

  • Added a Discord markdown → HTML parser plus selection/cursor helper utilities.
  • Added a client-side editor component with formatting toolbar, variable insertion, live preview, and character counter.
  • Added a focused Vitest + RTL test suite covering parser utilities and the editor component.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
web/src/lib/discord-markdown.ts New parser + helpers to render Discord-style markdown and support editor actions (wrap/insert).
web/src/components/ui/discord-markdown-editor.tsx New UI component implementing toolbar actions, variable insertion UI, split editor/preview, and shortcuts.
web/tests/components/ui/discord-markdown-editor.test.tsx New unit/component tests for parser behavior, helpers, and editor interactions.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/ui/discord-markdown-editor.tsx`:
- Around line 34-42: The props interface DiscordMarkdownEditorProps should be
made immutable to prevent accidental mutation; replace the mutable interface
with a readonly type (e.g. convert to export type DiscordMarkdownEditorProps =
Readonly<{ ... }> or mark each property readonly) so all properties (value,
onChange, variables, maxLength, placeholder, className, disabled) are read-only,
and update any local references to use the new type name if you switch from
interface to type.
- Around line 210-215: The nested ternary used to compute charCountColor (based
on value.length and maxLength with isOverLimit) hurts readability; replace it
with a small descriptive helper (e.g., getCharCountColor) or an if/else block
that first checks isOverLimit, then checks the 90% threshold, and returns the
corresponding class string ('text-red-500', 'text-yellow-500',
'text-muted-foreground'), and then use that helper/variable in place of the
ternary wherever charCountColor is used.
- Around line 265-281: The variable dropdown in the DiscordMarkdownEditor
component (where showVariables is rendered and variables.map calls
insertVariable) needs click-outside-to-close and keyboard navigation plus proper
ARIA roles: attach a ref to the dropdown container and add a document-level
click listener to detect outside clicks and setShowVariables(false), add keydown
handlers on the dropdown to handle Escape (close), ArrowUp/ArrowDown (move a
highlighted index state) and Enter (call insertVariable for the highlighted
option), and mark the container with role="listbox" and each item with
role="option" and aria-selected tied to the highlighted index; update the
variables.map rendering to read and set the highlighted index on mouse/keyboard
focus so both mouse and keyboard selection work.

In `@web/src/lib/discord-markdown.ts`:
- Around line 50-130: The parseInlineAndBlocks function is too complex; extract
each block-type branch into small helper functions to reduce cognitive
complexity: create handlers like handleHeading(line), handleBlockQuote(line),
handleUnorderedList(line, inList), handleOrderedList(line, inList),
handleEmptyOrParagraph(line) and a closeListIfOpen(inList) utility; each handler
should accept the current line (and inList state when needed), push appropriate
HTML fragments into the shared result array (or return fragments and updated
inList) and call escapeHtml/parseInline internally, then replace the large
switch/if chain in parseInlineAndBlocks with simple calls to these helpers so
parseInlineAndBlocks only coordinates flow and manages the inList state.

In `@web/tests/components/ui/discord-markdown-editor.test.tsx`:
- Around line 122-129: The shared onChange mock in the test suite causes
cross-test pollution; update the DiscordMarkdownEditor tests to reset or
recreate the mock before each test by moving defaultProps creation into a
beforeEach (or call vi.clearAllMocks()/vi.resetAllMocks()) so onChange is fresh
for every test; reference the defaultProps object and the onChange mock used in
the describe('DiscordMarkdownEditor') block and ensure each test uses a newly
created vi.fn() or cleared mock.
- Around line 131-211: Add keyboard-shortcut unit tests for
DiscordMarkdownEditor to verify Ctrl+B, Ctrl+I and Ctrl+U apply formatting by
simulating user keyboard events; in each test render DiscordMarkdownEditor with
value "hello" and a vi.fn() onChange, use userEvent.setup(), focus the textarea
(getByPlaceholderText('Enter your message...')), select the text via the Ctrl+A
keyboard sequence, then send Ctrl+B / Ctrl+I / Ctrl+U and assert onChange was
called with the expected formatted string (e.g., '**hello**' for bold, '*hello*'
for italic, and the underline format your component uses, e.g., '__hello__' if
applicable), reusing the same patterns shown in the suggested tests and
referencing the onChange prop and textarea placeholder to locate elements.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d4b29a71-8d85-4966-a001-d70e1f398e6c

📥 Commits

Reviewing files that changed from the base of the PR and between f3675bb and 9f59be6.

📒 Files selected for processing (3)
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
  • web/tests/components/ui/discord-markdown-editor.test.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • web/tests/components/ui/discord-markdown-editor.test.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
web/tests/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

web/tests/**/*.test.{ts,tsx}: Write web dashboard tests using Vitest 4 with the jsdom environment and React Testing Library, matching the web/src/ structure
Maintain test coverage thresholds of 85% across all metrics (statements, branches, functions, lines) for web dashboard tests

Files:

  • web/tests/components/ui/discord-markdown-editor.test.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: ESM only - do not use CommonJS modules
Use src/logger.js; do not use console.*

Files:

  • web/tests/components/ui/discord-markdown-editor.test.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
**/*.{js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use parameterized SQL only; do not use string concatenation for SQL queries

Files:

  • web/src/lib/discord-markdown.ts
🧠 Learnings (4)
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/tests/**/*.test.{ts,tsx} : Write web dashboard tests using Vitest 4 with the `jsdom` environment and React Testing Library, matching the `web/src/` structure

Applied to files:

  • web/tests/components/ui/discord-markdown-editor.test.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/tests/**/*.test.{ts,tsx} : Maintain test coverage thresholds of 85% across all metrics (statements, branches, functions, lines) for web dashboard tests

Applied to files:

  • web/tests/components/ui/discord-markdown-editor.test.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to tests/**/*.test.js : Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Applied to files:

  • web/tests/components/ui/discord-markdown-editor.test.tsx
📚 Learning: 2026-03-26T00:04:14.693Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T00:04:14.693Z
Learning: Applies to src/**/*.{js,ts} : Use the safe Discord messaging helpers in src/utils/safeSend.js instead of raw reply/send/edit calls

Applied to files:

  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
🪛 ast-grep (0.42.0)
web/src/components/ui/discord-markdown-editor.tsx

[warning] 311-311: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 GitHub Check: SonarCloud Code Analysis
web/src/components/ui/discord-markdown-editor.tsx

[warning] 144-152: Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF46NIh8m09xZMJDN&open=AZ1PF46NIh8m09xZMJDN&pullRequest=422


[warning] 213-215: Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF46NIh8m09xZMJDO&open=AZ1PF46NIh8m09xZMJDO&pullRequest=422

web/src/lib/discord-markdown.ts

[warning] 59-59: Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDX&open=AZ1PF5BrIh8m09xZMJDX&pullRequest=422


[warning] 13-13: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDQ&open=AZ1PF5BrIh8m09xZMJDQ&pullRequest=422


[warning] 135-135: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDe&open=AZ1PF5BrIh8m09xZMJDe&pullRequest=422


[warning] 82-82: Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDa&open=AZ1PF5BrIh8m09xZMJDa&pullRequest=422


[warning] 16-16: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDT&open=AZ1PF5BrIh8m09xZMJDT&pullRequest=422


[warning] 144-144: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDh&open=AZ1PF5BrIh8m09xZMJDh&pullRequest=422


[warning] 14-14: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDR&open=AZ1PF5BrIh8m09xZMJDR&pullRequest=422


[warning] 15-15: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDS&open=AZ1PF5BrIh8m09xZMJDS&pullRequest=422


[warning] 55-123: Expected a for-of loop instead of a for loop with this simple iteration.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDW&open=AZ1PF5BrIh8m09xZMJDW&pullRequest=422


[warning] 141-141: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDg&open=AZ1PF5BrIh8m09xZMJDg&pullRequest=422


[failure] 50-50: Refactor this function to reduce its Cognitive Complexity from 46 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDV&open=AZ1PF5BrIh8m09xZMJDV&pullRequest=422


[warning] 97-97: Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDc&open=AZ1PF5BrIh8m09xZMJDc&pullRequest=422


[warning] 150-150: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDj&open=AZ1PF5BrIh8m09xZMJDj&pullRequest=422


[warning] 12-12: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDP&open=AZ1PF5BrIh8m09xZMJDP&pullRequest=422


[warning] 156-156: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDl&open=AZ1PF5BrIh8m09xZMJDl&pullRequest=422


[warning] 138-138: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDf&open=AZ1PF5BrIh8m09xZMJDf&pullRequest=422


[warning] 153-153: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDk&open=AZ1PF5BrIh8m09xZMJDk&pullRequest=422


[warning] 147-147: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDi&open=AZ1PF5BrIh8m09xZMJDi&pullRequest=422


[warning] 71-71: Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDZ&open=AZ1PF5BrIh8m09xZMJDZ&pullRequest=422

🔇 Additional comments (8)
web/src/lib/discord-markdown.ts (3)

9-17: HTML escaping implementation is correct.

The escapeHtml function properly escapes the five critical HTML entities. The chained .replace() approach works correctly for this use case.


133-162: Inline parsing order and correctness look good.

The order of regex replacements correctly handles precedence: inline code first (prevents inner parsing), then combined bold+italic (***), then separate bold/italic. Template variable regex \{\{(\w+)\}\} safely restricts to alphanumeric characters.


164-211: Helper functions are well-designed.

wrapSelection, insertAtCursor, and wrapLine correctly handle cursor/selection position calculations. The implementations are pure functions with clear return types.

web/src/components/ui/discord-markdown-editor.tsx (3)

59-128: Toolbar action definitions are well-structured.

The TOOLBAR_ACTIONS array cleanly separates concerns and makes the toolbar easily extensible. Each action correctly uses the helper functions.


156-208: Callbacks are properly memoized.

applyAction, insertVariable, and handleKeyDown are correctly wrapped in useCallback with appropriate dependencies. The requestAnimationFrame pattern for restoring cursor position after React re-render is appropriate.


310-313: No additional sanitization needed—HTML escaping is comprehensive.

The parseDiscordMarkdown function already escapes all HTML entities (&, <, >, ", ') before parsing markdown, preventing XSS attacks. This escaping is applied consistently across all content paths (code blocks, headings, quotes, lists, and regular paragraphs) and is verified by existing tests. Adding DOMPurify would be redundant and unnecessary overhead.

web/tests/components/ui/discord-markdown-editor.test.tsx (2)

16-86: Parser utility tests provide solid coverage.

Tests cover all inline formatting, code blocks with language attribute, headings, lists, block quotes, template variables, and XSS escaping. Good edge case coverage for security.


88-116: Helper function tests verify cursor/selection positioning.

Tests for wrapSelection, insertAtCursor, and wrapLine correctly validate both the text output and the cursor/selection position updates.

@github-project-automation github-project-automation bot moved this from Backlog to In Review in Volvox.Bot Apr 2, 2026
@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-422 April 2, 2026 17:11 Destroyed
@BillChirico
Copy link
Copy Markdown
Collaborator Author

@coderabbitai autofix stacked pr

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found.

coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 2, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a92090a7a1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copilot AI review requested due to automatic review settings April 2, 2026 17:24
@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-422 April 2, 2026 17:24 Destroyed
@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-422 April 2, 2026 17:27 Destroyed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/modules/ai.test.js`:
- Around line 87-91: The test currently asserts DB-origin messages
(historyRef[0..1]) without timestamps while requiring a timestamp only for the
in-memory message, which locks in an inconsistent getHistoryAsync contract;
update the test to assert a consistent message shape by expecting a numeric
timestamp for every history entry (e.g., change the expectations for
historyRef[0] and historyRef[1] to include timestamp checks like
historyRef[i].timestamp === expect.any(Number)) or alternatively relax
assertions to only assert shared fields and not bake differing shapes into
tests; locate the test variables/historyRef in tests/modules/ai.test.js and
update the expectations to either require timestamps for all messages or to use
toMatchObject for common fields only so getHistoryAsync/hydration mapping (the
code path that constructs historyRef) can remain consistent for consumers.

In `@web/src/components/dashboard/config-sections/ReputationSection.tsx`:
- Around line 120-121: The map and filter callbacks in the chain (the .map and
.filter calls that currently use explicit parameter types `s: string` and `n:
number`) have redundant type annotations; remove those annotations so the
callbacks read with inferred parameter types (e.g., use `.map(s =>
Number(s.trim()))` and `.filter(n => Number.isFinite(n) && n > 0)`), leaving the
logic unchanged but reducing verbosity.

In `@web/src/components/ui/discord-markdown-editor.tsx`:
- Around line 343-347: The current preview and status containers in
discord-markdown-editor.tsx use non-semantic <div> elements with role="region"
and role="status"; replace the preview <div className="min-h-[200px] ..."> that
has role="region" and aria-label="Preview" with a semantic <section> (keeping
className and aria-label) and replace the status <div role="status"> block with
an <output> element (preserving classes/ARIA as needed), ensuring semantics
improve accessibility while retaining styling and labels.
- Around line 205-210: The requestAnimationFrame callback in applyAction (and
similarly in insertVariable) can run after unmount and access a stale
textareaRef; change these to capture the raf id and cancel it on unmount (or use
an isMounted flag) so you don't call textarea.focus() or
textarea.setSelectionRange when the component is unmounted: inside the component
keep the raf id (or mounted boolean) and on cleanup call
cancelAnimationFrame(id) (or set mounted=false), and update applyAction and
insertVariable to check textareaRef.current and/or mounted before invoking
focus/setSelectionRange and to clear the scheduled raf when unmounting.
- Around line 74-77: In renderPreviewNode, the code forwards all DOM attributes
onto React elements via the props object populated from
node.getAttributeNames(); add a concise inline comment above that loop (and
reference parseDiscordMarkdown) documenting the security assumption that
forwarded attributes come only from parser-generated attributes (e.g.,
data-variable from {{var}}) where var is constrained by \w+ and therefore safe,
and note that if parseDiscordMarkdown's generation rules change this could allow
attribute injection—recommend maintaining this whitelist-like constraint or
switching to an explicit attribute whitelist/validation if future changes
broaden input.

In `@web/src/lib/discord-markdown.ts`:
- Around line 193-205: The series of global replacements on the formatted string
(the successive formatted = formatted.replace(...) calls in
web/src/lib/discord-markdown.ts) should use replaceAll to express the intent to
replace all occurrences; update each occurrence that currently uses
formatted.replace with a regexp having the global flag to
formatted.replaceAll(...) (keeping the same regexes and replacement strings),
e.g., for the bold/italic, spoiler, and variable replacements, change the calls
on the formatted variable to use replaceAll while preserving the exact patterns
and replacement HTML.

In `@web/tests/components/dashboard/config-editor-autosave.test.tsx`:
- Around line 20-24: Replace the inline Next mock that creates a new router spy
on each call by adopting the reusable pattern from config-context.test.tsx:
declare a top-level reusable mockPush (vi.fn()) and a mutable mockPathname
variable, then mock 'next/navigation' so useRouter returns an object with push:
mockPush and usePathname returns mockPathname; update tests to mutate
mockPathname or inspect mockPush as needed. Ensure you reference the existing
symbols usePathname and useRouter in the mock so behavior is stable across test
cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e5eb7ac6-de0f-4758-9109-e21b43de7f7d

📥 Commits

Reviewing files that changed from the base of the PR and between 9f59be6 and aeec684.

📒 Files selected for processing (7)
  • tests/modules/ai.test.js
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Agent
  • GitHub Check: Greptile Review
  • GitHub Check: Cursor Bugbot
  • GitHub Check: E2E Tests (1/2)
  • GitHub Check: E2E Tests (2/2)
  • GitHub Check: Test
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • tests/modules/ai.test.js
  • web/src/components/dashboard/config-editor.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
web/tests/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

web/tests/**/*.test.{ts,tsx}: Write web dashboard tests using Vitest 4 with the jsdom environment and React Testing Library, matching the web/src/ structure
Maintain test coverage thresholds of 85% across all metrics (statements, branches, functions, lines) for web dashboard tests

Files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: ESM only - do not use CommonJS modules
Use src/logger.js; do not use console.*

Files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • tests/modules/ai.test.js
  • web/src/components/dashboard/config-editor.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESM-only syntax: import/export, never require()/module.exports

Files:

  • tests/modules/ai.test.js
tests/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

tests/**/*.test.js: Write bot tests using Vitest 4 with the node environment, matching the src/ structure in the tests/ directory
Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Files:

  • tests/modules/ai.test.js
**/*.{js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use parameterized SQL only; do not use string concatenation for SQL queries

Files:

  • tests/modules/ai.test.js
  • web/src/lib/discord-markdown.ts
🧠 Learnings (12)
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/src/app/dashboard/**/*.tsx : For dashboard routes, add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts`: use exact equality for leaf routes (`pathname === '/dashboard/my-route'`) and subtree checks (`pathname.startsWith('/dashboard/my-route/')`); export `metadata` using `createPageMetadata(title)` for SSR entry points

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/tests/**/*.test.{ts,tsx} : Write web dashboard tests using Vitest 4 with the `jsdom` environment and React Testing Library, matching the `web/src/` structure

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
📚 Learning: 2026-03-26T00:04:14.693Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T00:04:14.693Z
Learning: Applies to {web/src/lib/page-titles.ts,web/src/**/+page.ts,web/src/**/+page.svelte} : New dashboard routes need title wiring in web/src/lib/page-titles.ts: use createPageMetadata() for SSR and keep DashboardTitleSync aligned for client navigation.

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to web/src/pages/dashboard/**/*.{ts,tsx} : Use shared title helpers from web/src/lib/page-titles.ts for setting browser titles in dashboard pages

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/tests/**/*.test.{ts,tsx} : Maintain test coverage thresholds of 85% across all metrics (statements, branches, functions, lines) for web dashboard tests

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-11T05:32:46.325Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T05:32:46.325Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Apply static metadata to server-rendered dashboard entry pages and use title template format for root app metadata

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-26T00:04:14.693Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T00:04:14.693Z
Learning: Applies to {config.json,src/api/utils/configAllowlist.js} : Config-backed features must be added to config.json and src/api/utils/configAllowlist.js. If a key is missing from SAFE_CONFIG_KEYS, the dashboard cannot save it.

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-10T23:29:51.063Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:29:51.063Z
Learning: Config editor save contract: maintain global save/discard, diff-modal confirmation, per-section PATCH batching, and partial-failure behavior

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-26T00:04:14.693Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T00:04:14.693Z
Learning: Applies to src/**/*.{js,ts} : Use the safe Discord messaging helpers in src/utils/safeSend.js instead of raw reply/send/edit calls

Applied to files:

  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to **/*.{js,ts,tsx} : Maintain a maximum line width of 100 characters

Applied to files:

  • web/src/components/ui/discord-markdown-editor.tsx
🪛 GitHub Check: SonarCloud Code Analysis
web/src/components/ui/discord-markdown-editor.tsx

[warning] 358-358: Use instead of the "status" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg1-lZoPOqjisHnA&open=AZ1PLg1-lZoPOqjisHnA&pullRequest=422


[warning] 343-347: Use

, or
instead of the "region" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg1-lZoPOqjisHm_&open=AZ1PLg1-lZoPOqjisHm_&pullRequest=422


[warning] 87-87: Prefer globalThis.window over window.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg1-lZoPOqjisHm-&open=AZ1PLg1-lZoPOqjisHm-&pullRequest=422

web/src/lib/discord-markdown.ts

[warning] 196-196: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg36lZoPOqjisHnH&open=AZ1PLg36lZoPOqjisHnH&pullRequest=422


[warning] 21-21: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDT&open=AZ1PF5BrIh8m09xZMJDT&pullRequest=422


[warning] 198-198: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg36lZoPOqjisHnJ&open=AZ1PLg36lZoPOqjisHnJ&pullRequest=422


[warning] 20-20: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDS&open=AZ1PF5BrIh8m09xZMJDS&pullRequest=422


[warning] 197-197: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg36lZoPOqjisHnI&open=AZ1PLg36lZoPOqjisHnI&pullRequest=422


[warning] 17-17: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDP&open=AZ1PF5BrIh8m09xZMJDP&pullRequest=422


[warning] 202-202: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg36lZoPOqjisHnK&open=AZ1PLg36lZoPOqjisHnK&pullRequest=422


[warning] 19-19: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDR&open=AZ1PF5BrIh8m09xZMJDR&pullRequest=422


[warning] 18-18: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDQ&open=AZ1PF5BrIh8m09xZMJDQ&pullRequest=422


[warning] 195-195: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg36lZoPOqjisHnG&open=AZ1PLg36lZoPOqjisHnG&pullRequest=422


[warning] 193-193: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg36lZoPOqjisHnE&open=AZ1PLg36lZoPOqjisHnE&pullRequest=422


[warning] 194-194: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg36lZoPOqjisHnF&open=AZ1PLg36lZoPOqjisHnF&pullRequest=422

🔇 Additional comments (11)
tests/modules/ai.test.js (1)

52-53: Good assertion hardening for message metadata.

Line 52 and Line 53 are a solid change: you keep semantic checks (role, content) and now verify
timestamp is numeric.

web/src/components/dashboard/config-editor.tsx (5)

1-8: Imports and directive look correct.

The 'use client' directive is appropriate for this interactive component. All imports use single quotes and are properly organized.


9-15: Clean wrapper pattern with ConfigProvider.

The separation of ConfigEditor as a wrapper and ConfigEditorContent as the consumer follows a good composition pattern, ensuring the context is available to the content component.


32-53: Early return logic is correct and handles all edge cases.

The guard clauses properly handle:

  1. No guild selected → prompt message
  2. Loading state → loading indicator
  3. Error state → error message with retry button
  4. No draft config → null render

The retry button correctly calls fetchConfig(guildId).


55-91: Main UI implementation is well-structured.

The layout correctly:

  • Uses semantic heading hierarchy (h1 for title, h2 for sections)
  • Properly disables controls during save operations
  • Uses optional chaining with nullish coalescing for draftConfig.ai?.systemPrompt ?? ''
  • Correctly preserves other ai properties when updating systemPrompt

The spread pattern in updateDraftConfig is correct per context snippet 4, which confirms GuildConfig is DeepPartial<BotConfig>.


17-30: No issues found. All three values (openDiffModal, discardChanges, and fetchConfig) are properly defined in the ConfigContextValue interface at lines 48, 49, and 58 respectively in web/src/components/dashboard/config-context.tsx. The destructuring is correct and complete.

web/src/components/dashboard/config-sections/ReputationSection.tsx (1)

34-34: No action needed. The path draftConfig.xp?.levelThresholds is correct—levelThresholds exists only in XpConfig, not in ReputationConfig. The implementation properly separates concerns: reputation settings (xpPerMessage, xpCooldownSeconds) are handled through onFieldChange, while XP level thresholds are managed through updateDraftConfig directly to the xp section.

web/tests/components/ui/discord-markdown-editor.test.tsx (1)

1-218: LGTM! Well-structured test suite with good coverage.

The test file follows best practices:

  • Factory pattern (createDefaultProps) with beforeEach properly isolates mocks between tests
  • Parameterized tests (it.each) reduce duplication for inline formatting and keyboard shortcuts
  • Good coverage of parser, helpers, and component behavior including edge cases (empty variables, disabled state, over-limit styling)
web/src/lib/discord-markdown.ts (3)

14-22: HTML escaping implementation is correct and covers XSS prevention.

The escapeHtml function properly handles the five critical HTML entities. The escaping is consistently applied:

  • Code block content at Line 73
  • Block-level content before inline parsing (headings, quotes, lists, paragraphs)
  • Language attribute in code blocks at Line 72

243-289: Editor helper functions are well-implemented.

The wrapSelection, insertAtCursor, and wrapLine functions are pure, correctly handle edge cases (empty selection, cursor at start), and return consistent result shapes that the editor component consumes.


24-43: Code block parsing handles edge cases correctly.

The parseCodeBlock function properly:

  • Returns content without language when the block starts with a newline
  • Returns raw content when there's no newline (single-line code block like ```hello```)
  • Validates language identifiers with a restrictive regex, preventing attribute injection

…nent

- Add discord-markdown.ts utility with parser, wrapSelection, insertAtCursor, wrapLine
- Add DiscordMarkdownEditor component with:
  - Toolbar (Bold, Italic, Underline, Strikethrough, Code, Code Block, Spoiler, Quote, H1-H3, Lists)
  - Variable inserter dropdown for template variables
  - Split view: raw editor + live Discord-style preview
  - Character counter with configurable limit
  - Keyboard shortcuts (Ctrl+B, Ctrl+I, Ctrl+U)
  - Follows existing Radix UI + Tailwind + Lucide patterns
- Use stable index-based keys for badge rows to prevent remount on edit
- Add timestamp to initConversationHistory startup hydration entries
- Replace placeholder text in Welcome Messages section with proper UI copy
- Fix block quote parser to require space after > (matching Discord behavior)
- Render plain newlines as line breaks instead of paragraph blocks
- Update ReputationSection JSDoc to remove stale 'announcements' reference
- Add fallback to reputation.levelThresholds for backward compatibility
Copilot AI review requested due to automatic review settings April 2, 2026 23:37
@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-422 April 2, 2026 23:37 Destroyed
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/src/components/dashboard/analytics-dashboard-sections.tsx (1)

118-128: 🧹 Nitpick | 🔵 Trivial

Remove the unused compareMode parameter.

The compareMode parameter is marked as unused (_compareMode) but remains in the function signature. The component relies on hasComparison instead (lines 131-132, 148-155), making compareMode redundant. Remove the parameter from both the component signature and the parent call site in analytics-dashboard.tsx:379-385 to clean up the API surface.

♻️ Proposed fix
 export function KpiCardItem({
   card,
-  compareMode: _compareMode,
   hasAnalytics,
   hasComparison,
 }: {
   card: KpiCard;
-  compareMode: boolean;
   hasAnalytics: boolean;
   hasComparison: boolean;
 }) {

Additionally, update the parent component at web/src/components/dashboard/analytics-dashboard.tsx:

 <KpiCardItem
   key={card.label}
   card={card}
-  compareMode={compareMode}
   hasAnalytics={analytics !== null}
   hasComparison={hasComparison}
 />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/analytics-dashboard-sections.tsx` around lines
118 - 128, Remove the unused compareMode prop from the KpiCardItem component
signature and its type annotation (currently written as compareMode:
_compareMode) so the component only accepts card, hasAnalytics, and
hasComparison; then update the parent call site where KpiCardItem is invoked in
analytics-dashboard.tsx to stop passing compareMode (remove the prop from the
JSX invocation). Ensure you only delete the compareMode parameter and its
underscore variant everywhere (signature, props destructuring/type) and remove
the prop from the parent component call so there are no lingering references.
src/modules/ai.js (1)

354-363: ⚠️ Potential issue | 🟠 Major

initConversationHistory computes timestamps from a column it does not select.

row.created_at is read at Line 377, but the outer query omits created_at, so hydrated timestamps always
fallback to Date.now(). That loses true chronology and can skew recency-dependent behavior.

💡 Proposed fix
-    const { rows } = await pool.query(
-      `SELECT channel_id, role, content
+    const { rows } = await pool.query(
+      `SELECT channel_id, role, content, created_at
        FROM (
          SELECT channel_id, role, content, created_at,
                 ROW_NUMBER() OVER (PARTITION BY channel_id ORDER BY created_at DESC) AS rn
          FROM conversations
          WHERE created_at >= NOW() - INTERVAL '1 day' * $2
        ) sub
        WHERE rn <= $1
        ORDER BY channel_id, created_at ASC`,
       [limit, ttl],
     );

Also applies to: 377-377

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/ai.js` around lines 354 - 363, The query in
initConversationHistory omits created_at from the outer SELECT but later code
reads row.created_at (so timestamps fall back to Date.now()); fix by including
created_at in the outer SELECT (e.g., change the outer SELECT to select
channel_id, role, content, created_at) so that initConversationHistory can
hydrate real timestamps and maintain correct chronology when iterating rows and
ordering by created_at.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/modules/actions/sendDm.test.js`:
- Around line 14-20: Add a new unit test that imports and exercises
sweepDmLimits directly: create stale entries in the same DM limiter store used
by the module (e.g., by calling recordDmSend or directly populating the limiter
structure), simulate entries older than the eviction threshold (use Jest fake
timers or manually set timestamps), call sweepDmLimits(), and assert the stale
entries are removed while recent entries remain; reference sweepDmLimits and
recordDmSend/checkDmRateLimit to locate and manipulate the limiter before and
after the sweep.

In `@tests/modules/ai.test.js`:
- Around line 87-102: Test asserts accept any numeric timestamp; mock the stored
created_at values and assert exact epoch numbers to ensure timestamps come from
DB mapping rather than a fallback Date.now(). In the ai.test.js tests that
examine historyRef (indexes 0..2), replace the expect.any(Number) timestamp
assertions with exact epoch comparisons by stubbing/mocking the DB rows'
created_at values (or injecting fixed Date.now) so each
expect(historyRef[i].timestamp).toBe(<expectedEpoch>) verifies the precise value
for the record created_at mapping.

In `@web/src/components/dashboard/config-editor.tsx`:
- Around line 84-89: The "Welcome Messages" section currently only renders
static text; either mark it as intentional scaffolding by adding a clear TODO
comment or implement the editor now using the DiscordMarkdownEditor component:
add state for welcomeMessage, render <DiscordMarkdownEditor> in the same section
with props wired to value and onChange (and any label/placeholder), and persist
changes via the existing save/update handler used elsewhere (reuse the config
save function used by other sections); reference the section heading and the
DiscordMarkdownEditor component so reviewers can find and verify the change.

In `@web/src/components/dashboard/config-sections/ReputationSection.tsx`:
- Around line 13-14: Rework ReputationSection to use updateDraftConfig for all
field updates instead of mixing patterns: replace calls that use onFieldChange
for xpPerMessage and xpCooldownSeconds with calls to updateDraftConfig that
accept an updater (prev => ({ ...prev, xpPerMessage: newValue })) and similarly
for xpCooldownSeconds so the component uses updateDraftConfig consistently
alongside the existing levelThresholds updates; ensure you preserve value
typing/casting inside the updater and remove or stop using onFieldChange for
those fields.

In `@web/tests/components/dashboard/config-editor-autosave.test.tsx`:
- Around line 10-11: Remove the unused import "act" from the import list in the
test file so the module import matches actual usage; specifically edit the
import statement that currently reads "import { act, render, screen, waitFor }
from '@testing-library/react';" to drop "act" (leave render, screen, waitFor) in
web/tests/components/dashboard/config-editor-autosave.test.tsx to eliminate the
unused-symbol warning.

---

Outside diff comments:
In `@src/modules/ai.js`:
- Around line 354-363: The query in initConversationHistory omits created_at
from the outer SELECT but later code reads row.created_at (so timestamps fall
back to Date.now()); fix by including created_at in the outer SELECT (e.g.,
change the outer SELECT to select channel_id, role, content, created_at) so that
initConversationHistory can hydrate real timestamps and maintain correct
chronology when iterating rows and ordering by created_at.

In `@web/src/components/dashboard/analytics-dashboard-sections.tsx`:
- Around line 118-128: Remove the unused compareMode prop from the KpiCardItem
component signature and its type annotation (currently written as compareMode:
_compareMode) so the component only accepts card, hasAnalytics, and
hasComparison; then update the parent call site where KpiCardItem is invoked in
analytics-dashboard.tsx to stop passing compareMode (remove the prop from the
JSX invocation). Ensure you only delete the compareMode parameter and its
underscore variant everywhere (signature, props destructuring/type) and remove
the prop from the parent component call so there are no lingering references.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f43331b0-dbc2-4205-a08a-c4b265bc18a0

📥 Commits

Reviewing files that changed from the base of the PR and between aeec684 and f6220b7.

📒 Files selected for processing (25)
  • .github/workflows/ci.yml
  • biome.json
  • src/modules/actions/buildPayload.js
  • src/modules/actions/xpBonus.js
  • src/modules/ai.js
  • src/modules/levelUpActions.js
  • src/modules/triage-parse.js
  • src/modules/triage.js
  • src/modules/welcomeOnboarding.js
  • tests/api/utils/configValidation.test.js
  • tests/modules/actions/addReaction.test.js
  • tests/modules/actions/announce.test.js
  • tests/modules/actions/nickPrefix.test.js
  • tests/modules/actions/sendDm.test.js
  • tests/modules/actions/webhook.test.js
  • tests/modules/actions/xpBonus.test.js
  • tests/modules/ai.test.js
  • web/src/components/dashboard/analytics-dashboard-sections.tsx
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
💤 Files with no reviewable changes (1)
  • web/src/components/dashboard/analytics-dashboard.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Agent
  • GitHub Check: Greptile Review
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Test
  • GitHub Check: E2E Tests (2/2)
  • GitHub Check: E2E Tests (1/2)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESM-only syntax: import/export, never require()/module.exports

Files:

  • src/modules/triage-parse.js
  • tests/modules/actions/addReaction.test.js
  • tests/modules/actions/xpBonus.test.js
  • src/modules/actions/xpBonus.js
  • src/modules/actions/buildPayload.js
  • src/modules/levelUpActions.js
  • tests/api/utils/configValidation.test.js
  • src/modules/welcomeOnboarding.js
  • tests/modules/actions/announce.test.js
  • tests/modules/actions/nickPrefix.test.js
  • tests/modules/actions/sendDm.test.js
  • tests/modules/actions/webhook.test.js
  • src/modules/triage.js
  • src/modules/ai.js
  • tests/modules/ai.test.js
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • src/modules/triage-parse.js
  • tests/modules/actions/addReaction.test.js
  • tests/modules/actions/xpBonus.test.js
  • src/modules/actions/xpBonus.js
  • src/modules/actions/buildPayload.js
  • src/modules/levelUpActions.js
  • tests/api/utils/configValidation.test.js
  • src/modules/welcomeOnboarding.js
  • tests/modules/actions/announce.test.js
  • tests/modules/actions/nickPrefix.test.js
  • tests/modules/actions/sendDm.test.js
  • tests/modules/actions/webhook.test.js
  • src/modules/triage.js
  • src/modules/ai.js
  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/analytics-dashboard-sections.tsx
  • tests/modules/ai.test.js
  • web/src/components/dashboard/config-editor.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
src/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.js: Never use console.* methods; use the Winston logger instead via import logger from '../logger.js' (adjust path as needed), then call logger.info(), logger.warn(), logger.error(), or logger.debug()
Always use safeReply(), safeSend(), or safeEditReply() instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully

Files:

  • src/modules/triage-parse.js
  • src/modules/actions/xpBonus.js
  • src/modules/actions/buildPayload.js
  • src/modules/levelUpActions.js
  • src/modules/welcomeOnboarding.js
  • src/modules/triage.js
  • src/modules/ai.js
src/modules/**/*.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create feature modules in src/modules/ and add corresponding config sections to config.json

Files:

  • src/modules/triage-parse.js
  • src/modules/actions/xpBonus.js
  • src/modules/actions/buildPayload.js
  • src/modules/levelUpActions.js
  • src/modules/welcomeOnboarding.js
  • src/modules/triage.js
  • src/modules/ai.js
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: ESM only - do not use CommonJS modules
Use src/logger.js; do not use console.*

Files:

  • src/modules/triage-parse.js
  • tests/modules/actions/addReaction.test.js
  • tests/modules/actions/xpBonus.test.js
  • src/modules/actions/xpBonus.js
  • src/modules/actions/buildPayload.js
  • src/modules/levelUpActions.js
  • tests/api/utils/configValidation.test.js
  • src/modules/welcomeOnboarding.js
  • tests/modules/actions/announce.test.js
  • tests/modules/actions/nickPrefix.test.js
  • tests/modules/actions/sendDm.test.js
  • tests/modules/actions/webhook.test.js
  • src/modules/triage.js
  • src/modules/ai.js
  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/analytics-dashboard-sections.tsx
  • tests/modules/ai.test.js
  • web/src/components/dashboard/config-editor.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
src/**/*.{js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{js,ts}: Use the safe Discord messaging helpers in src/utils/safeSend.js instead of raw reply/send/edit calls
Community features should be gated behind config..enabled. Moderation commands are the exception.

Files:

  • src/modules/triage-parse.js
  • src/modules/actions/xpBonus.js
  • src/modules/actions/buildPayload.js
  • src/modules/levelUpActions.js
  • src/modules/welcomeOnboarding.js
  • src/modules/triage.js
  • src/modules/ai.js
**/*.{js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use parameterized SQL only; do not use string concatenation for SQL queries

Files:

  • src/modules/triage-parse.js
  • tests/modules/actions/addReaction.test.js
  • tests/modules/actions/xpBonus.test.js
  • src/modules/actions/xpBonus.js
  • src/modules/actions/buildPayload.js
  • src/modules/levelUpActions.js
  • tests/api/utils/configValidation.test.js
  • src/modules/welcomeOnboarding.js
  • tests/modules/actions/announce.test.js
  • tests/modules/actions/nickPrefix.test.js
  • tests/modules/actions/sendDm.test.js
  • tests/modules/actions/webhook.test.js
  • src/modules/triage.js
  • src/modules/ai.js
  • tests/modules/ai.test.js
  • web/src/lib/discord-markdown.ts
tests/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

tests/**/*.test.js: Write bot tests using Vitest 4 with the node environment, matching the src/ structure in the tests/ directory
Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Files:

  • tests/modules/actions/addReaction.test.js
  • tests/modules/actions/xpBonus.test.js
  • tests/api/utils/configValidation.test.js
  • tests/modules/actions/announce.test.js
  • tests/modules/actions/nickPrefix.test.js
  • tests/modules/actions/sendDm.test.js
  • tests/modules/actions/webhook.test.js
  • tests/modules/ai.test.js
**/*.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use double quotes in JSON files (standard JSON format); this exception applies only to JSON files, not JavaScript/TypeScript

Files:

  • biome.json
web/tests/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

web/tests/**/*.test.{ts,tsx}: Write web dashboard tests using Vitest 4 with the jsdom environment and React Testing Library, matching the web/src/ structure
Maintain test coverage thresholds of 85% across all metrics (statements, branches, functions, lines) for web dashboard tests

Files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/dashboard/analytics-dashboard-sections.tsx
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
🧠 Learnings (13)
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to **/*.js : Use ESM-only syntax: `import`/`export`, never `require()`/`module.exports`

Applied to files:

  • biome.json
📚 Learning: 2026-03-26T00:04:14.693Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T00:04:14.693Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : ESM only - do not use CommonJS modules

Applied to files:

  • biome.json
📚 Learning: 2026-03-26T00:04:14.693Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T00:04:14.693Z
Learning: Applies to src/**/*.{js,ts} : Use the safe Discord messaging helpers in src/utils/safeSend.js instead of raw reply/send/edit calls

Applied to files:

  • src/modules/levelUpActions.js
  • tests/modules/actions/announce.test.js
  • web/src/components/ui/discord-markdown-editor.tsx
  • web/src/lib/discord-markdown.ts
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to tests/**/*.test.js : Maintain test coverage thresholds: statements 85%, branches 82%, functions 85%, lines 85%; never lower thresholds—add tests to cover new code instead

Applied to files:

  • tests/api/utils/configValidation.test.js
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to src/**/*.js : Always use `safeReply()`, `safeSend()`, or `safeEditReply()` instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully

Applied to files:

  • tests/modules/actions/announce.test.js
  • web/src/lib/discord-markdown.ts
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/tests/**/*.test.{ts,tsx} : Write web dashboard tests using Vitest 4 with the `jsdom` environment and React Testing Library, matching the `web/src/` structure

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/tests/components/ui/discord-markdown-editor.test.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/analytics-dashboard-sections.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to web/src/app/dashboard/**/*.tsx : For dashboard routes, add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts`: use exact equality for leaf routes (`pathname === '/dashboard/my-route'`) and subtree checks (`pathname.startsWith('/dashboard/my-route/')`); export `metadata` using `createPageMetadata(title)` for SSR entry points

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/analytics-dashboard-sections.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-26T00:04:14.693Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-26T00:04:14.693Z
Learning: Applies to {web/src/lib/page-titles.ts,web/src/**/+page.ts,web/src/**/+page.svelte} : New dashboard routes need title wiring in web/src/lib/page-titles.ts: use createPageMetadata() for SSR and keep DashboardTitleSync aligned for client navigation.

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-11T06:42:38.728Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-11T06:42:38.728Z
Learning: Applies to web/src/pages/dashboard/**/*.{ts,tsx} : Use shared title helpers from web/src/lib/page-titles.ts for setting browser titles in dashboard pages

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/ReputationSection.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to tests/**/*.test.js : Write bot tests using Vitest 4 with the `node` environment, matching the `src/` structure in the `tests/` directory

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
📚 Learning: 2026-03-12T02:03:36.493Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.493Z
Learning: Applies to **/*.{js,ts,tsx} : Maintain a maximum line width of 100 characters

Applied to files:

  • web/src/components/ui/discord-markdown-editor.tsx
🪛 GitHub Check: SonarCloud Code Analysis
web/tests/components/dashboard/config-editor-autosave.test.tsx

[warning] 10-10: Remove this unused import of 'act'.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PYv2TqMfC8Ejjani0&open=AZ1PYv2TqMfC8Ejjani0&pullRequest=422

web/src/components/ui/discord-markdown-editor.tsx

[warning] 89-89: Prefer globalThis.window over window.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PLg1-lZoPOqjisHm-&open=AZ1PLg1-lZoPOqjisHm-&pullRequest=422

web/src/lib/discord-markdown.ts

[warning] 17-17: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDP&open=AZ1PF5BrIh8m09xZMJDP&pullRequest=422


[warning] 20-20: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDS&open=AZ1PF5BrIh8m09xZMJDS&pullRequest=422


[warning] 21-21: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDT&open=AZ1PF5BrIh8m09xZMJDT&pullRequest=422


[warning] 231-231: Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PcIXrnHkZsPFPHY0-&open=AZ1PcIXrnHkZsPFPHY0-&pullRequest=422


[warning] 19-19: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDR&open=AZ1PF5BrIh8m09xZMJDR&pullRequest=422


[warning] 18-18: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PF5BrIh8m09xZMJDQ&open=AZ1PF5BrIh8m09xZMJDQ&pullRequest=422


[warning] 235-235: Prefer String#codePointAt() over String#charCodeAt().

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZ1PcIXrnHkZsPFPHY0_&open=AZ1PcIXrnHkZsPFPHY0_&pullRequest=422

🔇 Additional comments (37)
src/modules/levelUpActions.js (1)

16-16: Import update looks correct and non-functional.

handleSendDm is imported with valid ESM syntax and consistent style; no issues found.

.github/workflows/ci.yml (1)

140-140: No change needed—root lockfile cache key is appropriate

This repository uses a pnpm monorepo with a single root pnpm-lock.yaml that manages all workspace dependencies. The web/pnpm-lock.yaml file does not exist, so the current cache key using only the root lockfile is correctly scoped. Playwright dependencies declared in web/package.json are resolved through the root lockfile and will trigger cache invalidation when updated.

tests/api/utils/configValidation.test.js (2)

414-416: LGTM! Formatting complies with line-width guideline.

The multi-line formatting is necessary to keep the line under 100 characters and follows the existing pattern for similar assertions in this file.


425-427: LGTM! Formatting complies with line-width guideline.

The multi-line formatting is necessary to keep the line under 100 characters and is consistent with other multi-line assertions in the file.

src/modules/actions/buildPayload.js (1)

32-33: LGTM! Formatting improvement for line-width compliance.

The multi-line formatting brings this conditional into compliance with the 100-character line-width guideline while maintaining consistency with the other multi-line conditionals in this function (lines 29-30, 34-35). No logic changes, just improved adherence to coding standards.

src/modules/actions/xpBonus.js (1)

68-72: Looks good: parameterized query shape is preserved.

The updated pool.query(...) formatting keeps SQL safety and argument order intact.

tests/modules/actions/xpBonus.test.js (1)

15-15: Import reordering is clean and non-functional.

No concerns with this change.

tests/modules/actions/addReaction.test.js (1)

10-10: Non-functional import reorder is fine.

tests/modules/actions/announce.test.js (1)

18-18: Import order change is safe.

tests/modules/actions/nickPrefix.test.js (1)

10-10: Refactor is purely stylistic and safe.

Also applies to: 12-12

tests/modules/actions/webhook.test.js (1)

10-10: Formatting-only updates look good.

These changes preserve test intent and behavior.

Also applies to: 92-92, 100-100, 115-115, 128-128, 141-141

web/tests/components/dashboard/config-editor-autosave.test.tsx (1)

20-27: Navigation mock pattern is now properly structured.

The next/navigation mock correctly uses module-level mockPush and mockPathname variables with proper reset in beforeEach. This aligns with the pattern in config-context.test.tsx and ensures stable, reusable mocks across tests.

Also applies to: 102-107

web/src/components/dashboard/config-sections/ReputationSection.tsx (2)

36-39: Fallback chain reads from two namespaces but writes to only one.

The levelThresholds reads from draftConfig.xp?.levelThresholds first, then falls back to draftConfig.reputation?.levelThresholds, but the onBlur handler at lines 130-133 only writes to xp.levelThresholds. This is a reasonable migration pattern, but ensure the backend and other consumers expect xp.levelThresholds as the canonical location.


130-133: Immutable update pattern is correct.

The updateDraftConfig callback properly preserves the existing config structure by spreading prev and prev.xp before setting the new levelThresholds value.

web/src/components/dashboard/config-editor.tsx (4)

9-15: Clean wrapper pattern for context provision.

The ConfigEditor wrapper correctly provides ConfigProvider to ConfigEditorContent, ensuring context is available throughout the component tree.


32-53: Conditional rendering handles all states correctly.

The component properly handles missing guild, loading, error, and missing config states in a logical order, with an actionable retry button for errors.


62-68: Button disabled states are correctly configured.

The discard button is disabled during save or when no changes exist. The save button additionally respects validation errors, ensuring users cannot save invalid configurations.


71-82: SystemPromptEditor integration uses proper immutable update pattern.

The onChange handler correctly preserves the existing config structure while updating only the systemPrompt field within the ai section.

src/modules/welcomeOnboarding.js (1)

234-234: Null-safe role guard is a good cleanup.

This keeps behavior intact while making the eligibility check simpler and safer.

src/modules/triage.js (2)

328-330: Responder empty-output guard is correctly null-safe.

The optional-chain check is concise and preserves the intended early return behavior.


541-549: sendResponses reformat is clean and non-functional.

No behavior change here; readability is improved.

src/modules/triage-parse.js (1)

77-77: Classifier parse validation update looks good.

This is a safe simplification with equivalent semantics.

tests/modules/ai.test.js (1)

122-122: SQL expectation update is aligned with hydration changes.

Good assertion update to include created_at in the cache-miss hydration query expectation.

biome.json (1)

2-2: LGTM!

Schema version bump from 2.4.8 to 2.4.10 is a minor update with no changes to linting or formatting rules.

web/src/lib/discord-markdown.ts (4)

1-92: LGTM! Well-structured markdown parser with proper XSS prevention.

The refactored code correctly:

  • Escapes HTML entities before any markdown processing
  • Separates code block parsing from inline/block parsing
  • Constrains template variables to \w+ pattern for security
  • Handles fenced code blocks with optional language metadata

94-223: LGTM!

The block-level parsing helpers are well-factored with clean state management for list transitions.


225-378: LGTM!

The inline parsing correctly handles:

  • Format priority (bold-italic *** before bold ** before italic *)
  • Inline code preservation without nested formatting
  • Variable name validation with word-character constraint

380-427: LGTM!

The editor helper functions correctly compute updated cursor/selection positions for seamless user experience after text transformations.

web/tests/components/ui/discord-markdown-editor.test.tsx (4)

1-19: LGTM!

Test setup correctly uses createDefaultProps() factory with beforeEach reset to prevent mock pollution across tests.


20-84: LGTM!

Comprehensive parser tests covering inline formatting, code blocks, block constructs, and XSS prevention.


86-113: LGTM!

Helper function tests verify both text transformations and cursor/selection position calculations.


115-225: LGTM!

Component tests comprehensively cover:

  • Toolbar controls and accessibility labels
  • Character counter with limit styling
  • Variable dropdown interaction
  • Keyboard shortcuts (Ctrl+B/I/U)
  • Preview rendering

This aligns well with the acceptance criteria from issue #370.

web/src/components/ui/discord-markdown-editor.tsx (5)

1-48: LGTM!

Clean imports and properly typed props using Readonly<{}> pattern.


65-97: LGTM!

Preview rendering correctly:

  • Guards against SSR with typeof window check
  • Documents security assumption for attribute forwarding
  • Recursively converts DOM nodes to React elements

99-178: LGTM!

Toolbar actions comprehensively cover all Discord markdown formats specified in the PR objectives with appropriate keyboard shortcuts.


184-287: LGTM!

Component implementation correctly handles:

  • requestAnimationFrame cleanup on unmount via rafIdsRef
  • Cursor restoration after toolbar actions
  • Keyboard shortcut processing
  • Character count color logic extracted to getCharCountColor()

288-387: LGTM!

JSX rendering implements:

  • Accessible toolbar with tooltips and ARIA labels
  • Responsive split-view layout
  • Semantic HTML (<section>, <output>)
  • Proper disabled state propagation
  • Discord-styled preview with prose typography

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

@BillChirico
Copy link
Copy Markdown
Collaborator Author

@coderabbitai autofix stacked pr

@BillChirico
Copy link
Copy Markdown
Collaborator Author

@claude review

@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-422 April 2, 2026 23:52 Destroyed
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

No merge conflicts detected when merging into main.

Your branch is good to go!

- Fix hydration mismatch in preview rendering (isMounted gate)
- Remove * as unordered list marker (Discord only supports -)
- Remove unused act import from autosave tests
- Add TODO for Welcome Messages section placeholder
- Unify ReputationSection to use updateDraftConfig consistently
- Remove stale reputation.levelThresholds fallback
- Add sweepDmLimits test for stale-entry eviction
- Strengthen ai.test.js hydration timestamp assertions with exact values
Copilot AI review requested due to automatic review settings April 3, 2026 00:07
@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-422 April 3, 2026 00:07 Destroyed
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.

Comment on lines 374 to 378
hydratedByChannel.get(channelId).push({
role: row.role,
content: row.content,
timestamp: row.created_at ? new Date(row.created_at).getTime() : Date.now(),
});
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initConversationHistory now assigns timestamp from row.created_at, but the hydration query’s outer SELECT only returns channel_id, role, content (no created_at). As a result row.created_at will always be undefined here and timestamps will fall back to Date.now(), losing DB ordering/time fidelity. Include created_at in the outer SELECT (and ensure it’s available on row).

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +14
export function ConfigEditor() {
return (
<ConfigProvider>
<ConfigEditorContent />
</ConfigProvider>
);
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConfigEditor is introduced as a new top-level component, but it isn’t referenced anywhere in web/src or web/src/app (no imports/usages found). If this is intended to be the main settings entry point, it should be wired into the appropriate route/component; otherwise consider removing it to avoid dead code and future drift.

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +90
{/* TODO: Integrate DiscordMarkdownEditor for welcome message template editing */}
<section className="space-y-3">
<h2 className="text-lg font-semibold">Welcome Messages</h2>
<p className="text-sm text-muted-foreground">
Configure welcome message templates for new server members.
</p>
</section>
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description indicates the DiscordMarkdownEditor is integrated into the dashboard template editing/settings flow (and closes #370), but this file still has a TODO for integrating it into welcome message template editing and no integration is present elsewhere. Either update the PR description/scope or complete the integration so the issue closure matches what ships.

Copilot uses AI. Check for mistakes.
@BillChirico BillChirico merged commit b06c511 into main Apr 3, 2026
33 of 34 checks passed
@github-project-automation github-project-automation bot moved this from In Review to Done in Volvox.Bot Apr 3, 2026
@BillChirico BillChirico deleted the feat/discord-markdown-editor branch April 3, 2026 00:19
MohsinCoding added a commit that referenced this pull request Apr 3, 2026
* feat(dashboard): add Discord markdown parser and WYSIWYG editor component

- Add discord-markdown.ts utility with parser, wrapSelection, insertAtCursor, wrapLine
- Add DiscordMarkdownEditor component with:
  - Toolbar (Bold, Italic, Underline, Strikethrough, Code, Code Block, Spoiler, Quote, H1-H3, Lists)
  - Variable inserter dropdown for template variables
  - Split view: raw editor + live Discord-style preview
  - Character counter with configurable limit
  - Keyboard shortcuts (Ctrl+B, Ctrl+I, Ctrl+U)
  - Follows existing Radix UI + Tailwind + Lucide patterns

* test: add DiscordMarkdownEditor and discord-markdown parser tests

- 29 tests covering parser, utility functions, and component behavior
- Tests: toolbar rendering, variable insertion, character counter,
  markdown preview, disabled state, XSS prevention

* fix: address markdown editor review feedback

* fix: restore dashboard config test compatibility

* fix: address discord markdown review feedback

* fix: resolve markdown parser edge cases

* fix(ci): stabilize markdown editor branch checks

* fix(sonar): remove regex hotspot parsing

* fix(sonar): remove remaining regex markdown hotspots

* fix: address remaining review feedback on markdown editor

- Use stable index-based keys for badge rows to prevent remount on edit
- Add timestamp to initConversationHistory startup hydration entries
- Replace placeholder text in Welcome Messages section with proper UI copy
- Fix block quote parser to require space after > (matching Discord behavior)
- Render plain newlines as line breaks instead of paragraph blocks
- Update ReputationSection JSDoc to remove stale 'announcements' reference
- Add fallback to reputation.levelThresholds for backward compatibility

* fix: address final review feedback on markdown editor

- Fix hydration mismatch in preview rendering (isMounted gate)
- Remove * as unordered list marker (Discord only supports -)
- Remove unused act import from autosave tests
- Add TODO for Welcome Messages section placeholder
- Unify ReputationSection to use updateDraftConfig consistently
- Remove stale reputation.levelThresholds fallback
- Add sweepDmLimits test for stale-entry eviction
- Strengthen ai.test.js hydration timestamp assertions with exact values
# Conflicts:
#	tests/modules/ai.test.js
#	web/src/components/dashboard/config-editor.tsx
#	web/tests/components/dashboard/config-editor-autosave.test.tsx
BillChirico added a commit that referenced this pull request Apr 3, 2026
* Add dashboard permission access controls

* Harden guild access resolution

* Validate web guild access responses

* Fix retry header fallback handling

* Align dashboard roles with moderator access

* fix(api): parallelize guild access checks

* refactor(web): share guild directory state

* fix(web): correct retry attempt logging

* docs: align moderator access comments

* fix(api): preserve dashboard xp actor attribution

* fix(api): cap guild access batch size

* fix(web): require botPresent in guild context

* fix(web): validate forwarded discord identities

* test: align dashboard and permission coverage

* test: increase root vitest timeout budget

* Refactor conditional checks for claude-review job

Signed-off-by: Bill Chirico <bill@chirico.dev>

* Update claude-review workflow to specify project context and enhance inline comment guidelines

- Clarified the project context in the review prompt for the Discord bot Volvox.Bot.
- Added detailed instructions for posting inline comments, including formatting requirements and examples for AI fix prompts.
- Ensured that the review process emphasizes only reporting issues without providing praise or compliments.

* feat(dashboard): add WYSIWYG Discord markdown editor component (#422)

* feat(dashboard): add Discord markdown parser and WYSIWYG editor component

- Add discord-markdown.ts utility with parser, wrapSelection, insertAtCursor, wrapLine
- Add DiscordMarkdownEditor component with:
  - Toolbar (Bold, Italic, Underline, Strikethrough, Code, Code Block, Spoiler, Quote, H1-H3, Lists)
  - Variable inserter dropdown for template variables
  - Split view: raw editor + live Discord-style preview
  - Character counter with configurable limit
  - Keyboard shortcuts (Ctrl+B, Ctrl+I, Ctrl+U)
  - Follows existing Radix UI + Tailwind + Lucide patterns

* test: add DiscordMarkdownEditor and discord-markdown parser tests

- 29 tests covering parser, utility functions, and component behavior
- Tests: toolbar rendering, variable insertion, character counter,
  markdown preview, disabled state, XSS prevention

* fix: address markdown editor review feedback

* fix: restore dashboard config test compatibility

* fix: address discord markdown review feedback

* fix: resolve markdown parser edge cases

* fix(ci): stabilize markdown editor branch checks

* fix(sonar): remove regex hotspot parsing

* fix(sonar): remove remaining regex markdown hotspots

* fix: address remaining review feedback on markdown editor

- Use stable index-based keys for badge rows to prevent remount on edit
- Add timestamp to initConversationHistory startup hydration entries
- Replace placeholder text in Welcome Messages section with proper UI copy
- Fix block quote parser to require space after > (matching Discord behavior)
- Render plain newlines as line breaks instead of paragraph blocks
- Update ReputationSection JSDoc to remove stale 'announcements' reference
- Add fallback to reputation.levelThresholds for backward compatibility

* fix: address final review feedback on markdown editor

- Fix hydration mismatch in preview rendering (isMounted gate)
- Remove * as unordered list marker (Discord only supports -)
- Remove unused act import from autosave tests
- Add TODO for Welcome Messages section placeholder
- Unify ReputationSection to use updateDraftConfig consistently
- Remove stale reputation.levelThresholds fallback
- Add sweepDmLimits test for stale-entry eviction
- Strengthen ai.test.js hydration timestamp assertions with exact values
# Conflicts:
#	tests/modules/ai.test.js
#	web/src/components/dashboard/config-editor.tsx
#	web/tests/components/dashboard/config-editor-autosave.test.tsx

* feat(dashboard): add visual Discord embed builder component (#423)

* feat(dashboard): add visual Discord embed builder component (Closes #371)

- Color picker with Discord preset colors + hex input
- Title, description fields with character limits (256, 4096)
- Thumbnail selector: none / user avatar / server icon / custom URL
- Field management: add/remove/reorder with inline toggle
- Footer text (2048) + icon URL, full-width image URL
- Timestamp toggle, format selector (text/embed/text+embed)
- Live Discord-style embed preview with accent color bar
- Variable template support with {{var}} badge rendering
- Lightweight Discord markdown preview (bold, italic, code)
- Character count indicators with color-coded warnings
- Aria-labels on switches for accessibility
- 29 unit tests covering all features

* fix: address review comments on embed-builder

* fix: address embed builder review feedback

* fix: remove regex hotspots from embed preview

* fix: address final embed builder review feedback

* test: cover auth route fallback branches

* test: cover auth route fallback cache recovery
# Conflicts:
#	tests/modules/ai.test.js
#	web/src/components/dashboard/config-editor.tsx
#	web/tests/components/dashboard/config-editor-autosave.test.tsx

* Fix guild access batching and XP auth fallback

* Enforce editor and embed character limits

---------

Signed-off-by: Bill Chirico <bill@chirico.dev>
Signed-off-by: MohsinCoding <91380732+MohsinCoding@users.noreply.github.com>
Co-authored-by: Bill Chirico <bill@chirico.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

feat(dashboard): WYSIWYG Discord markdown editor component

2 participants