From 5ad932de067046be42b11e4d6918e3158ab8adaa Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 10 Mar 2026 11:52:37 +0000 Subject: [PATCH] feat: add TextSelectionOverlay for text node selection - Add dedicated TextSelectionOverlay (single selection only) - Hide DOM overlay when WASM backend + text edit mode (WASM highlight only) - Branch SingleSelectionOverlay on tspan to use TextSelectionOverlay - Keeps text overlay logic trackable in one entry point Co-authored-by: universe --- .../grida-canvas-react/viewport/surface.tsx | 13 ++++++++ .../ui/surface-text-selection-overlay.tsx | 31 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 editor/grida-canvas-react/viewport/ui/surface-text-selection-overlay.tsx diff --git a/editor/grida-canvas-react/viewport/surface.tsx b/editor/grida-canvas-react/viewport/surface.tsx index efd80c416f..7436e63076 100644 --- a/editor/grida-canvas-react/viewport/surface.tsx +++ b/editor/grida-canvas-react/viewport/surface.tsx @@ -50,6 +50,7 @@ import { SnapGuide } from "./ui/snap"; import { Knob } from "./ui/knob"; import { cursors } from "../../components/cursor/cursor-data"; import { SurfaceTextEditor } from "./ui/surface-text-editor"; +import { TextSelectionOverlay } from "./ui/surface-text-selection-overlay"; import { SurfaceVectorEditor } from "./ui/surface-vector-editor"; import { SurfaceGradientEditor } from "./ui/surface-gradient-editor"; import { SurfaceImageEditor } from "./ui/surface-image-editor"; @@ -953,6 +954,18 @@ function SingleSelectionOverlay({ const { node, distribution, rotation, boundingSurfaceRect, size, object } = data; + if (node.type === "tspan") { + return ( +
+ +
+ ); + } + const padding = node.type === "container" || node.type === "component" ? { diff --git a/editor/grida-canvas-react/viewport/ui/surface-text-selection-overlay.tsx b/editor/grida-canvas-react/viewport/ui/surface-text-selection-overlay.tsx new file mode 100644 index 0000000000..27230845b5 --- /dev/null +++ b/editor/grida-canvas-react/viewport/ui/surface-text-selection-overlay.tsx @@ -0,0 +1,31 @@ +"use client"; + +import React from "react"; +import { + useBackendState, + useContentEditModeMinimalState, +} from "@/grida-canvas-react/provider"; + +/** + * Text-dedicated selection overlay (single selection only). + * + * Entry point for all text-specific selection overlay logic. When the selected + * node is a text node (tspan), SingleSelectionOverlay delegates here. + * + * Behavior: + * - **WASM + text edit mode:** Renders nothing. The outline is drawn by WASM + * via highlightStrokes; no DOM overlay avoids a stale rect as the user types. + * - **Otherwise:** Renders the frame (children, typically NodeOverlay). + */ +export function TextSelectionOverlay({ + children, +}: React.PropsWithChildren<{ node_id: string; readonly?: boolean }>) { + const backend = useBackendState(); + const content_edit_mode = useContentEditModeMinimalState(); + + if (backend === "canvas" && content_edit_mode?.type === "text") { + return null; + } + + return <>{children}; +}