feat(editor): Editor molecule#718
Merged
Merged
Conversation
3 tasks
640d630 to
da4efac
Compare
Replaces the headless-primitives + ready-mades direction with a single <TextEditor> on the useEditor engine, configured by StarterKit-style kits and explicit fixedMenu/bubbleMenu/floatingMenu props. The library ships no assembled editors; each app builds its own component on <TextEditor>. - ADR-0004 rewritten and renamed (...-primitives-and-readymades -> ...-composition-model): subpath is a dependency wall not a tree-shaking tool; v0 monolith retained for human-gated removal; gameplan port is the acceptance gate - spec/editor.md rewritten: engine, component + L0-L4 ladder, kits (configure/disable + structure), two-axis menu model, placeholder-via-storage, TextStyle dep, isAvailable hide-pruning - CONTEXT.md editor vocabulary updated (kits, app editor component) - v1-release/issues/editor-family: 9 dependency-ordered issues + README (status lines, keep/new/delete inventory, guardrails) - research/12 marked superseded Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the editor family per spec/editor.md + ADR-0004: one
<TextEditor> on the useEditor engine, configured by kits and explicit
menu props — no ready-made assembled editors.
Issue 03 — menus: rename :buttons -> :items across the menu components;
add isAvailable(editor) to predefined items (schema mark/node or
extension check) so MenuItems hides unavailable items and drops empty
groups (one preset adapts across kits).
Issue 05 — kits (new kits.ts): CommentKit / RichTextKit / InlineKit as
configurable StarterKit-style bundles; flat `Partial<Opts> | false`
members; heading threaded into StarterKit; Color co-registers TextStyle;
InlineKit single-line via a one-block Document. Data members
(mention/tag) inert until given items.
Issue 06 — component: rebuild TextEditor.vue on useEditor (required
extensions, unnamed v-model + format, reactive placeholder via
editor.storage.placeholder + editable, autofocus/uploadFunction/
maxHeight, fixedMenu/bubbleMenu/floatingMenu props + slots). Delete the
RichTextEditor/CommentEditor/InlineEditor ready-mades.
Issue 02 alignment: add TextStyle export (fix v3 named import); make Tag
a complete node + gated suggestion; move mention/tag to canonical
{ items }; storage-thread Placeholder; wire the richer LinkExtension
(inline edit popup, Mod-k, paste handling) as the exported Link.
Issue 08 (exports): index.ts now ships the spec surface; ready-mades
removed; stories re-composed on TextEditor + kits + presets.
Tests: 34 editor tests (kits via getSchema/resolveExtensions, TextEditor
with a real editor in jsdom, menu self-pruning). v0 monolith untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Realign the v1 editor to the renderless <TextEditor> design and fix the two
library bugs the gameplan parity port surfaced.
Renderless redesign (spec/editor.md + ADR-0004 amendment):
- <TextEditor> renders no UI of its own — it owns the editor lifecycle,
v-model, upload and placeholder threading, and exposes { editor, isEmpty }
through its #default slot. Drop the fixedMenu/bubbleMenu/floatingMenu props
and the #actions/#fixedMenu/#bubbleMenu/#floatingMenu slots; the consumer
composes layout from the building blocks inside the slot.
- Add focus/blur/transaction emits and a defineExpose escape hatch.
- Move placeholder threading into setPlaceholder() next to the extension so
the component never reaches into editor storage.
String-icon menu convention:
- Predefined menu items ship a default lucide-* string icon; MenuItems masks
it into an icon span (Button's house path). Add InlineCode, Undo, Redo.
- Migrate editor node-views (image/iframe/image-group, image viewer, link
popup, slash list) from ~icons/lucide/* component imports to lucide-* spans.
- RichTextKit now includes StyleClipboard.
Parity fixes (from the gameplan port, see .parity-evidence):
- Bubble/floating menus rendered nothing: EditorBubbleMenu/EditorFloatingMenu
imported the Extension object from @tiptap/extension-* instead of the Vue
component from @tiptap/vue-3/menus. Fix the imports + the test mocks, which
had masked the bug by mocking the same wrong package.
- Image/video upload was dead: tiptap v3 froze extension.options into an
immutable getter, so uploadAware's cached uploadFunction stayed null. Drop
uploadAware; resolve the function at use-time via resolveUploadOptions
(editor.storage.upload) across image/video/content-paste/image-group.
Tests: 32 pass (kits, menu, useEditor, TextEditor).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s, docs Rename the v1 component TextEditor → Editor and export it from frappe-ui/editor (the v0 `TextEditor` alias stays at top-level frappe-ui, so the two no longer collide). Building blocks now resolve the editor from context via editor-context.ts, so the :editor prop is optional inside <Editor>. - iframe: replace InsertIframe.vue with a dialog-controller (IframeInsertDialog + iframeInsertDialogController) - link: rework LinkEditorPopup, add link-shortcut-plugin, and wire the custom LinkExtension into the exported Link - restore tippy.js (the in-place v0 monolith still imports it; it was dropped in error and broke any consumer of top-level frappe-ui) - docs: rewrite molecules/editor.md to the v1 model and add the v0→v1 migration guide (## Editor) to migration.md - spec/issues: rename to <Editor>; mark editor-family 07/08/09 done 97/97 editor unit tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two correctness fixes surfaced by a TipTap-conventions review: - useEditor: in `format: 'json'`, every keystroke wrote a fresh getJSON() object back through v-model, which re-fired the content watcher and ran setContent(), rebuilding the doc and resetting the selection. The echo guard only covered HTML string-equality. Track the exact emitted value and skip the watcher when the incoming ref is our own write. - MenuItems: the editor is built on @tiptap/core (no reactive customRef wrapper like @tiptap/vue-3 installs), so isActive/isDisabled reads never re-rendered on selection changes — EditorFixedMenu showed stale pressed and disabled (Undo/Redo) state. Bump a version ref on each transaction and route the bindings through it. Scoped per MenuItems instance so it works with or without <Editor>. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8e6d352 to
c80be37
Compare
Contributor
Author
|
/barista review |
|
Re-reviewing per @netchampfaris — focused on the public API surface of the new editor family. Concerns — the new surface is clean, but the deprecation bridge misdirects migrating consumers.
API tightness — looks good. This is the direction the library should move:
No public prop/slot/event drift found. The two items above are both in the deprecation shim, not the new API. |
Point migrating consumers at the real surface (<Editor> + RichTextKit from frappe-ui/editor) instead of the nonexistent <RichTextEditor>, and guard the warning behind a module-scoped flag and import.meta.env.DEV so it fires once per process and never in production. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pull Bot
pushed a commit
to Zezo-Ai/Zezo-Ai-frappe-ui
that referenced
this pull request
Jun 2, 2026
The `secrets` context is not available in step `if:` conditions, so
`if: ${{ secrets.RELEASE_TOKEN == '' }}` made beta-release.yml invalid.
GitHub failed every run at parse time ("workflow file issue"), so PR frappe#718
(labeled `beta-release`) never bumped the version or published a beta.
Hoist RELEASE_TOKEN to a job-level env var and test `env.RELEASE_TOKEN`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the v1 editor family under the
frappe-ui/editorsubpath (source insrc/molecules/editor). The API is built around one renderless<Editor>component plus composable building blocks and configurable extension kits — there are no monolithic "ready-made" editors; consumers compose the pieces they need.Public surface (
frappe-ui/editor)useEditorcomposable (built on@tiptap/core) and the<Editor>component.<Editor>is renderless: it owns the editor lifecycle,v-model(HTML or JSON viaformat), upload, and placeholder threading, and exposes{ editor, isEmpty }to its slot.EditorContent,EditorFixedMenu,EditorBubbleMenu,EditorFloatingMenu. Usable inside<Editor>(they read the editor from context) or standalone on auseEditorref via an explicit:editorprop.CommentKit,RichTextKit,InlineKit: configurable extension bundles. Each member can be reconfigured or removed (false), so one preset adapts across use cases.Link,Code/CodeBlock,Image/ImageGroup/ImageViewer/Video/Iframe,Mention,Tag,Emoji,SlashCommands,Table,TaskList,Toc,Color/Highlight,Typography,TextAlign,Placeholder, plusStarterKit. Tree-shakes naturally.minimalToolbar,commentToolbar,articleToolbar). Items self-prune when their extension isn't loaded.uploadFunctionis threaded through editor storage and read at use-time (v3-safe, sinceextension.optionsis frozen).Other changes
@components,@molecules,@utils,@composables,frappe-ui/editor) across Vite, Vitepress, and tsconfig.Fixes from a TipTap-conventions review
format="json"no longer resets the selection on every keystroke — the v-model echo guard previously only covered HTML; JSON's fresh-object identity defeated it, so each edit re-ransetContent()and rebuilt the doc. Now the exact emitted value is tracked and skipped.@tiptap/core-based editor isn't a reactive ref like@tiptap/vue-3's, soisActive/isDisabledreads inEditorFixedMenuwent stale on selection changes.MenuItemsnow bumps a version on each editor transaction to drive re-evaluation (scoped per instance, works with or without<Editor>).Validation
yarn vitest --run src/molecules/editor— 97 passingyarn buildyarn docs:buildDocs preview: https://ui.frappe.io/pr-preview/pr-718/
Coverage: 56.13% (+6.69% vs
main)