feat(dashboard): add WYSIWYG Discord markdown editor component#422
feat(dashboard): add WYSIWYG Discord markdown editor component#422BillChirico merged 12 commits intomainfrom
Conversation
|
|
🚅 Deployed to the volvox-bot-pr-422 environment in volvox-bot
|
There was a problem hiding this comment.
💡 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".
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughAdds 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
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📋 Issue PlannerBuilt with CodeRabbit's Coding Plans for faster development and fewer bugs. View plan used: ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
| 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]
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
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
web/src/components/ui/discord-markdown-editor.tsxweb/src/lib/discord-markdown.tsweb/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.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/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 thejsdomenvironment and React Testing Library, matching theweb/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.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/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.tsxweb/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.tsxweb/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.
[warning] 213-215: Extract this nested ternary operation into an independent statement.
web/src/lib/discord-markdown.ts
[warning] 59-59: Use the "RegExp.exec()" method instead.
[warning] 13-13: Prefer String#replaceAll() over String#replace().
[warning] 135-135: Prefer String#replaceAll() over String#replace().
[warning] 82-82: Use the "RegExp.exec()" method instead.
[warning] 16-16: Prefer String#replaceAll() over String#replace().
[warning] 144-144: Prefer String#replaceAll() over String#replace().
[warning] 14-14: Prefer String#replaceAll() over String#replace().
[warning] 15-15: Prefer String#replaceAll() over String#replace().
[warning] 55-123: Expected a for-of loop instead of a for loop with this simple iteration.
[warning] 141-141: Prefer String#replaceAll() over String#replace().
[failure] 50-50: Refactor this function to reduce its Cognitive Complexity from 46 to the 15 allowed.
[warning] 97-97: Use the "RegExp.exec()" method instead.
[warning] 150-150: Prefer String#replaceAll() over String#replace().
[warning] 12-12: Prefer String#replaceAll() over String#replace().
[warning] 156-156: Prefer String#replaceAll() over String#replace().
[warning] 138-138: Prefer String#replaceAll() over String#replace().
[warning] 153-153: Prefer String#replaceAll() over String#replace().
[warning] 147-147: Prefer String#replaceAll() over String#replace().
[warning] 71-71: Use the "RegExp.exec()" method instead.
🔇 Additional comments (8)
web/src/lib/discord-markdown.ts (3)
9-17: HTML escaping implementation is correct.The
escapeHtmlfunction 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, andwrapLinecorrectly 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_ACTIONSarray cleanly separates concerns and makes the toolbar easily extensible. Each action correctly uses the helper functions.
156-208: Callbacks are properly memoized.
applyAction,insertVariable, andhandleKeyDownare correctly wrapped inuseCallbackwith appropriate dependencies. TherequestAnimationFramepattern for restoring cursor position after React re-render is appropriate.
310-313: No additional sanitization needed—HTML escaping is comprehensive.The
parseDiscordMarkdownfunction 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, andwrapLinecorrectly validate both the text output and the cursor/selection position updates.
|
@coderabbitai autofix stacked pr |
|
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. |
There was a problem hiding this comment.
💡 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".
There was a problem hiding this comment.
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
📒 Files selected for processing (7)
tests/modules/ai.test.jsweb/src/components/dashboard/config-editor.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/src/lib/discord-markdown.tsweb/tests/components/dashboard/config-editor-autosave.test.tsxweb/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.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxtests/modules/ai.test.jsweb/src/components/dashboard/config-editor.tsxweb/tests/components/ui/discord-markdown-editor.test.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/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 thejsdomenvironment and React Testing Library, matching theweb/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.tsxweb/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.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxtests/modules/ai.test.jsweb/src/components/dashboard/config-editor.tsxweb/tests/components/ui/discord-markdown-editor.test.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/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.tsxweb/src/components/dashboard/config-editor.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/src/lib/discord-markdown.ts
**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use ESM-only syntax:
import/export, neverrequire()/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 thenodeenvironment, matching thesrc/structure in thetests/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.jsweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.
[warning] 343-347: Use
[warning] 87-87: Prefer globalThis.window over window.
web/src/lib/discord-markdown.ts
[warning] 196-196: Prefer String#replaceAll() over String#replace().
[warning] 21-21: Prefer String#replaceAll() over String#replace().
[warning] 198-198: Prefer String#replaceAll() over String#replace().
[warning] 20-20: Prefer String#replaceAll() over String#replace().
[warning] 197-197: Prefer String#replaceAll() over String#replace().
[warning] 17-17: Prefer String#replaceAll() over String#replace().
[warning] 202-202: Prefer String#replaceAll() over String#replace().
[warning] 19-19: Prefer String#replaceAll() over String#replace().
[warning] 18-18: Prefer String#replaceAll() over String#replace().
[warning] 195-195: Prefer String#replaceAll() over String#replace().
[warning] 193-193: Prefer String#replaceAll() over String#replace().
[warning] 194-194: Prefer String#replaceAll() over String#replace().
🔇 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
timestampis 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
ConfigEditoras a wrapper andConfigEditorContentas 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:
- No guild selected → prompt message
- Loading state → loading indicator
- Error state → error message with retry button
- 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
aiproperties when updatingsystemPromptThe spread pattern in
updateDraftConfigis correct per context snippet 4, which confirmsGuildConfigisDeepPartial<BotConfig>.
17-30: No issues found. All three values (openDiffModal,discardChanges, andfetchConfig) are properly defined in theConfigContextValueinterface at lines 48, 49, and 58 respectively inweb/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 pathdraftConfig.xp?.levelThresholdsis correct—levelThresholdsexists only inXpConfig, not inReputationConfig. The implementation properly separates concerns: reputation settings (xpPerMessage, xpCooldownSeconds) are handled throughonFieldChange, while XP level thresholds are managed throughupdateDraftConfigdirectly to thexpsection.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) withbeforeEachproperly 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
escapeHtmlfunction 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, andwrapLinefunctions 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
parseCodeBlockfunction 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
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
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 | 🔵 TrivialRemove the unused
compareModeparameter.The
compareModeparameter is marked as unused (_compareMode) but remains in the function signature. The component relies onhasComparisoninstead (lines 131-132, 148-155), makingcompareModeredundant. Remove the parameter from both the component signature and the parent call site inanalytics-dashboard.tsx:379-385to 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
initConversationHistorycomputes timestamps from a column it does not select.
row.created_atis read at Line 377, but the outer query omitscreated_at, so hydrated timestamps always
fallback toDate.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
📒 Files selected for processing (25)
.github/workflows/ci.ymlbiome.jsonsrc/modules/actions/buildPayload.jssrc/modules/actions/xpBonus.jssrc/modules/ai.jssrc/modules/levelUpActions.jssrc/modules/triage-parse.jssrc/modules/triage.jssrc/modules/welcomeOnboarding.jstests/api/utils/configValidation.test.jstests/modules/actions/addReaction.test.jstests/modules/actions/announce.test.jstests/modules/actions/nickPrefix.test.jstests/modules/actions/sendDm.test.jstests/modules/actions/webhook.test.jstests/modules/actions/xpBonus.test.jstests/modules/ai.test.jsweb/src/components/dashboard/analytics-dashboard-sections.tsxweb/src/components/dashboard/analytics-dashboard.tsxweb/src/components/dashboard/config-editor.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/src/lib/discord-markdown.tsweb/tests/components/dashboard/config-editor-autosave.test.tsxweb/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, neverrequire()/module.exports
Files:
src/modules/triage-parse.jstests/modules/actions/addReaction.test.jstests/modules/actions/xpBonus.test.jssrc/modules/actions/xpBonus.jssrc/modules/actions/buildPayload.jssrc/modules/levelUpActions.jstests/api/utils/configValidation.test.jssrc/modules/welcomeOnboarding.jstests/modules/actions/announce.test.jstests/modules/actions/nickPrefix.test.jstests/modules/actions/sendDm.test.jstests/modules/actions/webhook.test.jssrc/modules/triage.jssrc/modules/ai.jstests/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.jstests/modules/actions/addReaction.test.jstests/modules/actions/xpBonus.test.jssrc/modules/actions/xpBonus.jssrc/modules/actions/buildPayload.jssrc/modules/levelUpActions.jstests/api/utils/configValidation.test.jssrc/modules/welcomeOnboarding.jstests/modules/actions/announce.test.jstests/modules/actions/nickPrefix.test.jstests/modules/actions/sendDm.test.jstests/modules/actions/webhook.test.jssrc/modules/triage.jssrc/modules/ai.jsweb/tests/components/dashboard/config-editor-autosave.test.tsxweb/src/components/dashboard/analytics-dashboard-sections.tsxtests/modules/ai.test.jsweb/src/components/dashboard/config-editor.tsxweb/tests/components/ui/discord-markdown-editor.test.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/src/lib/discord-markdown.ts
src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.js: Never useconsole.*methods; use the Winston logger instead viaimport logger from '../logger.js'(adjust path as needed), then calllogger.info(),logger.warn(),logger.error(), orlogger.debug()
Always usesafeReply(),safeSend(), orsafeEditReply()instead of raw Discord.js methods for safe Discord messaging that handles errors gracefully
Files:
src/modules/triage-parse.jssrc/modules/actions/xpBonus.jssrc/modules/actions/buildPayload.jssrc/modules/levelUpActions.jssrc/modules/welcomeOnboarding.jssrc/modules/triage.jssrc/modules/ai.js
src/modules/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Create feature modules in
src/modules/and add corresponding config sections toconfig.json
Files:
src/modules/triage-parse.jssrc/modules/actions/xpBonus.jssrc/modules/actions/buildPayload.jssrc/modules/levelUpActions.jssrc/modules/welcomeOnboarding.jssrc/modules/triage.jssrc/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.jstests/modules/actions/addReaction.test.jstests/modules/actions/xpBonus.test.jssrc/modules/actions/xpBonus.jssrc/modules/actions/buildPayload.jssrc/modules/levelUpActions.jstests/api/utils/configValidation.test.jssrc/modules/welcomeOnboarding.jstests/modules/actions/announce.test.jstests/modules/actions/nickPrefix.test.jstests/modules/actions/sendDm.test.jstests/modules/actions/webhook.test.jssrc/modules/triage.jssrc/modules/ai.jsweb/tests/components/dashboard/config-editor-autosave.test.tsxweb/src/components/dashboard/analytics-dashboard-sections.tsxtests/modules/ai.test.jsweb/src/components/dashboard/config-editor.tsxweb/tests/components/ui/discord-markdown-editor.test.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/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.jssrc/modules/actions/xpBonus.jssrc/modules/actions/buildPayload.jssrc/modules/levelUpActions.jssrc/modules/welcomeOnboarding.jssrc/modules/triage.jssrc/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.jstests/modules/actions/addReaction.test.jstests/modules/actions/xpBonus.test.jssrc/modules/actions/xpBonus.jssrc/modules/actions/buildPayload.jssrc/modules/levelUpActions.jstests/api/utils/configValidation.test.jssrc/modules/welcomeOnboarding.jstests/modules/actions/announce.test.jstests/modules/actions/nickPrefix.test.jstests/modules/actions/sendDm.test.jstests/modules/actions/webhook.test.jssrc/modules/triage.jssrc/modules/ai.jstests/modules/ai.test.jsweb/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 thenodeenvironment, matching thesrc/structure in thetests/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.jstests/modules/actions/xpBonus.test.jstests/api/utils/configValidation.test.jstests/modules/actions/announce.test.jstests/modules/actions/nickPrefix.test.jstests/modules/actions/sendDm.test.jstests/modules/actions/webhook.test.jstests/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 thejsdomenvironment and React Testing Library, matching theweb/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.tsxweb/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.tsxweb/src/components/dashboard/config-editor.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/ui/discord-markdown-editor.tsxweb/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.jstests/modules/actions/announce.test.jsweb/src/components/ui/discord-markdown-editor.tsxweb/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.jsweb/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.tsxweb/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.tsxweb/src/components/dashboard/analytics-dashboard-sections.tsxweb/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.tsxweb/src/components/dashboard/analytics-dashboard-sections.tsxweb/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.tsxweb/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.tsxweb/src/components/dashboard/config-editor.tsxweb/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'.
web/src/components/ui/discord-markdown-editor.tsx
[warning] 89-89: Prefer globalThis.window over window.
web/src/lib/discord-markdown.ts
[warning] 17-17: Prefer String#replaceAll() over String#replace().
[warning] 20-20: Prefer String#replaceAll() over String#replace().
[warning] 21-21: Prefer String#replaceAll() over String#replace().
[warning] 231-231: Prefer using an optional chain expression instead, as it's more concise and easier to read.
[warning] 19-19: Prefer String#replaceAll() over String#replace().
[warning] 18-18: Prefer String#replaceAll() over String#replace().
[warning] 235-235: Prefer String#codePointAt() over String#charCodeAt().
🔇 Additional comments (37)
src/modules/levelUpActions.js (1)
16-16: Import update looks correct and non-functional.
handleSendDmis 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 appropriateThis repository uses a pnpm monorepo with a single root
pnpm-lock.yamlthat manages all workspace dependencies. Theweb/pnpm-lock.yamlfile does not exist, so the current cache key using only the root lockfile is correctly scoped. Playwright dependencies declared inweb/package.jsonare 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/navigationmock correctly uses module-levelmockPushandmockPathnamevariables with proper reset inbeforeEach. This aligns with the pattern inconfig-context.test.tsxand 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
levelThresholdsreads fromdraftConfig.xp?.levelThresholdsfirst, then falls back todraftConfig.reputation?.levelThresholds, but theonBlurhandler at lines 130-133 only writes toxp.levelThresholds. This is a reasonable migration pattern, but ensure the backend and other consumers expectxp.levelThresholdsas the canonical location.
130-133: Immutable update pattern is correct.The
updateDraftConfigcallback properly preserves the existing config structure by spreadingprevandprev.xpbefore setting the newlevelThresholdsvalue.web/src/components/dashboard/config-editor.tsx (4)
9-15: Clean wrapper pattern for context provision.The
ConfigEditorwrapper correctly providesConfigProvidertoConfigEditorContent, 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
onChangehandler correctly preserves the existing config structure while updating only thesystemPromptfield within theaisection.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:sendResponsesreformat 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_atin 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 withbeforeEachreset 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 windowcheck- 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:
requestAnimationFramecleanup on unmount viarafIdsRef- 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
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
|
@coderabbitai autofix stacked pr |
|
@claude review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
✅ No merge conflicts detected when merging into 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
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
| hydratedByChannel.get(channelId).push({ | ||
| role: row.role, | ||
| content: row.content, | ||
| timestamp: row.created_at ? new Date(row.created_at).getTime() : Date.now(), | ||
| }); |
There was a problem hiding this comment.
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).
| export function ConfigEditor() { | ||
| return ( | ||
| <ConfigProvider> | ||
| <ConfigEditorContent /> | ||
| </ConfigProvider> | ||
| ); |
There was a problem hiding this comment.
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.
| {/* 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> |
There was a problem hiding this comment.
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.
* 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
* 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>




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 utilityparseDiscordMarkdown()— converts Discord-flavored markdown to HTMLstrikethrough,code,code blocks, ||spoilers||, > quotes, # headings (H1-H3), bullet lists, numbered lists{{var}}rendering as styled badgeswrapSelection(),insertAtCursor(),wrapLine()web/src/components/ui/discord-markdown-editor.tsx— Editor component{{username}}, etc.) at cursor positionDashboard integration / supporting fixes
Component API
web/tests/components/ui/discord-markdown-editor.test.tsx— 29 testsTesting