Phase 4b: inline editor insert dialogs (replace window.prompt link/image/file)#33
Conversation
Replace the blocking window.prompt link / image / file-link flows (both the toolbar and slash commands) with an accessible in-Studio dialog: - new EditorInsertDialog: labelled fields, Enter to submit, Escape to cancel, backdrop click to dismiss, autofocus; "Remove link" when editing a link - new editorInsert helpers (normalizeUrl, fileLabelFromPath, hasUrl) — bare domains gain https://; relative paths/anchors/mailto preserved (unit-tested) - SourceDraftEditor now owns the dialog state and performs the editor mutations; EditorToolbar just requests open via callbacks (no window.prompt anywhere) - removed the unused legacy MarkdownToolbar (last window.prompt; dead code) - dialog styled with design-system tokens Adds unit tests (editorInsert) and e2e (link insert, image insert, Escape cancels). Stacked on feat/design-system-foundation (Phase 4a). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 882b2be6b9
ℹ️ 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".
| return; | ||
| } | ||
| const defaultPath = | ||
| latestUpload?.kind === "pdf" ? latestUpload.publicPath : "/files/"; |
There was a problem hiding this comment.
Avoid inserting file links with an empty label
When there is no latest PDF upload, the file dialog is prefilled with /files/; because that non-empty value enables submission, clicking “Insert file link” immediately reaches fileLabelFromPath('/files/'), whose basename is '', and the editor then attempts to insert a link with empty text/no visible label. This regresses the old prompt flow, which fell back to the full path for trailing-slash input. Please either make this a placeholder until the user enters a file path or make the label helper fall back when the basename is empty.
Useful? React with 👍 / 👎.
| // of React and Tiptap so they can be unit-tested directly. | ||
|
|
||
| const ABSOLUTE_OR_RELATIVE = | ||
| /^(https?:|mailto:|tel:|\/|#|\.\/|\.\.\/)/iu; |
There was a problem hiding this comment.
Preserve extension-only relative paths
When a user enters a same-directory relative file such as guide.md or photo.jpg, this pattern treats it as a bare domain and rewrites it to https://guide.md/https://photo.jpg. The dialog labels accept paths and the helper claims relative paths are preserved, so Markdown links/images to local files without a leading ./ are now published as broken external URLs.
Useful? React with 👍 / 👎.
| editor.chain().focus().setImage({ src, alt, title: alt }).run(); | ||
| } | ||
| } else { | ||
| const path = values.url.trim(); |
There was a problem hiding this comment.
Normalize file-link URLs before insertion
For the file-link dialog, entering a bare external URL like example.com/file.pdf passes validation because hasUrl normalizes it, but this branch inserts the raw trimmed value. The resulting Markdown link points to the relative path example.com/file.pdf instead of https://example.com/file.pdf, unlike the link and image dialog branches that normalize before writing.
Useful? React with 👍 / 👎.
| className="editor-dialog" | ||
| role="dialog" | ||
| aria-modal="true" | ||
| aria-labelledby={titleId} |
There was a problem hiding this comment.
Trap focus inside the modal dialog
Because this is a custom aria-modal dialog rather than a native modal, keyboard focus is not automatically contained; when users Tab or Shift+Tab past the URL/text/action controls, focus can move to toolbar/editor controls behind the overlay while the dialog is still open. Add focus trapping or make the background inert so keyboard users cannot operate the underlying editor from an active modal.
Useful? React with 👍 / 👎.
Phase 4b — editor link/image/file UX
Second PR of the Phase 4 UX pass. Stacked on #32 (Phase 4a) — base is
feat/design-system-foundation, so this diff is 4b only. Merge #32 first.Brings the editor up to Google-Docs/Notion conventions by removing the blocking native
window.promptflows.What changed
EditorInsertDialog— accessible in-Studio dialog (role="dialog",aria-modal, labelled fields, autofocus, Enter submits, Escape/backdrop cancels) for inserting links, images, and file links. Editing an existing link offers Remove link.editorInserthelpers —normalizeUrl(bare domains gainhttps://; relative paths, anchors,mailto:/tel:preserved),fileLabelFromPath,hasUrl. Unit-tested.SourceDraftEditorowns the dialog state and performs editor mutations;EditorToolbarand the slash commands just request the dialog — nowindow.promptanywhere in the editor path.MarkdownToolbar(the lastwindow.prompt; dead code).Tests
editorInsert.test.ts.href), image insert (assertssrc), Escape cancels without inserting.pnpm build✅ ·pnpm test✅ (studio 133/133) ·pnpm lint✅ ·pnpm test:e2e✅ (22/22).Notes
Next
🤖 Generated with Claude Code