docs(design): finalize SVG subset + inline-normalize CSS for T-320
Pin the bounded SVG subset from a real d2 sample + our templates (markers for arrowheads in; mask deferred to v1-ignore; foreignObject/filters/ gradients out, so mermaid isn't a launch target). Class styling resolves via an inline-normalize preprocess (flatten <style> classes to inline attrs) so the painter stays a pure attribute renderer. Tool-PATH for the d2 binary now resolves via D-104. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8087,3 +8087,23 @@ Resolution order: (1) explicit settings path → use it; honest error + fallback
|
||||
First run: auto-detect each tool ONCE — probe the login-shell PATH + well-known dirs (~/.local/bin, /usr/local/bin, /opt/homebrew, /home/linuxbrew/.linuxbrew/bin) — and WRITE the discovered absolute paths into the map (pinned, not re-probed each launch). Provide a re-detect action.
|
||||
|
||||
Storage: user-scope SettingsStore keyed by machine (D-93) — never committed. Surface in the Config tab (T-414) + flag unresolved tools in the Problems panel (echoes D-58''s soft floor). Generalizes D-58''s CLIDE_PQL_BIN override to all supporter tools. First consumer: the d2 template (T-494).', NULL, '2026-06-28 17:50:13', '2026-06-28 17:50:13.728', '2026-06-28 17:50:13.728', NULL, 'db0f73d19a558e556733e8b42ef7081a', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ETJQP0CT6X7W3CWZ6NS9G', 'description', 'Template for the unified drawing card (T-317, D-91): render an SVG file referenced in the JSON. clide-owned rendering per the own-the-rendering-stack guardrail — no opinionated SVG package without justification; evaluate what is feasible via CustomPaint / a minimal path parser, and scope accordingly (SVG is a broad spec; define the supported subset). Optional label + description beneath (T-318). Display-only (D-78). Depends on the core engine (T-318).
|
||||
|
||||
PROMOTED TO ENGINE (D-103, 2026-06-28): this is now the drawing-card''s rendering FOUNDATION, not one template among many. Build the clide-owned CustomPaint SVG renderer over a BOUNDED subset (rect/line/polyline/polygon/circle/ellipse/path, text incl. the Phosphor font, image, g + transform, basic fill/stroke/opacity/rx). T-318 (envelope + dispatch + Flutter overlay) and every template lower onto this; d2/graphviz/mermaid output renders for free. No longer blocked by T-318 (dependency inverted). Open: pin the subset to what our own templates + d2/graphviz emit. Schema: docs/design/drawing-card-schema.md.', 'Template for the unified drawing card (T-317, D-91): render an SVG file referenced in the JSON. clide-owned rendering per the own-the-rendering-stack guardrail — no opinionated SVG package without justification; evaluate what is feasible via CustomPaint / a minimal path parser, and scope accordingly (SVG is a broad spec; define the supported subset). Optional label + description beneath (T-318). Display-only (D-78). Depends on the core engine (T-318).
|
||||
|
||||
PROMOTED TO ENGINE (D-103, 2026-06-28): this is now the drawing-card''s rendering FOUNDATION, not one template among many. Build the clide-owned CustomPaint SVG renderer over a BOUNDED subset (rect/line/polyline/polygon/circle/ellipse/path, text incl. the Phosphor font, image, g + transform, basic fill/stroke/opacity/rx). T-318 (envelope + dispatch + Flutter overlay) and every template lower onto this; d2/graphviz/mermaid output renders for free. No longer blocked by T-318 (dependency inverted). Open: pin the subset to what our own templates + d2/graphviz emit. Schema: docs/design/drawing-card-schema.md.
|
||||
|
||||
SVG SUBSET FINALIZED (2026-06-28, grounded in a real d2 sample + our own templates):
|
||||
|
||||
STRUCTURE: <svg> (viewBox/width/height, incl. nested svg), <g> (transform, opacity, class), <defs>, <marker> (+ marker-start/mid/end; orient=auto, refX/refY, viewBox) — edge arrowheads.
|
||||
SHAPES: rect (rx/ry), circle, ellipse, line, polyline, polygon, path (full data: M L H V C S Q T A Z + relatives).
|
||||
TEXT: text + tspan (x/y/dx/dy, font-family incl. Phosphor, font-size/weight, text-anchor, dominant-baseline).
|
||||
RASTER: image (href/xlink:href, x/y/w/h, preserveAspectRatio) — our image/compare templates.
|
||||
STYLING: presentation attrs (fill, fill-opacity, stroke, stroke-width, stroke-linecap/linejoin, stroke-dasharray, opacity, color), transform (translate/scale/rotate/matrix), class= resolved via the normalizer below.
|
||||
DEFERRED v1: <mask> (d2 masks connections for clean edge/node joins) — ignore and rely on node-over-edge paint order; add basic mask only if d2 output looks wrong.
|
||||
OUT: foreignObject, filters, <animate>/SMIL, scripting, <use>/<symbol>, gradients, patterns, clipPath. (So mermaid, which leans on foreignObject, is not a launch target.)
|
||||
|
||||
CSS DECISION — inline-normalize (option A): d2 emits a <style> block of FLAT single-class selectors (.fill-B1, .color-N1, .shape, .connection, .text-bold → presentation props). A preprocessing normalizer parses <style> into class->props, merges each element''s class props into inline presentation attributes (inline wins), and drops <style>; the painter then only ever sees inline attrs — a pure, testable presentation-attribute renderer. Chosen over a render-time CSS resolver to keep the painter free of any CSS concept.
|
||||
|
||||
BUILD SPLIT: (1) inline-style normalizer; (2) path-data parser; (3) the CustomPaint painter (shapes/text/image/marker/transform); (4) injected image-href resolver (like image.show). Fixtures: real d2 + graphviz output.', NULL, '2026-06-28 17:54:38', '2026-06-28 17:54:38.871', '2026-06-28 17:54:38.871', NULL, 'afedea62e1486510e317fc4afb0df528', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGYS06SDJGZ4W3Z9V9HDEATW', 'description', NULL, 'The pre-commit hook re-exports + stages .pql/changelog on commit but only catches the tickets + ticket_history tables — it leaves ticket_deps (blocker edges) and ticket_idmap (new T-NNN <-> record_id mappings) UNSTAGED. Seen repeatedly this session (D-103 re-sequencing blockers; T-495 id mapping), each needing a manual follow-up commit; otherwise the planning state silently doesn''t persist and a later branch switch drops it — exactly the failure the hook exists to prevent. Fix: stage ALL of .pql/changelog/ (git add .pql/changelog/), not just the tables the export rewrote. Repro: pql ticket block X --by Y + create a ticket, commit unrelated files, observe ticket_deps/ticket_idmap still modified afterward.', NULL, '2026-06-28 18:00:41', '2026-06-28 18:00:41.453', '2026-06-28 18:00:41.453', NULL, '81bf67c8003a8e48eb20c5c0b16fc90c', 2) ON CONFLICT(hash) DO NOTHING;
|
||||
|
||||
@@ -323,3 +323,4 @@ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGVNCZS0JZP5Y7596GBV4RFC', 'T-493', '2026-06-28 10:45:31.721', '2026-06-28 10:45:31.721', NULL, 'eec40f4714242cf343035481684f2aba', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGX26B0NAC9WRJMVB0QE6CV8', 'T-494', '2026-06-28 14:01:13.733', '2026-06-28 14:01:13.733', NULL, '37bf4baec69cb3d06072793cd3d60a46', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGYPJ6FTEPP4JK3D7D01ZSMM', 'T-495', '2026-06-28 17:50:02.366', '2026-06-28 17:50:02.366', NULL, '40dbd25d323a8192e8f65a6e1c2dd33d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGYS06SDJGZ4W3Z9V9HDEATW', 'T-496', '2026-06-28 18:00:41.420', '2026-06-28 18:00:41.420', NULL, 'd2e472745c710eb00555b2b824d4e214', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||
|
||||
@@ -9944,3 +9944,22 @@ Resolution order: (1) explicit settings path → use it; honest error + fallback
|
||||
First run: auto-detect each tool ONCE — probe the login-shell PATH + well-known dirs (~/.local/bin, /usr/local/bin, /opt/homebrew, /home/linuxbrew/.linuxbrew/bin) — and WRITE the discovered absolute paths into the map (pinned, not re-probed each launch). Provide a re-detect action.
|
||||
|
||||
Storage: user-scope SettingsStore keyed by machine (D-93) — never committed. Surface in the Config tab (T-414) + flag unresolved tools in the Problems panel (echoes D-58''s soft floor). Generalizes D-58''s CLIDE_PQL_BIN override to all supporter tools. First consumer: the d2 template (T-494).', 'backlog', 'medium', NULL, NULL, 'D-104', '2026-06-28 17:50:02.366', '2026-06-28 17:50:13.728', NULL, '53f89d410f3d83567393d0baa2ae8c4f', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ETJQP0CT6X7W3CWZ6NS9G', 'task', '06FB2EDCBYRBDSV9V1PJ1KE3CM', 'Drawing card template: SVG render', 'Template for the unified drawing card (T-317, D-91): render an SVG file referenced in the JSON. clide-owned rendering per the own-the-rendering-stack guardrail — no opinionated SVG package without justification; evaluate what is feasible via CustomPaint / a minimal path parser, and scope accordingly (SVG is a broad spec; define the supported subset). Optional label + description beneath (T-318). Display-only (D-78). Depends on the core engine (T-318).
|
||||
|
||||
PROMOTED TO ENGINE (D-103, 2026-06-28): this is now the drawing-card''s rendering FOUNDATION, not one template among many. Build the clide-owned CustomPaint SVG renderer over a BOUNDED subset (rect/line/polyline/polygon/circle/ellipse/path, text incl. the Phosphor font, image, g + transform, basic fill/stroke/opacity/rx). T-318 (envelope + dispatch + Flutter overlay) and every template lower onto this; d2/graphviz/mermaid output renders for free. No longer blocked by T-318 (dependency inverted). Open: pin the subset to what our own templates + d2/graphviz emit. Schema: docs/design/drawing-card-schema.md.
|
||||
|
||||
SVG SUBSET FINALIZED (2026-06-28, grounded in a real d2 sample + our own templates):
|
||||
|
||||
STRUCTURE: <svg> (viewBox/width/height, incl. nested svg), <g> (transform, opacity, class), <defs>, <marker> (+ marker-start/mid/end; orient=auto, refX/refY, viewBox) — edge arrowheads.
|
||||
SHAPES: rect (rx/ry), circle, ellipse, line, polyline, polygon, path (full data: M L H V C S Q T A Z + relatives).
|
||||
TEXT: text + tspan (x/y/dx/dy, font-family incl. Phosphor, font-size/weight, text-anchor, dominant-baseline).
|
||||
RASTER: image (href/xlink:href, x/y/w/h, preserveAspectRatio) — our image/compare templates.
|
||||
STYLING: presentation attrs (fill, fill-opacity, stroke, stroke-width, stroke-linecap/linejoin, stroke-dasharray, opacity, color), transform (translate/scale/rotate/matrix), class= resolved via the normalizer below.
|
||||
DEFERRED v1: <mask> (d2 masks connections for clean edge/node joins) — ignore and rely on node-over-edge paint order; add basic mask only if d2 output looks wrong.
|
||||
OUT: foreignObject, filters, <animate>/SMIL, scripting, <use>/<symbol>, gradients, patterns, clipPath. (So mermaid, which leans on foreignObject, is not a launch target.)
|
||||
|
||||
CSS DECISION — inline-normalize (option A): d2 emits a <style> block of FLAT single-class selectors (.fill-B1, .color-N1, .shape, .connection, .text-bold → presentation props). A preprocessing normalizer parses <style> into class->props, merges each element''s class props into inline presentation attributes (inline wins), and drops <style>; the painter then only ever sees inline attrs — a pure, testable presentation-attribute renderer. Chosen over a render-time CSS resolver to keep the painter free of any CSS concept.
|
||||
|
||||
BUILD SPLIT: (1) inline-style normalizer; (2) path-data parser; (3) the CustomPaint painter (shapes/text/image/marker/transform); (4) injected image-href resolver (like image.show). Fixtures: real d2 + graphviz output.', 'backlog', 'low', NULL, NULL, NULL, '2026-06-10 11:11:55', '2026-06-28 17:54:38.871', NULL, 'd4994ff93518b92deef01b3e12a8077e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGYS06SDJGZ4W3Z9V9HDEATW', 'bug', NULL, 'pre-commit changelog hook leaves ticket_deps/ticket_idmap unstaged', NULL, 'backlog', 'medium', NULL, NULL, NULL, '2026-06-28 18:00:41.419', '2026-06-28 18:00:41.419', NULL, 'dda7cc3fb4d964d6d80a0c726f4372a8', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGYS06SDJGZ4W3Z9V9HDEATW', 'bug', NULL, 'pre-commit changelog hook leaves ticket_deps/ticket_idmap unstaged', 'The pre-commit hook re-exports + stages .pql/changelog on commit but only catches the tickets + ticket_history tables — it leaves ticket_deps (blocker edges) and ticket_idmap (new T-NNN <-> record_id mappings) UNSTAGED. Seen repeatedly this session (D-103 re-sequencing blockers; T-495 id mapping), each needing a manual follow-up commit; otherwise the planning state silently doesn''t persist and a later branch switch drops it — exactly the failure the hook exists to prevent. Fix: stage ALL of .pql/changelog/ (git add .pql/changelog/), not just the tables the export rewrote. Repro: pql ticket block X --by Y + create a ticket, commit unrelated files, observe ticket_deps/ticket_idmap still modified afterward.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-28 18:00:41.419', '2026-06-28 18:00:41.452', NULL, '6fed07a2fa2199d1b287829a4d7e47dd', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||
|
||||
Reference in New Issue
Block a user