From 5ef2c7e8d35be8017a5196fd9f13c6e5590294b4 Mon Sep 17 00:00:00 2001 From: lunaqiu Date: Sat, 15 Aug 2026 11:14:58 +0800 Subject: [PATCH] fix(canvas): ellipsize overflowing frame labels The frame label input had no `text-overflow`, so a name wider than the frame's capped screen width was hard-clipped mid-character with no sign that more text followed. Zooming out made it worse, since the label cap tracks `nodeWidth * zoom` while the text stays 12px. Add `text-ellipsis` to the label input. It only paints while the input is unfocused, so the idle label truncates with an ellipsis and editing still scrolls normally. Refs #101 --- apps/web/src/components/Nodes/frame/FrameNode.tsx | 4 +++- docs/architecture/canvas-zoom-rendering.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/Nodes/frame/FrameNode.tsx b/apps/web/src/components/Nodes/frame/FrameNode.tsx index bf06a13b4..8b707964f 100644 --- a/apps/web/src/components/Nodes/frame/FrameNode.tsx +++ b/apps/web/src/components/Nodes/frame/FrameNode.tsx @@ -510,7 +510,9 @@ export const FrameNode = memo( tooltipOffset={0} size={1} className={clsx( - 'nodrag col-start-1 row-start-1 w-full min-w-0! bg-transparent px-1.5 text-xs font-medium outline-none', + // `text-ellipsis` only paints on an unfocused input, so the + // idle label shows "…" while editing still scrolls normally. + 'nodrag col-start-1 row-start-1 w-full min-w-0! bg-transparent px-1.5 text-xs font-medium text-ellipsis outline-none', isEditingLabel ? 'text-fg-default cursor-text' : 'text-fg-muted hover:text-fg-default cursor-pointer', diff --git a/docs/architecture/canvas-zoom-rendering.md b/docs/architecture/canvas-zoom-rendering.md index 0c1038a92..c7e37e013 100644 --- a/docs/architecture/canvas-zoom-rendering.md +++ b/docs/architecture/canvas-zoom-rendering.md @@ -102,7 +102,7 @@ When frame labels collide, the higher-priority label wins: The first three interaction states force the affected label to remain visible and use matching overlay layers in descending order. With no interaction, the outer frame wins because zoomed-out views prioritize structural context over nested detail; the inner label returns after sufficient screen-space separation. -Frame label width is capped to the transformed frame width with a 48 px usability floor. Overflowing names truncate visually while the input title retains access to the complete name. +Frame label width is capped to the transformed frame width with a 48 px usability floor. An overflowing name truncates with an ellipsis so the clipped remainder is visible as such; the ellipsis disappears while the label is being edited, where the input scrolls instead. `FrameNode` owns the hierarchy and collision policy because it is frame-specific. `NodeWrapper` remains generic: it converts node coordinates to screen coordinates, applies owner-provided semantic visibility and width, handles interaction reveal, and performs opacity/FLIP transitions.