Skip to content

Conversation

@softmarshmallow
Copy link
Member

@softmarshmallow softmarshmallow commented May 27, 2025

Migrating from react -> Pure js Editor class.

day-238-grida-canvas-scripting-interface-v2.mp4

Summary by CodeRabbit

  • New Features

    • Introduced a unified editor interface for managing document state, actions, and UI updates.
    • Added new hooks and components for global editor access and state management.
    • Implemented animation for smooth transformation transitions.
  • Refactor

    • Consolidated multiple hooks and state selectors into a single editor-centric pattern across the application.
    • Centralized node property updates and editor actions for improved consistency and maintainability.
    • Updated type definitions and interfaces for editor actions, node changes, and event handling.
  • Bug Fixes

    • Improved type safety and error handling for node property updates and editor actions.
  • Chores

    • Deprecated several legacy hooks and functions, replacing them with new, more robust alternatives.
    • Updated documentation and type annotations to reflect the new editor architecture.

@codesandbox
Copy link

codesandbox bot commented May 27, 2025

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@vercel
Copy link

vercel bot commented May 27, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
grida ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 29, 2025 9:04am
6 Skipped Deployments
Name Status Preview Comments Updated (UTC)
code ⬜️ Ignored (Inspect) May 29, 2025 9:04am
legacy ⬜️ Ignored (Inspect) May 29, 2025 9:04am
backgrounds ⬜️ Skipped (Inspect) May 29, 2025 9:04am
blog ⬜️ Skipped (Inspect) May 29, 2025 9:04am
docs ⬜️ Skipped (Inspect) May 29, 2025 9:04am
viewer ⬜️ Skipped (Inspect) May 29, 2025 9:04am

@coderabbitai
Copy link

coderabbitai bot commented May 27, 2025

Walkthrough

This update refactors the editor's architecture and API, consolidating numerous hooks and state accessors into a unified useCurrentEditor interface. It introduces a generic node property change mechanism, expands the Editor class with comprehensive document and node manipulation methods, updates type signatures for numeric changes, and streamlines reducer logic for node updates. Several components and hooks are updated to use the new editor-centric API and state hooks.

Changes

Files/Groups Change Summary
editor/grida-canvas/editor.ts, index.ts Major expansion of the Editor class, new interfaces for editor actions, new types for structured changes, and a proxy class for node updates.
editor/grida-canvas/action.ts, reducers/node.reducer.ts, reducers/document.reducer.ts, reducers/event-target.reducer.ts, reducers/methods/transform.ts Replaces many specific node change actions with a generic "node/change/*" action; centralizes node property update logic; removes toggle locked/active actions; updates reducer structure.
editor/grida-canvas/animation.ts Adds animateTransformTo function for smooth transform interpolation.
editor/grida-canvas/reducers/surface.reducer.ts, scaffolds/sidecontrol/controls/*, scaffolds/sidecontrol/ui/index.tsx Updates numeric change types from TChange<number> to editor.api.NumberChange.
editor/grida-canvas/renderer.tsx, viewport/*, viewport/ui/*, viewport/surface.tsx, viewport/hotkeys.tsx, viewport/surface-context-menu.tsx, viewport/surface-hooks.ts, viewport/ui/text-editor.tsx Refactors to use new state hooks (useCurrentEditor, useTransformState, etc.) and routes actions/state through the editor instance.
editor/grida-canvas-react-starter-kit/*, scaffolds/playground-canvas/*, scaffolds/sidebar/sidebar-node-hierarchy-list.tsx, scaffolds/sidecontrol/sidecontrol-*, scaffolds/sidecontrol/controls/*, scaffolds/sidecontrol/ui/index.tsx, theme/templates/formcollection/page.tsx, app/(tools)/(playground)/playground/image/_page.tsx, app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/template-duo-001-viewer.tsx Updates imports and usage to the new editor API and state hooks; replaces direct hook-based state/actions with editor instance methods.
editor/grida-canvas-react/devtools/api-host.ts, devtools/index.tsx, devtools/__unsafe-console.tsx, plugins/use-recorder.ts, use-editor.tsx, index.ts Moves useRecorder hook to plugins, adds global editor exposure for devtools, updates exports to match new API.
editor/grida-canvas/backends/dom.ts, reducers/tools/id.ts Marks certain APIs as deprecated.
packages/grida-canvas-schema/grida.ts Generalizes UnknownNodeProperties type and clarifies nullability of fill property.

Sequence Diagram(s)

sequenceDiagram
  participant UI_Component
  participant useCurrentEditor
  participant Editor_Instance
  participant Reducer

  UI_Component->>useCurrentEditor: Get editor instance
  useCurrentEditor-->>UI_Component: Return Editor_Instance

  UI_Component->>Editor_Instance: Call action (e.g., insertNode, changeNodeText)
  Editor_Instance->>Reducer: Dispatch action (e.g., "node/change/*")
  Reducer->>Reducer: Apply property update via property handler
  Reducer-->>Editor_Instance: Return updated state

  Editor_Instance-->>UI_Component: State updates via subscription/hooks
Loading

Possibly related PRs

  • cf235 [Grida Canvas] Hierarchy Tree DnD #350: Refactors editor state and action hooks to use a unified useCurrentEditor interface and updates node insertion logic; related through shared focus on editor state management and node hierarchy UI integration.

Poem

In the land of code where editors hop,
A single hook now leads the crop.
No more toggles, wild and free—
Just "node/change/*" for property spree!
With actions and state all neat and tight,
This bunny's canvas feels just right.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@vercel vercel bot temporarily deployed to Preview – blog May 28, 2025 10:35 Inactive
@vercel vercel bot temporarily deployed to Preview – backgrounds May 28, 2025 10:35 Inactive
@vercel vercel bot temporarily deployed to Preview – docs May 28, 2025 10:35 Inactive
@vercel vercel bot temporarily deployed to Preview – viewer May 28, 2025 10:35 Inactive
@vercel vercel bot temporarily deployed to Preview – blog May 28, 2025 11:46 Inactive
@vercel vercel bot temporarily deployed to Preview – backgrounds May 28, 2025 11:46 Inactive
@vercel vercel bot temporarily deployed to Preview – docs May 28, 2025 11:46 Inactive
@vercel vercel bot temporarily deployed to Preview – viewer May 28, 2025 11:46 Inactive
@vercel vercel bot temporarily deployed to Preview – blog May 28, 2025 12:22 Inactive
@vercel vercel bot temporarily deployed to Preview – docs May 28, 2025 12:22 Inactive
@vercel vercel bot temporarily deployed to Preview – backgrounds May 28, 2025 12:22 Inactive
@vercel vercel bot temporarily deployed to Preview – viewer May 28, 2025 12:22 Inactive
@vercel vercel bot temporarily deployed to Preview – viewer May 28, 2025 16:28 Inactive
@vercel vercel bot temporarily deployed to Preview – blog May 28, 2025 16:28 Inactive
@vercel vercel bot temporarily deployed to Preview – blog May 29, 2025 07:16 Inactive
@vercel vercel bot temporarily deployed to Preview – viewer May 29, 2025 07:16 Inactive
@vercel vercel bot temporarily deployed to Preview – backgrounds May 29, 2025 07:16 Inactive
@vercel vercel bot temporarily deployed to Preview – docs May 29, 2025 07:16 Inactive
@vercel vercel bot temporarily deployed to Preview – docs May 29, 2025 08:59 Inactive
@vercel vercel bot temporarily deployed to Preview – blog May 29, 2025 08:59 Inactive
@vercel vercel bot temporarily deployed to Preview – viewer May 29, 2025 08:59 Inactive
@vercel vercel bot temporarily deployed to Preview – backgrounds May 29, 2025 08:59 Inactive
@softmarshmallow softmarshmallow marked this pull request as ready for review May 29, 2025 09:05
Copy link

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

🧹 Nitpick comments (13)
editor/grida-canvas/backends/dom.ts (1)

6-7: Enhance deprecation notice with migration guidance

The @deprecated tag is appropriate, but it would be more helpful to consumers if you point them to the new API and indicate a removal timeline. For example:

/**
 * @deprecated Use the `Editor` class via the `useCurrentEditor` hook instead. 
 *             Scheduled for removal in v3.0.
 */

This makes it clear what to use going forward and when domapi will be removed.

editor/grida-canvas/animation.ts (1)

3-38: Consider enhancing the animation utility for production use.

The implementation correctly interpolates between transform matrices, but could benefit from these improvements:

  1. Cancellation mechanism: Add ability to cancel ongoing animations
  2. Configurable duration: Make the 200ms duration a parameter
  3. Easing functions: Consider non-linear interpolation options
  4. Input validation: Validate that transforms are valid 2x3 matrices
 export function animateTransformTo(
   from: cmath.Transform,
   to: cmath.Transform,
-  update: (t: cmath.Transform) => void
+  update: (t: cmath.Transform) => void,
+  options: { duration?: number; easing?: (t: number) => number } = {}
 ) {
-  const duration = 200; // ms
+  const duration = options.duration ?? 200;
+  const easing = options.easing ?? ((t) => t); // linear by default
   const start = performance.now();
+  let cancelled = false;
 
   function step(now: number) {
+    if (cancelled) return;
     const elapsed = now - start;
-    const progress = Math.min(elapsed / duration, 1);
+    const progress = easing(Math.min(elapsed / duration, 1));
 
     // ... rest of implementation
   }
 
+  return () => { cancelled = true; }; // Return cancellation function
 }
editor/grida-canvas-react/renderer.tsx (1)

140-140: Fix the banned {} type usage.

The static analysis tool correctly identifies that {} should not be used as a type, as it means "any non-nullable value" rather than an empty object.

-export function Transformer({ children }: React.PropsWithChildren<{}>) {
+export function Transformer({ children }: React.PropsWithChildren) {

-export function AutoInitialFitTransformer({
-  children,
-}: React.PropsWithChildren<{}>) {
+export function AutoInitialFitTransformer({
+  children,
+}: React.PropsWithChildren) {

React.PropsWithChildren already has a default generic parameter of unknown, so no explicit type parameter is needed.

Also applies to: 165-165

🧰 Tools
🪛 Biome (1.9.4)

[error] 140-140: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

editor/grida-canvas-react/devtools/api-host.tsx (1)

4-14: Good devtools integration, but optimize the cleanup.

The global editor exposure for devtools is well-implemented with proper cleanup. However, the delete operator can impact performance.

Apply this diff to improve performance:

-      delete globalThis["grida"];
+      globalThis["grida"] = undefined;
🧰 Tools
🪛 Biome (1.9.4)

[error] 11-11: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

packages/grida-canvas-schema/grida.ts (1)

1190-1191: Clarify the nullable behavior intention.

The change from fill?: T; to fill?: T | undefined; is somewhat redundant since optional properties already include undefined. The FIXME comment suggests this may need to support null values as well. Consider clarifying whether the property should support null, undefined, or both, and update the type accordingly.

If null support is needed:

-      // FIXME: make it nullable
-      fill?: T | undefined;
+      fill?: T | null | undefined;

Or if explicit undefined isn't needed:

-      // FIXME: make it nullable
-      fill?: T | undefined;
+      fill?: T | null;
editor/grida-canvas-react-starter-kit/starterkit-hierarchy/index.tsx (1)

317-323: Consider consistency in state access patterns.

The data loader uses direct state access (editor.state.document.nodes and editor.state.document_ctx.__ctx_nid_to_children_ids). While this might be intentional for performance in the synchronous data loader, consider if there should be a consistent pattern for accessing state throughout the codebase.

If performance is not critical here, consider using state selectors for consistency:

dataLoader: {
  getItem(itemId) {
    return useEditorState(editor, (state) => state.document.nodes[itemId]);
  },
  getChildren: (itemId) => {
    if (itemId === "<root>") {
      return children;
    }
    return useEditorState(editor, (state) => state.document_ctx.__ctx_nid_to_children_ids[itemId]);
  },
},
editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (2)

231-231: Remove commented-out code.

This commented line appears to be leftover from refactoring and should be removed to keep the code clean.

  const sid = ids.join(",");
- // const is_root = ids.length === 0 && scene.children.includes(ids[0]); // assuming when root is selected, only root is selected
  const types = new Set(nodes.map((n) => n.type));

688-735: Consider optimizing the useNodeState selector for better performance.

The current selector returns many properties, which could cause unnecessary re-renders when any property changes. Consider splitting this into multiple selectors or using more granular hooks for different sections.

For example, group related properties:

// Text-related properties
const textProps = useNodeState(node_id, (node) => ({
  text: node.text,
  fontFamily: node.fontFamily,
  fontWeight: node.fontWeight,
  fontSize: node.fontSize,
  // ... other text properties
}));

// Layout-related properties  
const layoutProps = useNodeState(node_id, (node) => ({
  layout: node.layout,
  direction: node.direction,
  mainAxisAlignment: node.mainAxisAlignment,
  // ... other layout properties
}));
editor/grida-canvas/index.ts (4)

1859-1866: Consider consistent change type usage.

The changeTextNodeLineHeight and changeTextNodeLetterSpacing methods use TChange<grida.program.nodes.TextNode["lineHeight"]> while other numeric properties use NumberChange. For consistency, consider using NumberChange if these properties are always numeric.

If lineHeight and letterSpacing are always numeric values, consider:

-    changeTextNodeLineHeight(
-      node_id: NodeID,
-      lineHeight: TChange<grida.program.nodes.TextNode["lineHeight"]>
-    ): void;
-    changeTextNodeLetterSpacing(
-      node_id: NodeID,
-      letterSpacing: TChange<grida.program.nodes.TextNode["letterSpacing"]>
-    ): void;
+    changeTextNodeLineHeight(
+      node_id: NodeID,
+      lineHeight: NumberChange
+    ): void;
+    changeTextNodeLetterSpacing(
+      node_id: NodeID,
+      letterSpacing: NumberChange
+    ): void;

1722-1736: Consider using a generic type parameter for the context property.

The context property is typed as any, which reduces type safety. Consider making the class generic to provide better type information.

-export class EditorConsumerVerboseError extends Error {
-  context: any;
-  constructor(message: string, context: any) {
+export class EditorConsumerVerboseError<TContext = unknown> extends Error {
+  context: TContext;
+  constructor(message: string, context: TContext) {
    super(message); // Pass message to the parent Error class
    this.name = this.constructor.name; // Set the error name
    this.context = context; // Attach the context object
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, this.constructor);
    }
  }

  toString(): string {
    return `${this.name}: ${this.message} - Context: ${JSON.stringify(this.context)}`;
  }
}

1839-1893: Consider applying the change type pattern consistently across all numeric parameters.

While some methods like changeNodeStrokeWidth, changeNodeOpacity, and changeNodeRotation now use editor.api.NumberChange, other methods that accept numeric values still use raw types. For consistency, consider using change types for all numeric parameters.

For example, methods like:

  • changeNodeMaxlength (line 1867-1870)
  • changeFlexContainerNodeGap (line 1889-1892)

Could benefit from using TChange<number> or NumberChange to support both absolute and delta changes uniformly across the API.


1955-1955: Consider using a more explicit type instead of -1 as a special value.

The startGuideGesture method accepts idx: number | -1, where -1 appears to be a special case. This is a code smell that can lead to confusion.

-startGuideGesture(axis: cmath.Axis, idx: number | -1): void;
+startGuideGesture(axis: cmath.Axis, idx: number | "new"): void;

Or alternatively:

-startGuideGesture(axis: cmath.Axis, idx: number | -1): void;
+startGuideGesture(axis: cmath.Axis, idx?: number): void;
editor/grida-canvas/editor.ts (1)

120-124: Address TODO: Implement instance-wise ID generation.

The current implementation could potentially generate duplicate IDs across different editor instances. Consider implementing an instance-specific ID generator.

Would you like me to implement an instance-wise ID generator that ensures unique IDs across editor instances? I can open a new issue to track this improvement.

Example approach:

private __nodeIdCounter = 0;
private __instanceId = Math.random().toString(36).substr(2, 9);

private __createNodeId(): editor.NodeID {
  return `${this.__instanceId}-${++this.__nodeIdCounter}`;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12ec24d and 9867ad2.

📒 Files selected for processing (54)
  • editor/app/(tools)/(playground)/playground/image/_page.tsx (2 hunks)
  • editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/template-duo-001-viewer.tsx (2 hunks)
  • editor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx (0 hunks)
  • editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx (2 hunks)
  • editor/grida-canvas-react-starter-kit/starterkit-hierarchy/index.tsx (17 hunks)
  • editor/grida-canvas-react-starter-kit/starterkit-preview/index.tsx (3 hunks)
  • editor/grida-canvas-react-starter-kit/starterkit-toolbar/brush-toolbar.tsx (6 hunks)
  • editor/grida-canvas-react-starter-kit/starterkit-toolbar/index.tsx (5 hunks)
  • editor/grida-canvas-react/devtools/__unsafe-console.tsx (1 hunks)
  • editor/grida-canvas-react/devtools/api-host.tsx (1 hunks)
  • editor/grida-canvas-react/devtools/index.tsx (1 hunks)
  • editor/grida-canvas-react/index.ts (1 hunks)
  • editor/grida-canvas-react/plugins/use-recorder.ts (1 hunks)
  • editor/grida-canvas-react/renderer.tsx (4 hunks)
  • editor/grida-canvas-react/use-editor.tsx (1 hunks)
  • editor/grida-canvas-react/viewport/hooks/use-edge-scrolling.ts (3 hunks)
  • editor/grida-canvas-react/viewport/hotkeys.tsx (16 hunks)
  • editor/grida-canvas-react/viewport/surface-context-menu.tsx (7 hunks)
  • editor/grida-canvas-react/viewport/surface-hooks.ts (5 hunks)
  • editor/grida-canvas-react/viewport/surface.tsx (30 hunks)
  • editor/grida-canvas-react/viewport/ui/measurement.tsx (2 hunks)
  • editor/grida-canvas-react/viewport/ui/path-editor.tsx (2 hunks)
  • editor/grida-canvas-react/viewport/ui/snap.tsx (1 hunks)
  • editor/grida-canvas-react/viewport/ui/text-editor.tsx (2 hunks)
  • editor/grida-canvas/action.ts (2 hunks)
  • editor/grida-canvas/animation.ts (1 hunks)
  • editor/grida-canvas/backends/dom.ts (1 hunks)
  • editor/grida-canvas/editor.ts (4 hunks)
  • editor/grida-canvas/index.ts (3 hunks)
  • editor/grida-canvas/reducers/document.reducer.ts (2 hunks)
  • editor/grida-canvas/reducers/event-target.reducer.ts (2 hunks)
  • editor/grida-canvas/reducers/methods/transform.ts (1 hunks)
  • editor/grida-canvas/reducers/node.reducer.ts (1 hunks)
  • editor/grida-canvas/reducers/surface.reducer.ts (3 hunks)
  • editor/grida-canvas/reducers/tools/id.ts (1 hunks)
  • editor/scaffolds/playground-canvas/playground.tsx (8 hunks)
  • editor/scaffolds/playground-canvas/toolbar.tsx (14 hunks)
  • editor/scaffolds/sidebar/sidebar-node-hierarchy-list.tsx (3 hunks)
  • editor/scaffolds/sidecontrol/controls/ext-zoom.tsx (4 hunks)
  • editor/scaffolds/sidecontrol/controls/font-size.tsx (1 hunks)
  • editor/scaffolds/sidecontrol/controls/letter-spacing.tsx (1 hunks)
  • editor/scaffolds/sidecontrol/controls/line-height.tsx (1 hunks)
  • editor/scaffolds/sidecontrol/controls/opacity.tsx (1 hunks)
  • editor/scaffolds/sidecontrol/controls/rotate.tsx (1 hunks)
  • editor/scaffolds/sidecontrol/controls/stroke-width.tsx (1 hunks)
  • editor/scaffolds/sidecontrol/controls/utils/types.ts (0 hunks)
  • editor/scaffolds/sidecontrol/sidecontrol-doctype-form.tsx (2 hunks)
  • editor/scaffolds/sidecontrol/sidecontrol-doctype-site.tsx (3 hunks)
  • editor/scaffolds/sidecontrol/sidecontrol-document-properties.tsx (3 hunks)
  • editor/scaffolds/sidecontrol/sidecontrol-global.tsx (4 hunks)
  • editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (13 hunks)
  • editor/scaffolds/sidecontrol/ui/index.tsx (4 hunks)
  • editor/theme/templates/formcollection/page.tsx (1 hunks)
  • packages/grida-canvas-schema/grida.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • editor/app/(workbench)/[org]/[proj]/[id]/form/start/page.tsx
  • editor/scaffolds/sidecontrol/controls/utils/types.ts
🧰 Additional context used
🧬 Code Graph Analysis (27)
editor/theme/templates/formcollection/page.tsx (1)
editor/grida-canvas-react/provider.tsx (1)
  • useDocumentState (722-732)
editor/scaffolds/sidecontrol/sidecontrol-doctype-form.tsx (1)
editor/grida-canvas-react/provider.tsx (1)
  • useSelectionState (709-715)
editor/grida-canvas-react-starter-kit/starterkit-preview/index.tsx (1)
editor/grida-canvas-react/provider.tsx (2)
  • useDocumentState (722-732)
  • useCurrentSceneState (754-760)
editor/grida-canvas-react/viewport/ui/path-editor.tsx (1)
editor/grida-canvas-react/provider.tsx (3)
  • useEditorFlagsState (691-702)
  • useToolState (827-841)
  • useTransformState (762-780)
editor/scaffolds/sidecontrol/controls/font-size.tsx (2)
editor/scaffolds/sidecontrol/controls/utils/types.ts (1)
  • TMixed (3-3)
editor/grida-canvas/index.ts (1)
  • NumberChange (1751-1751)
editor/grida-canvas/animation.ts (1)
packages/grida-cmath/index.ts (1)
  • Transform (130-130)
editor/grida-canvas-react/viewport/ui/measurement.tsx (1)
editor/grida-canvas-react/provider.tsx (1)
  • useTransformState (762-780)
editor/scaffolds/sidebar/sidebar-node-hierarchy-list.tsx (2)
editor/grida-canvas-react/index.ts (1)
  • useCurrentEditor (1-1)
editor/components/ui/sidebar.tsx (1)
  • SidebarGroupAction (706-706)
editor/scaffolds/sidecontrol/controls/stroke-width.tsx (2)
editor/scaffolds/sidecontrol/controls/utils/types.ts (1)
  • TMixed (3-3)
editor/grida-canvas/index.ts (1)
  • NumberChange (1751-1751)
editor/scaffolds/sidecontrol/controls/letter-spacing.tsx (2)
editor/scaffolds/sidecontrol/controls/utils/types.ts (1)
  • TMixed (3-3)
editor/grida-canvas/index.ts (1)
  • NumberChange (1751-1751)
editor/scaffolds/sidecontrol/sidecontrol-global.tsx (1)
editor/grida-canvas-react/index.ts (1)
  • useCurrentEditor (1-1)
editor/grida-canvas-react/renderer.tsx (2)
editor/grida-canvas-react/provider.tsx (2)
  • useCurrentSceneState (754-760)
  • useTransformState (762-780)
editor/grida-canvas-react/index.ts (2)
  • useTransformState (9-9)
  • AutoInitialFitTransformer (19-19)
editor/scaffolds/sidecontrol/controls/line-height.tsx (2)
editor/scaffolds/sidecontrol/controls/utils/types.ts (1)
  • TMixed (3-3)
editor/grida-canvas/index.ts (1)
  • NumberChange (1751-1751)
editor/scaffolds/sidecontrol/sidecontrol-doctype-site.tsx (1)
editor/grida-canvas-react/index.ts (1)
  • useCurrentEditor (1-1)
editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/template-duo-001-viewer.tsx (1)
editor/grida-canvas-react/index.ts (1)
  • useCurrentEditor (1-1)
editor/scaffolds/sidecontrol/controls/rotate.tsx (2)
editor/scaffolds/sidecontrol/controls/utils/types.ts (1)
  • TMixed (3-3)
editor/grida-canvas/index.ts (1)
  • NumberChange (1751-1751)
editor/grida-canvas-react/viewport/surface-context-menu.tsx (2)
editor/grida-canvas-react/index.ts (1)
  • useCurrentEditor (1-1)
editor/grida-canvas-react/provider.tsx (2)
  • useSelectionState (709-715)
  • useCurrentSelection (255-613)
editor/scaffolds/sidecontrol/controls/opacity.tsx (2)
editor/scaffolds/sidecontrol/controls/utils/types.ts (1)
  • TMixed (3-3)
editor/grida-canvas/index.ts (1)
  • NumberChange (1751-1751)
editor/grida-canvas-react-starter-kit/starterkit-toolbar/index.tsx (3)
editor/grida-canvas-react/index.ts (3)
  • useCurrentEditor (1-1)
  • useToolState (10-10)
  • useEditorFlagsState (11-11)
editor/grida-canvas-react/provider.tsx (2)
  • useToolState (827-841)
  • useEditorFlagsState (691-702)
editor/grida-canvas-react-starter-kit/starterkit-toolbar/utils.ts (2)
  • toolbar_value_to_cursormode (42-70)
  • ToolbarToolType (3-16)
editor/scaffolds/sidecontrol/ui/index.tsx (1)
editor/grida-canvas/index.ts (1)
  • NumberChange (1751-1751)
editor/scaffolds/playground-canvas/playground.tsx (4)
editor/grida-canvas-react/devtools/api-host.tsx (1)
  • WindowCurrentEditorProvider (16-19)
editor/grida-canvas-react/index.ts (2)
  • useToolState (10-10)
  • useCurrentEditor (1-1)
editor/grida-canvas-react/provider.tsx (2)
  • useToolState (827-841)
  • useCurrentSceneState (754-760)
editor/grida-canvas-react/use-editor.tsx (1)
  • useCurrentEditor (25-33)
editor/grida-canvas-react/viewport/ui/text-editor.tsx (2)
editor/grida-canvas-react/index.ts (2)
  • useCurrentEditor (1-1)
  • useTransformState (9-9)
editor/grida-canvas-react/provider.tsx (1)
  • useTransformState (762-780)
editor/grida-canvas-react/viewport/hooks/use-edge-scrolling.ts (2)
editor/grida-canvas-react/index.ts (4)
  • useCurrentEditor (1-1)
  • useEditorState (1-1)
  • useToolState (10-10)
  • useTransformState (9-9)
editor/grida-canvas-react/provider.tsx (3)
  • useToolState (827-841)
  • useTransformState (762-780)
  • usePointerState (817-820)
editor/grida-canvas-react-starter-kit/starterkit-toolbar/brush-toolbar.tsx (3)
editor/grida-canvas-react/index.ts (2)
  • useCurrentEditor (1-1)
  • useBrushState (6-6)
packages/grida-canvas-bitmap/index.ts (1)
  • brush (422-483)
editor/grida-canvas-react/provider.tsx (1)
  • useBrushState (843-846)
editor/grida-canvas-react/viewport/hotkeys.tsx (2)
editor/grida-canvas-react/index.ts (2)
  • useCurrentEditor (1-1)
  • useToolState (10-10)
editor/grida-canvas-react/provider.tsx (3)
  • useToolState (827-841)
  • useA11yActions (903-988)
  • useCurrentSelection (255-613)
editor/grida-canvas-react/viewport/surface.tsx (8)
editor/grida-canvas-react/use-editor.tsx (2)
  • useCurrentEditor (25-33)
  • useEditorState (35-47)
editor/grida-canvas-react/provider.tsx (9)
  • useTransformState (762-780)
  • useGestureState (997-1016)
  • useSelectionState (709-715)
  • useToolState (827-841)
  • useBrushState (843-846)
  • useDocumentState (722-732)
  • useCurrentSceneState (754-760)
  • usePointerState (817-820)
  • useMultipleSelectionOverlayClick (855-887)
packages/grida-cmath/index.ts (4)
  • transform (523-533)
  • transform (860-868)
  • transform (1308-1344)
  • Transform (130-130)
editor/grida-canvas-react/viewport/ui/marquee.tsx (1)
  • MarqueeArea (15-29)
editor/grida-canvas/index.ts (1)
  • DropzoneIndication (144-152)
editor/grida-canvas-react/viewport/ui/knob.tsx (1)
  • Knob (6-85)
editor/grida-canvas-react/viewport/context.ts (1)
  • useViewport (11-19)
editor/grida-canvas-react/viewport/surface-hooks.ts (1)
  • useSurfaceSelectionGroups (129-137)
editor/grida-canvas/reducers/node.reducer.ts (3)
packages/grida-canvas-schema/grida.ts (3)
  • UnknwonNode (851-871)
  • UnknownNodeProperties (873-873)
  • Node (789-804)
packages/grida-canvas-cg/lib.ts (2)
  • LinearGradientPaint (317-322)
  • RadialGradientPaint (324-329)
editor/grida-canvas/action.ts (1)
  • NodeChangeAction (594-606)
🪛 Biome (1.9.4)
editor/grida-canvas-react/devtools/api-host.tsx

[error] 11-11: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

editor/grida-canvas-react/renderer.tsx

[error] 167-167: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

editor/grida-canvas/reducers/node.reducer.ts

[error] 367-367: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

editor/grida-canvas/editor.ts

[error] 1551-1569: The constructor should not return a value.

The constructor is here:

Returning a value from a constructor is ignored.

(lint/correctness/noConstructorReturn)

🔇 Additional comments (108)
editor/scaffolds/sidecontrol/sidecontrol-doctype-form.tsx (1)

25-25: LGTM! Clean migration to granular state hook.

The change from useDocument to useSelectionState is consistent with the broader refactor to use more specific state hooks. The new hook provides the same selection property that the component needs.

Also applies to: 56-56

editor/grida-canvas/reducers/tools/id.ts (1)

7-7: Good deprecation practice.

The deprecation notice clearly indicates the new recommended approach (editor.id()) while maintaining backward compatibility. This supports the gradual migration to the centralized editor instance.

editor/grida-canvas-react/viewport/ui/measurement.tsx (1)

8-8: LGTM! Consistent with the state management refactor.

The migration from useTransform to useTransformState maintains the same functionality while using the new centralized state pattern. The { transform } destructuring correctly accesses the transform matrix from the returned object.

Also applies to: 67-67

editor/theme/templates/formcollection/page.tsx (1)

20-20: LGTM! Proper migration to state-specific hook.

The change from useDocument to useDocumentState follows the established refactor pattern of using more granular state hooks. The { document } destructuring correctly accesses the document object from the new hook's return value.

Also applies to: 26-26

editor/grida-canvas-react/devtools/__unsafe-console.tsx (1)

6-6: LGTM: Import reordering aligns with refactoring.

The import reordering is clean and the removal of useEditorApi usage (mentioned in the summary) aligns with the broader migration to the unified useCurrentEditor interface.

editor/grida-canvas-react/devtools/index.tsx (2)

23-23: LGTM: Import path reflects component reorganization.

The updated import path for __UNSAFE_CONSOLE correctly reflects the reorganization of devtools components.


27-27: LGTM: Hook extraction to plugin location.

Moving useRecorder to a dedicated plugin location (../plugins/use-recorder) improves modularity and separation of concerns.

editor/grida-canvas-react-starter-kit/starterkit-preview/index.tsx (3)

5-5: LGTM: Hook replacement aligns with editor state consolidation.

Replacing useDocument with useDocumentState aligns with the architectural migration to centralize state access through useCurrentEditor.


25-25: LGTM: Consistent state hook migration.

The replacement of useCurrentScene with useCurrentSceneState follows the same pattern as other components and maintains the unified editor interface.


47-48: LGTM: Hook usage properly updated.

The destructuring assignments have been correctly updated to use the new state hooks while maintaining the same interface and functionality.

editor/grida-canvas-react/viewport/ui/path-editor.tsx (2)

3-6: LGTM: Systematic hook migration to state-based variants.

The replacement of direct hooks (useEditorFlags, useTool, useTransform) with their state-based variants follows the systematic migration pattern and maintains the same interfaces while using the unified useCurrentEditor internally.


15-17: LGTM: Hook usage correctly updated.

The destructuring assignments have been properly updated to use the new state hooks. Based on the relevant code snippets, these hooks provide the same data structure with proper memoization for performance optimization.

editor/grida-canvas-react-starter-kit/starterkit-artboard-list/index.tsx (2)

9-9: LGTM: Clean migration to unified editor hook.

The import change from useDocument to useCurrentEditor aligns perfectly with the PR objective of consolidating editor state access.


18-21: LGTM: Proper method access through editor object.

The refactor correctly updates the hook usage pattern - accessing insertNode through the editor object instead of destructuring it directly. The node insertion logic remains unchanged and properly typed.

editor/scaffolds/sidebar/sidebar-node-hierarchy-list.tsx (1)

4-4: LGTM: Consistent migration pattern implemented.

The changes follow the same pattern as other files in this refactor:

  • Import updated to useCurrentEditor
  • Method access through the editor object (editor.createScene())
  • Functionality preserved without any logic changes

This maintains consistency across the codebase migration.

Also applies to: 18-18, 27-27

editor/scaffolds/sidecontrol/controls/font-size.tsx (2)

7-8: LGTM: Clean import organization for type standardization.

The type-only import of editor and simplified import of TMixed improves import clarity and aligns with the API standardization effort.


16-16: LGTM: Type standardization improves API consistency.

The change from TChange<number> to editor.api.NumberChange standardizes the numeric change type across the editor API. This maintains the same functionality while improving type consistency throughout the codebase.

editor/grida-canvas-react/use-editor.tsx (1)

5-5: LGTM: Improved modularity through hook extraction.

The removal of useRecorder and its related imports is part of a beneficial architectural change. Moving recording functionality to a dedicated plugin file (editor/grida-canvas-react/plugins/use-recorder.ts) improves:

  • Separation of concerns: Core editor state management is now distinct from recording features
  • Modularity: Recording functionality can be maintained independently
  • Bundle optimization: Applications not using recording won't include this code

This aligns well with the PR objective of creating a cleaner, more modular editor architecture.

editor/scaffolds/sidecontrol/controls/letter-spacing.tsx (1)

1-2: LGTM! Clean type migration to the new editor API.

The changes properly migrate from the local TChange type to the standardized editor.api.NumberChange type, aligning with the broader architectural refactor. The use of type-only imports is appropriate and the type compatibility should be maintained.

Also applies to: 10-10

editor/scaffolds/sidecontrol/sidecontrol-global.tsx (1)

75-75: Excellent migration to the unified editor interface!

The changes successfully migrate from the hook-based approach to the centralized useCurrentEditor pattern:

  • useDocumentState now provides just the state data
  • useCurrentEditor provides the editor instance with action methods
  • The method call editor.changeNodeProps("page", k, v) properly replaces the previous hook-based approach

This aligns perfectly with the PR's goal of consolidating editor interactions into a unified interface.

Also applies to: 89-89, 158-158, 188-188

editor/scaffolds/sidecontrol/controls/opacity.tsx (1)

5-6: Consistent type migration - LGTM!

The changes follow the same pattern as other control components, migrating from TChange<number> to editor.api.NumberChange. The type-only import usage is correct and maintains compatibility.

Also applies to: 13-13

editor/scaffolds/sidecontrol/controls/rotate.tsx (1)

1-2: Perfect consistency with the type refactor!

This file completes the consistent pattern seen across all control components, successfully migrating from TChange<number> to editor.api.NumberChange. The changes maintain functionality while adopting the standardized type system.

Also applies to: 10-10

editor/scaffolds/sidecontrol/controls/stroke-width.tsx (1)

1-2: LGTM! Type refactoring aligns with centralization pattern.

The change from local TChange<number> to editor.api.NumberChange is correct and consistent with the broader refactoring to centralize types in the editor API. Since NumberChange is defined as TChange<number> in the editor module, this change maintains semantic equivalence while improving consistency.

Also applies to: 10-10

editor/scaffolds/sidecontrol/controls/line-height.tsx (1)

2-3: LGTM! Consistent with type centralization pattern.

The changes follow the same pattern as other control components, replacing local TChange<number> with the centralized editor.api.NumberChange type. This improves consistency across the codebase.

Also applies to: 11-11

editor/grida-canvas-react/renderer.tsx (1)

4-4: LGTM! Hook replacements align with editor API refactoring.

The changes correctly replace the old hooks with their new equivalents:

  • useCurrentScene()useCurrentSceneState()
  • useTransform()useTransformState()
  • fit("*")editor.fit("*")

These changes are consistent with the broader refactoring to centralize editor functionality in the Editor class instance.

Also applies to: 53-53, 141-141, 161-161, 168-168

editor/app/(workbench)/[org]/[proj]/(console)/(campaign)/campaigns/[campaign]/design/template-duo-001-viewer.tsx (1)

15-15: LGTM! Clean migration to unified editor API.

The migration from useDocument and useTransform hooks to useCurrentEditor is correctly implemented. The functionality remains the same - selecting and fitting nodes - but now uses the unified editor interface as intended by the architectural refactor.

Also applies to: 278-278, 283-284

editor/grida-canvas/reducers/methods/transform.ts (1)

607-610: LGTM! Correctly implements the new generic node change action pattern.

The migration from the specific "node/change/rotation" action to the generic "node/change/*" action with simplified payload aligns with the broader architectural refactor. This change centralizes node property updates through a unified action interface while maintaining the same rotation functionality.

editor/scaffolds/sidecontrol/sidecontrol-doctype-site.tsx (1)

14-14: LGTM! Consistent migration to unified editor interface.

The changes correctly migrate from useDocument to useCurrentEditor and update the schemaPutProperty call to use the editor instance method. This maintains the same functionality while following the new unified editor API pattern.

Also applies to: 68-68, 139-139

editor/app/(tools)/(playground)/playground/image/_page.tsx (1)

46-46: LGTM! Excellent API improvement with the unified editor interface.

The migration to useCurrentEditor is correctly implemented, and the new insertNode method that returns the node ID is a significant improvement over the previous approach. This eliminates the need for manual ID management and makes the code cleaner and more intuitive.

The flow is well-structured:

  1. editor.insertNode() creates the node and returns its ID
  2. The returned ID is used in the promise chain for editor.changeNodeSrc()

Also applies to: 127-127, 133-149

editor/grida-canvas-react/index.ts (1)

1-14: Excellent API consolidation supporting the editor refactor.

The export restructuring effectively supports the migration to a unified editor instance pattern. The addition of useCurrentEditor and granular state hooks (useDocumentState, useBrushState, useNodeActions, etc.) creates a more modular and consistent API surface. Moving useRecorder to a dedicated plugin module also improves separation of concerns.

editor/grida-canvas-react/devtools/api-host.tsx (1)

16-19: Clean component pattern for the devtools provider.

The provider component follows React patterns correctly by using the hook and rendering null.

editor/grida-canvas-react/plugins/use-recorder.ts (1)

7-16: Excellent concurrent-safe implementation.

The hook correctly uses useSyncExternalStoreWithSelector with deep equality checking for optimal React 18+ compatibility and performance. The state synchronization pattern is well-implemented.

editor/grida-canvas-react/viewport/ui/text-editor.tsx (3)

3-5: Perfect alignment with the unified editor architecture.

The import updates correctly reflect the new architecture, replacing separate transform hooks with the unified useCurrentEditor pattern and the granular useTransformState hook.


14-15: Clean integration of the new editor instance pattern.

The unified editor instance and transform state access properly implements the architectural refactor, consolidating multiple hooks into a cohesive interface.


59-68: Excellent consolidation of editor actions.

The event handlers now properly use the unified editor instance methods (editor.tryExitContentEditMode, editor.changeNodeText) instead of separate hook-based actions, creating a more cohesive and maintainable API surface.

editor/scaffolds/playground-canvas/playground.tsx (4)

32-35: LGTM! Import updates align with the new editor architecture.

The replacement of useTool and useCurrentScene with their "State" variants (useToolState, useCurrentSceneState) is consistent with the refactoring to centralize editor state management through the useCurrentEditor hook.


126-126: LGTM! WindowCurrentEditorProvider addition supports development tooling.

The addition of WindowCurrentEditorProvider inside the StandaloneDocumentEditor component tree enables external access to the current editor instance, which aligns with the scripting interface objectives mentioned in the PR.

Also applies to: 274-274


510-511: LGTM! Hook usage updates maintain compatibility with new architecture.

The replacement of useTool() with useToolState() and useCurrentScene() with useCurrentSceneState() maintains the same destructuring patterns and data access while routing through the centralized editor state management.

Also applies to: 571-571


710-710: LGTM! Method calls updated to use centralized editor API.

The changes from direct insertNode() calls to editor.insertNode() after obtaining the editor instance via useCurrentEditor() correctly implements the new editor-centric API pattern. This centralizes document manipulation through the unified Editor class interface.

Also applies to: 736-736, 753-753

editor/grida-canvas/reducers/surface.reducer.ts (2)

3-3: LGTM! Import statement consolidation improves code organization.

Consolidating the imports into a single line is a good cleanup with no functional impact.


199-199: LGTM! Type parameter updates align with systematic type refinement.

The change from TChange<number> to editor.api.NumberChange is part of the broader type system refactoring to use more specific and centralized type definitions. The function logic remains unchanged, indicating the types have compatible structures.

Also applies to: 218-218

packages/grida-canvas-schema/grida.ts (1)

873-873: LGTM! Generic type parameter adds flexibility while maintaining compatibility.

Making UnknownNodeProperties generic with a default parameter T = unknown allows for more specific typing when needed while maintaining backward compatibility with existing usage.

editor/grida-canvas/reducers/event-target.reducer.ts (2)

934-940: LGTM: Action type consolidation aligns with the broader refactor.

The change from specific action type "node/change/cornerRadius" to the generic "node/change/*" is consistent with the broader refactor to consolidate node change actions mentioned in the AI summary.


994-999: LGTM: Improved payload structure for flex gap updates.

The changes consolidate the action type to the generic "node/change/*" and improve the payload structure by setting both mainAxisGap and crossAxisGap instead of a single gap property. This provides more granular control over flex layout properties.

editor/grida-canvas-react/viewport/ui/snap.tsx (1)

2-20: LGTM: Improved modularity with granular state hooks.

The refactor successfully migrates from combined hooks (useEventTarget, useTransform) to more granular state management with useCurrentEditor, useGestureState, useTransformState, and useEditorState. This centralization through the editor instance improves consistency and modularity across the codebase.

The surface_snapping state access through useEditorState(editor, (state) => state.surface_snapping) provides better type safety and encapsulation.

editor/scaffolds/sidecontrol/ui/index.tsx (2)

17-17: LGTM: Type import standardization.

The change from local TChange type to editor.api.NumberChange standardizes the change event types across the editor UI components, aligning with the broader API refactor.


187-187: LGTM: Consistent type annotations for NumberChange.

The type annotations and casts are correctly updated to use editor.api.NumberChange instead of the previous TChange<number>. This provides better type consistency across the editor API and maintains the same functionality for both "delta" and "set" change types.

Also applies to: 225-235, 247-250

editor/grida-canvas-react/viewport/surface-hooks.ts (4)

2-2: LGTM: Updated imports for centralized state management.

The import changes replace direct document and transform hooks with their state counterparts (useDocumentState, useTransformState) and introduce useCurrentEditor for centralized editor access. This aligns with the broader refactor to improve state management consistency.

Also applies to: 6-6


246-247: LGTM: Consistent state access pattern.

The hook usage has been properly updated to use the new state hooks pattern:

  • useDocumentState() for document and document_ctx access
  • useTransformState() for transform access
  • useCurrentEditor() for editor instance access

This centralizes state management through the editor instance as intended by the refactor.

Also applies to: 298-300


346-346: LGTM: Centralized rotation calculation.

Using instance.getNodeAbsoluteRotation(node_id) instead of direct document access centralizes the rotation calculation logic through the editor instance, improving encapsulation and consistency.


408-408: LGTM: Properly updated dependency array.

The dependency array correctly reflects the new state access pattern, removing document_ctx since it's now accessed through useDocumentState() hook rather than being a separate dependency.

editor/grida-canvas-react-starter-kit/starterkit-toolbar/index.tsx (3)

29-33: Migration to centralized editor API looks good.

The import changes correctly migrate from individual provider hooks to the unified editor interface. This aligns well with the architectural refactoring objective.


55-57: Proper hook usage for editor state access.

The migration to useCurrentEditor(), useToolState(), and useEditorFlagsState() follows the new centralized pattern correctly and maintains the same functionality.


82-87: Consistent editor method calls for tool updates.

All tool state updates now properly use editor.setTool() instead of direct setters, which centralizes the tool management through the editor instance. The conversion logic using toolbar_value_to_cursormode is preserved correctly.

Also applies to: 99-100, 118-119, 125-126

editor/grida-canvas-react/viewport/surface-context-menu.tsx (4)

11-12: Import updates align with editor centralization.

The migration to useCurrentSelection, useSelectionState, and useCurrentEditor correctly consolidates the editor state access pattern.

Also applies to: 16-16


22-25: Proper hook usage for centralized editor state.

The editor instance and selection state are correctly accessed through the new centralized hooks, maintaining the same functionality while improving the architectural consistency.


53-53: Editor paste method integration.

The fallback to editor.paste() when clipboard API fails is correctly implemented, ensuring consistent paste behavior through the centralized editor interface.


84-84: Consistent editor method usage for document operations.

All document manipulation operations (ordering, grouping, auto-layout, deletion) now properly use the centralized editor methods (editor.order(), editor.contain(), editor.autoLayout(), editor.deleteNode()), maintaining functionality while improving architectural consistency.

Also applies to: 94-94, 105-105, 115-115, 126-126

editor/scaffolds/sidecontrol/controls/ext-zoom.tsx (4)

14-14: Appropriate mixed import strategy for state hooks.

The imports correctly use useTransformState from the provider for specific state slicing while using useCurrentEditor and useEditorState for centralized editor access. This mixed approach is logical for the migration.

Also applies to: 16-19


22-25: Correct editor state access pattern.

The editor instance and UI state (ruler, pixelgrid) are properly accessed through the centralized hooks, with transform state appropriately sourced from the specific state hook.


44-44: Comprehensive zoom control integration.

All zoom operations (editor.scale(), editor.zoomIn(), editor.zoomOut(), editor.fit()) are correctly implemented using the centralized editor methods, maintaining the expected zoom functionality.

Also applies to: 51-51, 59-59, 67-67, 75-75, 83-83, 90-90, 98-98


107-107: UI toggle methods properly integrated.

The pixel grid and ruler toggle functionality correctly uses editor.togglePixelGrid() and editor.toggleRuler() methods, maintaining the UI control functionality through the centralized editor interface.

Also applies to: 117-117

editor/grida-canvas-react/viewport/hooks/use-edge-scrolling.ts (4)

5-8: State hook imports correctly updated.

The imports properly migrate to the new state hooks (usePointerState, useToolState, useTransformState) and centralized editor access (useCurrentEditor, useEditorState), following the established migration pattern.

Also applies to: 11-11


27-31: Proper editor state access for edge scrolling.

The editor instance and state access are correctly implemented. The dragging state is properly accessed through useEditorState, and other state hooks follow the new pattern while maintaining the edge scrolling functionality.


62-62: Transform operation correctly centralized.

The transform update properly uses instance.transform(next) instead of a direct setter, centralizing the transform operation through the editor instance while maintaining the edge scrolling behavior.


68-68: Complete useEffect dependency array.

The dependency array correctly includes all state variables (dragging, pointer.client, transform, viewport, enabled, tool.type) that are used within the effect, ensuring proper reactivity and preventing stale closures.

editor/grida-canvas-react-starter-kit/starterkit-toolbar/brush-toolbar.tsx (4)

5-5: LGTM! Clean migration to unified editor API.

The refactoring correctly replaces the previous useBrush and useDocument hooks with the unified useCurrentEditor approach, maintaining the same functionality while improving consistency across the codebase.

Also applies to: 49-50


77-77: Correctly migrated brush control methods.

The brush size and opacity changes properly use the editor instance methods while maintaining the same NumberChange API pattern.

Also applies to: 107-107


154-154: Brush selection properly migrated.

The brush change handler correctly uses the editor instance method.


296-296: EyedropButton correctly uses editor instance.

The clipboard color setter is properly migrated to use editor.setClipboardColor.

Also applies to: 304-304

editor/scaffolds/sidecontrol/sidecontrol-document-properties.tsx (3)

25-25: Import statements correctly updated.

The imports properly migrate from individual hooks to the unified editor approach with useCurrentEditor, useDocumentState, and useCurrentSceneState.

Also applies to: 34-34


37-45: Scene background control properly migrated.

The component correctly uses useCurrentSceneState for state access and routes the background color change through editor.changeSceneBackground with the proper scene_id parameter.


53-60: Document properties management correctly migrated.

All schema operations (schemaDefineProperty, schemaRenameProperty, schemaUpdateProperty, schemaDeleteProperty) are properly routed through the editor instance while maintaining their original signatures.

Also applies to: 96-103

editor/grida-canvas-react/viewport/hotkeys.tsx (4)

2-2: Hook setup correctly migrated.

The imports and initialization properly use useCurrentEditor while maintaining specific hooks like useCurrentSelection, useToolState, and useA11yActions for their specialized interfaces.

Also applies to: 7-7, 320-324


328-340: Configuration methods properly migrated in blur handler.

All surface configuration methods are correctly routed through the editor instance, maintaining the same reset behavior on window blur.


349-362: Modifier key handlers correctly migrated.

The Meta/Control, Alt, and Shift key handlers properly use editor configuration methods for both keydown and keyup events, maintaining the same modifier behavior.

Also applies to: 378-389


411-915: All hotkey handlers consistently migrated.

The extensive hotkey handler migrations are all correctly implemented, including:

  • Tool switching (setTool)
  • Selection operations (select, deleteNode, duplicate)
  • History operations (undo, redo)
  • Clipboard operations (cut, copy)
  • Transform operations (nudgeResize, order, align, distributeEvenly)
  • View operations (scale, fit, zoomIn, zoomOut)
  • Content operations (toggleBold, setOpacity, autoLayout, contain)

All maintain their original behavior while using the unified editor API.

editor/grida-canvas-react-starter-kit/starterkit-hierarchy/index.tsx (3)

10-10: Import statements properly updated.

The imports correctly migrate to use useCurrentEditor, useEditorState, and useCurrentSceneState for the unified editor approach.

Also applies to: 38-38


51-51: Scene management operations correctly migrated.

All scene operations are properly routed through the editor instance:

  • duplicateScene, deleteScene in context menu
  • loadScene for selection
  • renameScene for name changes
  • State access via useEditorState with proper selectors

Also applies to: 70-70, 79-79, 91-93, 111-111, 175-175


197-197: Node hierarchy operations properly migrated.

All node operations correctly use editor methods:

  • Context menu actions (copy, order, toggleNodeActive, fit, toggleNodeLocked, deleteNode)
  • Selection and hover handling
  • Move operations
  • Name changes

Also applies to: 205-205, 224-224, 231-231, 240-240, 248-248, 256-256, 265-265, 295-295, 312-312, 362-362, 365-365, 386-386, 400-400, 412-412

editor/scaffolds/playground-canvas/toolbar.tsx (4)

68-69: LGTM! Clean migration to the unified editor API.

The refactoring properly uses the centralized useCurrentEditor hook and delegates text changes through the editor instance.

Also applies to: 134-134, 142-142


149-150: Consistent tool management through the editor instance.

The toolbar correctly uses the centralized editor for all tool state changes.

Also applies to: 164-168, 181-181, 200-200, 212-212


243-244: Proper mode management through the editor API.

The auxiliary toolbar correctly delegates tool and mode changes to the editor instance.

Also applies to: 256-256, 273-273


309-310: Excellent use of the state selector pattern.

The clipboard color management properly uses useEditorState with a selector function and updates through the editor instance.

Also applies to: 343-343

editor/grida-canvas/reducers/node.reducer.ts (4)

1-467: Excellent refactoring that improves maintainability and extensibility.

The consolidation of node property updates into a generic handler with centralized property definitions is a significant improvement. This approach:

  • Reduces code duplication
  • Makes it easier to add new properties
  • Ensures consistent validation and update logic
  • Aligns well with the broader editor API refactoring

The architecture is well-thought-out and will make the codebase more maintainable.

🧰 Tools
🪛 Biome (1.9.4)

[error] 367-367: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)


8-19: Well-designed property handler pattern.

The defineNodeProperty helper provides a clean abstraction for defining property-specific assertions and application logic. The type safety with generics ensures compile-time correctness.


384-393: Excellent refactor to generic property handling.

The new "node/change/*" case elegantly handles all property updates through a single mechanism. The destructuring pattern cleanly separates metadata from actual property updates.


24-360:

Details

❓ Verification inconclusive

Verify complete property coverage.

The safe_properties map is comprehensive, but please verify that all properties previously handled by individual cases are now covered.

Run this script to check for any properties that might be missing handlers:


🏁 Script executed:

#!/bin/bash
# Description: Check for node properties that might not have handlers defined

# Search for all property keys used in the UnknownNode type
ast-grep --pattern 'type UnknwonNode = $_' | head -100

# Search for any node property assignments in the codebase that might not be in safe_properties
rg -A 2 'node\.' --type ts | grep -E '^\s*(node|draft)\.[a-zA-Z]+\s*=' | grep -v -E '(type|id|children|_type|_id)' | sort | uniq

Length of output: 1630


Verify all node schema properties have handlers

Our initial search didn’t reveal any unhandled draft.<prop> assignments, but to ensure full coverage please cross-reference:

  • editor/grida-canvas/reducers/node.reducer.ts – the keys in the safe_properties map
  • packages/grida-canvas-schema/grida.ts – all fields listed under export type UnknwonNode = …

In particular, confirm you have handlers for:

  • VideoNode props (autoplay, loop, muted, playbackRate)
  • HTMLIFrameNode attributes (allowFullScreen, sandbox, etc.)
  • Effect settings (blur, innerShadow, layerBlur, clipContent)
  • TextNode formatting (fontFamily, textDecoration, rich-text span attributes)
  • Any new Component/Instance/TemplateInstance-specific properties

If any schema fields are missing, add corresponding defineNodeProperty entries.

editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (2)

48-73: Well-executed migration to the new editor hooks architecture.

The migration from general hooks like useDocument and useSelection to more specific hooks like useCurrentEditor, useEditorState, and useNodeState is a good architectural improvement that:

  • Provides more granular state access
  • Likely improves performance by reducing unnecessary re-renders
  • Aligns with the unified editor API pattern

Also applies to: 93-93


1195-1284: Excellent modularization of position and dimension controls.

The extraction of SectionPosition and SectionDimension components is a great refactoring that:

  • Improves code organization and readability
  • Makes the components more reusable
  • Follows the single responsibility principle
  • Uses the new hooks pattern consistently
editor/grida-canvas/action.ts (2)

594-606: Clean and flexible action type design.

The refactored NodeChangeAction type is well-designed:

  • The generic "node/change/*" variant provides flexibility for simple property updates
  • Specific action types are retained for complex operations that require special handling
  • Type safety is maintained through proper use of Partial and Omit

This aligns perfectly with the reducer implementation.


504-512: Good type refinement for brush actions.

The change from TChange<number> to editor.api.NumberChange for brush size and opacity actions improves type safety and maintains consistency with other numeric property changes throughout the codebase.

editor/grida-canvas/index.ts (7)

1722-1736: Good addition of verbose error class.

The EditorConsumerVerboseError class provides excellent debugging capabilities by attaching context data to errors. The implementation follows best practices with proper error name setting and stack trace capture.


1738-1751: Well-designed change type system.

The TChange<T> pattern elegantly handles both absolute ("set") and relative ("delta") value changes. The NonNullable<T> constraint on delta values is a smart type safety measure.


1895-1914: Clean interface design for editor features.

The new interfaces (IBrushToolActions, IPixelGridActions, IRulerActions, IGuide2DActions) follow SOLID principles well with focused responsibilities and clear method signatures.


2023-2024: Excellent improvement to insertNode method.

Returning the NodeID from insertNode is a significant improvement that enables better programmatic control over newly created nodes.


1738-1751: Well-designed change types!

The TChange<T> generic type and NumberChange specialization provide a clean abstraction for representing both absolute and relative changes. The use of NonNullable<T> for delta values ensures type safety.


1895-1943: Excellent modular interface design!

The new action interfaces (IBrushToolActions, IPixelGridActions, IRulerActions, IGuide2DActions, ICameraActions) are well-structured with clear separation of concerns. The documentation on camera methods is particularly helpful.


2023-2023: Good API improvement: insertNode now returns the created node ID.

The change to have insertNode return a NodeID is a significant improvement that enables better programmatic control and chaining of operations.

editor/grida-canvas/editor.ts (2)

15-49: Well-implemented utility functions.

Both throttle and resolveNumberChangeValue are properly implemented with good error handling. The number change resolver correctly validates node properties before applying delta changes.


899-918: Verify undefined handling for optional numeric properties.

The changeTextNodeLineHeight method correctly handles the case, but ensure this pattern is consistent across all methods dealing with optional numeric properties.

editor/grida-canvas-react/viewport/surface.tsx (5)

5-19: LGTM! Clean refactor to centralized state hooks.

The migration from individual hooks to unified state hooks through useCurrentEditor() improves consistency and reduces the number of separate contexts. This aligns well with the Editor API V2 migration objective.


173-173: LGTM! Consistent with the editor instance pattern.

The pointer move handler correctly delegates to the editor instance.


194-231: LGTM! Event handlers properly migrated to editor methods.

All mouse and pointer event handlers consistently use the centralized editor instance methods, maintaining the same behavior while improving the architecture.


394-408: Well-implemented marquee overlay component.

The MarqueeOverlay component correctly:

  • Uses the editor state to access marquee data
  • Transforms coordinates from canvas to surface space
  • Returns null when no marquee is active

497-515: LGTM! Node interaction handlers properly refactored.

The NodeTitleBar component correctly uses editor methods for all interactions including hover, rename, and selection operations.

editor/grida-canvas/reducers/document.reducer.ts (2)

794-814: Good consolidation of node change actions.

The refactor to handle all node changes through a single "node/change/*" case improves maintainability by centralizing the logic. The fontFamily case is correctly kept separate due to its side effect of updating googlefonts.


947-950: Improved hierarchy graph update approach.

Creating a new editor.dq.Context from the draft document and assigning its snapshot is a more reliable approach than manually mutating the children ID mapping. This ensures the document context accurately reflects the reordered state.

Comment on lines +18 to +30
return React.useMemo(
() => ({
status: state.status,
nframes: state.nframes,
start: () => recorder.start(),
stop: () => recorder.stop(),
clear: () => recorder.clear(),
replay: () => recorder.play(),
exit: () => recorder.exit(),
dumps: () => recorder.dumps(),
}),
[state, recorder]
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix method naming inconsistency.

The memoized object structure is good, but there's a naming inconsistency between the exposed method name and the actual call.

Apply this diff to fix the method naming:

-      replay: () => recorder.play(),
+      replay: () => recorder.replay(),

Or if play() is the correct method to call, rename the exposed method:

-      replay: () => recorder.play(),
+      play: () => recorder.play(),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In editor/grida-canvas-react/plugins/use-recorder.ts around lines 18 to 30, the
exposed method name 'replay' does not match the actual method called 'play' on
the recorder object. To fix this, either rename the exposed method from 'replay'
to 'play' to match the recorder method, or if you want to keep the exposed
method as 'replay', change the call to recorder.replay() if that method exists.
Ensure the method names are consistent between the exposed API and the
underlying calls.

Comment on lines +109 to +141
fill: defineNodeProperty<"fill">({
assert: (node) =>
node.type === "vector" ||
node.type === "path" ||
node.type === "image" ||
node.type === "rectangle" ||
node.type === "ellipse" ||
node.type === "text" ||
node.type === "richtext" ||
node.type === "container" ||
node.type === "component",
apply: (draft, value, prev) => {
if (value === null) {
draft.fill = undefined;
return;
}

switch (value.type) {
case "linear_gradient":
case "radial_gradient":
draft.fill = {
...(value as cg.LinearGradientPaint | cg.RadialGradientPaint),
id: `gradient-${v4()}`,
};
break;
case "solid":
draft.fill = value as
| grida.program.nodes.i.props.SolidPaintToken
| cg.SolidPaint;
break;
}
},
}),
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Inconsistent null handling across paint and text properties.

The codebase handles null values inconsistently:

  • fill and stroke properties convert null to undefined (lines 122, 192)
  • text property preserves null using value ?? null (line 346)

This inconsistency could lead to unexpected behavior. Consider standardizing the approach.

Standardize null handling across all properties. Either consistently convert null to undefined or preserve null values:

  text: defineNodeProperty<"text">({
    assert: (node) => node.type === "text",
    apply: (draft, value, prev) => {
-     draft.text = value ?? null;
+     draft.text = value === null ? undefined : value;
    },
  }),

Also applies to: 184-211, 343-348

🤖 Prompt for AI Agents
In editor/grida-canvas/reducers/node.reducer.ts between lines 109-141, 184-211,
and 343-348, the handling of null values for fill, stroke, and text properties
is inconsistent; fill and stroke convert null to undefined, while text preserves
null. To fix this, choose a consistent approach for null handling across all
these properties—either convert all null values to undefined or preserve null
values as is—and update the code accordingly to apply this standard uniformly.

Comment on lines +126 to +140
switch (value.type) {
case "linear_gradient":
case "radial_gradient":
draft.fill = {
...(value as cg.LinearGradientPaint | cg.RadialGradientPaint),
id: `gradient-${v4()}`,
};
break;
case "solid":
draft.fill = value as
| grida.program.nodes.i.props.SolidPaintToken
| cg.SolidPaint;
break;
}
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential issue with gradient ID generation.

The gradient handlers generate new IDs using v4() every time a gradient is applied. This could lead to:

  1. Unnecessary ID churn when the same gradient is reapplied
  2. Potential performance impact from frequent UUID generation
  3. Loss of gradient identity tracking

Consider reusing existing gradient IDs when appropriate or generating IDs at a higher level.

  case "linear_gradient":
  case "radial_gradient":
    draft.fill = {
      ...(value as cg.LinearGradientPaint | cg.RadialGradientPaint),
-     id: `gradient-${v4()}`,
+     id: value.id || `gradient-${v4()}`,
    };
    break;

Also applies to: 196-210

🤖 Prompt for AI Agents
In editor/grida-canvas/reducers/node.reducer.ts around lines 126 to 140, the
current code generates a new UUID for gradient IDs every time a gradient is
applied, causing unnecessary ID changes and potential performance issues. To fix
this, modify the logic to check if the gradient already has an ID and reuse it
instead of generating a new one with v4(). Only generate a new ID if none
exists. Apply the same fix to lines 196 to 210 where similar gradient ID
generation occurs.

Comment on lines +384 to 394
case "node/change/*": {
const { type: _, node_id: __, ...values } = action;
for (const [key, value] of Object.entries(values)) {
applyNodeProperty(
draft as grida.program.nodes.UnknownNodeProperties,
key as keyof grida.program.nodes.UnknwonNode,
value
);
}
break;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety in the generic node change handler.

The generic handler looks good, but the type casting on line 389 could be safer. Consider validating that the key is actually a valid node property before casting.

  case "node/change/*": {
    const { type: _, node_id: __, ...values } = action;
    for (const [key, value] of Object.entries(values)) {
+     // Validate key is a known property
+     if (!(key in safe_properties)) {
+       console.warn(`Skipping unknown property: "${key}"`);
+       continue;
+     }
      applyNodeProperty(
        draft as grida.program.nodes.UnknownNodeProperties,
        key as keyof grida.program.nodes.UnknwonNode,
        value
      );
    }
    break;
  }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In editor/grida-canvas/reducers/node.reducer.ts around lines 384 to 394, the
code casts keys from the action values to node property keys without validation,
which risks unsafe type assertions. To fix this, add a runtime check to confirm
each key is a valid property of the node before casting and applying it. This
can be done by verifying the key against a known set of valid node property keys
or using a type guard function to ensure type safety before calling
applyNodeProperty.

Comment on lines +8 to +17
function defineNodeProperty<
K extends keyof grida.program.nodes.UnknwonNode,
>(handlers: {
assert?: (node: grida.program.nodes.UnknwonNode) => boolean;
apply: (
draft: grida.program.nodes.UnknownNodeProperties,
value: NonNullable<grida.program.nodes.UnknwonNode[K]>,
prev?: grida.program.nodes.UnknwonNode[K]
) => void;
}) {
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Address the type name inconsistency between UnknwonNode and UnknownNodeProperties.

While UnknwonNode appears to be an existing typo in the codebase, the inconsistency between line 9 using UnknwonNode and line 13 using UnknownNodeProperties makes the code harder to maintain. Consider using consistent naming throughout.


🏁 Script executed:

#!/bin/bash
# Description: Check the usage of UnknwonNode vs UnknownNode in the codebase

# Search for UnknwonNode usage
echo "=== UnknwonNode usage ==="
rg "UnknwonNode" -A 2 -B 2

echo -e "\n=== UnknownNode usage ==="
# Search for UnknownNode usage (without the typo)
rg "UnknownNode(?!Properties)" -A 2 -B 2

Length of output: 11634


Rename UnknwonNode to UnknownNode for consistent naming

The core node type is misspelled as UnknwonNode in the schema but its properties helper uses the correctly spelled UnknownNodeProperties. This one-letter typo propagates confusion and makes maintenance harder. Please:

  • In packages/grida-canvas-schema/grida.ts, rename
    export type UnknwonNode = …
    to
    export type UnknownNode = …
  • Update all references—including in
    • editor/grida-canvas/reducers/node.reducer.ts
    • editor/grida-canvas-react/provider.tsx
    • editor/grida-canvas/action.ts
    • editor/grida-canvas/editor.ts
    • editor/grida-canvas/viewport/surface-hooks.ts
    • and any other files importing grida.program.nodes.UnknwonNode—to use UnknownNode instead.
  • Ensure the generic helper signatures align:
    defineNodeProperty<K extends keyof grida.program.nodes.UnknownNode>(…)
    with UnknownNodeProperties as before.

This consistent renaming will eliminate the typo and align your type definitions.

🤖 Prompt for AI Agents
In editor/grida-canvas/reducers/node.reducer.ts around lines 8 to 17, the type
name UnknwonNode is misspelled and inconsistent with UnknownNodeProperties.
Rename all occurrences of UnknwonNode to UnknownNode in this file and across the
codebase, including the type definition in packages/grida-canvas-schema/grida.ts
and all imports in related files. Ensure the generic type parameter and function
signatures use UnknownNode consistently to fix the typo and maintain uniform
naming.

Comment on lines +362 to +377
function applyNodeProperty<K extends keyof grida.program.nodes.UnknwonNode>(
draft: grida.program.nodes.UnknownNodeProperties,
key: K,
value: any
) {
"use strict";
if (!(key in safe_properties)) {
throw new Error(`property handler not found: "${key}"`);
}
const property = safe_properties[key as keyof typeof safe_properties];
if (property) {
const prev = (draft as any)[key];
// TODO: assert - decide pre or after
property.apply(draft, value, prev);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove redundant "use strict" directive and improve type safety.

The function has several issues:

  1. The "use strict" directive on line 367 is redundant in ES modules
  2. Using any type for the value parameter loses type safety
  3. TODO comment about assertion timing should be addressed
 function applyNodeProperty<K extends keyof grida.program.nodes.UnknwonNode>(
   draft: grida.program.nodes.UnknownNodeProperties,
   key: K,
-  value: any
+  value: grida.program.nodes.UnknwonNode[K]
 ) {
-  "use strict";
   if (!(key in safe_properties)) {
     throw new Error(`property handler not found: "${key}"`);
   }
   const property = safe_properties[key as keyof typeof safe_properties];
   if (property) {
     const prev = (draft as any)[key];
-    // TODO: assert - decide pre or after
+    // Assert before applying to fail fast
+    if (property.assert && !property.assert(draft as grida.program.nodes.UnknwonNode)) {
+      throw new Error(`Invalid node type for property "${key}"`);
+    }
     property.apply(draft, value, prev);
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function applyNodeProperty<K extends keyof grida.program.nodes.UnknwonNode>(
draft: grida.program.nodes.UnknownNodeProperties,
key: K,
value: any
) {
"use strict";
if (!(key in safe_properties)) {
throw new Error(`property handler not found: "${key}"`);
}
const property = safe_properties[key as keyof typeof safe_properties];
if (property) {
const prev = (draft as any)[key];
// TODO: assert - decide pre or after
property.apply(draft, value, prev);
}
}
function applyNodeProperty<K extends keyof grida.program.nodes.UnknwonNode>(
draft: grida.program.nodes.UnknownNodeProperties,
key: K,
value: grida.program.nodes.UnknwonNode[K]
) {
if (!(key in safe_properties)) {
throw new Error(`property handler not found: "${key}"`);
}
const property = safe_properties[key as keyof typeof safe_properties];
if (property) {
const prev = (draft as any)[key];
// Assert before applying to fail fast
if (property.assert && !property.assert(draft as grida.program.nodes.UnknwonNode)) {
throw new Error(`Invalid node type for property "${key}"`);
}
property.apply(draft, value, prev);
}
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 367-367: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

🤖 Prompt for AI Agents
In editor/grida-canvas/reducers/node.reducer.ts lines 362 to 377, remove the
redundant "use strict" directive as ES modules are strict by default. Replace
the `any` type of the `value` parameter with a more specific type that
corresponds to the property type for key K to improve type safety. Address the
TODO comment by deciding whether to assert before or after applying the property
and implement the chosen assertion accordingly.

⚠️ Potential issue

Remove redundant 'use strict' directive.

The implementation is solid, but the 'use strict' directive is redundant in module scope.

Apply this diff to fix the issue:

 ) {
-  "use strict";
   if (!(key in safe_properties)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function applyNodeProperty<K extends keyof grida.program.nodes.UnknwonNode>(
draft: grida.program.nodes.UnknownNodeProperties,
key: K,
value: any
) {
"use strict";
if (!(key in safe_properties)) {
throw new Error(`property handler not found: "${key}"`);
}
const property = safe_properties[key as keyof typeof safe_properties];
if (property) {
const prev = (draft as any)[key];
// TODO: assert - decide pre or after
property.apply(draft, value, prev);
}
}
function applyNodeProperty<K extends keyof grida.program.nodes.UnknwonNode>(
draft: grida.program.nodes.UnknownNodeProperties,
key: K,
value: any
) {
if (!(key in safe_properties)) {
throw new Error(`property handler not found: "${key}"`);
}
const property = safe_properties[key as keyof typeof safe_properties];
if (property) {
const prev = (draft as any)[key];
// TODO: assert - decide pre or after
property.apply(draft, value, prev);
}
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 367-367: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

🤖 Prompt for AI Agents
In editor/grida-canvas/reducers/node.reducer.ts around lines 362 to 377, remove
the redundant 'use strict' directive inside the applyNodeProperty function since
modules are strict by default. Simply delete the line containing "use strict";
without altering any other code.

Comment on lines +1545 to 1571
export class NodeProxy<T extends grida.program.nodes.Node> {
constructor(
private editor: Editor,
private node_id: string
) {
// @ts-expect-error - this is a workaround to allow the proxy to be used as a node
return new Proxy(this, {
get: (target, prop: string) => {
return (target.editor.getNodeSnapshotById(target.node_id) as T)[
prop as keyof T
];
},
set: (target, prop: string, value) => {
try {
target.editor.dispatch({
type: "node/change/*",
node_id: target.node_id,
[prop]: value,
});
return true;
} catch (e) {
return false; // unknown prop
}
},
}) as T;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor NodeProxy to avoid returning from constructor.

While the Proxy pattern works, returning from a constructor is non-standard and flagged by static analysis. Consider using a factory method instead for cleaner code.

Apply this refactor for a cleaner implementation:

-export class NodeProxy<T extends grida.program.nodes.Node> {
-  constructor(
-    private editor: Editor,
-    private node_id: string
-  ) {
-    // @ts-expect-error - this is a workaround to allow the proxy to be used as a node
-    return new Proxy(this, {
+export class NodeProxy<T extends grida.program.nodes.Node> {
+  private constructor(
+    private editor: Editor,
+    private node_id: string
+  ) {}
+
+  static create<T extends grida.program.nodes.Node>(
+    editor: Editor,
+    node_id: string
+  ): T {
+    const instance = new NodeProxy<T>(editor, node_id);
+    return new Proxy(instance, {
       get: (target, prop: string) => {
         return (target.editor.getNodeSnapshotById(target.node_id) as T)[
           prop as keyof T
         ];
       },
       set: (target, prop: string, value) => {
         try {
           target.editor.dispatch({
             type: "node/change/*",
             node_id: target.node_id,
             [prop]: value,
           });
           return true;
         } catch (e) {
           return false; // unknown prop
         }
       },
     }) as T;
   }
 }

Then update the getNodeById method:

-  public getNodeById(
-    node_id: editor.NodeID
-  ): NodeProxy<grida.program.nodes.Node> {
-    return new NodeProxy(this, node_id);
-  }
+  public getNodeById(
+    node_id: editor.NodeID
+  ): grida.program.nodes.Node {
+    return NodeProxy.create(this, node_id);
+  }

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1551-1569: The constructor should not return a value.

The constructor is here:

Returning a value from a constructor is ignored.

(lint/correctness/noConstructorReturn)

🤖 Prompt for AI Agents
In editor/grida-canvas/editor.ts around lines 1545 to 1571, the NodeProxy class
currently returns a Proxy instance directly from its constructor, which is
non-standard and flagged by static analysis. Refactor by removing the Proxy
return from the constructor and instead create a static factory method (e.g.,
NodeProxy.create) that returns the Proxy instance. Then update the getNodeById
method to use this factory method to obtain the proxied node. This approach
maintains the Proxy functionality while adhering to standard class construction
patterns.

@softmarshmallow softmarshmallow merged commit cd08e14 into main May 29, 2025
9 checks passed
@coderabbitai coderabbitai bot mentioned this pull request May 30, 2025
@coderabbitai coderabbitai bot mentioned this pull request Oct 14, 2025
13 tasks
This was referenced Oct 28, 2025
This was referenced Dec 7, 2025
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