Embed viewer overhaul, Figma import fixes (geometry, strokes, lists), and CG bug fixes#621
Embed viewer overhaul, Figma import fixes (geometry, strokes, lists), and CG bug fixes#621softmarshmallow wants to merge 25 commits intomainfrom
Conversation
Modified the embed URLs in the documentation and debug page to reflect the new 'figma' endpoint. Added a new page for handling Figma embeds, including file parameter parsing and remote file fetching logic.
…format support Updated the documentation to include detailed information about the new embed endpoints (`/embed/v1/`, `/embed/v1/figma`, and `/embed/v1/debug`). Added support for additional file formats (`.grida`, `.grida1`) in the embed viewer and debug page. Improved the embed debug page to allow switching between general and Figma modes, and updated the embed bridge to handle new file formats.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds FlatBuffers debugging guidance and cross-boundary encode/decode tests; expands WASM Scene command surface and keyboard-driven viewport controls; fixes geometry by adding content-origin propagation and converting rotations to radians; introduces a shared embed viewer supporting Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser
participant EmbedPage
participant Fetcher
participant Converter
participant Editor
participant WASM
User->>Browser: Open /embed/v1/?file=...
Browser->>EmbedPage: Load page
EmbedPage->>Fetcher: Fetch file URL
Fetcher-->>EmbedPage: ArrayBuffer + headers
EmbedPage->>EmbedPage: Infer filename/extension
EmbedPage->>EmbedPage: validateExt(ext, hasConverter)
alt native .grida/.grida1
EmbedPage->>Editor: onFile(File)
else convertible format
EmbedPage->>Converter: converter(ArrayBuffer)
Converter-->>EmbedPage: .grida bytes
EmbedPage->>Editor: onFile(File(.grida))
end
Editor->>WASM: io.load(File) → parse FlatBuffers
WASM-->>Editor: document ready
Editor->>WASM: camera.fit("<scene>")
WASM-->>Browser: Render viewport
sequenceDiagram
participant Input as Text Input
participant FauxList as applyFauxList()
participant State as Counters
participant Output as Rewritten Text
Input->>FauxList: text, lineTypes[], lineIndentations[]
FauxList->>State: init counters per indentation level
loop per line
FauxList->>State: determine lineType
alt UNORDERED
FauxList->>Output: insert bullet prefix + indentation
else ORDERED
State->>State: increment counter for level
FauxList->>Output: insert "N. " + indentation
else NONE
FauxList->>Output: insert indentation only
end
FauxList->>Output: record prefixLengths
end
FauxList-->>Output: return rewritten text + prefixLengths
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
encodeFeNoises was creating tables inside a startVector/endVector block, which corrupts the buffer — FlatBuffers only allows inline scalars/structs in that context. The Rust decoder panicked on the invalid offsets, causing load_scene_grida to fail silently and leaving loaded_scenes stale from the initial empty document. Build all FeNoiseEffect offsets first, then pass them to createFeNoisesVector, matching the pattern used by encodeFeShadows.
Introduced a new Affine Transform Visualizer tool that allows users to interactively visualize 2D affine transformations. Updated the sitemap to include a link to the new tool and added relevant metadata for improved SEO. Enhanced the tools page with a new entry for the Affine Transform Visualizer, including descriptions and icons.
…zoom Wire Tab/Shift+Tab (sibling nav), Enter/Shift+Enter (child/parent nav), and Shift+1/2/0 (zoom to fit/selection/100%) to the WASM embed viewer. Selection commands go through WASM _command() + redraw(). Zoom commands go through the JS camera API since the camera is JS-driven (state.transform → WASM syncTransform). Adds SurfaceCamera interface to keep CanvasSurfaceUI decoupled from the full Editor.
…metry cache bounds PathNode, PolygonNode, and VectorNode can have shape data offset from the transform origin (e.g. an SVG path starting at M4.5 11). The geometry cache was computing local_bounds as (0, 0, width, height), discarding the rect().x / rect().y origin offset from tight bounds. This caused incorrect R-tree AABB entries, which made viewport culling drop visible nodes during interactive rendering (flush path). The export path (render_to_canvas / snapshot) was unaffected because it bypasses viewport culling. Add content_origin_x/y fields to NodeGeoData and GeoInput, populated from rect().x/y for Path, Polygon, and Vector nodes (0.0 for all others). Use these in the Leaf local_bounds computation so the world-space bounding box correctly reflects where pixels are drawn. Fixes: shapes rendered as outline-only (no fill) in interactive mode, incorrect hit-test regions, misplaced bounding box overlays and size labels for Path/Polygon/Vector nodes with non-zero content origin.
Implement fillOverrideTable lookup when converting Figma REST API fillGeometry paths to Grida PathNode/VectorNode children. Previously, all fill geometry paths received the node-level fills unconditionally, ignoring per-region fill overrides. Add resolveFillOverride() which checks each path's overrideID against the node's fillOverrideTable: - No overrideID or missing/null table entry → node-level fills - PaintOverride with fills array → use those fills - PaintOverride with empty fills [] → no fill (transparent) This affects multi-region vectors (icons with mixed filled/unfilled regions), boolean operations with per-region fills, and outlined text with per-character colors.
…cene graph AffineTransform::new(tx, ty, rotation) expects rotation in radians, but Container/Tray nodes store rotation in degrees. The scene graph and geometry cache passed the degree value directly, which was silently treated as radians. For a 90° container, this produced cos(90 rad) ≈ -0.45 instead of cos(π/2) ≈ 0 — a completely garbled rotation matrix. The bug was latent because no current code path produces non-zero container rotation (the editor has no UI for it, and the Figma import workaround sets rotation=0 for flipped containers). Fix: call .to_radians() at all four call sites where container/tray rotation feeds into AffineTransform::new. Add a regression test that creates a container with rotation=90° and verifies the geometry cache produces the correct world transform matrix (cos≈0, sin≈1) for both the container and its child.
…hildren The container flip propagation was only handling flips (det < 0) and skipping pure rotations (det > 0, e.g. 180°). But the Figma relativeTransform convention doesn't match the Grida container model (position + rotation_degrees via from_box_center) for ANY non-identity 2x2 transform — not just flips. Change the propagation check from '!identity && !pureRotation' to just '!identity' so that rotated containers (e.g. Corner Bottom Right with [[-1,0],[0,-1]] = 180° rotation) also get their transforms baked into children's geometry. This also includes: BOOLEAN_OPERATION fillGeometry support, SVG path transform baking utility, RECTANGLE/ELLIPSE geometry-to-group conversion for rotated shapes, and child graph merge for boolean ops.
Model INSIDE strokes as BooleanPathOperationNode(intersection) that clips strokeGeometry to fillGeometry at render time via Skia Path::op(Intersect). No schema changes — reuses the existing boolean operation node type. For the common single-fill/single-stroke case the intersection node has two direct children (fill clone + stroke path). When multiple fill or stroke geometries exist, intermediate BooleanOp(union) nodes merge them into single operands before the intersection, preventing incorrect pairwise intersection across all children. The boolean node carries the original stroke color as its fill paint so the renderer paints the clipped stroke band with the correct color.
…ontent Added a new configuration option `prefer_path_for_geometry` to the PlaygroundMenuContent, enhancing the geometry handling capabilities.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
docs/wg/feat-fig/stroke-geometry-alignment.md (2)
17-30:⚠️ Potential issue | 🟡 MinorThe stroke-geometry model still contradicts the measurements.
The prose still says
strokeGeometryis always the same CENTER expansion, but the table below shows CENTER as[-5, -5]..[25, 25]and INSIDE/OUTSIDE as[-10, -10]..[30, 30]. Please reword the model and the later observations so they match the fixture data.Also applies to: 46-63
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/wg/feat-fig/stroke-geometry-alignment.md` around lines 17 - 30, The text claims strokeGeometry is always a CENTER expansion but the examples/fixture data show different bounding boxes per strokeAlign; update the prose and the later observations so the model matches the fixtures: change the definition of strokeGeometry (and any sentences mentioning it) to state that strokeGeometry is the stroke outline after applying strokeAlign (so CENTER yields [-5,-5]..[25,25] while INSIDE/OUTSIDE yield [-10,-10]..[30,30]), and adjust the table rows/descriptions for CENTER, OUTSIDE, and INSIDE accordingly; ensure references to fillGeometry, strokeGeometry, and strokeAlign are consistent with this new definition.
74-99:⚠️ Potential issue | 🟡 MinorLabel the pseudocode fences.
These three fenced blocks are still unlabeled, so markdownlint will keep flagging MD040 here.
```textis sufficient.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/wg/feat-fig/stroke-geometry-alignment.md` around lines 74 - 99, The three pseudocode fenced blocks under the OUTSIDE/INSIDE/CENTER examples are unlabeled causing markdownlint MD040; update each fence to include a language label (e.g., replace the opening ``` with ```text) for the blocks describing OUTSIDE, INSIDE, and CENTER so they become fenced code blocks like ```text ... ``` and satisfy the linter while leaving the block contents unchanged.packages/grida-canvas-io-figma/lib.ts (1)
537-554:⚠️ Potential issue | 🟠 Major
H/Vcommands are still transformed without current-point tracking.For non-diagonal matrices this leaves stale one-axis commands in the path, and even the diagonal branch computes the rebase bounds against
y = 0/x = 0instead of the current point. Rotated/flipped paths that useH/Vwill still import shifted or malformed here.Also applies to: 621-625
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/grida-canvas-io-figma/lib.ts` around lines 537 - 554, The H/V handlers are updating g.nums without using the current point, so transformed horizontal/vertical commands become stale under rotations/skews; fix by using the current point when computing transformed coordinates and by converting non-diagonal H/V into explicit L (line-to) commands. Specifically, inside the "H" and "V" cases use the tracked current point (cx, cy from your track state) instead of 0 when computing transformed coordinates with the matrix components a,b,c,d (and any translation if present), and for non-diagonal matrices (when Math.abs(b) or Math.abs(c) >= 1e-9) replace the command type (g.type) with "L" and emit both x and y in g.nums after applying the full 2×2 transform and translation so subsequent processing has correct absolute/relative points; ensure you still call track(newX, newY) (or track(newX, cy) / track(cx, newY) for diagonal cases) and preserve isRel semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@editor/grida-canvas-hosted/playground/uxhost-menu.tsx`:
- Around line 183-185: The playground is forcing render-only geometry by setting
prefer_path_for_geometry: true in the FactoryContext, which makes imported Path
nodes non-editable; change the FactoryContext construction (the context object
where gradient_id_generator and prefer_path_for_geometry are set) to not set
prefer_path_for_geometry by default, or gate it behind an explicit debug flag
(e.g., a playground debug option) so the property is only true when the debug
flag is enabled; update any initialization or config check that creates this
context to read the debug toggle and only set prefer_path_for_geometry when that
toggle is true.
In `@packages/grida-canvas-io-figma/lib.ts`:
- Around line 1627-1629: The OUTSIDE compositing branch currently assumes the
fill paint will occlude the inner half of the stroke, but resolveFillOverride()
can return an empty fills array or transparent paint so that occlusion never
happens; change the OUTSIDE rendering path (the code that uses fillGeometry and
composes the stroke) to perform a geometric subtraction/mask of fillGeometry
from the stroke geometry (e.g., compute strokeMinusFill =
strokeGeometry.subtract(fillGeometry) or apply a clip that excludes
fillGeometry) before drawing the stroke, and ensure you treat an explicit empty
fills array or fully transparent fill as “no fill” when building fillGeometry so
subtraction/masking is still applied; update the same logic used at the other
location referenced around lines 1833–1840.
- Around line 2183-2221: pNeedsBake branch folds parent translation into each
child's relativeTransform but leaves those translations in grandparent coords
while resetting the container to identity (rotation = 0) and not updating its
bounds, causing double-offset children and stale clipping; fix by converting the
children's relativeTransform translations back into the new container-local AABB
frame: compute the container AABB (aabbX,aabbY) and subtract it from each
child's relativeTransform[?][2] (ctx2/cty) so child positions become local to
the new container origin, then set processedNode.layout_inset_left/top to
aabbX/aabbY, update processedNode size (width/height) to the AABB extents, and
keep processedNode.rotation = 0 so downstream positioning_trait() reads
consistent container-local transforms (refer to pNeedsBake, relativeTransform,
processedNode.layout_inset_left/layout_inset_top, processedNode.rotation, and
size properties).
---
Duplicate comments:
In `@docs/wg/feat-fig/stroke-geometry-alignment.md`:
- Around line 17-30: The text claims strokeGeometry is always a CENTER expansion
but the examples/fixture data show different bounding boxes per strokeAlign;
update the prose and the later observations so the model matches the fixtures:
change the definition of strokeGeometry (and any sentences mentioning it) to
state that strokeGeometry is the stroke outline after applying strokeAlign (so
CENTER yields [-5,-5]..[25,25] while INSIDE/OUTSIDE yield [-10,-10]..[30,30]),
and adjust the table rows/descriptions for CENTER, OUTSIDE, and INSIDE
accordingly; ensure references to fillGeometry, strokeGeometry, and strokeAlign
are consistent with this new definition.
- Around line 74-99: The three pseudocode fenced blocks under the
OUTSIDE/INSIDE/CENTER examples are unlabeled causing markdownlint MD040; update
each fence to include a language label (e.g., replace the opening ``` with
```text) for the blocks describing OUTSIDE, INSIDE, and CENTER so they become
fenced code blocks like ```text ... ``` and satisfy the linter while leaving the
block contents unchanged.
In `@packages/grida-canvas-io-figma/lib.ts`:
- Around line 537-554: The H/V handlers are updating g.nums without using the
current point, so transformed horizontal/vertical commands become stale under
rotations/skews; fix by using the current point when computing transformed
coordinates and by converting non-diagonal H/V into explicit L (line-to)
commands. Specifically, inside the "H" and "V" cases use the tracked current
point (cx, cy from your track state) instead of 0 when computing transformed
coordinates with the matrix components a,b,c,d (and any translation if present),
and for non-diagonal matrices (when Math.abs(b) or Math.abs(c) >= 1e-9) replace
the command type (g.type) with "L" and emit both x and y in g.nums after
applying the full 2×2 transform and translation so subsequent processing has
correct absolute/relative points; ensure you still call track(newX, newY) (or
track(newX, cy) / track(cx, newY) for diagonal cases) and preserve isRel
semantics.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e306ed67-6b4b-4b7c-af0d-c2af0c735397
📒 Files selected for processing (3)
docs/wg/feat-fig/stroke-geometry-alignment.mdeditor/grida-canvas-hosted/playground/uxhost-menu.tsxpackages/grida-canvas-io-figma/lib.ts
| const context: iofigma.restful.factory.FactoryContext = { | ||
| gradient_id_generator: () => v4(), | ||
| prefer_path_for_geometry: true, |
There was a problem hiding this comment.
Don’t force render-only geometry mode in the playground editor.
The importer documents prefer_path_for_geometry as a render-only switch and explicitly notes that the resulting Path nodes are not editable in the editor. Hardcoding it here turns normal Figma imports in the playground into non-editable geometry. Gate this behind an explicit debug option or leave it off by default.
Possible fix
const context: iofigma.restful.factory.FactoryContext = {
gradient_id_generator: () => v4(),
- prefer_path_for_geometry: true,
resolve_image_src: (ref) =>
ref in images ? `res://images/${ref}` : null,
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@editor/grida-canvas-hosted/playground/uxhost-menu.tsx` around lines 183 -
185, The playground is forcing render-only geometry by setting
prefer_path_for_geometry: true in the FactoryContext, which makes imported Path
nodes non-editable; change the FactoryContext construction (the context object
where gradient_id_generator and prefer_path_for_geometry are set) to not set
prefer_path_for_geometry by default, or gate it behind an explicit debug flag
(e.g., a playground debug option) so the property is only true when the debug
flag is enabled; update any initialization or config check that creates this
context to read the debug toggle and only set prefer_path_for_geometry when that
toggle is true.
| // PaintOverride.fills is the per-region fill array. | ||
| // An empty array means explicitly no fill (transparent). | ||
| return override.fills ?? node.fills; |
There was a problem hiding this comment.
OUTSIDE compositing breaks when the fill does not actually paint.
resolveFillOverride() correctly preserves [] as “no fill”, but the OUTSIDE branch still relies on drawing the fill child on top to hide the inward half of the stroke. If a node/region has no fill, or the fill is transparent, that occlusion never happens and the stroke renders as the full band instead of outside-only. This needs geometric subtraction/masking against fillGeometry, not paint order alone.
Also applies to: 1833-1840
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/grida-canvas-io-figma/lib.ts` around lines 1627 - 1629, The OUTSIDE
compositing branch currently assumes the fill paint will occlude the inner half
of the stroke, but resolveFillOverride() can return an empty fills array or
transparent paint so that occlusion never happens; change the OUTSIDE rendering
path (the code that uses fillGeometry and composes the stroke) to perform a
geometric subtraction/mask of fillGeometry from the stroke geometry (e.g.,
compute strokeMinusFill = strokeGeometry.subtract(fillGeometry) or apply a clip
that excludes fillGeometry) before drawing the stroke, and ensure you treat an
explicit empty fills array or fully transparent fill as “no fill” when building
fillGeometry so subtraction/masking is still applied; update the same logic used
at the other location referenced around lines 1833–1840.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/grida-canvas-io-figma/__tests__/iofigma.rest-api.vector.test.ts (2)
593-600: Minor: Consider typed helper instead ofas anycast.The
converthelper usesnode as anyto bypass type checking. While acceptable for tests, a more explicit type annotation on the parameter would preserve type safety:function convert(node: figrest.VectorNode & figrest.HasGeometryTrait) {This is a minor style suggestion given the fixture already uses type assertions.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/grida-canvas-io-figma/__tests__/iofigma.rest-api.vector.test.ts` around lines 593 - 600, Change the untyped cast in the test helper to a stricter parameter type: update the convert function signature to accept a node typed as figrest.VectorNode & figrest.HasGeometryTrait instead of using node as any, and pass that typed node into iofigma.restful.factory.document (helper name: convert; target call: iofigma.restful.factory.document).
732-745: Consider adding defensive assertions before non-null assertions.The
find()calls returnPathNode | undefined, but the test uses the non-null assertion!directly. If the naming convention changes and paths aren't found, the test would fail with an unhelpful "cannot read property" error rather than a clear assertion failure.const fill1 = paths.find((p) => p.name.includes("Fill 1"))!; const fill2 = paths.find((p) => p.name.includes("Fill 2"))!; const fill3 = paths.find((p) => p.name.includes("Fill 3"))!; + + expect(fill1).toBeDefined(); + expect(fill2).toBeDefined(); + expect(fill3).toBeDefined(); // Fill 1: overrideID=1 → empty fills → no fill_paints expect(fill1.fill_paints).toBeUndefined();This is a minor robustness improvement for test maintainability.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/grida-canvas-io-figma/__tests__/iofigma.rest-api.vector.test.ts` around lines 732 - 745, The test uses non-null assertions on results of paths.find for fill1, fill2, and fill3 which can yield undefined; add explicit assertions that each find returned a value before dereferencing (e.g., expect(fill1).toBeDefined(), expect(fill2).toBeDefined(), expect(fill3).toBeDefined()) so failures show clear messages; update the references around the existing fill1/fill2/fill3 uses in the same test block to use these defensive checks prior to accessing fill_paints.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/grida-canvas-io-figma/__tests__/iofigma.rest-api.vector.test.ts`:
- Around line 593-600: Change the untyped cast in the test helper to a stricter
parameter type: update the convert function signature to accept a node typed as
figrest.VectorNode & figrest.HasGeometryTrait instead of using node as any, and
pass that typed node into iofigma.restful.factory.document (helper name:
convert; target call: iofigma.restful.factory.document).
- Around line 732-745: The test uses non-null assertions on results of
paths.find for fill1, fill2, and fill3 which can yield undefined; add explicit
assertions that each find returned a value before dereferencing (e.g.,
expect(fill1).toBeDefined(), expect(fill2).toBeDefined(),
expect(fill3).toBeDefined()) so failures show clear messages; update the
references around the existing fill1/fill2/fill3 uses in the same test block to
use these defensive checks prior to accessing fill_paints.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 071d96ca-34ea-45cc-b85a-bb34f294417a
📒 Files selected for processing (1)
packages/grida-canvas-io-figma/__tests__/iofigma.rest-api.vector.test.ts
…accuracy - Container flip propagation: subtract AABB origin from children's translations so they are container-local, not grandparent-space. Also set container layout_target_width/height to AABB dimensions. - transformSvgPath H/V commands: add explicit TODO comments for the known limitation that non-diagonal transforms silently skip H/V (requires current-point tracking to promote to L). Low risk since Figma geometry=paths does not use H/V commands. - Docs: fix inaccurate claim that strokeGeometry is 'always the same CENTER expansion' — CENTER produces sw band, INSIDE/OUTSIDE produce 2×sw band. Add fence languages to pseudocode blocks.
…tive viewer - Added information about the headless rendering capabilities of `@grida/refig`, including supported formats and links to documentation. - Introduced a section for the interactive Embed Viewer, detailing its features and providing links to relevant SDK documentation. - Updated README and other documentation files to reflect new features, including rich text support and improved export options.
- Replaced `fig2grida` with in-memory conversion functions `figBytesToGridaDocument` and `restJsonToGridaDocument` to enhance performance by avoiding unnecessary packing into `.grida` archives. - Updated the `FileConverter` type to return a `GridaDocumentResult` containing the document and assets directly. - Improved the Embed Debug Page UI for better clarity and usability, including updated labels and descriptions for various controls and states. - Enhanced documentation comments to reflect the new conversion methods and their usage.
- Introduced a new `convert` subcommand to the CLI for converting Figma files to `.grida` and `.grida1` formats without rendering. - Implemented in-memory processing for both FlatBuffers archives and JSON snapshots, enhancing performance and usability. - Updated README with usage examples and detailed descriptions of the new conversion options and output formats.
Summary
A batch of improvements spanning the embed viewer, Figma import pipeline, and the core graphics (CG) rendering engine.
Embed Viewer
refig→figma, add new/embed/v1/general viewer supporting.grida/.grida1filesuse-embed-viewerhook; decouple camera interface (SurfaceCamera) from full EditorFigma Import (
io-figma)BOOLEAN_OPERATIONstroke skip with universalstrokeAlignhandlingdisable_faux_listflag); 26 new testsCore Graphics (CG / Rust)
rect().x/yorigin in PathNode/PolygonNode/VectorNode geometry cache bounds — fixes incorrect R-tree AABBs, viewport culling drops, hit-test misses, and misplaced bounding box overlaysAffineTransform::new— adds regression testI/O Format
FeNoiseEffecttable offsets outside vector block to prevent buffer corruption and Rust decoder panicTools
/tools/affine-transform)Test coverage
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests