Skip to content

Daily RC#486

Merged
softmarshmallow merged 21 commits intomainfrom
canary
Jan 2, 2026
Merged

Daily RC#486
softmarshmallow merged 21 commits intomainfrom
canary

Conversation

@softmarshmallow
Copy link
Copy Markdown
Member

@softmarshmallow softmarshmallow commented Jan 1, 2026

  • New Features

    • Added social media artboard presets (Twitter, Facebook, Instagram, Dribbble, LinkedIn)
    • Enhanced multi-selection editing with mixed-property support for batch updates
    • Improved paint/fill/stroke editing UI with better gradient and stop management
    • Stroke properties now supported on containers, polygons, and stars
  • Improvements

    • Refined ruler z-index layering for proper UI stacking
    • Enhanced gradient editor with easier stop addition/removal
    • Improved viewport handling and canvas initialization
    • Consolidated property panel UI components for consistency
    • Isolate eyedropper color picker integration with UI notifications

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
docs Ready Ready Preview, Comment Jan 2, 2026 6:35am
grida Ready Ready Preview, Comment Jan 2, 2026 6:35am
5 Skipped Deployments
Project Deployment Review Updated (UTC)
code Ignored Ignored Jan 2, 2026 6:35am
legacy Ignored Ignored Jan 2, 2026 6:35am
backgrounds Skipped Skipped Jan 2, 2026 6:35am
blog Skipped Skipped Jan 2, 2026 6:35am
viewer Skipped Skipped Jan 2, 2026 6:35am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 1, 2026

Walkthrough

The 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

Cohort / File(s) Summary
DPR & Mount API
editor/app/(dev)/canvas/tools/io-markdown/page.tsx, editor/app/(dev)/canvas/tools/io-svg/page.tsx, editor/grida-canvas-hosted/playground/playground.tsx, editor/grida-canvas/editor.ts
Pass dpr (window.devicePixelRatio
UI Providers & Editor Core
editor/grida-canvas/editor.i.ts, editor/grida-canvas/editor.ts, editor/grida-canvas/editor.ts (EditorSurface), editor/grida-canvas-react/use-editor.tsx, editor/grida-canvas/backends/dom.ts
Introduce editor.ui types (UINotifier, EyeDropper, UIUXProviders); add ui param wired through Editor/EditorSurface and use for notify/clipboard/eyedropper replaces global/window usage; add viewport null-safety guards in dom backend
PropertySection UI Migration
editor/scaffolds/sidecontrol/ui/index.tsx, many sidecontrol files (.../chunks/*, .../controls/*, sidecontrol-node-selection.tsx)
Add PropertySection, PropertyRows, PropertyRow and header/action primitives; migrate many sidebar components and layouts from Sidebar* → PropertySection* and update numerous sidecontrol UIs to use the new primitives
Paint/Fills/Strokes Refactor
editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx, editor/scaffolds/sidecontrol/chunks/section-fills.tsx, editor/scaffolds/sidecontrol/chunks/section-strokes.tsx, editor/scaffolds/sidecontrol/controls/paint-gradient.tsx, editor/scaffolds/sidecontrol/controls/paint.tsx
Centralize paint editing via usePaintContentEditMode, convert ChunkPaints to controlled value/onValueChange API, add SectionFillsMixed/SectionStrokesMixed for multi-node editing, add stop-add/remove UI and layout changes
Mixed Properties & Hooks
editor/grida-canvas-react/use-mixed-properties.ts, packages/grida-mixed-properties/index.ts, editor/grida-canvas-react/index.ts
Make useMixedProperties selector-driven and generic; export and extend mixed-properties types (add values in MixedProperty and mixed option); re-export new hooks (useNodeState, useBackendState, useMixedProperties)
Schema & Node Defaults (stroke / corner radius)
packages/grida-canvas-schema/grida.ts, editor/grida-canvas/reducers/tools/initial-node.ts, packages/grida-canvas-io-figma/lib.ts, editor/grida-canvas/reducers/event-target.reducer.ts
Add stroke support to Container/shape nodes and defaults (stroke_width, stroke_align, stroke_cap, stroke_join, stroke_miter_limit); add corner-radius to polygon/star nodes; extend Figma IO node traits with stroke_trait; change corner-radius handling to type-driven branching
Z-index & Rulers
editor/grida-canvas-react/ui-config.ts, editor/grida-canvas-react/viewport/surface.tsx
Add RULER_Z_INDEX = 110, lower FLOATING_BAR_Z_INDEX to 100, and switch ruler overlays to inline zIndex styling using the constant
Small UI/API Changes
editor/scaffolds/sidecontrol/controls/color.tsx, editor/scaffolds/sidecontrol/controls/fill.tsx, editor/scaffolds/sidecontrol/controls/export.tsx
Add optional onFocus/onBlur props to RGBA32FColorControl; remove FillControl re-export; make ExportNodeControl internal (non-exported) and migrate export UI to PropertySection layout
Starter Kit & Docs
editor/grida-canvas-react-starter-kit/data/artboards.json, editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx, editor/grida-canvas-react-starter-kit/starterkit-hierarchy/tree-node.tsx, branding/README.md, docs/wg/feat-text-editing/index.md
Add social media artboard presets; new artboard insert selects created artboard and sets fill; add performance TODOs; update branding URL to grida.co/brand; add text-editing design manifesto

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

breaking, migration

Poem

🐰 I nibble pixels with a DPR-aware hop,
UI providers carried in my little hop-top,
PropertySections bloom where Sidebars were,
Strokes and fills sing—mixed selections confer,
Hooray — the editor hops forward, full stop!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.92% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Daily RC' is vague and generic, providing no meaningful information about the changeset's primary purpose or scope. Use a more descriptive title that captures the main focus, such as 'Add multi-selection editing and stroke properties support' or 'Refactor UI components and enhance paint editing'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch canary

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…ng state subscriptions and improving paint entry management
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.length to track whether initialization has occurred. This works but is unconventional and makes the intent less clear. Consider using a separate initialized flag (via state or ref) to explicitly signal one-time initialization, or restructure the effect to only depend on tree.

+ 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 rect getter and getViewportRect() 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 size getter and getViewportSize() 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 anchor is not provided (lines 799-815), the code updates both corner_radius AND all four rectangular_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 classes

The restructuring around GradientStopsSlider, the “Stops” header with a plus button, and GradientStopRow using PropertyRow with focused/onFocus all hangs together well, and the per-row update/remove handlers keep stops immutable.

One thing to double-check: className="flex-1/4" and className="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 factories

Extending ContainerNode with i.IStroke and seeding stroke defaults in createContainerNode (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 have stroke_width/stroke_align/etc. undefined unless provided in the prototype. For a tighter invariant, consider aligning its defaults with createContainerNode so 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 use useMixedProperties

ModeMixedNodeProperties’ base section and “Appearance” header make good use of useMixedProperties:

  • name, active, locked, blend_mode all respect mixed state via .mixed, .value, and .ids.
  • Updates fan out to either the per-property ids or the full ids fallback, 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 carefully

The new SectionMixedPosition and SectionLayout/SectionLayoutMixed components:

  • Use useMixedProperties to 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 support

The corner-radius and padding sections:

  • Single-node: respect supports.cornerRadius / supports.cornerRadius4 to choose between 1-value and 4-value controls.
  • Mixed: use useMixedProperties to determine whether 4-corner values are consistent enough to show a detailed editor, otherwise fall back to a scalar radius editor with disabled when 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 useEffect hooks reset openPaintIndex when the node context changes. The first reacts to content_edit_mode changes and the second to node_id changes. 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: The hidden prop is not part of React.HtmlHTMLAttributes<HTMLDivElement>.

The hidden prop is destructured but HtmlHTMLAttributes doesn'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 the forwardRef typing.

The as any cast 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 unstable options references.

If callers pass options inline (e.g., { ignoredKey: ["id"] }), the useMemo will recompute on every render since options?.ignoredKey and options?.compare will 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.

writeClipboardMedia uses navigator.clipboard directly (line 4566), while writeClipboardSVG uses this.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: Nested requestAnimationFrame is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3fc6a52 and 7de5269.

⛔ Files ignored due to path filters (1)
  • branding/project-maps.png is excluded by !**/*.png
📒 Files selected for processing (33)
  • branding/README.md
  • docs/wg/feat-text-editing/index.md
  • editor/app/(dev)/canvas/tools/io-markdown/page.tsx
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/grida-canvas-react-starter-kit/data/artboards.json
  • editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx
  • editor/grida-canvas-react-starter-kit/starterkit-hierarchy/tree-node.tsx
  • editor/grida-canvas-react/index.ts
  • editor/grida-canvas-react/ui-config.ts
  • editor/grida-canvas-react/use-editor.tsx
  • editor/grida-canvas-react/use-mixed-properties.ts
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/grida-canvas/backends/dom.ts
  • editor/grida-canvas/editor.i.ts
  • editor/grida-canvas/editor.ts
  • editor/grida-canvas/reducers/event-target.reducer.ts
  • editor/grida-canvas/reducers/tools/initial-node.ts
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/scaffolds/sidecontrol/controls/color.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/scaffolds/sidecontrol/controls/fill.tsx
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • editor/scaffolds/sidecontrol/controls/width-height.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • packages/grida-canvas-io-figma/lib.ts
  • packages/grida-canvas-schema/grida.ts
  • packages/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.tsx
  • editor/grida-canvas/backends/dom.ts
  • editor/scaffolds/sidecontrol/controls/color.tsx
  • editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx
  • editor/grida-canvas/reducers/event-target.reducer.ts
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • packages/grida-canvas-io-figma/lib.ts
  • editor/app/(dev)/canvas/tools/io-markdown/page.tsx
  • editor/grida-canvas-react/use-editor.tsx
  • editor/grida-canvas-react/use-mixed-properties.ts
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/scaffolds/sidecontrol/controls/width-height.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/grida-canvas/reducers/tools/initial-node.ts
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/grida-canvas/editor.i.ts
  • editor/grida-canvas-react/index.ts
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/grida-canvas/editor.ts
  • packages/grida-mixed-properties/index.ts
  • packages/grida-canvas-schema/grida.ts
  • editor/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.tsx
  • editor/scaffolds/sidecontrol/controls/color.tsx
  • editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • editor/app/(dev)/canvas/tools/io-markdown/page.tsx
  • editor/grida-canvas-react/use-editor.tsx
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/scaffolds/sidecontrol/controls/width-height.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/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.tsx
  • editor/grida-canvas/backends/dom.ts
  • editor/scaffolds/sidecontrol/controls/color.tsx
  • editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx
  • editor/grida-canvas/reducers/event-target.reducer.ts
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • packages/grida-canvas-io-figma/lib.ts
  • editor/app/(dev)/canvas/tools/io-markdown/page.tsx
  • editor/grida-canvas-react/use-editor.tsx
  • editor/grida-canvas-react/use-mixed-properties.ts
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/scaffolds/sidecontrol/controls/width-height.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/grida-canvas/reducers/tools/initial-node.ts
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/grida-canvas/editor.i.ts
  • editor/grida-canvas-react/index.ts
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/grida-canvas/editor.ts
  • packages/grida-canvas-schema/grida.ts
  • editor/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.tsx
  • editor/grida-canvas/backends/dom.ts
  • editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx
  • editor/grida-canvas/reducers/event-target.reducer.ts
  • editor/grida-canvas-react/use-editor.tsx
  • editor/grida-canvas-react/use-mixed-properties.ts
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/grida-canvas/reducers/tools/initial-node.ts
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/grida-canvas/editor.i.ts
  • editor/grida-canvas-react/index.ts
  • editor/grida-canvas/editor.ts
  • editor/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.tsx
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/scaffolds/sidecontrol/controls/width-height.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/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.ts
  • packages/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.tsx
  • editor/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.ts
  • editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx
  • editor/grida-canvas/reducers/event-target.reducer.ts
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • editor/app/(dev)/canvas/tools/io-markdown/page.tsx
  • editor/grida-canvas-react/use-editor.tsx
  • editor/grida-canvas-react/use-mixed-properties.ts
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/grida-canvas-react/index.ts
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/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.ts
  • editor/grida-canvas/reducers/event-target.reducer.ts
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • packages/grida-canvas-io-figma/lib.ts
  • editor/app/(dev)/canvas/tools/io-markdown/page.tsx
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/grida-canvas/editor.ts
  • packages/grida-mixed-properties/index.ts
  • packages/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.ts
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • editor/app/(dev)/canvas/tools/io-markdown/page.tsx
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/grida-canvas-react/index.ts
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/grida-canvas/editor.ts
  • packages/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.ts
  • editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx
  • editor/grida-canvas/reducers/event-target.reducer.ts
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • packages/grida-canvas-io-figma/lib.ts
  • editor/app/(dev)/canvas/tools/io-markdown/page.tsx
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/grida-canvas-react/viewport/surface.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/grida-canvas/reducers/tools/initial-node.ts
  • editor/grida-canvas-hosted/playground/playground.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • packages/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.tsx
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/scaffolds/sidecontrol/controls/width-height.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/grida-canvas-react/index.ts
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/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.tsx
  • editor/grida-canvas/reducers/event-target.reducer.ts
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • packages/grida-canvas-io-figma/lib.ts
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/grida-canvas/reducers/tools/initial-node.ts
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/grida-canvas/editor.ts
  • packages/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.ts
  • editor/scaffolds/sidecontrol/controls/paint.tsx
  • packages/grida-canvas-io-figma/lib.ts
  • editor/app/(dev)/canvas/tools/io-svg/page.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/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.tsx
  • editor/grida-canvas-react/use-editor.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/grida-canvas/editor.i.ts
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/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.tsx
  • editor/grida-canvas-react/use-editor.tsx
  • editor/grida-canvas-react/use-mixed-properties.ts
  • editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/grida-canvas-react/index.ts
  • editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
  • editor/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.tsx
  • editor/grida-canvas-react/use-editor.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/scaffolds/sidecontrol/chunks/chunk-paints.tsx
  • editor/grida-canvas/editor.ts
  • packages/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.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/grida-canvas/editor.i.ts
  • editor/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.tsx
  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/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.tsx
  • editor/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.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • 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/scaffolds/**/*.{ts,tsx} : Use /editor/scaffolds for feature-specific larger components, pages, and editors

Applied to files:

  • editor/scaffolds/sidecontrol/chunks/scale-tool.tsx
  • editor/scaffolds/sidecontrol/ui/index.tsx
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
  • editor/scaffolds/sidecontrol/controls/export.tsx
  • editor/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)

Comment thread editor/grida-canvas-react-starter-kit/data/artboards.json
Comment thread editor/grida-canvas/editor.i.ts
Comment thread editor/scaffolds/sidecontrol/chunks/section-fills.tsx
Comment thread editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
Comment thread editor/scaffolds/sidecontrol/chunks/section-strokes.tsx
Comment thread editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-label to 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-label for 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7de5269 and 7d806df.

📒 Files selected for processing (3)
  • editor/grida-canvas/editor.i.ts
  • editor/scaffolds/sidecontrol/chunks/section-fills.tsx
  • editor/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.tsx
  • editor/grida-canvas/editor.i.ts
  • editor/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.tsx
  • editor/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.tsx
  • editor/grida-canvas/editor.i.ts
  • editor/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.tsx
  • editor/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.tsx
  • editor/grida-canvas/editor.i.ts
  • editor/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.tsx
  • editor/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.tsx
  • editor/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.ts
  • editor/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.ts
  • editor/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.ui namespace provides clean dependency injection for UI/UX concerns. Both issues from the previous review have been successfully addressed:

  1. ✅ The example at line 4639 now correctly references UINotifier (previously UIToast)
  2. ✅ The EyeDropper.open return type at line 4662-4664 now includes | undefined to match the documented cancellation behavior

The 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 eyedropper allows lazy instantiation
editor/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 useCallback hooks (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 calculateNewStopOffset function 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 handleAddStop callback correctly creates and inserts a new stop, using object identity with indexOf to reliably locate the new stop's position after sorting.


85-94: LGTM!

The GradientStopsSlider is properly wrapped and integrated with correct prop forwarding.


102-121: LGTM!

The stop rows are correctly rendered using the new PropertyRows container, and the removal logic uses a clean functional approach with filter.


126-142: LGTM!

The component signature is properly extended with focus management props, and PropertyRow is used correctly according to the UI primitives API.

Comment on lines +226 to +228
{!empty && (
<ChunkPaints value={commonPaints} onValueChange={handleValueChange} />
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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.tsx

Repository: 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 3

Repository: 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 15

Repository: 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 2

Repository: 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 2

Repository: 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 -i

Repository: 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 -n

Repository: 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 2

Repository: 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 -100

Repository: 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 8

Repository: 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 2

Repository: 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.

Comment thread editor/scaffolds/sidecontrol/controls/paint-gradient.tsx
@softmarshmallow softmarshmallow merged commit 072a484 into main Jan 2, 2026
10 checks 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