Skip to content

Add 33 OOXML-aligned shapes (slides Phase 1 foundation)#202

Merged
hackerwins merged 65 commits into
mainfrom
feat/slides-shapes-p1
May 9, 2026
Merged

Add 33 OOXML-aligned shapes (slides Phase 1 foundation)#202
hackerwins merged 65 commits into
mainfrom
feat/slides-shapes-p1

Conversation

@hackerwins

@hackerwins hackerwins commented May 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Phase 1 of a four-phase roadmap to grow the slides shape library from 4 (rect, ellipse, line, arrow) to OOXML's 187 prstGeom presets. This PR lands 35 OOXML-aligned shape kinds (2 lines + 15 basic + 8 block arrows + 4 callouts + 6 equation), the path-builder registry architecture, the categorised picker popover, and visual harness coverage.

Spec: docs/design/slides/slides-shapes-p1.md
Plan: docs/tasks/active/20260509-slides-shapes-p1-todo.md
Lessons: docs/tasks/active/20260509-slides-shapes-p1-lessons.md

Architecture

  • Registry + dispatcher: packages/slides/src/view/canvas/shape-renderer.ts switches from a per-kind switch to Map<ShapeKind, (size, adjustments) => Path2D> lookup. Each shape lives in view/canvas/shapes/<category>/<kind>.ts as a pure path-geometry function. The dispatcher applies fill / stroke / theme color / evenodd fill-rule centrally. line and arrow keep bespoke renderers in shape-special.ts (open path, two-tone arrow head). Unknown kinds fall back to a placeholder rect with one-time console.warn — forward-compat for the future PPTX importer.
  • adjustments?: number[] added to ShapeElement.data, mirroring OOXML <a:avLst>. Phase 1 stores defaults only; P2 adds toolbar inputs, P3 adds drag handles, P4 adds preset-formula evaluator. Storing the field from day one means no Yorkie migration when later phases add editing.
  • Picker UI: 5 inline insert buttons → 1 Text-box toggle + categorised Shape ▾ popover (Radix DropdownMenu). 35 canvas-rendered icons sourced from the same PATH_BUILDERS the canvas uses.
  • Path2D test shim extended for quadraticCurveTo / bezierCurveTo / arc / addPath / evenodd fill-rule so per-shape unit tests via isPointInPath work without a real browser.

Coverage post-merge

Phase Cumulative shapes Adds
P1 (this PR) 35 31 new builders + 2 refactored (rect, ellipse) + 2 specials (line, arrow)
P2 55 flowchart 14 + stars 6; toolbar number inputs for adjustments
P3 105 rest of GS parity + actionButtons; drag handles
P4 187 DrawingML prstGeom formula evaluator

Test plan

  • pnpm verify:fast green (2400+ tests across 5 packages, 50 commits each verified).
  • Snapshot test pins all 33 builders' Path2D ops (shapes/registry.snap.test.ts).
  • insert.test.ts covers new category defaults (callout outlined, block-arrow filled).
  • Frontend ShapePicker test covers all 35 shapes via shape-picker-helpers.ts.
  • Manual smoke iteration: 8 visual bugs caught and fixed (can lid, cloud silhouette, donut implicit lineTo, plus miter spike, mathPlus / mathMultiply / mathNotEqual overlapping sub-paths, picker stroke width).
  • Visual baselines need regeneration before next visual gate run — see "Follow-ups" below.

Backward compatibility

  • Existing rect / ellipse / line / arrow data round-trips unchanged.
  • New adjustments? field is optional; existing documents serialise it as undefined and renderers apply defaults.
  • The new default-stroke-on-filled-inserts (T12) applies only to defaultsForShape for newly inserted shapes — existing shapes' fill/stroke data is preserved.
  • YorkieShapeElement parallel schema (packages/frontend/src/types/slides-document.ts) updated to import ShapeKind from @wafflebase/slides (single source of truth).

Visual fix iteration (from manual smoke)

After the first user smoke pass, 8 visual bugs were caught and fixed:

Bug Fix
Picker icons too thick (24px) STROKE_WIDTH 1.5 → 1.0
can: extra horizontal line at top + double-stroked lid Replace bezier dome with half-ellipse arcs; lid drawn as lower half-arc only; explicit moveTo to suppress Canvas2D implicit lineTo
cloud: 5 separate circles + pentagon connectors 6-lobe outer-arc-only single silhouette via circle-circle intersection
donut: extra horizontal line on right Explicit moveTo between outer/inner ellipses (Canvas2D implicit lineTo)
plus: phantom rectangle at center Dispatcher lineJoin = 'round' — eliminates miter spikes at concave corners
mathPlus center square Single 12-vertex cross polygon (was 2 overlapping rect sub-paths)
mathMultiply center square Single 12-vertex X-union polygon
mathNotEqual slash×bar artifacts Single 20-vertex union polygon traced clockwise

Follow-ups (from code review)

Code review (50 commits, 96 files, +9766/−165) returned 0 Critical, 4 Important (all P2-deferrable):

  1. Visual baselines must be regenerated after merge — run pnpm verify:browser:docker:update, commit the new PNGs (5 new scenarios: catalog × 3 themes + donut + callout). Without this, the next pnpm verify:browser:docker run fails.
  2. wedgeRoundRectCallout only renders the tail when tailY > h (default downward case). Other 3 callout variants attach to the closest edge. P2 toolbar inputs will expose this — either match the closest-edge logic in P2 or document the limitation in the spec.
  3. ADJUSTMENT_SPECS for mathMinus / mathMultiply reuses MATH_PLUS_ADJUSTMENTS (label "Arm thickness"). P2's toolbar UI will show the same label for shapes where it doesn't quite fit. Split or rename when P2 ships.
  4. Spec wording on callout adjustment unit semantics ("% of frame" vs. "thousandths from frame centre") could be tightened to make the future PPTX importer's job easier.

Plus several Minor items captured in the lessons file: chevron notch math is approximate (defer to P4 preset engine), Path2D shim curve approximations have ~1px tolerance at boundaries, per-shape lineJoin override slot for crisp arrowheads when stroke widths grow (P2).

🤖 Generated with Claude Code

hackerwins added 30 commits May 9, 2026 22:03
Phase 1 of a four-phase roadmap to grow the slides shape library
from 4 (rect, ellipse, line, arrow) to OOXML's 187 prstGeom presets.
This commit lands only the design + plan; subsequent commits land
the data model, dispatcher refactor, 31 new path builders, and the
categorised picker popover.

Goal coverage after Phase 1: 35 OOXML-aligned ShapeKind values
(2 lines special + 15 basic + 8 block arrows + 4 callouts + 6
equation), with adjustments storage forward-compatible with later
phases' editing UIs and OOXML preset import.
Phase 1 of the four-phase shape-library expansion (see
docs/design/slides/slides-shapes-p1.md). Adds 31 new ShapeKind
members aligned with OOXML prstGeom preset names, and an optional
`adjustments: number[]` field on ShapeElement.data that mirrors
`<a:avLst>`. No renderer changes yet — those land in subsequent
tasks. Existing rect/ellipse/line/arrow data round-trips unchanged.
T1's data-model expansion in @wafflebase/slides also needs to
propagate to the frontend's parallel Yorkie schema, which inlines
its own ShapeKind union instead of re-exporting. Without this fix,
later tasks writing kind: 'roundRect' (etc.) through the Yorkie
store would have the inline union narrow them to never.
Establishes the PathBuilder/AdjustmentSpec types and the two
registry maps that subsequent tasks populate one shape at a time.
The dispatcher in Task 4 reads from these maps; an empty registry
is safe because the dispatcher falls back to a placeholder rect for
unknown ShapeKind values.
Pure code move ahead of the dispatcher refactor. line/arrow do not
fit the (size, adjustments) -> Path2D builder pattern (open path,
two-tone arrow head), so they keep bespoke renderers in their own
file. The dispatcher rewrite in the next commit switches to these.
line/arrow keep their bespoke renderers (lifted to shape-special.ts
in the previous commit). All other kinds resolve through the
PATH_BUILDERS registry, with a placeholder-rect fallback for
kinds that have not yet registered a builder. Subsequent commits
fill in the registry one shape at a time.
Establishes the per-shape file pattern: one builder + one test file
per ShapeKind, plus a one-line registration in shapes/index.ts. The
dispatcher now routes 'rect' through the builder; placeholder
fallback only fires for kinds still pending registration.

Adds a Path2D + isPointInPath shim to test-canvas-env so builder
unit tests can hit-test geometry under node/jsdom (neither ships a
real Path2D). Adapts the rect-related shape-/slide-/element-renderer
test assertions from fillRect/strokeRect to ctx.fill(path) /
ctx.stroke(path) to match the dispatcher's Path2D-aware draw calls.
T7 builders need quadraticCurveTo (roundRect, callouts), bezierCurveTo
(can), and arc (cloud) plus an evenodd fill rule for donut. Extend the
test-canvas-env Path2D stub to record those ops as polyline
approximations and accept a fillRule argument on isPointInPath. Add a
self-test so future shim regressions surface immediately.
Donut needs the inner ellipse punched out as a hole, which only works
with the evenodd fill rule. Add EVENODD_KINDS to the dispatcher and
forward 'evenodd' or 'nonzero' to ctx.fill(path, ...). Move the
unknown-kind fallback test from 'donut' (now registered) to
'rightArrow' (still unregistered).
T8 will register rightArrow, so move the unknown-kind fallback
test to use 'wedgeRectCallout' (registered in T9) as its
placeholder so the dispatcher's unknown-kind path remains
exercised.
hackerwins and others added 19 commits May 9, 2026 22:03
Pins every registered path builder's canvas operations into a single
snapshot, guarding against accidental geometry regressions when
adding/refactoring builders. Updates require explicit --update intent.
InsertKind now mirrors ShapeKind | 'text' so all 35 kinds are
selectable from the editor controller. buildInsertElement applies
per-category defaults (filled / outlined / line) instead of a
per-kind switch. Existing rect/ellipse/line/arrow defaults are
preserved.
Paints a shape outline at icon size into the supplied context using
the same PATH_BUILDERS the canvas uses, so the upcoming Shape ▾
picker's previews cannot drift from the actual geometry. line/arrow
get a small dedicated picker glyph since their canvas-time
renderers paint themed fills.
Single Shape ▾ trigger replaces the previous five inline insert
buttons. Popover renders five category sections (Lines, Shapes,
Block Arrows, Callouts, Equation) as 6-col grids of canvas-rendered
icons sourced from the slides PATH_BUILDERS, so previews stay in
sync with canvas geometry without bespoke SVG assets.
Replaces the five inline insert buttons with a single Text-box
toggle plus the categorised Shape ▾ popover. Insert mode wiring
is unchanged: clicks still call editor.setInsertMode(kind), and
the next pointer drag on the slide creates that shape.
Records the workflow, test infrastructure, path-builder design,
picker UX, dispatcher invariants, and subagent-execution lessons
that came out of shipping the 35-shape catalogue. Notes things to
watch in Phase 2 (regular-polygon helper, adjustments toolbar UI
binding to ADJUSTMENT_SPECS).
Five new scenarios cover the Phase 1 shape library's render
surface end-to-end:
- Full 35-shape catalog (5x7 grid) under default-light, default-dark,
  and material themes — verifies role-bound fills/strokes resolve
  correctly across palettes for every new builder.
- Single large donut — verifies the dispatcher's evenodd opt-in keeps
  the hole visible at scale.
- wedgeRectCallout with default tail — verifies the closest-edge tail
  attachment and outlined default fill.

New scenarios will need baseline regeneration via
pnpm verify:browser:docker:update before the next visual diff run.
Filled-category shapes (basic + block arrows + equation = 28 kinds)
now insert with both an accent1 fill AND a thin text-coloured stroke
at width 1, matching Google Slides convention. Without the stroke,
shapes whose silhouette depends on contour (can lid, cloud, donut
hole) read as flat blobs against the slide background, and the
picker preview (stroke-only) was inconsistent with the rendered
result. Existing rect/ellipse documents are unaffected — the change
only applies to newly inserted shapes.
The previous bezierCurveTo control points produced an over-domed
silhouette plus the separately-drawn lid ellipse stroked twice over
the bezier curve at the top. Replaced with proper half-ellipse arcs
for the body outline; lid stays as a separate sub-path so it renders
cleanly as the can opening when stroked. No adjustment-spec change.
At 24x24 the wedgeRect/wedgeRoundRect/wedgeEllipse/cloudCallout
tails extend below the frame and the small thought-bubble circles
collapse into a few pixels — unrecognizable previews. Special-case
the four callouts in renderShapeIcon to use their bubble proxy
(rect / roundRect / ellipse / cloud) so the picker shows the
identifying body shape. The slide canvas still renders the full
callout at full size unchanged.
Two problems with the previous can fix:

1. Canvas2D's path.ellipse() emits an implicit lineTo from the
   current pen position to the arc's start point. After the body
   sub-path's closePath() the pen sat at (0, ry); the lid full-
   ellipse started at (w, ry). The implicit lineTo painted a
   horizontal line across the top of the can in real browsers.
   The test-canvas shim doesn't replicate this implicit lineTo,
   so the bug only manifested in the browser.

2. The lid full-ellipse double-stroked the top arc (already drawn
   by the body silhouette's upper half), thickening the top.

Fix: explicit moveTo(w, ry) before the lid arc, and only draw the
LOWER half of the lid (the visible "can opening" line). The upper
half is the body silhouette's top arc and shouldn't be drawn twice.
The previous cloud builder painted N=5 full circles plus implicit
Canvas2D lineTo segments between arc start points — visually a
flower of separate circle outlines connected by a pentagon, not a
cloud. The new builder uses N=6 lobes and computes each adjacent
pair's outer intersection point. Each lobe contributes only its
OUTER arc (from previous-lobe intersection to next-lobe
intersection), so the path is one continuous closed silhouette
that reads as a puffy cloud.

cloudCallout's bubble portion picks up the same silhouette via
its existing buildCloud() composition, so its picker proxy stays
correct without further changes.
User reported borders look too thick at the 24×24 picker scale —
even simple polygon outlines (rect, ellipse, hexagon) read as
chunky. Drop STROKE_WIDTH to 1.0 to better match other 24px line
icons in the toolbar.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
After the outer ellipse traces back to (w, h/2) at 2π, drawing the
inner ellipse implicitly emits a lineTo from (w, h/2) to the inner
arc's start at (outerRx + innerRx, outerRy). In real browsers that
extra segment is stroked, painting a horizontal line from the
inner hole's right edge to the outer ring's right edge. The
test-canvas Path2D shim's `ellipse()` does not replicate this
implicit lineTo, so unit tests passed.

Same class of bug as the recent can fix. Insert an explicit moveTo
to the inner arc's start so Canvas2D doesn't fill in the gap.

Snapshot updated to include the new subpath; rendered output now
shows a clean ring on stroked donuts.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The plus path is a single 12-vertex outline with four concave
90° corners (the inner notches between arms). Default Canvas2D
miter joins splay outward at concave corners, painting tiny
spikes that point toward the cross's centre. Four spikes meeting
at the middle read as a small phantom square outline when the
shape is stroked.

Set `ctx.lineJoin = 'round'` in the dispatcher's stroke branch.
The picker icon renderer already sets this, so the fix only
affects the slide-canvas dispatcher; rounded joins are also a
better default for the rest of the shape catalog.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The previous implementation drew two separate diagonal rectangles
as two closed sub-paths. When stroked, both rectangles' boundaries
were painted — including the four short edges that sit inside the
overlap region — which read as a small square outline at the
centre of the X.

Build the cross unrotated as a 12-vertex `+` outline (4 arm tips
× 2 outer corners + 4 inner notches), then rotate every vertex by
45° about the centre. The single closed polygon contains only the
outer outline of the X union, so the centre fills/strokes cleanly.

Snapshot updated to a single subpath with 12 points; rendered
output now shows clean `×` glyphs at all sizes (icon, slide,
filled, and stroked).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Same fix as mathMultiply: two overlapping path.rect() sub-paths
each stroke independently, painting a visible square outline at
the centre where the bars cross. Replaced with a single closed
12-vertex polygon outlining the union (matching the basic/plus
implementation, just with mathPlus's smaller default thickness).
Three sub-paths (top bar rect + bottom bar rect + rotated slash
polygon) each stroke independently, painting a small parallelogram
outline in each bar where the slash crosses — visible as a
4-edge artifact at the slash×bar intersection.

Replaced with a single closed polygon traced clockwise from the
slash's upper-right tip: alternates between slash edges and bar
edges at each of the 8 slash×bar crossings. Walks 20 vertices in
total — slash end-caps (4), top-bar right + left segments (8),
bottom-bar right + left segments (8). Default-adjustment geometry
verified by hand; existing point-in-path test points still pass.
@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@hackerwins has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 53 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6d54c459-81ba-4cb6-8e00-027369f284de

📥 Commits

Reviewing files that changed from the base of the PR and between f14fccc and 9e93522.

⛔ Files ignored due to path filters (29)
  • packages/frontend/tests/visual/baselines/harness-visual.browser.desktop.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.mobile.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.mobile.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-callout-tail.desktop.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-callout-tail.mobile.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-callout-tail.mobile.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-callout-tail.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-donut-evenodd.desktop.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-donut-evenodd.mobile.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-donut-evenodd.mobile.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-donut-evenodd.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-dark.desktop.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-dark.mobile.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-dark.mobile.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-light.desktop.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-light.mobile.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-light.mobile.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-light.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-material.desktop.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-material.mobile.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-material.mobile.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-canvas-shapes-catalog-material.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-toolbar.desktop.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-toolbar.mobile.dark.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-toolbar.mobile.png is excluded by !**/*.png
  • packages/frontend/tests/visual/baselines/harness-visual.browser.slides-toolbar.png is excluded by !**/*.png
  • packages/slides/src/view/canvas/shapes/__snapshots__/registry.snap.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (96)
  • docs/design/README.md
  • docs/design/slides/slides-shapes-p1.md
  • docs/tasks/active/20260509-slides-shapes-p1-lessons.md
  • docs/tasks/active/20260509-slides-shapes-p1-todo.md
  • packages/frontend/scripts/verify-visual-browser.mjs
  • packages/frontend/src/app/harness/visual/slides-scenarios.tsx
  • packages/frontend/src/app/slides/shape-picker-helpers.ts
  • packages/frontend/src/app/slides/shape-picker.tsx
  • packages/frontend/src/app/slides/slides-formatting-toolbar.tsx
  • packages/frontend/src/types/slides-document.ts
  • packages/frontend/tests/app/slides/shape-picker.test.ts
  • packages/slides/src/index.ts
  • packages/slides/src/model/element.ts
  • packages/slides/src/node.ts
  • packages/slides/src/view/canvas/element-renderer.test.ts
  • packages/slides/src/view/canvas/shape-icon.test.ts
  • packages/slides/src/view/canvas/shape-icon.ts
  • packages/slides/src/view/canvas/shape-renderer.test.ts
  • packages/slides/src/view/canvas/shape-renderer.ts
  • packages/slides/src/view/canvas/shape-special.ts
  • packages/slides/src/view/canvas/shapes/arrows/chevron.test.ts
  • packages/slides/src/view/canvas/shapes/arrows/chevron.ts
  • packages/slides/src/view/canvas/shapes/arrows/down-arrow.test.ts
  • packages/slides/src/view/canvas/shapes/arrows/down-arrow.ts
  • packages/slides/src/view/canvas/shapes/arrows/left-arrow.test.ts
  • packages/slides/src/view/canvas/shapes/arrows/left-arrow.ts
  • packages/slides/src/view/canvas/shapes/arrows/left-right-arrow.test.ts
  • packages/slides/src/view/canvas/shapes/arrows/left-right-arrow.ts
  • packages/slides/src/view/canvas/shapes/arrows/pentagon-arrow.test.ts
  • packages/slides/src/view/canvas/shapes/arrows/pentagon-arrow.ts
  • packages/slides/src/view/canvas/shapes/arrows/quad-arrow.test.ts
  • packages/slides/src/view/canvas/shapes/arrows/quad-arrow.ts
  • packages/slides/src/view/canvas/shapes/arrows/right-arrow.test.ts
  • packages/slides/src/view/canvas/shapes/arrows/right-arrow.ts
  • packages/slides/src/view/canvas/shapes/arrows/up-arrow.test.ts
  • packages/slides/src/view/canvas/shapes/arrows/up-arrow.ts
  • packages/slides/src/view/canvas/shapes/basic/can.test.ts
  • packages/slides/src/view/canvas/shapes/basic/can.ts
  • packages/slides/src/view/canvas/shapes/basic/cloud.test.ts
  • packages/slides/src/view/canvas/shapes/basic/cloud.ts
  • packages/slides/src/view/canvas/shapes/basic/diamond.test.ts
  • packages/slides/src/view/canvas/shapes/basic/diamond.ts
  • packages/slides/src/view/canvas/shapes/basic/donut.test.ts
  • packages/slides/src/view/canvas/shapes/basic/donut.ts
  • packages/slides/src/view/canvas/shapes/basic/ellipse.test.ts
  • packages/slides/src/view/canvas/shapes/basic/ellipse.ts
  • packages/slides/src/view/canvas/shapes/basic/hexagon.test.ts
  • packages/slides/src/view/canvas/shapes/basic/hexagon.ts
  • packages/slides/src/view/canvas/shapes/basic/octagon.test.ts
  • packages/slides/src/view/canvas/shapes/basic/octagon.ts
  • packages/slides/src/view/canvas/shapes/basic/parallelogram.test.ts
  • packages/slides/src/view/canvas/shapes/basic/parallelogram.ts
  • packages/slides/src/view/canvas/shapes/basic/pentagon.test.ts
  • packages/slides/src/view/canvas/shapes/basic/pentagon.ts
  • packages/slides/src/view/canvas/shapes/basic/plus.test.ts
  • packages/slides/src/view/canvas/shapes/basic/plus.ts
  • packages/slides/src/view/canvas/shapes/basic/rect.test.ts
  • packages/slides/src/view/canvas/shapes/basic/rect.ts
  • packages/slides/src/view/canvas/shapes/basic/round-rect.test.ts
  • packages/slides/src/view/canvas/shapes/basic/round-rect.ts
  • packages/slides/src/view/canvas/shapes/basic/rt-triangle.test.ts
  • packages/slides/src/view/canvas/shapes/basic/rt-triangle.ts
  • packages/slides/src/view/canvas/shapes/basic/trapezoid.test.ts
  • packages/slides/src/view/canvas/shapes/basic/trapezoid.ts
  • packages/slides/src/view/canvas/shapes/basic/triangle.test.ts
  • packages/slides/src/view/canvas/shapes/basic/triangle.ts
  • packages/slides/src/view/canvas/shapes/builder.ts
  • packages/slides/src/view/canvas/shapes/callouts/cloud-callout.test.ts
  • packages/slides/src/view/canvas/shapes/callouts/cloud-callout.ts
  • packages/slides/src/view/canvas/shapes/callouts/wedge-ellipse-callout.test.ts
  • packages/slides/src/view/canvas/shapes/callouts/wedge-ellipse-callout.ts
  • packages/slides/src/view/canvas/shapes/callouts/wedge-rect-callout.test.ts
  • packages/slides/src/view/canvas/shapes/callouts/wedge-rect-callout.ts
  • packages/slides/src/view/canvas/shapes/callouts/wedge-round-rect-callout.test.ts
  • packages/slides/src/view/canvas/shapes/callouts/wedge-round-rect-callout.ts
  • packages/slides/src/view/canvas/shapes/equation/math-divide.test.ts
  • packages/slides/src/view/canvas/shapes/equation/math-divide.ts
  • packages/slides/src/view/canvas/shapes/equation/math-equal.test.ts
  • packages/slides/src/view/canvas/shapes/equation/math-equal.ts
  • packages/slides/src/view/canvas/shapes/equation/math-minus.test.ts
  • packages/slides/src/view/canvas/shapes/equation/math-minus.ts
  • packages/slides/src/view/canvas/shapes/equation/math-multiply.test.ts
  • packages/slides/src/view/canvas/shapes/equation/math-multiply.ts
  • packages/slides/src/view/canvas/shapes/equation/math-not-equal.test.ts
  • packages/slides/src/view/canvas/shapes/equation/math-not-equal.ts
  • packages/slides/src/view/canvas/shapes/equation/math-plus.test.ts
  • packages/slides/src/view/canvas/shapes/equation/math-plus.ts
  • packages/slides/src/view/canvas/shapes/index.test.ts
  • packages/slides/src/view/canvas/shapes/index.ts
  • packages/slides/src/view/canvas/shapes/registry.snap.test.ts
  • packages/slides/src/view/canvas/slide-renderer.test.ts
  • packages/slides/src/view/canvas/test-canvas-env.test.ts
  • packages/slides/src/view/canvas/test-canvas-env.ts
  • packages/slides/src/view/editor/editor.ts
  • packages/slides/src/view/editor/interactions/insert.test.ts
  • packages/slides/src/view/editor/interactions/insert.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/slides-shapes-p1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 182.6s

Lane Status Duration
sheets:build ✅ pass 13.7s
docs:build ✅ pass 12.7s
slides:build ✅ pass 9.5s
verify:fast ✅ pass 100.6s
frontend:build ✅ pass 18.6s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 5.0s
cli:build ✅ pass 2.2s
verify:entropy ✅ pass 19.9s

Verification: verify:integration

Result: ✅ PASS

hackerwins added 2 commits May 9, 2026 22:13
Adds the 5 new harness scenarios to scripts/verify-visual-browser.mjs's
hardcoded scenario list (was missed when the scenarios first landed)
and captures their baselines across all 4 profiles (desktop, mobile,
desktop.dark, mobile.dark). 20 new baselines:

- slides-canvas-shapes-catalog-light  (full 35-shape grid)
- slides-canvas-shapes-catalog-dark
- slides-canvas-shapes-catalog-material
- slides-canvas-donut-evenodd
- slides-canvas-callout-tail

Also picks up legitimate baseline drift from the toolbar (5 inline
buttons → Text-box + Shape ▾ popover) and the harness-visual root
images that contain the toolbar.

Without this commit, the verify-browser CI job fails for any PR
that adds a scenario to the harness.
CI's Linux x86_64 runner produces ULP-level (15th decimal) drift
versus Apple Silicon when path builders walk Math.sin / Math.cos
through the shim's arc / bezier polyline approximation, so the
unrounded snapshot was not portable: e.g. y=6.005165928857689 on
mac vs 6.005165928857686 on Linux.

Adds a quantize() helper that rounds every numeric leaf to 6
decimals before snapshotting. Geometry regressions stay visible
(micrometer precision is well below any meaningful shift), CPU-
microarch noise does not leak into the snapshot.
@codecov

codecov Bot commented May 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@hackerwins
hackerwins merged commit 25bafcf into main May 9, 2026
4 checks passed
@hackerwins
hackerwins deleted the feat/slides-shapes-p1 branch May 9, 2026 13:32
hackerwins added a commit that referenced this pull request May 10, 2026
Each package already has its own README, but there was no top-level
entry point inside `packages/` that summarised what lives where.
Splits the listing into engines (sheets, docs, slides) and apps and
services (frontend, backend, cli, documentation) so a new contributor
can orient before drilling into a single package.

Four active tasks have shipped on main (PR #189 undo-destroys-initial-block,
#202+#205 slides-shapes-p1, #206 slides-thumb-responsive, #204
contributing-md) but their todo files were still sitting in
`docs/tasks/active/` because some checkboxes were never ticked. Mark
the remaining items done and run `pnpm tasks:archive` to move the five
files into `archive/2026/05/`.

While there, eight April task docs were still loose at the archive
root (predating the by-month layout). Move them into `archive/2026/04/`
so the directory is uniformly organised and the index counts stay
accurate.

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins hackerwins mentioned this pull request May 11, 2026
6 tasks
hackerwins added a commit that referenced this pull request May 11, 2026
Adds @wafflebase/slides as a third surface alongside Sheets and Docs,
plus 53-shape library (Phase 1+2), adjustment handles for 9 pilot
shapes (P3-A.1), live snap guides, align/distribute toolbar, themed
authoring, and layout-change UI. Also ships Sheets cell comments
(Phase B), docs peer-avatar caret jump, yorkie-js-sdk 0.7.8 upgrade,
and CLI/REST API improvements (docs export imageFetcher, expired
session refresh, REST docs split). Minor bump because slides is a
new top-level package.

Highlights: #184 #185 #186 #187 #188 #189 #190 #191 #192 #197 #198
#201 #202 #203 #204 #205 #206 #207 #209 #210
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