feat(rich-editor): rich markdown field + @ mentions for skill & deploy modals#5215
feat(rich-editor): rich markdown field + @ mentions for skill & deploy modals#5215waleedlatif1 wants to merge 5 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Adds a full Fixes false “unsaved changes” on open by Skill modal/import: textarea → rich editor; paste/import can destructure full Reviewed by Cursor Bugbot for commit c46ef1b. Configure here. |
|
@greptile review |
|
@cursor review |
Greptile SummaryThis PR adds a
Confidence Score: 5/5Safe to merge — all new surfaces reuse the existing TipTap extension stack and the changes are well-isolated behind feature boundaries. The mention subsystem, the controlled RichMarkdownField, and the dirty-on-open fix are all well-structured with unit tests covering the key invariants. The only notable change from the extraction refactor is that the slash command popup no longer hides its scrollbar, which is a cosmetic gap rather than a behavioural one. suggestion-menu-chrome.ts — the shared scroll class is missing the scrollbar-hiding CSS that the original slash command list carried. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Host as Host Component
participant UEM as useEditorMentions
participant UMM as useMarkdownMentions
participant RQ as React Query hooks
participant Store as MentionStore
participant TipTap as TipTap Mention Extension
participant List as MentionList
Host->>UEM: mount(editor, workspaceId)
UEM->>TipTap: "storage.mention.enabled = true"
UEM->>TipTap: "storage.mention.onOpen = setActive"
Note over Host: User types @
TipTap->>TipTap: Suggestion plugin fires
TipTap->>UEM: onOpen → setActive(true)
UEM->>UMM: enabled: true
UMM->>RQ: enable all workspace queries
RQ-->>UMM: data arrives
UMM-->>UEM: items[]
UEM->>Store: store.set(items[])
Store-->>List: useSyncExternalStore notifies
List->>List: filter by query, render chips
Note over Host: User selects item
List->>TipTap: command(kind, id, label)
TipTap->>TipTap: insertContent mention node
TipTap->>Host: onUpdate serialized as sim:kind/id
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Host as Host Component
participant UEM as useEditorMentions
participant UMM as useMarkdownMentions
participant RQ as React Query hooks
participant Store as MentionStore
participant TipTap as TipTap Mention Extension
participant List as MentionList
Host->>UEM: mount(editor, workspaceId)
UEM->>TipTap: "storage.mention.enabled = true"
UEM->>TipTap: "storage.mention.onOpen = setActive"
Note over Host: User types @
TipTap->>TipTap: Suggestion plugin fires
TipTap->>UEM: onOpen → setActive(true)
UEM->>UMM: enabled: true
UMM->>RQ: enable all workspace queries
RQ-->>UMM: data arrives
UMM-->>UEM: items[]
UEM->>Store: store.set(items[])
Store-->>List: useSyncExternalStore notifies
List->>List: filter by query, render chips
Note over Host: User selects item
List->>TipTap: command(kind, id, label)
TipTap->>TipTap: insertContent mention node
TipTap->>Host: onUpdate serialized as sim:kind/id
Reviews (5): Last reviewed commit: "feat(rich-editor): render mentions as ic..." | Re-trigger Greptile |
Greptile SummaryThis PR adds a controlled
Confidence Score: 4/5The PR is safe to merge. The core mechanics — duplicate plugin key prevention, sim: link round-trip, dirty-on-open baseline, streaming mirror — are all covered by new tests. The two findings are both about edge-case consistency rather than broken paths. Two inconsistencies worth addressing before shipping:
Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Host as Host Component
participant UEM as useEditorMentions
participant UMM as useMarkdownMentions
participant Store as MentionStore
participant Ext as Mention Extension
participant List as MentionList
Host->>UEM: mount (workspaceId)
UEM->>Ext: "storage.enabled = true"
UEM->>Ext: "storage.onOpen = () => setActive(true)"
Note over UMM: queries disabled (active=false)
Host->>Ext: "user types @"
Ext->>Ext: allow() → true
Ext->>UEM: onOpen() → setActive(true)
UEM->>UMM: "enabled=true → React Query fires"
UMM-->>UEM: items[] (async)
UEM->>Store: store.set(items)
Store-->>List: useSyncExternalStore notify
List-->>Host: menu populated reactively
Host->>List: user selects item
List->>Ext: command(item)
Ext->>Ext: insertContent [label](sim:kind/id)
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Host as Host Component
participant UEM as useEditorMentions
participant UMM as useMarkdownMentions
participant Store as MentionStore
participant Ext as Mention Extension
participant List as MentionList
Host->>UEM: mount (workspaceId)
UEM->>Ext: "storage.enabled = true"
UEM->>Ext: "storage.onOpen = () => setActive(true)"
Note over UMM: queries disabled (active=false)
Host->>Ext: "user types @"
Ext->>Ext: allow() → true
Ext->>UEM: onOpen() → setActive(true)
UEM->>UMM: "enabled=true → React Query fires"
UMM-->>UEM: items[] (async)
UEM->>Store: store.set(items)
Store-->>List: useSyncExternalStore notify
List-->>Host: menu populated reactively
Host->>List: user selects item
List->>Ext: command(item)
Ext->>Ext: insertContent [label](sim:kind/id)
Reviews (2): Last reviewed commit: "feat(rich-editor): rich markdown field +..." | Re-trigger Greptile |
|
@greptile review |
|
@cursor review |
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit b393ea9. Configure here.
|
@greptile review |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit bb68df9. Configure here.
…y modals - Add controlled, file-less RichMarkdownField (sibling of the file editor) used for skill Content and deploy version descriptions; placeholder/typography match chip fields - Add @-mention menu (TipTap suggestion) inserting portable [label](sim:kind/id) links; wired into the field and the file viewer via a shared useEditorMentions hook - Extract a shared suggestion-popup renderer + menu chrome (slash + mention) - Fix false dirty-on-open: normalize the editor's dirty baseline to canonical markdown - Always show the deployment version number (v3 · name) so named versions keep a short ref - Skill import: drop the paste box (Create-tab editor auto-destructures a pasted SKILL.md), reorder GitHub → Upload
- RichMarkdownField reports the original value when the doc matches its canonical
form, so a non-canonical input never reads as a false unsaved change (skill +
version description modals)
- Add sim: mention link navigation (Cmd/Ctrl-click) to the modal field
- versions: keep the v{n} fallback as the rename guard/seed so re-submitting the
displayed token is a no-op (no redundant "v3 · v3"); document the clear-name no-op
- Clarify the lazy query-gating comment in useMarkdownMentions
Bump the field's remount key in the reset guard so the seed-once rich editor re-seeds when content is reset from a changed initialValues (same skill id keeps the React key otherwise stable), keeping the editor and saved value in sync.
- Render @ mentions as an inline chip node (entity icon + label) instead of a blue link; still serializes to the portable [label](sim:kind/id) markdown so it round-trips and stays agent-readable (shared mentionIcon resolver) - Cap the mention/slash menu height + width and scroll it, matching the chat menu - Give the version description editor more height; lift the 2000-char limit to a high anti-abuse cap (client + contract) and drop the visible counter
- Mount the slash/@ menu popup inside the host dialog (when present) instead of document.body: Radix's scroll-lock blocks wheel events outside the dialog subtree, so a body-level popup couldn't scroll in a modal. position:fixed keeps it viewport-positioned (the modal centers via flex, no transform) so it isn't clipped - Fix the invalid max-w arbitrary value (calc needs spaces) that left the menu uncapped - Match the version-description editor's dynamic-import loading height to the field so the modal doesn't grow when the chunk loads
|
@greptile review |
bb68df9 to
c46ef1b
Compare
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c46ef1b. Configure here.
Summary
RichMarkdownField(sibling of the file-viewer editor) and use it for the skill Content field and the deploy version description — same WYSIWYG markdown experience, with placeholder/typography matched to the chip fields@-mention menu (TipTap suggestion) that inserts portable[label](sim:kind/id)markdown links for files, folders, tables, knowledge bases, workflows, skills, and integrations; wired into both the field and the file viewer via a shareduseEditorMentionshook, with the popup lifecycle + menu chrome shared with the/slash commandv3 · name) so a named version keeps a short, never-truncated referenceSKILL.md), and reorder to GitHub → UploadType of Change
Testing
bun run type-check,biome, andcheck:api-validationclean; 351 file-viewer/skills/deploy unit tests pass (incl. new round-trip, dirty-on-open, and sim-link regression tests)Checklist