feat(pages): add slash commands and @mentions to the page editor - #251
Conversation
The editor's placeholder promised a "/" menu that never existed, and there was no way to mention people. Both work now. - Slash menu: type "/" to insert text, headings, bulleted/numbered/to-do lists, a quote, a code block, a 3x3 table, an image, or a divider. Filter by typing. - @mentions: type "@" to mention a workspace member; the chip stores the member id. Members are loaded on the page and read lazily so ones that arrive after the editor mounts still show up. Both menus share a small suggestion-popup helper that renders a body-level menu under the caret with arrow-key navigation and Enter to pick, so there's no extra popup dependency. Closes #188 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Strix Security ReviewNo security issues found. Updated for Reviewed by Strix |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughAdds slash-command and ChangesSlash Commands and Mentions
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant PageDetailPage
participant WorkspaceService
participant usePageEditor
participant TipTapEditor as TipTap Editor
participant Renderer as createSuggestionRenderer
participant Menu as SlashMenu / MentionMenu
PageDetailPage->>WorkspaceService: listMembers(workspaceSlug)
WorkspaceService-->>PageDetailPage: member list
PageDetailPage->>usePageEditor: mentionItems = mentionMembers
usePageEditor->>TipTapEditor: configure SlashCommand + createMention(getMentionItems)
TipTapEditor->>Renderer: open suggestion for "/" or "@"
Renderer->>Menu: render items and selection state
Menu-->>Renderer: onSelect(index)
Renderer->>TipTapEditor: props.command(item) / props.run(editor, range)
TipTapEditor-->>PageDetailPage: inserted block or mention
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Adds the missing TipTap “/” command menu promised by the page editor placeholder, and introduces @ mentions for workspace members, integrating both via a shared suggestion popup renderer.
Changes:
- Adds a shared
suggestionPopuphelper to render a body-level suggestion menu under the caret with keyboard navigation. - Introduces a Slash Commands extension (headings/lists/quote/code/table/image/divider) and wires it into
usePageEditor. - Adds workspace-member
@mentions(member list loaded on the page and passed into the editor).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/web/src/pages/PageDetailPage.tsx | Loads workspace members and passes them into usePageEditor for @ mention suggestions. |
| apps/web/src/index.css | Styles inserted mention chips via .page-mention. |
| apps/web/src/components/page-editor/usePageEditor.ts | Registers the new SlashCommand + mention extensions and keeps mention items current via a ref. |
| apps/web/src/components/page-editor/suggestionPopup.tsx | New reusable renderer for TipTap Suggestion popups with keyboard selection. |
| apps/web/src/components/page-editor/slashCommands.tsx | Implements the / command list, filtering, and command execution. |
| apps/web/src/components/page-editor/mentionTypes.ts | Adds a shared MentionItem type. |
| apps/web/src/components/page-editor/mentions.ts | Configures the TipTap mention extension and ties it to the suggestion popup + items provider. |
| apps/web/src/components/page-editor/MentionMenu.tsx | Renders the mention suggestion UI with member avatars. |
| apps/web/src/components/page-editor/index.ts | Re-exports MentionItem for consumers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
apps/web/src/pages/PageDetailPage.tsx (1)
195-199: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winMention label falls back to raw email address.
label: m.member_display_name || m.member_email || 'Member'will render a member's email directly into the mention chip text, which gets baked into the saved page HTML (renderLabelembeds@{label}in stored content). Consider omitting the email fallback (e.g. use a generic placeholder) to avoid persisting email addresses into page content.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/pages/PageDetailPage.tsx` around lines 195 - 199, The mention label fallback in the member mapping currently uses member_email, which causes raw email addresses to be stored in page HTML via renderLabel. Update the mapping in PageDetailPage so the label for mentions uses member_display_name or a generic placeholder like "Member", and avoid using member_email anywhere in the mention chip text.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/components/page-editor/mentions.ts`:
- Around line 13-20: The mention suggestion filtering in the mentions setup is
still trimming and matching only a single token, which breaks multi-word names.
Update the suggestion configuration in the mention extension so it preserves
`allowSpaces` behavior and keeps filtering in the `items` callback for
multi-word queries like `@John Doe`, using the existing `suggestion`/`items`
logic as the place to adjust.
In `@apps/web/src/components/page-editor/slashCommands.tsx`:
- Around line 139-173: The active slash menu item is not kept visible when
selectedIndex changes during keyboard navigation. Update SlashMenu to track the
selected button and call scrollIntoView on the active item whenever
selectedIndex changes, using the existing button render in the items.map loop;
apply the same fix to MentionMenu as noted. Ensure the scroll happens inside the
max-h-72 scrollable container so ArrowUp/ArrowDown keeps the highlighted item in
view.
In `@apps/web/src/components/page-editor/suggestionPopup.tsx`:
- Around line 26-33: The popup positioning in suggestionPopup.tsx only updates
from the existing place() calls triggered by onStart/onUpdate, so the menu
drifts when the editor scrolls or resizes while it is open. Add active scroll
and resize listeners around the popup lifecycle in the shared renderer logic,
and have them call place() using the current caret rect so the slash-command and
mention popups stay anchored. Clean up those listeners when the popup closes or
is destroyed, and reference the existing place(), onStart, and onUpdate flow
when wiring the fix.
In `@apps/web/src/pages/PageDetailPage.tsx`:
- Around line 186-206: The `@-mention` member list in PageDetailPage can remain
stale when workspaceSlug changes, so clear mentionMembers as soon as the effect
reruns before calling workspaceService.listMembers. Update the useEffect that
loads members to reset the mention state on workspace changes, while keeping the
existing cancelled guard and mapping logic in place so only the current
workspace’s members can be selected.
---
Nitpick comments:
In `@apps/web/src/pages/PageDetailPage.tsx`:
- Around line 195-199: The mention label fallback in the member mapping
currently uses member_email, which causes raw email addresses to be stored in
page HTML via renderLabel. Update the mapping in PageDetailPage so the label for
mentions uses member_display_name or a generic placeholder like "Member", and
avoid using member_email anywhere in the mention chip text.
🪄 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: CHILL
Plan: Pro Plus
Run ID: 437af2a2-0e48-4751-8344-9e2cc01966ef
📒 Files selected for processing (9)
apps/web/src/components/page-editor/MentionMenu.tsxapps/web/src/components/page-editor/index.tsapps/web/src/components/page-editor/mentionTypes.tsapps/web/src/components/page-editor/mentions.tsapps/web/src/components/page-editor/slashCommands.tsxapps/web/src/components/page-editor/suggestionPopup.tsxapps/web/src/components/page-editor/usePageEditor.tsapps/web/src/index.cssapps/web/src/pages/PageDetailPage.tsx
|
@martian56 fix ai comments? |
CodeRabbit + Copilot on PR #251: - validate the slash "Image" URL through safeUrl so an http(s)/relative URL is required, blocking javascript:/data: payloads in the img src. - seed the mention ref from the initial members so an already-cached list works on the very first "@". - allow spaces in mention queries so multi-word names keep filtering. - reposition the suggestion popup on scroll/resize so it stays pinned to the caret while an editor with a scrollable body moves. - scroll the keyboard-highlighted item into view in both menus. - clear cached members when the workspace changes so the menu can't briefly offer, or insert, a member from the previous workspace. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Closes #188. The page editor's placeholder promised a "/" menu that never existed, and there was no way to mention people. Both work now.
/to insert text, headings (H1-H3), bulleted / numbered / to-do lists, a quote, a code block, a 3x3 table, an image (by URL), or a divider. Type to filter the list; arrow keys + Enter to pick.@to mention a workspace member. The inserted chip carries the member's id (data-id). Members are loaded on the page and read lazily through a ref so members that arrive after the editor mounts still show up in the menu.Both menus share a small
suggestionPopuphelper that renders a body-level menu under the caret with keyboard navigation, so there's no new popup dependency (uses the already-installed@tiptap/suggestionand@tiptap/extension-mention).Scope note
This delivers the two interactive pieces the issue calls out as the priority ("at minimum, make the advertised '/' actually work") plus member mentions. Richer embed nodes (issue-link cards, sub-page embeds, iframe/video) are a larger, separate effort and are not included here.
Testing
tsc -b+ ESLint clean. Browser-verified on a page:/, the full command menu appeared; picking "Heading 2" turned the block into an H2 and removed the/text.@, the member menu appeared; picking a member inserted a styled@Test Adminchip carrying the member id.AI assistance
Produced with the help of Claude Code (Claude Opus 4.8). AI-assisted commits carry a
Co-Authored-Bytrailer.Summary by CodeRabbit
@mention member picker in the page editor with search, keyboard selection, avatar support, and an empty state when no members match./slash commands to insert common blocks (headings, lists, quote, code, table, image, divider).