Editor: custom StarterKit, media node view overhaul, and fixes#765
Merged
Conversation
- compose starter kit from individual tiptap extensions instead of @tiptap/starter-kit, so link/code/codeBlock can never double-register with our replacements (typed as false-only options) - bake HeadingIds in whenever headings are enabled - InlineKit now pushes individual marks instead of disabling members - pin all starter member packages to 3.11.0 via resolutions
- /link now inserts a selected "Link" text and opens the link editor in edit mode, instead of setting an empty href on nothing - link toolbar button only shows when openLinkEditor command exists
- delete staged local files when editor is destroyed mid-upload and drop the old entry after a successful reupload - skip dimension dispatch when the node at pos no longer matches the probed src (reupload can swap nodes at the same position) - render media container while upload placeholder is loading
- compare toRaw(content) against lastEmitted so reactive-wrapped JSON doesn't defeat the bounce-back check - reset applyingExternalUpdate in finally so a setContent throw can't leave external updates permanently ignored - don't attach popup document listeners if destroyed before the rAF
- plain Enter returns to navigate mode (spreadsheet-style) - clicks inside the editing cell only move the caret
…edia
- upload engine passes { signal, onProgress } to the upload function;
shared UploadFunction type used by image/video/paste/gallery options
- per-upload progress + abort tracked in the shared uploadProgressMap,
used by both node views and the gallery dialog (one state system)
- size validation runs before staging, so over-limit files are rejected
without base64-encoding them; error placeholder keeps retry/replace
- file-size helpers consolidated into utils/fileSize (was 4 copies)
- video poster capture for upload previews
- dragged-type detection only on dragenter (no per-dragover churn)
YouTube's player throws SecurityError reading its own cookies in an opaque-origin sandbox and renders a black box. allow-same-origin keeps the embed's OWN origin (not ours); the escape concern only applies to same-origin content, which the allowlist precludes.
- MediaToolbar generalized (image/video/embed) with inline align buttons, no popup; embeds get the same toolbar with a Change link action (updateIframeAt swaps src in place, keeping caption/align) - video nodes get an align attribute, same dispatch as images - custom video controls (play/seek/time/mute/fullscreen) replace the native controls attribute in node views; hidden while resizing - resize commit keeps inline drag styles until the attr re-render lands, fixing the one-frame size flash on tall media; resize drag uses pointer events end to end - upload error state rendered inside the media box (overlay on preview, inline in the placeholder) instead of below it - drop indicator: 3px rounded gray bar via configured Dropcursor
dialog: - gapped, rounded grid cells with grab cursor + drag dim state - captions stay visible once set; hover-reveal only when empty - image count in header, single hint line, count-aware Insert CTA - per-cell progress/cancel from the shared upload state node view: - on-selection dark toolbar (edit + inline 2/3/4 column buttons) replaces the always-visible Edit button and columns dropdown - click selects the node with the standard selection ring; selection survives column switches (pointerdown swallowed on the toolbar) - cells match the dialog look (gap + rounding) - menu label: Image -> Image / Gallery
- app.use(FrappeUI, { config: {...} }) applies config keys at install,
one declarative entry point instead of scattered setConfig calls
- new maxFileSize config key; file-size helpers read it first and fall
back to the legacy window/frappe.boot globals (deprecated)
- export FrappeUIConfig type and the fileSize utils
- Replace the bottom-right diagonal resize button on media/embed node views with Notion-style pill handles on the left and right edges (shared MediaResizeHandles component); left-edge drags invert the pointer delta in useNodeViewResize. - Fix the post-resize layout bug where the container stretched to full editor width: stop clearing inline drag styles that Vue style bindings own (Vue skips re-writing unchanged values), and clear only attribute-sized media elements (new mediaSizing option, the iframe opts into 'style'). - Restyle the gallery's remove button from a white circle to the dark media-toolbar idiom. - Add `isolate` to media node roots so internal toolbar z-indexes can't leak above outside UI. - Extract the inline upload progress overlay into a shared UploadProgressIndicator component (media node view + gallery cells). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Surface every first-party embed integration (YouTube, Vimeo, CodePen, CodeSandbox, Figma, Google Docs, Notion) directly in the slash menu, generated from PLATFORM_CONFIGS; each opens the same embed dialog pre-titled for the platform with a platform example placeholder. openIframeDialog(platform?) threads the platform through the command, controller, and dialog. A custom iframe allowlist that excludes a platform's hosts prunes its shortcut (allowlistPermitsHosts). - Group slash commands under dropdown-style section headers (Text, Lists, Media, Embeds, Insert). Grouping is display-only: headers derive from consecutive runs of item.group in the filtered list, so keyboard navigation stays flat and empty groups vanish on filter. - Align the legacy TextEditor's Commands.iframe declaration with the new openIframeDialog signature (divergent merged declarations are a TS2717 error). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…stored dimension The resize locked its aspect ratio from the node's stored attrs (height / width). When a media node stores only `width` — its height coming from CSS `height: auto` — that ratio disagreed with what was painted: a tall image distorted to a wider shape mid-drag, then snapped back on release once the inline drag styles were cleared and `height: auto` re-derived the true ratio. Lock the ratio from the element's rendered box (offsetHeight / offsetWidth) at drag start instead — the painted box is the source of truth for what the user sees, so the shape is preserved for any attr state. `getAspectRatio` remains a fallback for the pre-layout case (offsetWidth 0). Adds regression tests for both paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Images/videos rendered at 2px, the gallery at 10px, and embeds at 12px. Standardize every media node — image, video, gallery, embed, and their caption overlays — on the design system's default `rounded` (8px) token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Minor nits — large editor PR, but well-tested and the new public surface is clean. No
Tests are solid (8 new |
fb6691b to
0b5d2ea
Compare
getMaxFileSize now reads only the app-provided maxFileSize config (setConfig). The window/frappe.boot fallback and its Window global augmentation were a migration shim with no remaining consumers. 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
A batch of work on the molecules editor (
src/molecules/editor): a custom StarterKit, a unified media node-view experience (images, video, galleries, embeds), and a set of correctness fixes.Highlights
Core
StarterKitwith a customfrappeStarterKitfor finer control over the bundled extensions.FrappeUIplugin (no window/global reads).useEditor/useFloatingPopup; stale-update guards in the media pipeline.Media node views
UploadProgressIndicator.isolateon media roots so internal toolbar z-indexes can't leak above outside UI.rounded) corners across image, video, gallery, and embed.Embeds
PLATFORM_CONFIGS; each opens the embed dialog pre-titled for the platform. Custom allowlists prune unavailable platforms.allow-same-originadded to the iframe sandbox so embeds render.Slash commands
Fixes
/linkinserts editable placeholder text.Testing
yarn vitest --run src/molecules/editor→ 101 passing (incl. new resize regression tests).🤖 Generated with Claude Code
Docs preview: https://ui.frappe.io/pr-preview/pr-765/
Coverage: 56.89% (+0.80% vs
main)