Grida Canvas - Export Settings (313)#484
Conversation
… compression control
…ngs and associated actions
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughRefactors the export type system and runtime to add WEBP/BMP, per-format quality and constraint-based scaling; introduces per-node metadata (export_settings, userdata) with storage and reducer support; updates editor export API to a generic, typed form; adjusts UI for section-based exports and ZIP multi-file flow. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Editor
participant MetadataStore
participant Exporter
participant Renderer
participant ImageRepo
Client->>Editor: exportNodeAs(node_id, format, config?)
Editor->>Editor: validate format supported
Editor->>MetadataStore: getNodeMetadata(node_id, "export_settings")
MetadataStore-->>Editor: NodeExportSettings[]?
Editor->>Editor: build runtime ExportConfig (constraints, quality)
alt format == "SVG"
Editor->>Exporter: exportNodeAs(node_id, "SVG", config)
Exporter->>Renderer: render to SVG
Renderer-->>Exporter: SVG string
Exporter-->>Editor: Promise<string>
Editor-->>Client: Promise<string>
else image format (PNG/JPEG/WEBP/BMP)
Editor->>Exporter: exportNodeAs(node_id, format, config)
Exporter->>Exporter: compute camera scale & target resolution
Exporter->>ImageRepo: fetch images
Exporter->>Renderer: new_with_store(scene, fonts, images, camera)
Renderer->>Renderer: render at target resolution
Renderer-->>Exporter: snapshot
Exporter->>Exporter: encode (apply quality if provided)
Exporter-->>Editor: Promise<Uint8Array>
Editor-->>Client: Promise<Uint8Array>
else
Editor-->>Client: throw Error("Non supported format: <format>")
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…ustom data storage and retrieval
… improved data handling and ZIP file creation
…y support for improved rendering capabilities
…enhance type definitions for clarity
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
editor/scaffolds/sidecontrol/controls/x-userdata.tsx (1)
16-29: User data editor is broken due to incorrect binding and validation logicThe code has two critical coupled issues that make the editor effectively read-only:
Monaco editor isn't bound to
txtstateLine 88 binds the editor to
value(from metadata) instead oftxt:<MonacoEditor language="json" value={JSON.stringify(value, null, 2)} // should be {txt} onChange={(value) => { setTxt(value); }} />User edits update
txt, but React continuously re-renders the editor with the metadata value, causing all edits to be discarded.Validation treats empty input as invalid
validate("")returnsundefined(line 20)setValid(validate(txt) !== false)evaluates totruewhen empty (line 52, sinceundefined !== false)- But
onSaveClicktreatsundefinedas an error and shows a toast (line 58,if (res)is falsy forundefined)- Users cannot clear userdata via the UI
Fix by binding the editor to
txtand usingundefinedexplicitly for "clear" semantics:<MonacoEditor language="json" value={txt} onChange={(v) => setTxt(v ?? "")} /> function validate(value: string | undefined): any | false { if (value == null || value.trim() === "") return undefined; // clear try { const parsed = JSON.parse(value); assert(parsed && typeof parsed === "object" && !Array.isArray(parsed)); return parsed; } catch { return false; } } const onSaveClick = () => { const res = validate(txt); if (res === false) { toast.error("Invalid User Data Format"); return; } editor.setUserData(node_id, (res ?? null) as Record<string, unknown> | null); };
🧹 Nitpick comments (7)
editor/scaffolds/sidecontrol/controls/export.tsx (2)
222-313: Consider enhancing error handling for partial export failures.The export logic correctly handles single vs. multi-file exports and provides good UX with spinner and toast feedback. However, if an individual export in the multi-file flow fails, the entire export may fail silently.
Consider wrapping individual exports in try-catch to handle partial failures gracefully:
🔎 Proposed enhancement for partial failure handling
const tasks = exportConfigs.map(async (config, index) => { + try { if (!config?.format) return null; const format = config.format; if ( !editorTypes.internal.export_settings.ALL_FORMATS.includes(format) ) { return null; } // Build export config from stored settings const exportConfig = buildExportConfig(config); const editorApi: editorTypes.api.IDocumentExportPluginActions = editor; const data = await editorApi.exportNodeAs( node_id, format, exportConfig ); const suffix = config.suffix ? `-${config.suffix}` : ""; const filename = `${name}${suffix}.${editorTypes.internal.export_settings.getFileExtension(format)}`; // Convert to Uint8Array for zip const bytes = io.zip.ensureUint8Array(data); files[filename] = bytes; return { filename, data }; + } catch (error) { + console.error(`Failed to export ${config.format}:`, error); + return null; + } });This ensures that one failing export doesn't break the entire multi-file export flow.
410-442: Consider validation for scale input bounds.The scale input allows values from 0.1 to 10, but the handleScaleChange function doesn't validate the parsed value falls within this range. Invalid values could be set if users manually type values outside the allowed range.
🔎 Proposed validation enhancement
const handleScaleChange = (newScale: string) => { const oldScale = scaleValue; - const scale = parseFloat(newScale) || 1; + let scale = parseFloat(newScale) || 1; + // Clamp to min/max bounds + scale = Math.max(0.1, Math.min(10, scale)); // If suffix matches the auto pattern for old scale, update it for new scale const currentSuffix = config.suffix; const shouldUpdateSuffix = isAutoSuffix(currentSuffix, oldScale); // Only image configs can have constraints if ( config.format === "PNG" || config.format === "JPEG" || config.format === "WEBP" || config.format === "BMP" ) { const updates: { constraints: { type: "scale"; value: number }; suffix?: string; } = { constraints: { type: "scale", value: scale }, }; if (shouldUpdateSuffix) { const newSuffix = getAutoSuffix(scale); if (newSuffix !== undefined) { updates.suffix = newSuffix; } // If scale is 1, we omit suffix (don't set it to undefined) } onUpdate(updates as Partial<grida.program.document.NodeExportSettings>); } };This ensures scale values stay within the defined 0.1-10 range specified by the input's min/max attributes.
editor/grida-canvas/reducers/document.reducer.ts (1)
2038-2050: Implementation looks correct; consider metadata cleanup on node deletion.The metadata action handling correctly initializes
draft.document.metadatawhen absent and delegates tometadataReducer. The pattern follows the established reducer composition approach.One consideration for future improvement: when nodes are deleted (in
self_try_remove_node), their associated metadata entries indocument.metadataare not cleaned up. This won't cause functional issues but may lead to orphaned metadata accumulating over time.editor/grida-canvas/backends/noop.ts (1)
2-3: GenericexportNodeAssignature correctly aligned with editor APIThe new generic
exportNodeAs<F extends grida.program.document.NodeExportSettings["format"]>(...)and conditional return type match the updatedExportConfigOf<F>and editor-facing API; this keeps the noop backend type-safe while remaining a simple stub.If you want stricter consistency with the other exporters, you could also widen
canExportNodeAs’sformatparameter togrida.program.document.NodeExportSettings["format"] | (string & {})so the class perfectly mirrors the shared interface, but that’s optional given it always returnsfalse.Also applies to: 37-43
crates/grida-canvas/src/export/types.rs (1)
6-13: Serde tag/value alignment and per-format quality look correct; consider clampingThe updated serde
renamevalues forExportConstraints("none" | "scale" | "scale-to-fit-width" | "scale-to-fit-height") match the TS side and should round‑trip cleanly. Addingquality: Option<u32>with#[serde(default)]for JPEG/WEBP also lines up with the new WASM bindings.One follow‑up you might want to add later: defensively clamp
qualityinto[0, 100](either in this constructor or where you pass it to Skia), so malformed JSON or buggy callers can’t feed obviously out‑of‑range values into the encoder.Also applies to: 29-36, 38-45, 117-122
editor/grida-canvas/editor.ts (1)
3651-3658: Node metadata and export-config APIs are clean and encapsulatedThe new
INodeMetadataActions/IExportConfigActionsimplementations look well-structured:
- Metadata is stored under
document.metadata[node_id]and namespaced by"export_settings"and"userdata", so the core node schema stays untouched.getNodeMetadata/setNodeMetadata/removeNodeMetadataprovide a single, typed entry point for both namespaces, which is what the React hooks (useNodeMetadata) are now consuming.- Export-config helpers (
getExportConfigs,addExportConfig,updateExportConfig,removeExportConfig,clearExportConfigs) manage arrays ofNodeExportSettingsper node and correctly remove the metadata entirely when the last config is deleted.If you want to trim a bit of type noise, you could later replace the
ReturnType<typeof this.getNodeMetadata<NS>>casts with a small internal helper or explicit type alias, but that’s purely ergonomic and doesn’t affect correctness.Also applies to: 3670-3757, 3765-3807
editor/grida-canvas/editor.i.ts (1)
4161-4286: LGTM!Well-organized centralization of export-related types and utilities. The type guards, format arrays, and MIME type mapping provide a solid foundation for the export system.
Minor observation: The falsy checks in
getFileExtension(line 4271) andgetMimeType(line 4281) are defensive against runtime values that bypass TypeScript, which is reasonable. However, if you trust the type system, these branches are unreachable. Consider whether to keep them for defensive programming or remove for stricter type reliance.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
crates/grida-canvas-wasm/lib/bin/grida_canvas_wasm.wasmis excluded by!**/*.wasm
📒 Files selected for processing (27)
crates/grida-canvas-wasm/lib/index.tscrates/grida-canvas-wasm/package.jsoncrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas/src/export/export_as_pdf.rscrates/grida-canvas/src/export/export_as_svg.rscrates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/types.rseditor/grida-canvas-react/index.tseditor/grida-canvas-react/provider.tsxeditor/grida-canvas-react/use-mixed-properties.tseditor/grida-canvas/action.tseditor/grida-canvas/backends/dom-export.tseditor/grida-canvas/backends/noop.tseditor/grida-canvas/backends/wasm.tseditor/grida-canvas/editor.i.tseditor/grida-canvas/editor.tseditor/grida-canvas/plugins/yjs/y-document.tseditor/grida-canvas/reducers/document.reducer.tseditor/grida-canvas/reducers/metadata.reducer.tseditor/grida-canvas/reducers/node.reducer.tseditor/grida-canvas/reducers/tools/initial-node.tseditor/scaffolds/sidecontrol/controls/export.tsxeditor/scaffolds/sidecontrol/controls/x-userdata.tsxeditor/scaffolds/sidecontrol/sidecontrol-document-properties.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsxpackages/grida-canvas-io/index.tspackages/grida-canvas-schema/grida.ts
💤 Files with no reviewable changes (2)
- editor/grida-canvas/reducers/node.reducer.ts
- editor/grida-canvas/reducers/tools/initial-node.ts
🧰 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/plugins/yjs/y-document.tseditor/grida-canvas-react/index.tseditor/grida-canvas-react/use-mixed-properties.tseditor/grida-canvas-react/provider.tsxeditor/scaffolds/sidecontrol/sidecontrol-document-properties.tsxeditor/grida-canvas/reducers/document.reducer.tspackages/grida-canvas-io/index.tseditor/grida-canvas/reducers/metadata.reducer.tscrates/grida-canvas-wasm/lib/index.tseditor/grida-canvas/backends/noop.tseditor/grida-canvas/editor.tseditor/scaffolds/sidecontrol/controls/x-userdata.tsxeditor/grida-canvas/action.tseditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/backends/wasm.tseditor/grida-canvas/backends/dom-export.tspackages/grida-canvas-schema/grida.tseditor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/sidecontrol-node-selection.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/plugins/yjs/y-document.tseditor/grida-canvas-react/index.tseditor/grida-canvas-react/use-mixed-properties.tseditor/grida-canvas-react/provider.tsxeditor/scaffolds/sidecontrol/sidecontrol-document-properties.tsxeditor/grida-canvas/reducers/document.reducer.tspackages/grida-canvas-io/index.tseditor/grida-canvas/reducers/metadata.reducer.tseditor/grida-canvas/backends/noop.tseditor/grida-canvas/editor.tseditor/scaffolds/sidecontrol/controls/x-userdata.tsxeditor/grida-canvas/action.tseditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/backends/wasm.tseditor/grida-canvas/backends/dom-export.tspackages/grida-canvas-schema/grida.tseditor/grida-canvas/editor.i.tseditor/scaffolds/sidecontrol/sidecontrol-node-selection.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/plugins/yjs/y-document.tseditor/grida-canvas-react/index.tseditor/grida-canvas-react/use-mixed-properties.tseditor/grida-canvas-react/provider.tsxeditor/grida-canvas/reducers/document.reducer.tseditor/grida-canvas/reducers/metadata.reducer.tseditor/grida-canvas/backends/noop.tseditor/grida-canvas/editor.tseditor/grida-canvas/action.tseditor/grida-canvas/backends/wasm.tseditor/grida-canvas/backends/dom-export.tseditor/grida-canvas/editor.i.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use React.js 19 for web applications
Files:
editor/grida-canvas-react/provider.tsxeditor/scaffolds/sidecontrol/sidecontrol-document-properties.tsxeditor/scaffolds/sidecontrol/controls/x-userdata.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.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/sidecontrol-document-properties.tsxeditor/scaffolds/sidecontrol/controls/x-userdata.tsxeditor/scaffolds/sidecontrol/controls/export.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
crates/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
crates/**/*.rs: Use Rust 2024 edition for wasm builds and graphics core
Use Skia graphics backend for 2D graphics, bound with skia-safe
Rust crates in /crates directory are under rapid development and serve as the new rendering backend; ensure high quality implementations
Files:
crates/grida-canvas/src/export/types.rscrates/grida-canvas/src/export/export_as_pdf.rscrates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas/src/export/export_as_svg.rs
crates/grida-canvas/**/*.rs
📄 CodeRabbit inference engine (crates/grida-canvas/AGENTS.md)
crates/grida-canvas/**/*.rs: UseNodeId(u64) for internal structs (NodeRecs, SceneGraph, caches) in the rendering engine for high-performance operations
UseUserNodeId(String) for public APIs that accept or return node IDs for stability and serialization
Handle NodeId to UserNodeId conversion viaIdConverterduring .grida file loading
Auto-generate IDs (ID=0) inNodeRepositoryfor factory-created nodes
Maintain bidirectional mapping between NodeId and UserNodeId at the application layer for API boundary management
Useskia-safecrate for painting operations in the rendering engine
Usemath2crate for geometry and common math operations
Runcargo fmtto maintain code formatting standards
Runcargo clippy --no-deps --all-targets --all-featuresfor linting to check style, performance, and correctness suggestions
Files:
crates/grida-canvas/src/export/types.rscrates/grida-canvas/src/export/export_as_pdf.rscrates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas/src/export/export_as_svg.rs
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/index.tspackages/grida-canvas-schema/grida.ts
crates/grida-canvas-wasm/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (crates/grida-canvas-wasm/AGENTS.md)
Use Web Workers for heavy graphics operations to improve performance and responsiveness
Files:
crates/grida-canvas-wasm/lib/index.ts
🧠 Learnings (36)
📓 Common learnings
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
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
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/**/+(grida-canvas-wasm.js|grida-canvas-wasm.wasm) : Include WASM artifacts (`grida-canvas-wasm.js` and `grida-canvas-wasm.wasm`) in git for faster CI builds
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: Export options should support both PNG (rasterized at working resolution) and SVG (vector output not available for custom images)
📚 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-react/index.tseditor/grida-canvas-react/use-mixed-properties.tseditor/scaffolds/sidecontrol/controls/x-userdata.tsxeditor/grida-canvas/backends/wasm.tseditor/grida-canvas/backends/dom-export.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-react/index.tscrates/grida-canvas/src/export/types.rscrates/grida-canvas/src/export/mod.rscrates/grida-canvas-wasm/package.jsoncrates/grida-canvas/src/export/export_as_image.rspackages/grida-canvas-io/index.tscrates/grida-canvas-wasm/lib/index.tseditor/grida-canvas/backends/noop.tseditor/grida-canvas/editor.tscrates/grida-canvas/src/export/export_as_svg.rseditor/grida-canvas/backends/wasm.tseditor/grida-canvas/backends/dom-export.tspackages/grida-canvas-schema/grida.ts
📚 Learning: 2025-12-14T23:30:42.112Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T23:30:42.112Z
Learning: Applies to editor/grida-*/**/*.{ts,tsx} : Use /editor/grida-* directories to isolate domain-specific modules; promote well-defined modules to <root>/packages
Applied to files:
editor/grida-canvas-react/index.tseditor/grida-canvas/editor.tseditor/grida-canvas/backends/wasm.tseditor/grida-canvas/backends/dom-export.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-react/index.tscrates/grida-canvas-wasm/package.jsonpackages/grida-canvas-io/index.tscrates/grida-canvas-wasm/lib/index.tseditor/grida-canvas/backends/wasm.tseditor/grida-canvas/backends/dom-export.tspackages/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 : Run `cargo fmt` to maintain code formatting standards
Applied to files:
crates/grida-canvas/src/export/types.rscrates/grida-canvas/src/export/mod.rscrates/grida-canvas-wasm/package.jsoncrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas-wasm/lib/index.tscrates/grida-canvas/src/export/export_as_svg.rs
📚 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: Applies to crates/grida-canvas-fonts/**/Cargo.toml : Include optional `serde = "1.0"` with `derive` feature for JSON serialization support
Applied to files:
crates/grida-canvas/src/export/types.rs
📚 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: Applies to crates/grida-canvas-fonts/**/serde_test.rs : JSON serialization tests should be organized in `serde_test.rs`
Applied to files:
crates/grida-canvas/src/export/types.rs
📚 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:
crates/grida-canvas/src/export/types.rscrates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas/src/export/export_as_svg.rspackages/grida-canvas-schema/grida.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: Applies to crates/grida-canvas-fonts/**/Cargo.toml : Include dev-dependency `serde_json = "1.0"` for JSON testing
Applied to files:
crates/grida-canvas/src/export/types.rs
📚 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 crates/**/*.rs : Rust crates in /crates directory are under rapid development and serve as the new rendering backend; ensure high quality implementations
Applied to files:
crates/grida-canvas/src/export/types.rscrates/grida-canvas/src/export/export_as_image.rs
📚 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 : Run `cargo clippy --no-deps --all-targets --all-features` for linting to check style, performance, and correctness suggestions
Applied to files:
crates/grida-canvas/src/export/types.rscrates/grida-canvas-wasm/package.jsoncrates/grida-canvas/src/export/export_as_image.rs
📚 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 `skia-safe` crate for painting operations in the rendering engine
Applied to files:
crates/grida-canvas/src/export/types.rscrates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas/src/export/export_as_svg.rs
📚 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 crates/**/*.rs : Use Skia graphics backend for 2D graphics, bound with skia-safe
Applied to files:
crates/grida-canvas/src/export/types.rscrates/grida-canvas/src/export/export_as_image.rs
📚 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/grida-canvas/reducers/document.reducer.tseditor/scaffolds/sidecontrol/controls/x-userdata.tsx
📚 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 : Auto-generate IDs (ID=0) in `NodeRepository` for factory-created nodes
Applied to files:
crates/grida-canvas/src/export/export_as_pdf.rscrates/grida-canvas/src/export/mod.rscrates/grida-canvas-wasm/package.jsoncrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas/src/export/export_as_svg.rs
📚 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 `NodeId` (u64) for internal structs (NodeRecs, SceneGraph, caches) in the rendering engine for high-performance operations
Applied to files:
crates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas/src/export/export_as_svg.rs
📚 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:
crates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/export_as_svg.rseditor/grida-canvas/backends/dom-export.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 : Maintain bidirectional mapping between NodeId and UserNodeId at the application layer for API boundary management
Applied to files:
crates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/export_as_svg.rs
📚 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 `math2` crate for geometry and common math operations
Applied to files:
crates/grida-canvas/src/export/mod.rscrates/grida-canvas/src/export/export_as_image.rscrates/grida-canvas/src/export/export_as_svg.rs
📚 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:
crates/grida-canvas/src/export/mod.rscrates/grida-canvas-wasm/lib/index.tseditor/grida-canvas/editor.tseditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/backends/dom-export.tseditor/grida-canvas/editor.i.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/**/+(grida-canvas-wasm.js|grida-canvas-wasm.wasm) : Include WASM artifacts (`grida-canvas-wasm.js` and `grida-canvas-wasm.wasm`) in git for faster CI builds
Applied to files:
crates/grida-canvas-wasm/package.jsoncrates/grida-canvas-wasm/lib/index.tseditor/grida-canvas/backends/wasm.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/**/*.{js,ts,jsx,tsx} : Use Web Workers for heavy graphics operations to improve performance and responsiveness
Applied to files:
crates/grida-canvas-wasm/package.jsoncrates/grida-canvas-wasm/lib/index.tseditor/grida-canvas/backends/wasm.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/**/*.grida : Validate .grida files using the `tool_io_grida` CLI tool to check file structure, parse all nodes, and detect parsing errors
Applied to files:
crates/grida-canvas-wasm/package.json
📚 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/**/Cargo.toml : Set environment variables `CC=emcc`, `CXX=em++`, and `AR=emar` when building Rust code for WebAssembly targets
Applied to files:
crates/grida-canvas-wasm/package.json
📚 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/**/Cargo.toml : Use the target `wasm32-unknown-emscripten` when building Rust code for WebAssembly compilation
Applied to files:
crates/grida-canvas-wasm/package.json
📚 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: Export options should support both PNG (rasterized at working resolution) and SVG (vector output not available for custom images)
Applied to files:
crates/grida-canvas/src/export/export_as_image.rseditor/grida-canvas/backends/wasm.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/editor.tseditor/scaffolds/sidecontrol/controls/export.tsxeditor/grida-canvas/backends/wasm.tseditor/grida-canvas/backends/dom-export.tseditor/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/controls/export.tsxeditor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx
📚 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: Ensure WASM-ready JSON serialization for web integration
Applied to files:
editor/grida-canvas/backends/wasm.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/backends/wasm.tseditor/grida-canvas/editor.i.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 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/backends/wasm.tseditor/grida-canvas/editor.i.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/wasm.tseditor/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/ui/**/*.{ts,tsx} : Use /editor/components/ui for shadcn UI components
Applied to files:
editor/grida-canvas/backends/wasm.tseditor/scaffolds/sidecontrol/sidecontrol-node-selection.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/**/*.{ts,tsx} : Use /editor/components for generally reusable components
Applied to files:
editor/grida-canvas/backends/wasm.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 : Custom images used as halftone shapes should be loaded as HTMLImageElement for efficient canvas rendering and preserve original image colors in as-is mode
Applied to files:
editor/grida-canvas/backends/dom-export.tseditor/grida-canvas/editor.i.ts
🧬 Code graph analysis (15)
editor/grida-canvas-react/provider.tsx (1)
editor/grida-canvas-react/use-editor.tsx (2)
useCurrentEditor(98-106)useEditorState(108-120)
editor/scaffolds/sidecontrol/sidecontrol-document-properties.tsx (1)
editor/scaffolds/sidecontrol/controls/x-userdata.tsx (1)
UserDataControl(31-108)
crates/grida-canvas/src/export/types.rs (1)
crates/grida-canvas-wasm/lib/index.ts (3)
ExportAsWEBP(73-82)ExportConstraints(41-55)ExportAsJPEG(64-72)
editor/grida-canvas/reducers/document.reducer.ts (2)
editor/grida-canvas/reducers/utils/immer.ts (1)
updateState(3-13)editor/grida-canvas/reducers/metadata.reducer.ts (1)
metadataReducer(4-33)
crates/grida-canvas/src/export/mod.rs (2)
crates/grida-canvas/src/export/export_as_pdf.rs (1)
export_node_as_pdf(15-71)crates/grida-canvas/src/export/export_as_svg.rs (1)
export_node_as_svg(14-52)
editor/grida-canvas/reducers/metadata.reducer.ts (2)
packages/grida-canvas-schema/grida.ts (1)
INodeMetadata(655-671)editor/grida-canvas/action.ts (1)
MetadataAction(1058-1076)
editor/grida-canvas/editor.ts (2)
editor/grida-canvas/editor.i.ts (3)
INodeMetadataActions(4030-4103)IExportConfigActions(4110-4147)ExportConfigOf(2558-2578)packages/grida-canvas-schema/grida.ts (2)
NodeExportSettings(607-610)format(126-133)
editor/scaffolds/sidecontrol/controls/x-userdata.tsx (2)
editor/grida-canvas-react/index.ts (2)
useCurrentEditor(1-1)useNodeMetadata(8-8)editor/grida-canvas-react/provider.tsx (1)
useNodeMetadata(867-880)
editor/grida-canvas/action.ts (1)
packages/grida-canvas-schema/grida.ts (1)
NodeExportSettings(607-610)
editor/scaffolds/sidecontrol/controls/export.tsx (9)
editor/grida-canvas/index.ts (1)
editor(4-4)editor/grida-canvas-react/index.ts (2)
useCurrentEditor(1-1)useNodeMetadata(8-8)editor/grida-canvas-react/use-editor.tsx (1)
useCurrentEditor(98-106)editor/components/sidebar/index.tsx (4)
SidebarSection(290-302)SidebarSectionHeaderItem(313-332)SidebarSectionHeaderLabel(334-348)SidebarSectionHeaderActions(350-369)editor/scaffolds/sidecontrol/ui/index.tsx (1)
PropertyLine(19-41)editor/components/ui/spinner.tsx (1)
Spinner(16-16)editor/components/ui-editor/popover.tsx (3)
Popover(62-62)PopoverTrigger(62-62)PopoverContent(62-62)editor/components/ui/select.tsx (3)
Select(177-177)SelectContent(178-178)SelectItem(180-180)editor/components/ui/input.tsx (1)
Input(21-21)
editor/grida-canvas/backends/wasm.ts (1)
editor/grida-canvas/editor.i.ts (1)
ExportConfigOf(2558-2578)
editor/grida-canvas/backends/dom-export.ts (2)
packages/grida-canvas-schema/grida.ts (2)
NodeExportSettings(607-610)format(126-133)editor/grida-canvas/editor.i.ts (1)
ExportConfigOf(2558-2578)
packages/grida-canvas-schema/grida.ts (2)
editor/grida-canvas/query/index.ts (1)
nodes(590-592)editor/grida-canvas/editor.i.ts (1)
NodeID(136-136)
editor/grida-canvas/editor.i.ts (1)
packages/grida-canvas-schema/grida.ts (6)
NodeExportSettings(607-610)NodeExportSettings_Image(628-636)NodeExportSettingsConstraints(612-626)NodeExportSettings_PDF(638-641)NodeExportSettings_SVG(643-646)format(126-133)
editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (4)
editor/scaffolds/sidecontrol/controls/export.tsx (2)
ExportMultipleLayers(713-849)ExportSection(150-200)editor/components/sidebar/index.tsx (3)
SidebarSection(290-302)SidebarSectionHeaderItem(313-332)SidebarSectionHeaderLabel(334-348)editor/scaffolds/sidecontrol/ui/index.tsx (1)
PropertyLine(19-41)editor/scaffolds/sidecontrol/controls/x-userdata.tsx (1)
UserDataControl(31-108)
🪛 Biome (2.1.2)
editor/grida-canvas/reducers/metadata.reducer.ts
[error] 18-19: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 20-20: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 28-28: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Safe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
🔇 Additional comments (26)
crates/grida-canvas-wasm/package.json (1)
4-4: LGTM! Version bump aligns with the PR's wasm toolchain update.The version increment to 0.89.0-canary.3 is consistent with the PR description and the expanded export feature set.
packages/grida-canvas-io/index.ts (2)
864-909: LGTM! Well-designed ZIP utilities for multi-file exports.The new
io.zipnamespace provides clean abstractions for the multi-file export feature:
ensureUint8Arrayhandles both string (SVG) and binary (PNG/JPEG) export datacreatewrapszipSyncwith type safety- Documentation is clear and includes examples
771-771: LGTM! Metadata propagation preserves per-node export settings.Adding
metadata: json.document.metadataensures that per-node export configurations and userdata are correctly preserved during document parsing.crates/grida-canvas-wasm/lib/index.ts (1)
41-91: LGTM! Well-structured per-format export type system.The expanded export type system provides:
- Clear constraint type documentation with examples
- Separate types for each image format (PNG, JPEG, WEBP, BMP)
- Quality settings for lossy formats with documented defaults
- Type-safe discriminated union via
formatfieldThis is a breaking change from the previous
ExportAsImageinterface, but the new structure is more maintainable and extensible.packages/grida-canvas-schema/grida.ts (3)
535-545: Consider whetherundefinedshould be allowed in JSONValue.The
JSONValuetype includesundefinedin object values ({ [key: string]: JSONValue | undefined }), butundefinedis not valid JSON according to the JSON specification. This might cause serialization issues when usingJSON.stringify(), which omits undefined values.If undefined values are intentional for sparse objects, this is acceptable. Otherwise, consider:
🔎 Strict JSON type alternative
export type JSONValue = | string | number | boolean | null | { [key: string]: JSONValue } | JSONValue[];Please confirm whether undefined values in objects are intentionally supported for the metadata use case.
604-671: LGTM! Well-designed export configuration type system.The new export settings types provide:
- Clear separation between image, PDF, and SVG export configurations
- Documented constraint types for image scaling
- Quality settings for lossy formats (JPEG, WEBP)
- Type-safe readonly properties
The structure supports the PR's expanded export capabilities while maintaining type safety.
866-866: LGTM! Document schema correctly extended for metadata support.The changes properly support the metadata migration:
Documentnow extendsINodeMetadatato include the metadata mapuserdataremoved from__base_scene_node_propertiessince it's now in metadataThis maintains backward compatibility while enabling the new per-node metadata system.
Also applies to: 1374-1374
crates/grida-canvas/src/export/export_as_pdf.rs (1)
7-7: LGTM! ImageRepository integration for PDF image rendering.The changes correctly add image repository support to PDF export:
- Import added for
ImageRepository- Parameter added to function signature
- Images cloned into renderer before scene loading
This parallels the SVG export implementation and enables proper image rendering in exported PDFs.
Also applies to: 18-18, 50-50
editor/grida-canvas-react/use-mixed-properties.ts (1)
38-38: Verify whether userdata has been fully removed from node serialization.Removing
"userdata"fromignoredKeymeans userdata values will now be included in the mixed property analysis. This is safe only if userdata is no longer serialized on node objects at runtime. The codebase shows userdata is now accessed through the metadata system (e.g.,useNodeMetadata(node_id, "userdata")), but it's unclear whether the property was fully removed from nodes or remains for backward compatibility.Confirm whether node objects still carry the
userdataproperty at runtime, or verify that the metadata migration is complete and userdata is exclusively stored in the separate metadata layer.editor/grida-canvas/plugins/yjs/y-document.ts (1)
117-130: LGTM!The addition of
metadatato the initial sync payload ensures that per-node export settings and userdata are properly synchronized in collaborative sessions. This is consistent with the document structure changes.editor/grida-canvas-react/index.ts (1)
2-15: LGTM!The
useNodeMetadatahook is properly exported from the provider module, extending the public API surface consistently with the existing export pattern.editor/grida-canvas/reducers/document.reducer.ts (1)
26-27: LGTM!The imports for
metadataReducerandschemaReducerare correctly added to support the new node metadata management system.editor/grida-canvas/action.ts (2)
76-78: LGTM!The
DocumentActionunion is properly extended to includeMetadataAction, maintaining the established action composition pattern.
1057-1076: Well-structured discriminated union type.The
MetadataActiontype uses proper TypeScript discriminated unions with namespace-based discrimination, allowing type-safe payloads:
"export_settings"→NodeExportSettings[]"userdata"→Record<string, unknown> | nullThe variants for
set,remove, andremove-allcover the necessary operations for metadata management.crates/grida-canvas/src/export/export_as_svg.rs (1)
14-52: LGTM!The
ImageRepositoryintegration follows the same pattern as PDF export. Cloning the repository into the renderer beforeload_sceneensures image assets are available during SVG rendering. The implementation correctly usesskia-safefor the canvas operations as per coding guidelines.editor/grida-canvas-react/provider.tsx (2)
103-104: LGTM!The API call updated from
instance.commands.changeNodeUserDatatoinstance.setUserDataaligns with the new metadata management approach.
860-880: Well-designed hook with proper type discrimination.The
useNodeMetadatahook provides type-safe access to node metadata with namespace-based return type inference. The conditional return type correctly discriminates betweenNodeExportSettings[]for export_settings andRecord<string, unknown> | nullfor userdata.The
as anycast on line 879 is a pragmatic workaround for TypeScript's limitations with complex conditional type inference in function returns. The type safety is preserved at the call site through the generic constraint and conditional return type.crates/grida-canvas/src/export/mod.rs (1)
99-116: LGTM!The
imagesparameter is correctly propagated to bothexport_node_as_pdfandexport_node_as_svgcalls, ensuring image assets are available during vector format exports. The implementation is consistent with the updated function signatures.editor/scaffolds/sidecontrol/sidecontrol-node-selection.tsx (1)
32-32: Export and userdata controls are wired consistently with new APIs
- Importing and using
ExportSectionandExportMultipleLayersbehindconfig.export !== "off"matches the new export configuration model and keeps single vs multi‑selection exports separate.UserDataControlis now invoked with justnode_id, which is consistent with its updated implementation that reads/writes viauseNodeMetadataandeditor.setUserData.The integration points in this file look correct and cohesive with the new export/metadata architecture.
Also applies to: 931-932, 1332-1340
editor/grida-canvas/backends/dom-export.ts (1)
5-5: DOM exporter generics and unsupported-format handling look solidThe DOM exporter’s type surface now matches the shared interface:
formatsis typed fromNodeExportSettings["format"][]and only lists["PNG", "JPEG", "SVG"], which is consistent with using html‑to‑image.canExportNodeAsand genericexportNodeAs<F>()cooperate with the editor’s validation to only route supported formats here.- Explicit branches for
"PDF","WEBP", and"BMP"throwing descriptive errors provide a clear failure mode even if this is called outside the usual guarded path.No issues from this refactor on the DOM backend side.
Also applies to: 103-107, 117-122, 124-158
editor/grida-canvas/editor.ts (1)
3645-3662: GenericexportNodeAsand clipboard helpers are correctly wired to the new exporter API
exportNodeAs<F>()validates bothformatsupport andcanExportNodeAsbefore delegating tothis.exporter.exportNodeAs, which is exactly what you want now that backends differ (DOM vs WASM).The conditional return type
Promise<F extends "SVG" ? string : Uint8Array>matches backend implementations and usage sites.Clipboard helpers now call the typed export surface with explicit configs:
- PNG:
{ format: "PNG", constraints: { type: "scale", value: 1 } }.- SVG:
{ format: "SVG" }.This keeps behavior explicit and in sync with
ExportConfigOf<F>.All of this looks consistent and should make future formats/config extensions much easier to manage.
Also applies to: 4533-4537, 4552-4553
editor/grida-canvas/backends/wasm.ts (1)
6-8: Fix incorrect export format mapping for WEBP and BMP in WASM backendThe
exportNodeAsImagemethod (lines 83–101) incorrectly maps requested formats to export payloads:switch (format) { case "BMP": case "PNG": exportFormat = { format: "PNG", constraints }; break; case "JPEG": case "WEBP": exportFormat = { format: "JPEG", constraints, quality: config?.quality, }; break; }This causes WEBP requests to be encoded as JPEG and BMP requests as PNG, creating a mismatch between the requested format and the actual binary output. The TypeScript types and Rust side both support distinct
"PNG" | "JPEG" | "WEBP" | "BMP"formats with serde serialization mapping each format to its respective encoder.Use direct format assignment instead:
switch (format) { case "PNG": case "BMP": exportFormat = { format, constraints }; break; case "JPEG": case "WEBP": exportFormat = { format, constraints, quality: config?.quality, }; break; default: throw new Error(`Unsupported image format: ${format}`); }⛔ Skipped due to learnings
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`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 pointsLearnt 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 : Custom images used as halftone shapes should be loaded as HTMLImageElement for efficient canvas rendering and preserve original image colors in as-is modeLearnt 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 exportsLearnt 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/**/*.{js,ts,jsx,tsx} : Use Web Workers for heavy graphics operations to improve performance and responsivenessLearnt 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 READMELearnt 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 ReactLearnt 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/**/+(grida-canvas-wasm.js|grida-canvas-wasm.wasm) : Include WASM artifacts (`grida-canvas-wasm.js` and `grida-canvas-wasm.wasm`) in git for faster CI buildsLearnt 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/**/*.grida : Validate .grida files using the `tool_io_grida` CLI tool to check file structure, parse all nodes, and detect parsing errorsLearnt 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 `skia-safe` crate for painting operations in the rendering engineLearnt 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>/packageseditor/grida-canvas/editor.i.ts (4)
2580-2593: LGTM!The generic signature with conditional return type is well-designed. The optional config parameter allows export with sensible defaults while providing full control when needed.
4016-4021: LGTM!The plugin action signature is consistent with the provider interface. Making
configrequired for plugin actions ensures explicit export configuration.
4023-4103: LGTM!The metadata interface is well-designed with a flexible namespace-based approach. The conditional return types provide excellent type safety, and the convenience methods for export settings and userdata reduce boilerplate for the common use cases.
4105-4147: LGTM!Clean semantic API for managing multiple export configurations per node. The index-based operations align well with the array-based storage model.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
editor/grida-canvas/editor.i.ts (1)
4194-4201: Remove redundantNonNullablewrapper.The
Formattype is already a union of string literals ("PNG" | "JPEG" | "PDF" | "SVG" | "WEBP" | "BMP"), which is non-nullable by definition. TheNonNullable<Format>wrapper on line 4194 is redundant.🔎 Proposed simplification
- export const MIME_TYPES: Record<NonNullable<Format>, string> = { + export const MIME_TYPES: Record<Format, string> = { PNG: "image/png", JPEG: "image/jpeg", PDF: "application/pdf", SVG: "image/svg+xml", WEBP: "image/webp", BMP: "image/bmp", } as const;
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
editor/grida-canvas/editor.i.tssupabase/drafts/20251214_grida_canvas_document_model.sql
🧰 Additional context used
📓 Path-based instructions (4)
supabase/**/*.sql
📄 CodeRabbit inference engine (AGENTS.md)
Database heavily relies on Supabase (PostgreSQL); use Supabase for database, auth, and storage
Files:
supabase/drafts/20251214_grida_canvas_document_model.sql
**/*.{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/editor.i.ts
{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/editor.i.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/editor.i.ts
🧠 Learnings (12)
📓 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 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
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
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: Export options should support both PNG (rasterized at working resolution) and SVG (vector output not available for custom images)
📚 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:
supabase/drafts/20251214_grida_canvas_document_model.sqleditor/grida-canvas/editor.i.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 : Auto-generate IDs (ID=0) in `NodeRepository` for factory-created nodes
Applied to files:
supabase/drafts/20251214_grida_canvas_document_model.sql
📚 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:
supabase/drafts/20251214_grida_canvas_document_model.sqleditor/grida-canvas/editor.i.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 : Maintain bidirectional mapping between NodeId and UserNodeId at the application layer for API boundary management
Applied to files:
supabase/drafts/20251214_grida_canvas_document_model.sql
📚 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 `NodeId` (u64) for internal structs (NodeRecs, SceneGraph, caches) in the rendering engine for high-performance operations
Applied to files:
supabase/drafts/20251214_grida_canvas_document_model.sql
📚 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/grida-canvas/editor.i.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/editor.i.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 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/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/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-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/editor.i.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 : Custom images used as halftone shapes should be loaded as HTMLImageElement for efficient canvas rendering and preserve original image colors in as-is mode
Applied to files:
editor/grida-canvas/editor.i.ts
⏰ 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
313)
Manage Export Settings per node & export at once.
PNG,JPEG,WEBP,PDF,SVGscale,scale-to-fit-width,scale-to-fit-heightsrgbonlyqualityfor webp, jpegday-313-grida-canvas-export-settings.mp4
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.