Skip to content

fix: improve node layout and fix image-related issues in rich-text nodes#59

Merged
winlp4ever merged 14 commits intomainfrom
fix/editor-image-block-serialize
Apr 28, 2026
Merged

fix: improve node layout and fix image-related issues in rich-text nodes#59
winlp4ever merged 14 commits intomainfrom
fix/editor-image-block-serialize

Conversation

@winlp4ever
Copy link
Copy Markdown
Contributor

No description provided.

- block-handle: drop nested mode so atom blocks (math, hr) get a handle
  and list-item bullets no longer collide with the floating button
- code-block: cache the shiki highlighter and switch to a sync
  highlightCodeSync path; first-load fallback shows the editable layer
  while grammars stream in, eliminating the plain-to-colored flash
- math-edit-popover: drop the inline preview, debounce live setNodeMarkup
  by 70ms, mark live updates as addToHistory:false so undo only sees the
  final commit
Three insertion paths share the canvas pipeline (downscale → upload):
- /image slash command opens the file picker
- drag-and-drop image files anywhere in the editor (refused inside code blocks)
- paste images from the clipboard

Persists the short server filePath as the image src instead of an inline
base64 dataUrl so markdown stays under backend embedding limits. The
ImageNodeView resolves filePath → dataUrl on render via GET /files, with
an in-memory cache pre-warmed at insert time. Patches markdown-it's
validateLink so file:// URLs round-trip through tiptap-markdown instead
of falling back to plain text.
…ze observer

Replace ResizeObserver-based content measurement with a pure-compute estimator
that mirrors canvas-lite's tokenize/layout pipeline, so display-mode nodes
(AI-generated, post-edit text) no longer clip when their persisted height is
under what the content actually needs. Math blocks get a flat per-block
allowance via the lite-markdown delimiters. Padding/scale math is unified in
one helper used by Shape, the min-height hook, and the mindmap importer.
Hook is gated to shape-with-markdown node types only — sheets, folders,
sandboxes, widgets, images, icons, slides keep their own intrinsic sizing.
…eload

The default prosemirror-markdown image serializer omits closeBlock since
it was written for inline images. Our image node is block-level, so the
following block (e.g. a code fence) glued onto the same line and the
document broke on reload.
Replaces the hand-rolled tree/row layout with igraph's Sugiyama via the
existing layout_directed helper. Cycles like a -> b -> c -> a are now
laid out as a layered chain with the back-edge drawn as an arch instead
of collapsing to a single horizontal line.

Adds anchor-aware component discovery: links created this turn that
touch an old (existing) node pull that node into the component as a
fixed anchor. The component runs through Sugiyama with the anchor
included, then the whole layout is translated so the leftmost anchor
lands back at its real-world position. The anchor itself is never
patched, so user-positioned content stays put while new descendants
slide into place around it. Multi-anchor components honor only one
anchor; secondary anchors keep their position with longer connecting
edges.

Cleanup: drops _classify_tree, _layout_tree_lr, _layout_row, and the
custom subtree-height pass. Tests refreshed to use semantic assertions
(rank ordering, distinct rows) instead of exact Reingold-Tilford pixel
positions, plus new tests covering the cycle and anchor cases.
Tab inserts two spaces (matches the canvas code-snippet editor and most
toolchains). Enter preserves the current line's leading whitespace and
adds one extra indent level after `:` for python/yaml or `{` for
brace-style languages (ts, js, java, c, cpp, csharp, rust, go, css, json).
Mount a per-env backend_data named volume on each backend service in build/docker-compose.yml and a single backend_data volume in build/docker-compose.images.yml so uploaded and agent-generated images under /app/data survive container rebuilds and image bumps. Database volumes are untouched.
…ayout

Sibling pitch now derives from median(heights) + 30 instead of max(heights)
+ 75. Median is robust to outlier-tall nodes — a single tall sheet no longer
pushes every sibling apart. An outlier may overlap its immediate neighbors
by a small amount, which reads as visual emphasis rather than a layout bug.
Rank pitch (max width + RANK_PAD) is unchanged.

Tree-shaped components now lay out as real mindmaps: when the component is
a clean tree (single root, no cycles, no multi-parents), the root has at
least two children, and no anchor is present, the root's direct children
are partitioned into a left and right set via greedy subtree-size balance
(tiebreaker prefers right). Sugiyama runs twice — LR on the right half,
RL on the left half — and the two halves are stitched by aligning the
shared root. Cycles, DAGs, single-child chains, and anchor cases keep the
single-direction LR layout from before.
Drop focus-within ring opacity from /50 to /20 and lower the ring-pulse-soft alphas to 0.30/0.10 so the active and streaming states feel softer next to the solid border.
Markdown-it doesn't decode %20 on parse, so the in-doc src kept its
encoded form after a reload; encodeURI re-encoded the % on the next
save and the URL grew %2520, %252520, etc. The serializer now decodes
iteratively (capped) before encoding so any number of save/parse cycles
collapse to a single canonical form. The resolver decodeURIs once
before the cache lookup and the GET /files filename, so both new
inserts (literal src) and reloaded encoded src land on the same key
the backend stores.
@winlp4ever winlp4ever merged commit 296c2ad into main Apr 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant