Daily RC#486
Conversation
WalkthroughThe PR adds DPR-aware canvas mounting, injects UI providers (notify/clipboard/eyedropper) into Editor/EditorSurface, migrates sidebar UI to PropertySection-based components with multi-node mixed-property support, extends schema and defaults with stroke/corner-radius fields, refactors paint/fill editing and mixed-properties to selector-driven APIs, and adjusts z-index and viewport safety handling. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client Code
participant Hook as useEditor Hook
participant Editor as Editor
participant Surface as EditorSurface
participant UI as UI Providers
Client->>Hook: useEditor(init?, backend?, ui?)
Hook->>Hook: build default UI providers (notify, clipboard, eyedropper)
Hook->>Editor: new Editor({...init, ui})
Editor->>Surface: new EditorSurface(config, ui, window)
Surface->>UI: use ui.notify / ui.clipboard / ui.eyedropper
Client->>Editor: mount(canvasElement, dpr)
Editor->>Surface: mount(canvasElement, dpr) — apply deviceScale = dpr
Surface->>Surface: initialize rendering with DPR-aware scale
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…for improved UI consistency
… integration into editor actions
…nhancing edit mode handling and component structure
…ng state subscriptions and improving paint entry management
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (17)
docs/wg/feat-text-editing/index.md (1)
27-27: Use hyphen for compound modifier.Line 27 should use "full-text shaping" as a compound adjective when modifying "pipelines."
🔎 Proposed fix
- Rich text authoring UI (toolbars, inline formatting UX). - Document-wide layout engines (pagination, full text shaping pipelines exposed publicly). + Rich text authoring UI (toolbars, inline formatting UX). + Document-wide layout engines (pagination, full-text shaping pipelines exposed publicly).editor/grida-canvas-react-starter-kit/starterkit-hierarchy/tree-node.tsx (1)
340-350: Use a dedicated initialization flag instead of length check in useEffect dependency.The dependency array uses
userExpandedItems.lengthto track whether initialization has occurred. This works but is unconventional and makes the intent less clear. Consider using a separateinitializedflag (via state or ref) to explicitly signal one-time initialization, or restructure the effect to only depend ontree.+ const initializedRef = useRef(false); useEffect(() => { - if (tree && userExpandedItems.length === 0) { + if (tree && !initializedRef.current) { + initializedRef.current = true; const currentExpanded = tree .getItems() .filter((item) => item.isExpanded()) .map((item) => item.getId()); setUserExpandedItems(currentExpanded); } - }, [tree, userExpandedItems.length]); + }, [tree]);editor/grida-canvas/backends/dom.ts (2)
49-66: Extract duplicated fallback DOMRect logic.The
rectgetter andgetViewportRect()method contain identical fallback logic (lines 49-66 and 85-102). Consider extracting this into a private helper method to reduce duplication.🔎 Proposed refactor to eliminate duplication
+ private getDefaultDOMRect(): DOMRect { + return { + left: 0, + top: 0, + right: typeof window !== "undefined" ? window.innerWidth : 0, + bottom: typeof window !== "undefined" ? window.innerHeight : 0, + width: typeof window !== "undefined" ? window.innerWidth : 0, + height: typeof window !== "undefined" ? window.innerHeight : 0, + x: 0, + y: 0, + toJSON: () => ({}), + } as DOMRect; + } + get rect() { const viewport = this.getViewport(); if (!viewport) { - // Return default rect when viewport element doesn't exist yet - return { - left: 0, - top: 0, - right: typeof window !== "undefined" ? window.innerWidth : 0, - bottom: typeof window !== "undefined" ? window.innerHeight : 0, - width: typeof window !== "undefined" ? window.innerWidth : 0, - height: typeof window !== "undefined" ? window.innerHeight : 0, - x: 0, - y: 0, - toJSON: () => ({}), - } as DOMRect; + return this.getDefaultDOMRect(); } return viewport.getBoundingClientRect(); } getViewportRect() { const viewport = this.getViewport(); if (!viewport) { - // Return default rect when viewport element doesn't exist yet - return { - left: 0, - top: 0, - right: typeof window !== "undefined" ? window.innerWidth : 0, - bottom: typeof window !== "undefined" ? window.innerHeight : 0, - width: typeof window !== "undefined" ? window.innerWidth : 0, - height: typeof window !== "undefined" ? window.innerHeight : 0, - x: 0, - y: 0, - toJSON: () => ({}), - } as DOMRect; + return this.getDefaultDOMRect(); } return viewport.getBoundingClientRect(); }Also applies to: 85-102
68-83: Extract duplicated fallback size logic.The
sizegetter andgetViewportSize()method contain identical fallback logic (lines 68-83 and 104-119). Consider extracting this into a private helper method to reduce duplication.🔎 Proposed refactor to eliminate duplication
+ private getDefaultSize(): { width: number; height: number } { + return { + width: typeof window !== "undefined" ? window.innerWidth : 0, + height: typeof window !== "undefined" ? window.innerHeight : 0, + }; + } + get size() { const viewport = this.getViewport(); if (!viewport) { - // Return default size when viewport element doesn't exist yet - // This can happen during initial render before ViewportRoot mounts - return { - width: typeof window !== "undefined" ? window.innerWidth : 0, - height: typeof window !== "undefined" ? window.innerHeight : 0, - }; + return this.getDefaultSize(); } const rect = viewport.getBoundingClientRect(); return { width: rect.width, height: rect.height, }; } getViewportSize(): { width: number; height: number } { const viewport = this.getViewport(); if (!viewport) { - // Return default size when viewport element doesn't exist yet - // This can happen during initial render before ViewportRoot mounts - return { - width: typeof window !== "undefined" ? window.innerWidth : 0, - height: typeof window !== "undefined" ? window.innerHeight : 0, - }; + return this.getDefaultSize(); } const rect = viewport.getBoundingClientRect(); return { width: rect.width, height: rect.height, }; }Also applies to: 104-119
editor/grida-canvas/reducers/event-target.reducer.ts (2)
772-836: Type-driven corner-radius handling improves maintainability.The refactored logic clearly separates rectangular corner radius support (rectangle, container, component, image, video) from unified corner radius (polygon, star, vector, boolean).
One observation: when
anchoris not provided (lines 799-815), the code updates bothcorner_radiusAND all fourrectangular_corner_radius_*keys. This maintains consistency across both representations but creates redundancy. If this is intentional for backward compatibility or data model consistency, consider adding a brief comment explaining the dual update.💡 Optional: Add explanatory comment for dual corner radius update
} else { const current = typeof node.corner_radius == "number" ? node.corner_radius : 0; const nextRadius = current + d; const nextRadiusClamped = Math.floor( Math.min(maxRadius, Math.max(0, nextRadius)) ); + // Update both unified corner_radius and rectangular variants + // to maintain consistency across the data model draft.document.nodes[node_id] = nodeReducer(node, { type: "node/change/*", corner_radius: nextRadiusClamped, rectangular_corner_radius_top_left: nextRadiusClamped, rectangular_corner_radius_top_right: nextRadiusClamped, rectangular_corner_radius_bottom_right: nextRadiusClamped, rectangular_corner_radius_bottom_left: nextRadiusClamped, node_id, }); }
774-836: Consider adding a default case to the switch statement.The switch statement handles specific node types that support corner radius, but lacks a default case. While the covered types appear comprehensive, adding a default case would make the code more defensive against future node types or edge cases.
💡 Optional: Add default case for robustness
case "boolean": { // These node types use unified corner_radius const current = typeof node.corner_radius == "number" ? node.corner_radius : 0; const nextRadius = current + d; const nextRadiusClamped = Math.floor( Math.min(maxRadius, Math.max(0, nextRadius)) ); draft.document.nodes[node_id] = nodeReducer(node, { type: "node/change/*", corner_radius: nextRadiusClamped, node_id, }); break; } + default: + // Node type doesn't support corner_radius adjustment + break; }editor/scaffolds/sidecontrol/controls/paint-gradient.tsx (1)
85-175: Gradient stop UI/focus wiring is solid; verify custom layout utility classesThe restructuring around
GradientStopsSlider, the “Stops” header with a plus button, andGradientStopRowusingPropertyRowwithfocused/onFocusall hangs together well, and the per-row update/remove handlers keepstopsimmutable.One thing to double-check:
className="flex-1/4"andclassName="flex-3/4"are not standard utility classes in common CSS utility frameworks; ensure these are backed by your design system or switch to width/basis utilities if they’re currently no-ops.packages/grida-canvas-schema/grida.ts (1)
2268-2284: Ensure ContainerNode stroke defaults are consistent across factoriesExtending
ContainerNodewithi.IStrokeand seeding stroke defaults increateContainerNode(stroke_width, align/cap/join, miter limit) makes containers first-class strokeable nodes and looks correct.However, the
createNodeDataFromPrototypeWithoutChildren"container"case still doesn’t initialize any of the new stroke fields, so containers created via that path will havestroke_width/stroke_align/etc. undefined unless provided in the prototype. For a tighter invariant, consider aligning its defaults withcreateContainerNodeso all containers have a well-defined stroke baseline regardless of construction path.Also applies to: 2870-2912
editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (3)
287-396: Mixed base/appearance controls correctly useuseMixedProperties
ModeMixedNodeProperties’ base section and “Appearance” header make good use ofuseMixedProperties:
name,active,locked,blend_modeall respect mixed state via.mixed,.value, and.ids.- Updates fan out to either the per-property
idsor the fullidsfallback, which keeps behavior predictable when only some nodes share a value.- Lock toggle behavior for mixed (disabled + closed icon) is a sensible UX compromise.
There’s some repetition in per-property loops (
target.forEach(id => …)), but that can be deferred; functionally it looks correct.Also applies to: 437-508
907-979: Position and layout mixed sections handle multi-node updates carefullyThe new
SectionMixedPositionandSectionLayout/SectionLayoutMixedcomponents:
- Use
useMixedPropertiesto derive shared or mixed values for position, rotation, width/height, layout, alignment, gap, and padding.- Apply constraints updates via editor commands (
changeNodePropertyPositioning,changeNodeSize,changeContainerNodeLayout,changeFlexContainerNode*, etc.) across the right subset of ids (e.g. only containers, only flex containers).- Provide sensible fallbacks when values are mixed (
grida.mixed) so controls either show a neutral state or avoid committing incorrect partial data.The logic is a bit verbose but structurally sound and matches the mixed-properties API.
Also applies to: 981-1076, 1078-1237
1585-1727: Corner radius and padding mixed controls correctly gate on supportThe corner-radius and padding sections:
- Single-node: respect
supports.cornerRadius/supports.cornerRadius4to choose between 1-value and 4-value controls.- Mixed: use
useMixedPropertiesto determine whether 4-corner values are consistent enough to show a detailed editor, otherwise fall back to a scalar radius editor withdisabledwhen mixed.- Padding mixed section correctly limits itself to container nodes and writes via
changeContainerNodePadding.The support checks and id targeting look right; behavior should be safe across heterogeneous selections.
Also applies to: 1729-1810
editor/scaffolds/sidecontrol/chunks/section-strokes.tsx (1)
161-175: Consider consolidating the two node-change reset effects.Both
useEffecthooks resetopenPaintIndexwhen the node context changes. The first reacts tocontent_edit_modechanges and the second tonode_idchanges. These could be merged into a single effect for clarity.🔎 Suggested consolidation
- // Reset user-controlled state when content edit mode changes to a different node - React.useEffect(() => { - const { content_edit_mode } = instance.state; - if (content_edit_mode && content_edit_mode.node_id !== node_id) { - setOpenPaintIndex(null); - } - }, [instance, node_id]); - - // Reset open state when node_id changes (different node selected) - const prevNodeIdRef = React.useRef(node_id); - React.useEffect(() => { - if (prevNodeIdRef.current !== node_id) { - prevNodeIdRef.current = node_id; - setOpenPaintIndex(null); - } - }, [node_id]); + // Reset open state when node context changes + const prevNodeIdRef = React.useRef(node_id); + React.useEffect(() => { + const { content_edit_mode } = instance.state; + const nodeChanged = prevNodeIdRef.current !== node_id; + const editModeOnDifferentNode = content_edit_mode && content_edit_mode.node_id !== node_id; + + if (nodeChanged || editModeOnDifferentNode) { + setOpenPaintIndex(null); + prevNodeIdRef.current = node_id; + } + }, [instance, node_id]);editor/scaffolds/sidecontrol/ui/index.tsx (2)
66-78: Thehiddenprop is not part ofReact.HtmlHTMLAttributes<HTMLDivElement>.The
hiddenprop is destructured butHtmlHTMLAttributesdoesn't include it by default. Consider adding it explicitly to the props type for clarity.🔎 Suggested type fix
export function PropertySection({ children, className, hidden, ...props -}: React.PropsWithChildren<React.HtmlHTMLAttributes<HTMLDivElement>>) { +}: React.PropsWithChildren<React.HtmlHTMLAttributes<HTMLDivElement> & { hidden?: boolean }>) { if (hidden) return null;
148-168: Consider improving theforwardReftyping.The
as anycast on line 158 bypasses type safety. Using proper generic typing would provide better intellisense and catch type errors.🔎 Suggested improvement
-export const PropertySectionHeaderAction = React.forwardRef( - function PropertySectionHeaderAction( - { - children, - ...props - }: React.PropsWithChildren<React.ComponentProps<typeof Button>>, - forwardedRef - ) { - return ( - <Button - ref={forwardedRef as any} +export const PropertySectionHeaderAction = React.forwardRef< + HTMLButtonElement, + React.ComponentProps<typeof Button> +>(function PropertySectionHeaderAction({ children, ...props }, forwardedRef) { + return ( + <Button + ref={forwardedRef}editor/grida-canvas-react/use-mixed-properties.ts (1)
71-79: Potential unnecessary re-renders from unstableoptionsreferences.If callers pass
optionsinline (e.g.,{ ignoredKey: ["id"] }), the useMemo will recompute on every render sinceoptions?.ignoredKeyandoptions?.comparewill be new references each time. Consider memoizing within the hook or documenting this requirement.🔎 Suggested improvement
+ // Stabilize options references to prevent unnecessary recomputation + const ignoredKey = options?.ignoredKey ?? (["id"] as (keyof WithId<T>)[]); + const compare = options?.compare; return useMemo(() => { return mixed<WithId<T>, typeof grida.mixed>(slice, { idKey: "id", - ignoredKey: options?.ignoredKey ?? (["id"] as (keyof WithId<T>)[]), + ignoredKey, mixed: grida.mixed, - compare: options?.compare, + compare, }); - }, [slice, options?.ignoredKey, options?.compare]); + }, [slice, ignoredKey, compare]);Alternatively, document that callers should memoize these options.
editor/grida-canvas/editor.ts (2)
4550-4567: Inconsistent clipboard API usage.
writeClipboardMediausesnavigator.clipboarddirectly (line 4566), whilewriteClipboardSVGusesthis.ui.clipboard(lines 4593-4595). For consistency and testability, consider using the UI provider for all clipboard operations.🔎 Suggested fix
const blob = new Blob([data as BlobPart], { type: "image/png" }); - await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]); + await this.ui.clipboard?.write([new ClipboardItem({ "image/png": blob })]); return true;
5375-5446: NestedrequestAnimationFrameis a known workaround - consider adding a TODO.The comment explains why nested RAF is needed (lack of textlayout size API), but this pattern can cause visual flicker and is fragile. Consider adding a TODO or tracking issue reference.
🔎 Suggested addition
// FIXME: nested raf. // why this is needed? // currently, the api does not expose a way or contains value for textlayout size, not the box size. // since we can't pre-calculate the delta, this is the dirty hack to first resize, then get the next size, shift delta. // => need api/data that holds actual textlayout size (non box size) + // TODO: Track this at [issue link] - expose textlayout size from geometry provider requestAnimationFrame(() => {
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
branding/project-maps.pngis excluded by!**/*.png
📒 Files selected for processing (33)
branding/README.mddocs/wg/feat-text-editing/index.mdeditor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/grida-canvas-hosted/playground/playground.tsxeditor/grida-canvas-react-starter-kit/data/artboards.jsoneditor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsxeditor/grida-canvas-react-starter-kit/starterkit-hierarchy/tree-node.tsxeditor/grida-canvas-react/index.tseditor/grida-canvas-react/ui-config.tseditor/grida-canvas-react/use-editor.tsxeditor/grida-canvas-react/use-mixed-properties.tseditor/grida-canvas-react/viewport/surface.tsxeditor/grida-canvas/backends/dom.tseditor/grida-canvas/editor.i.tseditor/grida-canvas/editor.tseditor/grida-canvas/reducers/event-target.reducer.tseditor/grida-canvas/reducers/tools/initial-node.tseditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/controls/color.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/scaffolds/sidecontrol/controls/fill.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/scaffolds/sidecontrol/controls/paint.tsxeditor/scaffolds/sidecontrol/controls/width-height.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/ui/index.tsxpackages/grida-canvas-io-figma/lib.tspackages/grida-canvas-schema/grida.tspackages/grida-mixed-properties/index.ts
💤 Files with no reviewable changes (1)
- editor/scaffolds/sidecontrol/controls/fill.tsx
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript 5 as the main language for most apps
Use Lucide or Radix Icons for icons
Files:
editor/grida-canvas-react-starter-kit/starterkit-hierarchy/tree-node.tsxeditor/grida-canvas/backends/dom.tseditor/scaffolds/sidecontrol/controls/color.tsxeditor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsxeditor/grida-canvas/reducers/event-target.reducer.tseditor/scaffolds/sidecontrol/controls/paint.tsxpackages/grida-canvas-io-figma/lib.tseditor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/grida-canvas-react/use-editor.tsxeditor/grida-canvas-react/use-mixed-properties.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/scaffolds/sidecontrol/controls/width-height.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/grida-canvas-react/viewport/surface.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/grida-canvas/reducers/tools/initial-node.tseditor/grida-canvas-hosted/playground/playground.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/editor.i.tseditor/grida-canvas-react/index.tseditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/grida-canvas/editor.tspackages/grida-mixed-properties/index.tspackages/grida-canvas-schema/grida.tseditor/grida-canvas-react/ui-config.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use React.js 19 for web applications
Files:
editor/grida-canvas-react-starter-kit/starterkit-hierarchy/tree-node.tsxeditor/scaffolds/sidecontrol/controls/color.tsxeditor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsxeditor/scaffolds/sidecontrol/controls/paint.tsxeditor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/grida-canvas-react/use-editor.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/scaffolds/sidecontrol/controls/width-height.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/grida-canvas-react/viewport/surface.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/grida-canvas-hosted/playground/playground.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
{editor/**/*.{ts,tsx},packages/grida-canvas-*/**/*.{ts,tsx}}
📄 CodeRabbit inference engine (AGENTS.md)
Use DOM (plain DOM as canvas) for website builder canvas, bound with React
Files:
editor/grida-canvas-react-starter-kit/starterkit-hierarchy/tree-node.tsxeditor/grida-canvas/backends/dom.tseditor/scaffolds/sidecontrol/controls/color.tsxeditor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsxeditor/grida-canvas/reducers/event-target.reducer.tseditor/scaffolds/sidecontrol/controls/paint.tsxpackages/grida-canvas-io-figma/lib.tseditor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/grida-canvas-react/use-editor.tsxeditor/grida-canvas-react/use-mixed-properties.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/scaffolds/sidecontrol/controls/width-height.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/grida-canvas-react/viewport/surface.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/grida-canvas/reducers/tools/initial-node.tseditor/grida-canvas-hosted/playground/playground.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/editor.i.tseditor/grida-canvas-react/index.tseditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/grida-canvas/editor.tspackages/grida-canvas-schema/grida.tseditor/grida-canvas-react/ui-config.ts
editor/grida-*/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use /editor/grida-* directories to isolate domain-specific modules; promote well-defined modules to /packages
Files:
editor/grida-canvas-react-starter-kit/starterkit-hierarchy/tree-node.tsxeditor/grida-canvas/backends/dom.tseditor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsxeditor/grida-canvas/reducers/event-target.reducer.tseditor/grida-canvas-react/use-editor.tsxeditor/grida-canvas-react/use-mixed-properties.tseditor/grida-canvas-react/viewport/surface.tsxeditor/grida-canvas/reducers/tools/initial-node.tseditor/grida-canvas-hosted/playground/playground.tsxeditor/grida-canvas/editor.i.tseditor/grida-canvas-react/index.tseditor/grida-canvas/editor.tseditor/grida-canvas-react/ui-config.ts
editor/scaffolds/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use /editor/scaffolds for feature-specific larger components, pages, and editors
Files:
editor/scaffolds/sidecontrol/controls/color.tsxeditor/scaffolds/sidecontrol/controls/paint.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/scaffolds/sidecontrol/controls/width-height.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
packages/grida-canvas-*/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Packages under /packages/grida-canvas-* power the canvas; some are published to npm, refer to individual package README
Files:
packages/grida-canvas-io-figma/lib.tspackages/grida-canvas-schema/grida.ts
{next.config.*,**/app/**/*.{ts,tsx},**/pages/**/*.{ts,tsx}}
📄 CodeRabbit inference engine (AGENTS.md)
Use Next.js 15 for web applications
Files:
editor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsx
docs/**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Documentation source of truth is in the ./docs directory; deployment is handled by apps/docs
Files:
docs/wg/feat-text-editing/index.md
docs/{wg,reference}/**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
Only actively maintain docs/wg/** and docs/reference/** directories; see docs/AGENTS.md for contribution scope
Files:
docs/wg/feat-text-editing/index.md
🧠 Learnings (23)
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to {editor/**/*.{ts,tsx},packages/grida-canvas-*/**/*.{ts,tsx}} : Use DOM (plain DOM as canvas) for website builder canvas, bound with React
Applied to files:
editor/grida-canvas/backends/dom.tseditor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsxeditor/grida-canvas/reducers/event-target.reducer.tseditor/scaffolds/sidecontrol/controls/paint.tsxeditor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/grida-canvas-react/use-editor.tsxeditor/grida-canvas-react/use-mixed-properties.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/grida-canvas-react/viewport/surface.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/grida-canvas-hosted/playground/playground.tsxeditor/grida-canvas-react/index.tseditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/grida-canvas/editor.ts
📚 Learning: 2025-12-01T00:22:19.083Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-wasm/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:19.083Z
Learning: Applies to crates/grida-canvas-wasm/**/main.rs : Update `grida-canvas-wasm.d.ts` TypeScript definitions file when new APIs are introduced via `main.rs`
Applied to files:
editor/grida-canvas/backends/dom.tseditor/grida-canvas/reducers/event-target.reducer.tseditor/scaffolds/sidecontrol/controls/paint.tsxpackages/grida-canvas-io-figma/lib.tseditor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/grida-canvas-react/viewport/surface.tsxeditor/grida-canvas-hosted/playground/playground.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/grida-canvas/editor.tspackages/grida-mixed-properties/index.tspackages/grida-canvas-schema/grida.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to packages/grida-canvas-*/**/*.{ts,tsx,js,jsx} : Packages under /packages/grida-canvas-* power the canvas; some are published to npm, refer to individual package README
Applied to files:
editor/grida-canvas/backends/dom.tseditor/scaffolds/sidecontrol/controls/paint.tsxeditor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/grida-canvas-react/viewport/surface.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/grida-canvas-hosted/playground/playground.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas-react/index.tseditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/grida-canvas/editor.tspackages/grida-canvas-schema/grida.ts
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Use Canvas 2D API with path commands for rendering geometric shapes (circle, square, triangle, etc.)
Applied to files:
editor/grida-canvas/backends/dom.tseditor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsxeditor/grida-canvas/reducers/event-target.reducer.tseditor/scaffolds/sidecontrol/controls/paint.tsxpackages/grida-canvas-io-figma/lib.tseditor/app/(dev)/canvas/tools/io-markdown/page.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/grida-canvas-react/viewport/surface.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/grida-canvas/reducers/tools/initial-node.tseditor/grida-canvas-hosted/playground/playground.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxpackages/grida-canvas-schema/grida.ts
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : When adding new parameters to the halftone tool, add state with useState, include in useEffect dependency array, pass to renderHalftone() function, use in rendering logic, and add UI control
Applied to files:
editor/scaffolds/sidecontrol/controls/color.tsxeditor/scaffolds/sidecontrol/controls/paint.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/scaffolds/sidecontrol/controls/width-height.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/grida-canvas-react/index.tseditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : When adding new shape types, update the Shape type union, add cases in drawShape() function, add cases in shapeToSVG() function, and add SelectItem in UI
Applied to files:
editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsxeditor/grida-canvas/reducers/event-target.reducer.tseditor/scaffolds/sidecontrol/controls/paint.tsxpackages/grida-canvas-io-figma/lib.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/grida-canvas/reducers/tools/initial-node.tseditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/grida-canvas/editor.tspackages/grida-canvas-schema/grida.ts
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : For SVG export, convert circles to <circle> elements, rectangles to <rect> elements, and polygons to <polygon> elements with calculated points
Applied to files:
editor/grida-canvas/reducers/event-target.reducer.tseditor/scaffolds/sidecontrol/controls/paint.tsxpackages/grida-canvas-io-figma/lib.tseditor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/chunks/section-strokes.tsx
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Calculate halftone dot radius using: radius = (1 - mapped) * maxRadius, where darker areas get larger dots
Applied to files:
editor/grida-canvas/reducers/event-target.reducer.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/components/ui/**/*.{ts,tsx} : Use /editor/components/ui for shadcn UI components
Applied to files:
editor/scaffolds/sidecontrol/controls/paint.tsxeditor/grida-canvas-react/use-editor.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/grida-canvas/editor.ts
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Use React hooks for state management (imageSrc, shape, grid, maxRadius, gamma, jitter, opacity, color, customShapeImage, imageDataRef, sizeRef)
Applied to files:
editor/scaffolds/sidecontrol/controls/paint.tsxeditor/grida-canvas-react/use-editor.tsxeditor/grida-canvas-react/use-mixed-properties.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/grida-canvas-react/index.tseditor/scaffolds/sidecontrol/chunks/section-strokes.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/grida-*/**/*.{ts,tsx} : Use /editor/grida-* directories to isolate domain-specific modules; promote well-defined modules to <root>/packages
Applied to files:
editor/scaffolds/sidecontrol/controls/paint.tsxeditor/grida-canvas-react/use-editor.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsxeditor/grida-canvas/editor.tspackages/grida-mixed-properties/index.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/lib/**/*.{ts,tsx} : Use /editor/lib for core, strictly designed modules with non-opinionated, reusable, and stable implementations
Applied to files:
editor/grida-canvas-react/use-editor.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/grida-canvas/editor.i.tseditor/grida-canvas/editor.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/components/**/*.{ts,tsx} : Use /editor/components for generally reusable components
Applied to files:
editor/grida-canvas-react/use-editor.tsxeditor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/editor.i.ts
📚 Learning: 2025-12-01T00:22:06.800Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-fonts/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:06.800Z
Learning: Provide a high-level UI API for design tool integration through the `parse_ui` interface
Applied to files:
editor/grida-canvas-react/use-editor.tsx
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Cache ImageData and dimensions in refs (imageDataRef, sizeRef) for efficient exports
Applied to files:
editor/app/(dev)/canvas/tools/io-svg/page.tsxeditor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Image input should be automatically scaled down to a maximum of 1024px for performance
Applied to files:
editor/app/(dev)/canvas/tools/io-svg/page.tsx
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to **/components/ui/**/*.{ts,tsx} : Use Shadcn UI for UI component library
Applied to files:
editor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/editor.i.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/scaffolds/**/*.{ts,tsx} : Use /editor/scaffolds for feature-specific larger components, pages, and editors
Applied to files:
editor/scaffolds/sidecontrol/chunks/scale-tool.tsxeditor/scaffolds/sidecontrol/ui/index.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/scaffolds/sidecontrol/chunks/section-strokes.tsx
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/www/**/*.{ts,tsx} : Use /editor/www for landing page specific components
Applied to files:
editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
📚 Learning: 2025-12-14T23:30:50.384Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: docs/AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:50.384Z
Learning: Applies to docs/docs/wg/** : Actively maintain and edit documentation in `/docs/wg/**` (working group docs including design notes, architecture, proposals, and WIP)
Applied to files:
docs/wg/feat-text-editing/index.md
📚 Learning: 2025-12-01T00:22:06.800Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-fonts/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:06.800Z
Learning: Implement the Blink (Chrome) font selection model in the core font selection pipeline
Applied to files:
docs/wg/feat-text-editing/index.md
📚 Learning: 2025-12-20T08:11:16.220Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas/AGENTS.md:0-0
Timestamp: 2025-12-20T08:11:16.220Z
Learning: Applies to crates/grida-canvas/**/*.rs : Use `UserNodeId` (String) for public APIs that accept or return node IDs for stability and serialization
Applied to files:
packages/grida-canvas-schema/grida.ts
📚 Learning: 2025-12-20T08:11:16.220Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas/AGENTS.md:0-0
Timestamp: 2025-12-20T08:11:16.220Z
Learning: Applies to crates/grida-canvas/**/*.rs : Handle NodeId to UserNodeId conversion via `IdConverter` during .grida file loading
Applied to files:
packages/grida-canvas-schema/grida.ts
🧬 Code graph analysis (13)
editor/scaffolds/sidecontrol/controls/color.tsx (3)
packages/grida-canvas-schema/grida.ts (1)
RGBA32F(484-490)packages/grida-canvas-cg/lib.ts (1)
RGBA32F(86-86)packages/grida-canvas-color/lib.ts (1)
RGBA32F(57-76)
editor/grida-canvas/reducers/event-target.reducer.ts (1)
editor/grida-canvas/reducers/node.reducer.ts (1)
nodeReducer(889-970)
editor/scaffolds/sidecontrol/controls/paint.tsx (1)
editor/scaffolds/sidecontrol/controls/paint-gradient.tsx (1)
GradientControl(18-126)
editor/app/(dev)/canvas/tools/io-markdown/page.tsx (1)
crates/grida-canvas-wasm/example/main.js (1)
dpr(7-7)
editor/grida-canvas-react/use-editor.tsx (1)
editor/grida-canvas/editor.i.ts (3)
UINotifier(4643-4646)EditorContentRenderingBackend(21-21)UIUXProviders(4668-4680)
editor/app/(dev)/canvas/tools/io-svg/page.tsx (1)
crates/grida-canvas-wasm/example/main.js (1)
dpr(7-7)
editor/scaffolds/sidecontrol/chunks/scale-tool.tsx (1)
editor/scaffolds/sidecontrol/ui/index.tsx (6)
PropertySection(66-78)PropertySectionHeaderItem(89-108)PropertySectionHeaderLabel(110-124)PropertySectionHeaderActions(126-146)PropertySectionContent(80-87)PropertyRow(184-211)
editor/grida-canvas-react/viewport/surface.tsx (2)
editor/grida-canvas-react/ui-config.ts (1)
RULER_Z_INDEX(87-87)editor/grida-canvas/editor.i.ts (1)
eager_canvas_input(1202-1208)
editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (3)
editor/grida-canvas-react/index.ts (2)
useCurrentEditor(1-1)useMixedProperties(18-18)editor/grida-canvas-react/use-mixed-properties.ts (1)
useMixedProperties(32-79)editor/scaffolds/sidecontrol/ui/index.tsx (6)
PropertySection(66-78)PropertySectionContent(80-87)PropertyRow(184-211)PropertySectionHeaderItem(89-108)PropertySectionHeaderLabel(110-124)PropertySectionHeaderActions(126-146)
editor/grida-canvas-hosted/playground/playground.tsx (1)
crates/grida-canvas-wasm/example/main.js (1)
dpr(7-7)
editor/scaffolds/sidecontrol/chunks/section-fills.tsx (4)
editor/grida-canvas-react/index.ts (5)
useCurrentEditor(1-1)useBackendState(15-15)useNodeActions(9-9)useNodeState(14-14)useMixedProperties(18-18)editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx (2)
usePaintContentEditMode(46-222)SectionPaintsHeader(412-433)editor/scaffolds/sidecontrol/ui/index.tsx (3)
PropertySection(66-78)PropertySectionHeaderItem(89-108)PropertySectionHeaderLabel(110-124)editor/grida-canvas-react/use-mixed-properties.ts (1)
useMixedProperties(32-79)
editor/scaffolds/sidecontrol/controls/export.tsx (1)
editor/scaffolds/sidecontrol/ui/index.tsx (7)
PropertySection(66-78)PropertySectionHeaderItem(89-108)PropertySectionHeaderLabel(110-124)PropertySectionHeaderActions(126-146)PropertySectionContent(80-87)PropertyRows(170-183)PropertyRow(184-211)
editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx (2)
editor/scaffolds/sidecontrol/ui/index.tsx (6)
PropertySectionContent(80-87)PropertyRows(170-183)PropertySectionHeaderItem(89-108)PropertySectionHeaderLabel(110-124)PropertySectionHeaderActions(126-146)PropertyRow(184-211)editor/scaffolds/sidecontrol/controls/paint.tsx (1)
PaintControl(224-253)
🪛 Biome (2.1.2)
editor/scaffolds/sidecontrol/chunks/section-fills.tsx
[error] 201-201: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 207-207: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🪛 LanguageTool
docs/wg/feat-text-editing/index.md
[grammar] ~27-~27: Use a hyphen to join words.
Context: ...nt-wide layout engines (pagination, full text shaping pipelines exposed publicly)...
(QB_NEW_EN_HYPHEN)
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
editor/scaffolds/sidecontrol/chunks/section-fills.tsx (1)
59-67: Consider simplifying the nested ternary logic.The paintList computation works correctly but has 4 levels of nesting. For better readability, consider extracting this into a helper function or using early assignments.
🔎 Suggested refactor for improved readability
- const isCanvasBackend = backend === "canvas"; - const paintList = isCanvasBackend - ? Array.isArray(paints) && paints.length > 0 - ? paints - : paint - ? [paint] - : [] - : paint - ? [paint] - : []; + const isCanvasBackend = backend === "canvas"; + const paintList = React.useMemo(() => { + if (isCanvasBackend && Array.isArray(paints) && paints.length > 0) { + return paints; + } + return paint ? [paint] : []; + }, [isCanvasBackend, paints, paint]);editor/scaffolds/sidecontrol/controls/paint-gradient.tsx (2)
96-101: Add aria-label for accessibility.The icon-only add-stop button should include an
aria-labelto improve screen reader accessibility.🔎 Proposed enhancement
- <Button variant="ghost" size="icon" onClick={handleAddStop}> + <Button variant="ghost" size="icon" onClick={handleAddStop} aria-label="Add gradient stop"> <PlusIcon className="size-3" /> </Button>
166-170: Add aria-label for accessibility.The icon-only remove button should include an
aria-labelfor screen reader users.🔎 Proposed enhancement
- <Button size="icon" variant="ghost" onClick={onRemove}> + <Button size="icon" variant="ghost" onClick={onRemove} aria-label="Remove gradient stop"> <MinusIcon /> </Button>
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
editor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript 5 as the main language for most apps
Use Lucide or Radix Icons for icons
Files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use React.js 19 for web applications
Files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
{editor/**/*.{ts,tsx},packages/grida-canvas-*/**/*.{ts,tsx}}
📄 CodeRabbit inference engine (AGENTS.md)
Use DOM (plain DOM as canvas) for website builder canvas, bound with React
Files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
editor/scaffolds/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use /editor/scaffolds for feature-specific larger components, pages, and editors
Files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
editor/grida-*/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use /editor/grida-* directories to isolate domain-specific modules; promote well-defined modules to /packages
Files:
editor/grida-canvas/editor.i.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to {editor/**/*.{ts,tsx},packages/grida-canvas-*/**/*.{ts,tsx}} : Use DOM (plain DOM as canvas) for website builder canvas, bound with React
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : When adding new shape types, update the Shape type union, add cases in drawShape() function, add cases in shapeToSVG() function, and add SelectItem in UI
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Use Canvas 2D API with path commands for rendering geometric shapes (circle, square, triangle, etc.)
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : When adding new shape types, update the Shape type union, add cases in drawShape() function, add cases in shapeToSVG() function, and add SelectItem in UI
Applied to files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : When adding new parameters to the halftone tool, add state with useState, include in useEffect dependency array, pass to renderHalftone() function, use in rendering logic, and add UI control
Applied to files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Use Canvas 2D API with path commands for rendering geometric shapes (circle, square, triangle, etc.)
Applied to files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsx
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : For SVG export, convert circles to <circle> elements, rectangles to <rect> elements, and polygons to <polygon> elements with calculated points
Applied to files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsx
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/scaffolds/**/*.{ts,tsx} : Use /editor/scaffolds for feature-specific larger components, pages, and editors
Applied to files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsx
📚 Learning: 2025-12-01T00:22:56.899Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Use React hooks for state management (imageSrc, shape, grid, maxRadius, gamma, jitter, opacity, color, customShapeImage, imageDataRef, sizeRef)
Applied to files:
editor/scaffolds/sidecontrol/chunks/section-fills.tsxeditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
📚 Learning: 2025-12-01T00:22:19.083Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-wasm/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:19.083Z
Learning: Applies to crates/grida-canvas-wasm/**/main.rs : Update `grida-canvas-wasm.d.ts` TypeScript definitions file when new APIs are introduced via `main.rs`
Applied to files:
editor/grida-canvas/editor.i.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to {editor/**/*.{ts,tsx},packages/grida-canvas-*/**/*.{ts,tsx}} : Use DOM (plain DOM as canvas) for website builder canvas, bound with React
Applied to files:
editor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/components/ui/**/*.{ts,tsx} : Use /editor/components/ui for shadcn UI components
Applied to files:
editor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/controls/paint-gradient.tsx
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/lib/**/*.{ts,tsx} : Use /editor/lib for core, strictly designed modules with non-opinionated, reusable, and stable implementations
Applied to files:
editor/grida-canvas/editor.i.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/components/**/*.{ts,tsx} : Use /editor/components for generally reusable components
Applied to files:
editor/grida-canvas/editor.i.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to **/components/ui/**/*.{ts,tsx} : Use Shadcn UI for UI component library
Applied to files:
editor/grida-canvas/editor.i.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to packages/grida-canvas-*/**/*.{ts,tsx,js,jsx} : Packages under /packages/grida-canvas-* power the canvas; some are published to npm, refer to individual package README
Applied to files:
editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
🧬 Code graph analysis (1)
editor/scaffolds/sidecontrol/controls/paint-gradient.tsx (5)
crates/grida-canvas/src/io/io_css.rs (1)
s(121-121)editor/grida-canvas-react-gradient/gradient-stops-slider.tsx (1)
GradientStopsSlider(14-281)editor/scaffolds/sidecontrol/ui/index.tsx (2)
PropertyRows(170-183)PropertyRow(184-211)editor/scaffolds/sidecontrol/ui/percentage.tsx (1)
InputPropertyPercentage(132-194)editor/scaffolds/sidecontrol/controls/color.tsx (1)
RGBA32FColorControl(77-182)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (9)
editor/grida-canvas/editor.i.ts (1)
4634-4683: LGTM! Previous issues resolved and design is solid.The new
editor.uinamespace provides clean dependency injection for UI/UX concerns. Both issues from the previous review have been successfully addressed:
- ✅ The example at line 4639 now correctly references
UINotifier(previouslyUIToast)- ✅ The
EyeDropper.openreturn type at line 4662-4664 now includes| undefinedto match the documented cancellation behaviorThe API design follows best practices:
- Clear separation between editor core logic and UI/UX dependencies
- Type-safe interfaces matching browser APIs (Clipboard, EyeDropper, AbortSignal)
- Well-documented with examples and MDN references
- Factory pattern for
eyedropperallows lazy instantiationeditor/scaffolds/sidecontrol/chunks/section-fills.tsx (2)
27-38: LGTM!The helper function correctly creates new fill paints with appropriate default opacity based on whether other paints already exist.
184-212: Hook ordering issue successfully resolved.✅ The previous critical issue regarding conditional hook calls has been properly fixed. The
useCallbackhooks (lines 184-195) now execute unconditionally before the early return at line 197, satisfying React's Rules of Hooks.editor/scaffolds/sidecontrol/controls/paint-gradient.tsx (6)
1-10: LGTM!The imports are correctly updated to support the new UI primitives and functionality.
31-61: Well-implemented stop placement logic with good documentation.The
calculateNewStopOffsetfunction correctly handles all edge cases and finds the optimal placement for new stops. The TODO comment provides valuable context about the duplication across components—consider addressing this in a future refactor to centralize the logic.
63-81: LGTM!The
handleAddStopcallback correctly creates and inserts a new stop, using object identity withindexOfto reliably locate the new stop's position after sorting.
85-94: LGTM!The
GradientStopsSlideris properly wrapped and integrated with correct prop forwarding.
102-121: LGTM!The stop rows are correctly rendered using the new
PropertyRowscontainer, and the removal logic uses a clean functional approach withfilter.
126-142: LGTM!The component signature is properly extended with focus management props, and
PropertyRowis used correctly according to the UI primitives API.
| {!empty && ( | ||
| <ChunkPaints value={commonPaints} onValueChange={handleValueChange} /> | ||
| )} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the section-fills.tsx file to verify the contentEditMode usage
cd editor/scaffolds/sidecontrol/chunks && wc -l section-fills.tsxRepository: gridaco/grida
Length of output: 78
🏁 Script executed:
# Read the file with line numbers to check both usages
cd editor/scaffolds/sidecontrol/chunks && cat -n section-fills.tsx | sed -n '140,230p'Repository: gridaco/grida
Length of output: 3464
🏁 Script executed:
# Also check if usePaintContentEditMode is used and what it does
cd editor/scaffolds/sidecontrol/chunks && rg -n "usePaintContentEditMode" section-fills.tsx -A 3Repository: gridaco/grida
Length of output: 256
🏁 Script executed:
# Search for where usePaintContentEditMode is defined to understand its constraints
fd -t f "*.{ts,tsx}" editor/scaffolds -x rg -l "usePaintContentEditMode"Repository: gridaco/grida
Length of output: 346
🏁 Script executed:
# Find and read chunk-paints.tsx to see usePaintContentEditMode definition
fd -type f -name "chunk-paints.tsx" editor/Repository: gridaco/grida
Length of output: 227
🏁 Script executed:
# Search for usePaintContentEditMode definition
rg -n "usePaintContentEditMode" editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx -A 15Repository: gridaco/grida
Length of output: 484
🏁 Script executed:
# Check if there's any multi-node support or handling in gradient stop selection
rg -n "selectedGradientStop\|openPaintIndex" editor/scaffolds/sidecontrol/chunks/ -B 2 -A 2Repository: gridaco/grida
Length of output: 39
🏁 Script executed:
# Read full chunk-paints.tsx to understand the hook better
fd -f -name "chunk-paints.tsx" editor/Repository: gridaco/grida
Length of output: 287
🏁 Script executed:
# Search for pattern of how multi-node selections are handled elsewhere
rg -n "ids.*string\[\]" editor/scaffolds/sidecontrol/chunks/ -A 5 -B 2Repository: gridaco/grida
Length of output: 1415
🏁 Script executed:
# Check if there's any documentation or comments about multi-node limitations
rg -n "multi.*node\|multiple.*node\|mixed.*paints" editor/scaffolds/sidecontrol/chunks/section-fills.tsx -iRepository: gridaco/grida
Length of output: 39
🏁 Script executed:
# Check section-strokes.tsx for a similar Mixed component pattern
fd -name "section-strokes.tsx" editor/ -x cat -nRepository: gridaco/grida
Length of output: 287
🏁 Script executed:
# Look for SectionStrokesMixed and how it handles ChunkPaints
rg -n "SectionStrokesMixed\|ChunkPaints" editor/scaffolds/sidecontrol/chunks/section-strokes.tsx -A 3 -B 2Repository: gridaco/grida
Length of output: 39
🏁 Script executed:
# Read chunk-paints.tsx to understand the ChunkPaints component and its contentEditMode prop
cat -n editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx | head -100Repository: gridaco/grida
Length of output: 3532
🏁 Script executed:
# Continue reading chunk-paints.tsx to understand ChunkPaints component props
cat -n editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx | sed -n '100,200p'Repository: gridaco/grida
Length of output: 3715
🏁 Script executed:
# Look for ChunkPaints component definition
rg -n "interface ChunkPaintsProps\|function ChunkPaints\|export.*ChunkPaints" editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx -A 8Repository: gridaco/grida
Length of output: 39
🏁 Script executed:
# Check if there's any handling of multiple nodes in content edit mode elsewhere
rg -n "content_edit_mode.*node_id" editor/scaffolds/sidecontrol/ -B 2 -A 2Repository: gridaco/grida
Length of output: 1988
Document the multi-node gradient editing limitation in SectionFillsMixed.
The ChunkPaints component in SectionFillsMixed (line 227) omits the contentEditMode prop that is passed in SectionFills. This prevents users from selecting gradient stops or opening paint accordions when multiple nodes with identical fills are selected.
However, adding contentEditMode is not straightforward: usePaintContentEditMode is architected around a single node_id string, and the editor's content_edit_mode state only tracks one node. Supporting multi-node gradient editing would require refactoring the underlying hooks to handle arrays of node_ids.
This appears to be a deliberate architectural constraint rather than an oversight. Either document this limitation with a comment in SectionFillsMixed, or file a separate task to redesign the paint editing system for multi-node support.
New Features
Improvements