You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A written specification for what bounds the surfaces this library draws — the three stroke canvases and the menu — staged as ready-for-agent tickets in the style of #244/#245/#246. The map is done when someone can unclip the drawing from the parent's box, and keep overflow: hidden working as containment, without another decision being needed.
The concrete defect this starts from: a stroke canvas is sized once to parent.getBoundingClientRect() and pinned at left/top: 0 (stroke.ts:87), so ink outside the parent's box is lost even when the host does not clip. That makes the parent bound display while it does not bound input: pointerdown takes pointer capture (pointer-source.ts:49), so a gesture that leaves the parent keeps driving the machine, and selection is angle-and-distance based with items at pointer-events: none. The menu DOM already overflows freely. Only the canvases clip, and only for implementation reasons.
Skills every session should consult.grilling and codebase-design. There is no domain-modeling skill installed on this machine — codebase-design is the stand-in for interface work, same as in #263.
The four surfaces.
Surface
Built by
Paint order
Lifetime
Lower stroke
createStrokeCanvas
behind the menu
the gesture
Menu
createMenu
—
the open sub-menu
Upper stroke
createStrokeCanvas
in front of the menu
the gesture
Gesture feedback
createStrokeCanvas
in front of the menu
gestureFeedbackDuration after the gesture, several at once
restack() in renderer.ts holds that order by sibling position, deliberately without z-index.
Relationship to #263. The wedge-ring map's "Always a shadow root" decision puts a core-owned wrapper <div> between parent and everything drawn here. That wrapper is the element whose box would decide what can escape, so the two maps touch at exactly one seam. #263's Out-of-scope line — "The wedge ring changes nothing about how strokes are drawn, so the two do not interact" — is true of the wedge geometry and false of the wrapper; it has been corrected there.
Standing decisions, settled while charting
Premises, not open questions.
Destination shape. A spec handed off as ready-for-agent tickets. Planning, not execution: no ticket here lands the change. The decisions are cheap to settle in prose and expensive to settle in a diff.
One rule for all four surfaces. The defect is an inconsistency between surfaces, so fixing one canvas without stating the rule leaves the next reader to re-derive it. The menu likely needs documentation rather than code.
Three canvases stay three. Two different reasons, both sufficient: the lower stroke sits behind the menu while the other two sit in front of it, and each feedback trace outlives the gesture that drew it, so several can fade concurrently while a new gesture draws. Consolidation is off the table; the restack() complexity is the price of the paint order, not an accident.
The never-resizes bug is in scope. A canvas is sized once at creation and never again, so a parent that resizes mid-gesture already draws into a stale surface. It is the same question — what is the surface sized to, and when is that re-evaluated — and any answer has to settle it anyway.
The hand-off must ship a way to see it. Nothing in the repo exercises a stroke leaving its parent: the playground clips (live-surface.tsx:354) and the e2e fixture uses overflow: auto. The spec must require a deliberately undersized parent that shows the escape, and a clipped counterpart proving overflow: hidden still contains everything. Both halves of the contract are otherwise untested, and the clipped half already has a consumer.
Decisions so far
Not yet specified
Mid-gesture scrolling of an ancestor. The parent rect is re-read every frame and points arrive in client coordinates, so a scroll during a gesture may drift the ink against the pointer. Whether that is a defect, and whether the sizing rule makes it better or worse, has not been looked at.
Whether the menu needs any code change at all. It already overflows; it may need only a documented contract. The answer depends on which element ends up being sized.
Fading traces when the surface grows. If a new gesture regrows the drawing surface while earlier feedback traces are still visible, what happens to those traces is unspecified.
Out of scope
Tier 2: position: fixed and client coordinates. Escaping an ancestor'soverflow: hidden, which would also retire toLocalPoint entirely. It subsumes this work's mechanism but takes away overflow: hidden as a clean CSS opt-out, and brings its own failure modes (transform/filter/contain ancestors silently reclipping, stacking-context fights). If it ever happens it is a fresh destination, not a continuation of this one.
Viewport-edge menu placement. Once the parent stops bounding the drawing, the viewport is the real bound and a menu opened near a screen edge puts labels off-screen. Already true today, so not caused here; it is a menu-placement feature (clamp the centre, or flip labels) and belongs with Map: land the wedge-ring menu design and a configurable theming API #263.
Destination
A written specification for what bounds the surfaces this library draws — the three stroke canvases and the menu — staged as
ready-for-agenttickets in the style of #244/#245/#246. The map is done when someone can unclip the drawing from the parent's box, and keepoverflow: hiddenworking as containment, without another decision being needed.The concrete defect this starts from: a stroke canvas is sized once to
parent.getBoundingClientRect()and pinned atleft/top: 0(stroke.ts:87), so ink outside the parent's box is lost even when the host does not clip. That makes the parent bound display while it does not bound input:pointerdowntakes pointer capture (pointer-source.ts:49), so a gesture that leaves the parent keeps driving the machine, and selection is angle-and-distance based with items atpointer-events: none. The menu DOM already overflows freely. Only the canvases clip, and only for implementation reasons.Notes
Domain. Vocabulary is the code's own: stroke, upper and lower stroke, gesture feedback / trace, canvas, surface, parent, wrapper, containment. Read
src/layout/stroke.ts,src/engine/renderer.ts,src/layout/gesture-feedback.tsandsrc/layout/menu.cssbefore touching a ticket.Skills every session should consult.
grillingandcodebase-design. There is nodomain-modelingskill installed on this machine —codebase-designis the stand-in for interface work, same as in #263.The four surfaces.
createStrokeCanvascreateMenucreateStrokeCanvascreateStrokeCanvasgestureFeedbackDurationafter the gesture, several at oncerestack()inrenderer.tsholds that order by sibling position, deliberately withoutz-index.Relationship to #263. The wedge-ring map's "Always a shadow root" decision puts a core-owned wrapper
<div>betweenparentand everything drawn here. That wrapper is the element whose box would decide what can escape, so the two maps touch at exactly one seam. #263's Out-of-scope line — "The wedge ring changes nothing about how strokes are drawn, so the two do not interact" — is true of the wedge geometry and false of the wrapper; it has been corrected there.Standing decisions, settled while charting
Premises, not open questions.
ready-for-agenttickets. Planning, not execution: no ticket here lands the change. The decisions are cheap to settle in prose and expensive to settle in a diff.restack()complexity is the price of the paint order, not an accident.live-surface.tsx:354) and the e2e fixture usesoverflow: auto. The spec must require a deliberately undersized parent that shows the escape, and a clipped counterpart provingoverflow: hiddenstill contains everything. Both halves of the contract are otherwise untested, and the clipped half already has a consumer.Decisions so far
Not yet specified
Out of scope
position: fixedand client coordinates. Escaping an ancestor'soverflow: hidden, which would also retiretoLocalPointentirely. It subsumes this work's mechanism but takes awayoverflow: hiddenas a clean CSS opt-out, and brings its own failure modes (transform/filter/containancestors silently reclipping, stacking-context fights). If it ever happens it is a fresh destination, not a continuation of this one.