agentic-mermaid 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT_NATIVE.md +671 -0
- package/CHANGELOG.md +19 -0
- package/Instructions_for_agents.md +89 -0
- package/README.md +8 -8
- package/assets/hero.png +0 -0
- package/dist/agent-core.d.ts +2 -2
- package/dist/agent-core.js +2 -2
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +4 -4
- package/dist/agentic-mermaid-mcp.js +5 -5
- package/dist/am.js +8 -15
- package/dist/{chunk-QKDLMOJJ.js → chunk-2P5FKUQC.js} +7 -9
- package/dist/{chunk-WN3YDOBG.js → chunk-2S3VSS6I.js} +4 -4
- package/dist/{chunk-CDPKJ2BQ.js → chunk-2SILJDW2.js} +2 -2
- package/dist/{chunk-ZYMMKQ3I.js → chunk-BJYMBT4I.js} +299 -973
- package/dist/{chunk-WI35L7IR.js → chunk-FDM43XU7.js} +2 -2
- package/dist/{chunk-4QECACIA.js → chunk-MYPDVZCO.js} +19 -19
- package/dist/{index-D54dqSdC.d.ts → index-DWPa2MJs.d.ts} +7 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp-cli-5J47WEY3.js +17 -0
- package/docs/README.md +34 -34
- package/docs/agent-api-cookbook.md +317 -0
- package/docs/agent-mutation-policy.md +104 -0
- package/docs/agent-workflow-examples.md +46 -0
- package/docs/api.md +418 -0
- package/docs/ascii.md +113 -0
- package/docs/code-mode-impact.md +214 -0
- package/docs/comparison.md +106 -0
- package/docs/computational-aesthetics-prototype-plan.md +252 -0
- package/docs/config.md +121 -0
- package/docs/custom-fonts.md +265 -0
- package/docs/custom-style-cookbook.md +275 -0
- package/docs/diagram-families.md +218 -0
- package/docs/features.md +154 -0
- package/docs/fork-differences.md +133 -0
- package/docs/getting-started.md +182 -0
- package/docs/issue-derived-test-cases.md +163 -0
- package/docs/mcp-code-mode-rationale.md +108 -0
- package/docs/mcp-http-transport.md +230 -0
- package/docs/mermaid-layout-complaints.md +851 -0
- package/docs/mutation-testing.md +285 -0
- package/docs/quality.md +224 -0
- package/docs/react.md +97 -0
- package/docs/style-authoring.md +336 -0
- package/docs/svg-semantic-contract.md +49 -0
- package/docs/testing-strategy.md +443 -0
- package/docs/theming.md +149 -0
- package/package.json +21 -6
- package/server.json +2 -2
- package/skills/agentic-mermaid-live-editor/SKILL.md +1 -1
- package/dist/mcp-cli-SBQSTJFC.js +0 -17
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# Authoring styles
|
|
2
|
+
|
|
3
|
+
A **style** is a partial description of how diagrams look. Every field is
|
|
4
|
+
optional: a style that only sets colors is a palette; a style that sets
|
|
5
|
+
stroke character, fills, typography, and a palette is a full look. Styles
|
|
6
|
+
apply uniformly to every built-in family. An extension gets the same contract
|
|
7
|
+
when its `FamilyDescriptor` advertises native Scene execution (`layout` +
|
|
8
|
+
`lowerScene`, exposed as the `scene` capability); discovery reports extensions
|
|
9
|
+
without that capability instead of promising style support they cannot render.
|
|
10
|
+
|
|
11
|
+
Styles compose by **stacking**: `RenderOptions.style` accepts a name, an
|
|
12
|
+
inline spec, or an array of either, merged left → right (later fields win,
|
|
13
|
+
colors merge per channel):
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { renderMermaidSVG, registerStyle } from 'agentic-mermaid'
|
|
17
|
+
|
|
18
|
+
// a built-in look
|
|
19
|
+
renderMermaidSVG(source, { style: 'hand-drawn' })
|
|
20
|
+
|
|
21
|
+
// hand-drawn geometry × the dracula Palette
|
|
22
|
+
renderMermaidSVG(source, { style: ['hand-drawn', 'dracula'] })
|
|
23
|
+
|
|
24
|
+
// an inline custom style — no registration needed
|
|
25
|
+
renderMermaidSVG(source, {
|
|
26
|
+
style: {
|
|
27
|
+
colors: { bg: '#fffdf7', fg: '#1c1917', accent: '#e11d48' },
|
|
28
|
+
font: 'Caveat',
|
|
29
|
+
stroke: 'jittered',
|
|
30
|
+
roughness: 0.8,
|
|
31
|
+
fill: 'hachure',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// register for reuse by canonical kind:name identity
|
|
36
|
+
registerStyle({ name: 'look:acme-brand', /* … */ })
|
|
37
|
+
renderMermaidSVG(source, { style: ['look:acme-brand', 'palette:nord'] })
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
You never pick a backend. The engine infers the machinery from what the
|
|
41
|
+
style asks for: `stroke: 'freehand'` or `fill: 'wash'` engages the hybrid
|
|
42
|
+
backend (pressure ribbons, watercolor); `stroke: 'jittered'`, hachure fills,
|
|
43
|
+
sketch parameters, or a page backdrop engage rough.js; anything else renders
|
|
44
|
+
on the crisp default backend with your palette and font.
|
|
45
|
+
|
|
46
|
+
## Progressive authoring: Levels 0–4
|
|
47
|
+
|
|
48
|
+
All five levels use the same `style` option and the same left-to-right merge
|
|
49
|
+
law. Moving up never requires a new package or family adapter.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { renderMermaidSVG } from 'agentic-mermaid'
|
|
53
|
+
import { verifyMermaid } from 'agentic-mermaid/agent'
|
|
54
|
+
|
|
55
|
+
// Level 0 — preset
|
|
56
|
+
renderMermaidSVG(source, { style: 'publication-figure' })
|
|
57
|
+
|
|
58
|
+
// Level 1 — compose a Look and Palette
|
|
59
|
+
renderMermaidSVG(source, { style: ['hand-drawn', 'nord'] })
|
|
60
|
+
|
|
61
|
+
// Level 2 — reusable ordinary StyleSpec JSON
|
|
62
|
+
const palette = { colors: { bg: '#fffdf7', fg: '#211a1a', accent: '#a33b20' } }
|
|
63
|
+
renderMermaidSVG(source, { style: palette })
|
|
64
|
+
|
|
65
|
+
// Level 3 — semantic SceneRole defaults, shared by layout and rendering
|
|
66
|
+
const roleStyle = {
|
|
67
|
+
roles: {
|
|
68
|
+
node: { fontSize: 16, fontWeight: 700, paddingX: 28, paddingY: 14,
|
|
69
|
+
cornerRadius: 8, lineWidth: 2, fillColor: '#fff7ed' },
|
|
70
|
+
edge: { fontSize: 12, fontWeight: 600, lineWidth: 2, bendRadius: 10 },
|
|
71
|
+
group: { fontSize: 13, fontWeight: 700, paddingX: 22, paddingY: 20 },
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Level 4 — ordered equality bindings plus inspect-only constraints
|
|
76
|
+
const policyStyle = {
|
|
77
|
+
...roleStyle,
|
|
78
|
+
semanticSlots: {
|
|
79
|
+
selected: { fillColor: '#fecdd3', borderColor: '#881337', lineWidth: 4, cue: 'outline' },
|
|
80
|
+
},
|
|
81
|
+
bindings: [
|
|
82
|
+
{ channel: 'category', value: 'Pro', slot: 'selected', role: 'pie-slice' },
|
|
83
|
+
],
|
|
84
|
+
constraints: [
|
|
85
|
+
{ kind: 'contrast', action: 'error', minimum: 4.5 },
|
|
86
|
+
{ kind: 'accent-area', action: 'warn', maxFraction: 0.25 },
|
|
87
|
+
],
|
|
88
|
+
}
|
|
89
|
+
const verification = verifyMermaid(source, { renderOptions: { style: policyStyle } })
|
|
90
|
+
// CLI equivalent for a saved record: am verify diagram.mmd --style brand.style.json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Role/slot values are defaults. Concrete Mermaid theme/config and element paint
|
|
94
|
+
remain authoritative. Bindings never target `emphasis`: family syntax such as
|
|
95
|
+
Pie `highlightSlice` owns its target and quantitative geometry. Constraints
|
|
96
|
+
inspect the admitted final Scene and return measurable, unmeasurable, or
|
|
97
|
+
not-applicable evidence; they never repaint or relayout. Transparent output has
|
|
98
|
+
no known host backdrop, so contrast reports unmeasurable rather than inventing
|
|
99
|
+
a ratio.
|
|
100
|
+
|
|
101
|
+
### Export and edit a built-in
|
|
102
|
+
|
|
103
|
+
Built-ins dogfood this same record format. Export one with `getStyle`, validate
|
|
104
|
+
or save the returned JSON, then pass it inline; selecting the name and importing
|
|
105
|
+
its export are behaviorally equivalent.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { getStyle, validateStyleSpec } from 'agentic-mermaid'
|
|
109
|
+
const exported = getStyle('publication-figure')!
|
|
110
|
+
if (validateStyleSpec(exported).length) throw new Error('invalid exported style')
|
|
111
|
+
renderMermaidSVG(source, { style: exported })
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Terminal projection
|
|
115
|
+
|
|
116
|
+
ASCII/Unicode receives the same resolved Style and compiled role face. Family
|
|
117
|
+
terminal adapters consume supported semantic projections—the Pie witness turns
|
|
118
|
+
an applicable category cue into a no-color marker while keeping
|
|
119
|
+
`highlightSlice` authoritative. Terminal output preserves hierarchy, emphasis,
|
|
120
|
+
symbols, line patterns, and available color; typography, radius, shared shadow,
|
|
121
|
+
and detailed graphical paint produce explicit projection diagnostics rather
|
|
122
|
+
than pretending pixel parity.
|
|
123
|
+
|
|
124
|
+
### Why there is no BrandPack yet
|
|
125
|
+
|
|
126
|
+
Level 4 is the active ceiling. No external consumer has shown that ordinary
|
|
127
|
+
version-controlled StyleSpec files are insufficient for repeated distribution,
|
|
128
|
+
exact selection, or installed-resource integrity, so the evidence-gated B4
|
|
129
|
+
BrandPack registry is deliberately **not promoted**. Render-time semver ranges,
|
|
130
|
+
dependency solving, ambient resource loading, and a second inheritance system
|
|
131
|
+
remain absent.
|
|
132
|
+
|
|
133
|
+
The generated [Section B capability report](./project/section-b-capability-report.md)
|
|
134
|
+
accounts for every role leaf, private-face projection, role fallback, admitted
|
|
135
|
+
family role/channel, and built-in export.
|
|
136
|
+
The [all-family evidence sheet](./design/families/section-b-brand-evidence.png)
|
|
137
|
+
shows one deliberately distinctive sentinel plus three holdout styles; its receipt
|
|
138
|
+
and honest hard-error baseline live in `eval/section-b-brand-evidence/`.
|
|
139
|
+
|
|
140
|
+
Every field is optional in a reusable fragment, but the resolved final stack
|
|
141
|
+
must make each renderer parameter applicable. `hachureAngle`, `hachureGap`,
|
|
142
|
+
and `fillWeight` require `fill: 'hachure'`; `washOpacity` and `washEdge`
|
|
143
|
+
require `fill: 'wash'`. Crisp/default rendering does not reinterpret
|
|
144
|
+
`fill: 'none'` or `fill: 'solid'`, so those values must be combined with a
|
|
145
|
+
field that selects the rough or hybrid backend. Rendering fails with a stable
|
|
146
|
+
`Invalid style spec` dependency diagnostic when a final stack would otherwise
|
|
147
|
+
change request identity without realizing that customization.
|
|
148
|
+
|
|
149
|
+
Canonical registered identities say what is being named: `look:hand-drawn`
|
|
150
|
+
and `palette:dracula`. Discovery exposes exactly one `kind` (`look` or
|
|
151
|
+
`palette`), an explicit `isDefault`, and a stable `inputName`. Stable short
|
|
152
|
+
inputs such as `hand-drawn` and `dracula` are descriptor-owned names, not
|
|
153
|
+
aliases. Light Tufte is intentionally one full Look with the canonical-only
|
|
154
|
+
input `look:tufte`; the ambiguous bare `tufte` input and duplicate
|
|
155
|
+
`palette:tufte` are not registered. The distinct `tufte-dark` palette is
|
|
156
|
+
unchanged. Use the stable `crisp` input for the default Look; `default` is not
|
|
157
|
+
accepted as a Style name.
|
|
158
|
+
|
|
159
|
+
## The contract you get for free
|
|
160
|
+
|
|
161
|
+
- **Any Look × any Palette.** Your `colors` are *defaults*: anything the user
|
|
162
|
+
sets via `RenderOptions` colors or Mermaid `themeVariables` wins. The full
|
|
163
|
+
precedence is one line: `defaults < style stack (left→right) <
|
|
164
|
+
themeVariables < explicit color options`.
|
|
165
|
+
- **Determinism.** All stochastic marks are seeded
|
|
166
|
+
`hash(options.seed, stableNodeId, substream)`. Source, resolved options,
|
|
167
|
+
frozen registry/capability/resource snapshots, runtime contract and seed fix
|
|
168
|
+
the bytes. `seed` re-rolls wobble without moving layout. Opt-in caller or
|
|
169
|
+
system fonts make PNG provenance `host-dependent`; the logical request
|
|
170
|
+
receipt is not a final-byte digest.
|
|
171
|
+
- **Semantics survive the graphical pipeline.** Markers, hit geometry and
|
|
172
|
+
strict-security guarantees pass through every backend. SVG retains
|
|
173
|
+
inspectable `class`/`data-*` identity and ARIA; PNG rasterizes the same
|
|
174
|
+
secured geometry and appearance but cannot embed DOM attributes in pixels.
|
|
175
|
+
Text is drawn last, never perturbed, with a page-colored halo (labels stay
|
|
176
|
+
legible on any fill).
|
|
177
|
+
- **Crisp is the canonical default.** `style: 'crisp'` (or unset) selects the
|
|
178
|
+
precise default backend.
|
|
179
|
+
|
|
180
|
+
## Field reference
|
|
181
|
+
|
|
182
|
+
<!-- BEGIN GENERATED STYLE SPEC FIELDS -->
|
|
183
|
+
| Group | Field | Type / values | Meaning |
|
|
184
|
+
|---|---|---|---|
|
|
185
|
+
| metadata | `formatVersion` | `1` | Persisted wire-format version. Optional on input and normalized to 1 on output. |
|
|
186
|
+
| metadata | `$schema` | `string` | Optional JSON Schema pointer for file-backed styles; ignored while rendering. |
|
|
187
|
+
| metadata | `name` | `string` | Canonical look:name or palette:name identity; required only when registering a style. |
|
|
188
|
+
| metadata | `blurb` | `string` | Short human-readable description used by discovery surfaces. |
|
|
189
|
+
| palette | `colors` | object: `bg`, `fg`, `line`, `accent`, `muted`, `surface`, `border` | Partial palette of safe, non-fetching CSS color tokens. |
|
|
190
|
+
| typography | `font` | `string` | Safe, non-fetching CSS font family or stack; the rendering environment supplies the font face. |
|
|
191
|
+
| roles | `roles` | object: partial records keyed by exact-style `SceneRole` | Partial semantic SceneRole defaults. Family-authored styling remains authoritative. |
|
|
192
|
+
| policy | `semanticSlots` | `Record<string, RoleStyleSpec>` | Named brand-neutral role-style slots selected by semantic bindings. |
|
|
193
|
+
| policy | `bindings` | `SemanticBinding[]` | Ordered equality bindings from authored/domain meaning to semantic slots. |
|
|
194
|
+
| policy | `constraints` | `BrandConstraint[]` | Closed inspect-only brand constraints with warn or error actions. |
|
|
195
|
+
| stroke | `stroke` | `crisp` \| `jittered` \| `freehand` | Stroke treatment; crisp is the default renderer. |
|
|
196
|
+
| stroke | `roughness` | `number`; minimum 0; maximum 10 | Rough.js stroke irregularity. |
|
|
197
|
+
| stroke | `bowing` | `number`; minimum 0; maximum 10 | Rough.js line bowing. |
|
|
198
|
+
| stroke | `passes` | `integer`; minimum 1; maximum 8 | Number of sketch strokes; 1 is single-pass and 2 is the usual double stroke. |
|
|
199
|
+
| stroke | `strokeWidth` | `number`; greater than 0; maximum 20 | Base stroke width in SVG user units. |
|
|
200
|
+
| fill | `fill` | `none` \| `hachure` \| `solid` \| `wash` | Fill policy for rough/hybrid rendering; none and solid require a final stack that activates one of those backends. |
|
|
201
|
+
| fill | `hachureAngle` | `number`; minimum -360; maximum 360 | Hachure line angle in degrees; requires fill hachure in the final stack. |
|
|
202
|
+
| fill | `hachureGap` | `number`; greater than 0; maximum 100 | Gap between hachure lines; requires fill hachure in the final stack. |
|
|
203
|
+
| fill | `fillWeight` | `number`; greater than 0; maximum 20 | Hachure line weight; requires fill hachure in the final stack. |
|
|
204
|
+
| fill | `washOpacity` | `number`; minimum 0; maximum 1 | Watercolor glaze opacity; requires fill wash in the final stack. |
|
|
205
|
+
| fill | `washEdge` | `number`; minimum 0; maximum 1 | Watercolor edge-darkening opacity; requires fill wash in the final stack. |
|
|
206
|
+
| page | `backdrop` | `plain` \| `paper-ruled` \| `grid` | Flat page furniture drawn behind the diagram. |
|
|
207
|
+
| advisory | `intent` | `premium` \| `draft` \| `lofi` | Advisory intent metadata for pickers and quality tooling. |
|
|
208
|
+
| advisory | `mono` | `boolean` | Advisory monochrome contract: express tone through shading and weight. |
|
|
209
|
+
<!-- END GENERATED STYLE SPEC FIELDS -->
|
|
210
|
+
|
|
211
|
+
The field table is generated from the same descriptors that define the
|
|
212
|
+
TypeScript type, runtime validator, and published JSON Schema. SVG declares a
|
|
213
|
+
style's `font`; PNG resolves verified bundled faces plus caller-supplied
|
|
214
|
+
`fontDirs` and then uses its documented fallback. See
|
|
215
|
+
[Fonts in custom styles](./custom-fonts.md).
|
|
216
|
+
|
|
217
|
+
JSON style records are first-class: `validateStyleSpec(json)` returns a list
|
|
218
|
+
of problems (`[]` means the value is structurally admissible as a fragment;
|
|
219
|
+
final-stack applicability is checked during resolution). Renderer-consumed
|
|
220
|
+
fields are declarative-only and cannot carry markup, scripts, or fetching URLs,
|
|
221
|
+
so validated records are safe to load from files and prompts and compatible with
|
|
222
|
+
`security: 'strict'`.
|
|
223
|
+
`$schema` is ignored metadata and may, intentionally, be a schema URL.
|
|
224
|
+
|
|
225
|
+
For file-backed styles, use the schema at
|
|
226
|
+
[`docs/schemas/style-spec.schema.json`](./schemas/style-spec.schema.json), also
|
|
227
|
+
published as `agentic-mermaid/style-spec.schema.json`. The
|
|
228
|
+
[`custom style cookbook`](./custom-style-cookbook.md) has complete JSON files,
|
|
229
|
+
screenshots, and CLI commands. For font resolution across SVG, PNG, browser,
|
|
230
|
+
and MCP surfaces, see [Fonts in custom styles](./custom-fonts.md).
|
|
231
|
+
|
|
232
|
+
JSON Schema provides editor discovery plus all structurally expressible type,
|
|
233
|
+
enum, and numeric constraints. Runtime `validateStyleSpec` is the normative
|
|
234
|
+
admission boundary and additionally enforces the recursive safe-color grammar;
|
|
235
|
+
the generated schema marks those leaves with
|
|
236
|
+
`x-agentic-mermaid-runtime-validator: safeCssColor` instead of pretending a
|
|
237
|
+
portable regular expression can validate nested CSS color functions.
|
|
238
|
+
|
|
239
|
+
Only a genuinely **new capability** (a new fill algorithm, compositor, or
|
|
240
|
+
layout-aware dialect) justifies code. Trusted hosts can implement and register
|
|
241
|
+
a namespaced `StyleBackend`; selection is an in-process `HostBackendPolicy`,
|
|
242
|
+
not serializable Style data or a CLI/MCP/editor field. Keep the determinism rule
|
|
243
|
+
(seed in, bytes out). Backends never dispatch on diagram family — they see
|
|
244
|
+
semantic scene marks and paint channels. A registered backend also declares
|
|
245
|
+
feature/operation-level `PrimitiveCapabilityClaim`s for every core Scene
|
|
246
|
+
primitive; registration rejects empty, duplicate, or cross-target claims so an
|
|
247
|
+
extension cannot imply support it has not described. Registration then executes
|
|
248
|
+
a frozen document fixture plus one exact witness for each declared first-party
|
|
249
|
+
core primitive/feature/operation claim. It rejects a backend that varies
|
|
250
|
+
identical `drawNode`/`render` calls, emits unsafe or malformed SVG, drops the
|
|
251
|
+
document sentinels, or fails any claim-keyed witness. Namespaced extension claims
|
|
252
|
+
with no core witness remain explicitly `unverified-extension`.
|
|
253
|
+
`runBackendConformance` exposes the immutable report and
|
|
254
|
+
`knownBackendDescriptors()` retains it for discovery.
|
|
255
|
+
|
|
256
|
+
Host backends must opt into both the versioned core lifecycle and Scene wire
|
|
257
|
+
contract explicitly; no built-in compatibility default is borrowed by
|
|
258
|
+
`registerBackend`:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
registerBackend(backend, {
|
|
262
|
+
version: '1.0.0',
|
|
263
|
+
compatibility: { core: '^0.2.0', scene: '^2.0.0' },
|
|
264
|
+
provenance: { owner: 'acme-diagrams', source: 'application-startup' },
|
|
265
|
+
})
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The host validates this identity and both ranges before executing any
|
|
269
|
+
conformance witness. Every external `FamilyDescriptor` declares a compatible
|
|
270
|
+
`core` range; one that provides `lowerScene` must additionally include
|
|
271
|
+
`scene: '^2.0.0'` in `identity.compatibility`. A family that remains
|
|
272
|
+
source-only or uses the direct-SVG extension fallback does not consume Scene.
|
|
273
|
+
|
|
274
|
+
Every external family supplies one bounded canonical `example`.
|
|
275
|
+
`registerFamily` stages the immutable descriptor and runs every declared
|
|
276
|
+
native capability twice through the canonical parse/serialize, layout, strict
|
|
277
|
+
SVG, portable PNG pre-raster, terminal, Scene and verification paths. It rolls
|
|
278
|
+
the candidate back if a hook throws, changes output between runs, mutates the
|
|
279
|
+
registry reentrantly, emits an invalid raster envelope, or fails to witness a
|
|
280
|
+
positive Scene role/primitive declaration. `getFamilyConformanceReport`
|
|
281
|
+
exposes the complete immutable evidence. The bounded `am capabilities --json`
|
|
282
|
+
discovery projection keeps every declared state, status, and diagnostic but
|
|
283
|
+
omits stable witness identifiers; a declaration without a passed witness is
|
|
284
|
+
never projected as `native`.
|
|
285
|
+
|
|
286
|
+
Each admission report is deliberately bounded. Backend admission directly proves SVG behavior
|
|
287
|
+
for the named document and exact core claim witnesses; it does not certify a
|
|
288
|
+
namespaced extension claim or family-scale visual, bounds, hit-testing, and
|
|
289
|
+
performance behavior. Extension authors still need those tests for what they
|
|
290
|
+
advertise. PNG is not a second backend conformance target: it inherits the
|
|
291
|
+
admitted secured SVG through canonical rasterizers, whose own parity and
|
|
292
|
+
color-profile suites are separate.
|
|
293
|
+
|
|
294
|
+
## The quality rubric — what makes a style GOOD
|
|
295
|
+
|
|
296
|
+
The mechanics above are the easy part. Across the built-in catalog, the
|
|
297
|
+
difference between a cheap style and a premium one was never the wiring —
|
|
298
|
+
always the judgement:
|
|
299
|
+
|
|
300
|
+
1. **Source it.** Start from real references (photos, the actual product),
|
|
301
|
+
not memory. Name the reference in your blurb. Naive-from-memory looks cheap.
|
|
302
|
+
2. **Premium defaults.** No pure `#000`/`#fff` — warm or desaturated neutrals.
|
|
303
|
+
A real typeface, not a bare fallback. Refined line weights. ONE accent per
|
|
304
|
+
figure unless the aesthetic is intrinsically polychrome.
|
|
305
|
+
3. **Monochrome discipline** (`mono: true`): tone comes from hatch density and
|
|
306
|
+
line weight, never from extra hues. And don't shade a region people write
|
|
307
|
+
inside — hand-drawn boxes stay open (`fill: 'none'`).
|
|
308
|
+
4. **Legibility gates.** Text must clear WCAG 4.5:1 against the page the halo
|
|
309
|
+
reveals; non-text strokes 3:1. Hairline + serif styles collapse at
|
|
310
|
+
thumbnail size — declare `intent` honestly and test small.
|
|
311
|
+
5. **Diagram-fit, not illustration-fit.** Outline + flat fill beats texture +
|
|
312
|
+
depth; edges are first-class; the style must read at a glance from stroke +
|
|
313
|
+
palette alone.
|
|
314
|
+
6. **Fidelity, not caricature.** Capture the 2–3 signatures that make the
|
|
315
|
+
reference recognisable and stop. Judge the result against its declared
|
|
316
|
+
`intent` — a lo-fi mock that looks polished has failed, exactly like a
|
|
317
|
+
premium style that looks cheap.
|
|
318
|
+
|
|
319
|
+
Worked example (the clearest lesson from the prototype): "Making Software"
|
|
320
|
+
*before* — pure black on white, generic serif, blue everywhere — read as
|
|
321
|
+
cheap. *After* — warm neutrals (`#fafaf9`/`#0c0a09`), Fraunces, hairlines,
|
|
322
|
+
rounded corners, one blue accent — read as premium. Same mechanics, different
|
|
323
|
+
judgement.
|
|
324
|
+
|
|
325
|
+
## Verify before you ship
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
bun run style:audit # Style + Palette contract across families
|
|
329
|
+
bun test src/__tests__/styled-output.test.ts --timeout 30000 # determinism + goldens + composition
|
|
330
|
+
bun test src/__tests__/scene-fidelity.test.ts # semantic/backend agreement
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Then render your style across every registered diagram type and *look at it small*
|
|
334
|
+
— the poster/contact-sheet harness in `scripts/sketch-prototype/` is the
|
|
335
|
+
visual-review surface. A style ships when it is distinctive at a glance,
|
|
336
|
+
legible at cell size, and honest to its intent.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# SVG semantic identity, references, and accessibility
|
|
2
|
+
|
|
3
|
+
Agentic Mermaid exposes one contract across every built-in family. Consumers should inspect these attributes instead of inferring meaning from geometry or renderer-specific CSS selectors.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
|
|
7
|
+
Every source-semantic Scene mark emits (nodes, relations, groups, participants,
|
|
8
|
+
class/ER members, chart data marks, sections/tasks/periods/events, and titles):
|
|
9
|
+
|
|
10
|
+
- `data-id`: deterministic and unique within one SVG;
|
|
11
|
+
- `data-role`: a closed `SceneRole` value;
|
|
12
|
+
- `data-from` / `data-to`: normalized source endpoints for relations;
|
|
13
|
+
- authored Mermaid class tokens on `class` where that family supports classes.
|
|
14
|
+
|
|
15
|
+
Source-authored IDs win. Anonymous marks receive semantic IDs derived from labels, endpoints, series/category identity, and a deterministic occurrence suffix. The public Scene model carries the same information as `SvgSemanticIdentity`; the DOM attributes and typed value are generated together. Layout furniture such as grids, ticks, halos, and duplicate visual labels retains typed Scene identity but does not emit a second DOM identity.
|
|
16
|
+
|
|
17
|
+
`data-id` is source-facing identity and is intentionally **not** changed by `RenderOptions.idPrefix`.
|
|
18
|
+
|
|
19
|
+
## Multi-diagram references
|
|
20
|
+
|
|
21
|
+
`idPrefix` namespaces local SVG `id` declarations and every supported local reference:
|
|
22
|
+
|
|
23
|
+
- `url(#…)` in markers, filters, clip paths, masks, gradients, and paints;
|
|
24
|
+
- `href="#…"` and `xlink:href="#…"`;
|
|
25
|
+
- every token in `aria-labelledby` and `aria-describedby`.
|
|
26
|
+
|
|
27
|
+
References are rewritten only when their target is declared in the same SVG. This prevents accidental rewriting of label text. Two instances rendered with distinct prefixes have no colliding declarations, while their semantic `data-id` values remain comparable. Prefixes accept ASCII letters, digits, `_`, `-`, `.`, and `:`; unsafe attribute/URL characters fail fast.
|
|
28
|
+
|
|
29
|
+
## Relation accessibility
|
|
30
|
+
|
|
31
|
+
Typed edges, messages, and relationships emit:
|
|
32
|
+
|
|
33
|
+
```xml
|
|
34
|
+
<… data-role="edge" data-from="API" data-to="DB"
|
|
35
|
+
role="graphics-symbol" aria-roledescription="relation"
|
|
36
|
+
aria-label="API to DB: reads" … />
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The Scene node carries `SvgSemanticAccessibility` and `SvgRelationSemantics` with the same endpoint and label values. Decorative label/halo marks do not repeat the relation ARIA node.
|
|
40
|
+
|
|
41
|
+
## Palette contract
|
|
42
|
+
|
|
43
|
+
For every concrete built-in palette, rendering deterministically raises insufficient colors toward the palette foreground:
|
|
44
|
+
|
|
45
|
+
- normal text roles: WCAG 2.x contrast **≥ 4.5:1**;
|
|
46
|
+
- faint informational text: **≥ 3:1**;
|
|
47
|
+
- relation lines and markers: **≥ 3:1** against the page background.
|
|
48
|
+
|
|
49
|
+
Colors that already pass are byte-preserved. Runtime CSS variables cannot be certified without their eventual background and therefore pass through unchanged.
|