agentic-mermaid 0.1.1 → 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 +155 -91
- package/CHANGELOG.md +163 -4
- package/Instructions_for_agents.md +10 -10
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +31 -38
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/assets/hero.png +0 -0
- package/dist/agent-core.d.ts +463 -0
- package/dist/agent-core.js +397 -0
- package/dist/agent.d.ts +73 -1868
- package/dist/agent.js +336 -37325
- package/dist/agentic-mermaid-mcp.js +7 -38399
- package/dist/am.js +33626 -40446
- package/dist/chunk-2P5FKUQC.js +3485 -0
- package/dist/chunk-2S3VSS6I.js +149 -0
- package/dist/chunk-2SILJDW2.js +37 -0
- package/dist/chunk-BJYMBT4I.js +61963 -0
- package/dist/chunk-FDM43XU7.js +615 -0
- package/dist/chunk-MYPDVZCO.js +4702 -0
- package/dist/index-DWPa2MJs.d.ts +6780 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +310 -33014
- package/dist/mcp-cli-5J47WEY3.js +17 -0
- package/docs/README.md +39 -31
- package/docs/agent-api-cookbook.md +44 -35
- package/docs/agent-mutation-policy.md +1 -1
- package/docs/agent-workflow-examples.md +2 -2
- package/docs/api.md +284 -77
- package/docs/ascii.md +26 -16
- package/docs/code-mode-impact.md +214 -0
- package/docs/comparison.md +23 -11
- package/docs/computational-aesthetics-prototype-plan.md +252 -0
- package/docs/config.md +26 -10
- package/docs/custom-fonts.md +265 -0
- package/docs/custom-style-cookbook.md +130 -9
- package/docs/diagram-families.md +48 -23
- package/docs/features.md +59 -52
- package/docs/fork-differences.md +6 -3
- package/docs/getting-started.md +23 -16
- package/docs/issue-derived-test-cases.md +29 -22
- package/docs/mcp-code-mode-rationale.md +13 -6
- package/docs/mcp-http-transport.md +20 -13
- package/docs/mermaid-layout-complaints.md +66 -52
- package/docs/mutation-testing.md +145 -66
- package/docs/quality.md +23 -14
- package/docs/react.md +2 -2
- package/docs/schemas/style-spec.schema.json +1221 -30
- package/docs/style-authoring.md +241 -38
- package/docs/svg-semantic-contract.md +49 -0
- package/docs/testing-strategy.md +166 -58
- package/docs/theming.md +19 -32
- package/examples/styles/catalog.json +54 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
- package/examples/styles/cupertino-prototype.mmd +10 -0
- package/examples/styles/cupertino-prototype.style.json +63 -0
- package/examples/styles/mid-century-report.style.json +2 -2
- package/examples/styles/star-chart-atlas.style.json +2 -1
- package/examples/styles/transit-route-map.style.json +2 -2
- package/examples/styles/vercel-inspired-prototype.mmd +6 -0
- package/examples/styles/vercel-inspired-prototype.style.json +87 -0
- package/package.json +85 -45
- package/server.json +2 -2
- package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
- package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
- package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
- package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
- package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
- package/TODO.md +0 -684
- package/dist/index-wjRjbsP-.d.ts +0 -1102
- package/docs/assets/improvements/box-start-connector.png +0 -0
- package/docs/assets/improvements/comment-dropped-lint.png +0 -0
- package/docs/assets/improvements/er-content-loss.png +0 -0
- package/docs/assets/improvements/fan-in-trunks.png +0 -0
- package/docs/assets/improvements/gantt-family.png +0 -0
- package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
- package/docs/assets/improvements/gantt-verify-seam.png +0 -0
- package/docs/assets/improvements/subgraph-container-edges.png +0 -0
- package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
- package/docs/assets/improvements/wrapper-init-directive.png +0 -0
- package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
- package/docs/assets/style-cookbook/mid-century-report.png +0 -0
- package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
- package/docs/assets/style-cookbook/transit-route-map.png +0 -0
- package/docs/contributing/adding-diagram-types.md +0 -144
- package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
- package/docs/contributing/diagram-family-citizenship.md +0 -82
- package/docs/contributing/harvesting-upstream-tests.md +0 -126
- package/docs/contributing/lessons-learned.md +0 -276
- package/docs/contributing/releasing.md +0 -82
- package/docs/contributing/visual-review-evidence.md +0 -53
- package/docs/design/families/README.md +0 -26
- package/docs/design/families/architecture-beta.md +0 -48
- package/docs/design/families/flowchart-parser-conformance.md +0 -22
- package/docs/design/families/gantt-research.md +0 -137
- package/docs/design/families/gantt.md +0 -329
- package/docs/design/families/journey-agentic-cards-before.png +0 -0
- package/docs/design/families/journey-experience-curve-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
- package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
- package/docs/design/families/journey-migration-parity.md +0 -439
- package/docs/design/families/journey-section-overlap-after.png +0 -0
- package/docs/design/families/journey-section-overlap-before.png +0 -0
- package/docs/design/families/journey-usage-research.md +0 -423
- package/docs/design/families/journey.md +0 -153
- package/docs/design/families/xychart.md +0 -85
- package/docs/design/system/README.md +0 -45
- package/docs/design/system/abstraction-audit.md +0 -224
- package/docs/design/system/abstraction-recommendations.md +0 -655
- package/docs/design/system/architecture.mmd +0 -29
- package/docs/design/system/architecture.svg +0 -137
- package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
- package/docs/design/system/layout-rubric.md +0 -138
- package/docs/design/system/route-contracts.md +0 -848
- package/docs/design/system/source-preservation-ladder.md +0 -52
- package/docs/design/system/styles-rollout.md +0 -216
- package/docs/design/system/ugly-layouts.md +0 -119
- package/docs/layout-characterization/README.md +0 -245
- package/docs/layout-characterization/contact-sheet-families.md +0 -498
- package/docs/layout-characterization/contact-sheet.md +0 -638
- package/docs/layout-characterization/properties.md +0 -281
- package/docs/layout-characterization/visual-quality.md +0 -160
- package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
- package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
- package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
- package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
- package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/journey.svg +0 -98
- package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
- package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
- package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
- package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
- package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
- package/docs/project/divergences.md +0 -98
- package/docs/project/dogfooding-docs-strategy.md +0 -66
- package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
- package/docs/project/lessons-learned.md +0 -1019
- package/docs/project/remove-role-styling-plan.md +0 -176
- package/examples/agent-improve-auth-flow.ts +0 -206
- package/examples/agent-loop.ts +0 -53
- package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
- package/llms.txt +0 -104
- package/skill-evals/README.md +0 -77
- package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
- package/skill-evals/fixtures/class-payment/input.mmd +0 -4
- package/skill-evals/fixtures/er-orders/input.mmd +0 -10
- package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
- package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
- package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
- package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
- package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
- package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
- package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
- package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
- package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
- package/skill-evals/shared-benchmark.json +0 -510
- package/src/agent/analyze.ts +0 -157
- package/src/agent/apply.ts +0 -146
- package/src/agent/architecture-body.ts +0 -458
- package/src/agent/certificates.ts +0 -110
- package/src/agent/class-body.ts +0 -319
- package/src/agent/core.ts +0 -207
- package/src/agent/create.ts +0 -106
- package/src/agent/describe.ts +0 -368
- package/src/agent/er-body.ts +0 -224
- package/src/agent/facts.ts +0 -360
- package/src/agent/families-builtin.ts +0 -920
- package/src/agent/families.ts +0 -232
- package/src/agent/family-layouts.ts +0 -832
- package/src/agent/flowchart-body.ts +0 -372
- package/src/agent/flowchart-unsupported.ts +0 -243
- package/src/agent/font-coverage.ts +0 -288
- package/src/agent/gantt-body.ts +0 -495
- package/src/agent/index.ts +0 -10
- package/src/agent/journey-body.ts +0 -374
- package/src/agent/label-metrics.ts +0 -28
- package/src/agent/layout-to-rendered.ts +0 -63
- package/src/agent/mutate.ts +0 -97
- package/src/agent/mutation-ops.ts +0 -51
- package/src/agent/op-schema.ts +0 -443
- package/src/agent/parse.ts +0 -223
- package/src/agent/pie-body.ts +0 -270
- package/src/agent/png.ts +0 -171
- package/src/agent/quadrant-body.ts +0 -336
- package/src/agent/quality.ts +0 -363
- package/src/agent/readability-audit.ts +0 -92
- package/src/agent/rendered-route-audit.ts +0 -61
- package/src/agent/sequence-body.ts +0 -345
- package/src/agent/serialize.ts +0 -214
- package/src/agent/state-body.ts +0 -589
- package/src/agent/structural-count.ts +0 -150
- package/src/agent/timeline-body.ts +0 -280
- package/src/agent/trace-log.ts +0 -43
- package/src/agent/types.ts +0 -995
- package/src/agent/verify.ts +0 -603
- package/src/agent/xychart-body.ts +0 -472
- package/src/architecture/config.ts +0 -282
- package/src/architecture/layout.ts +0 -496
- package/src/architecture/parser.ts +0 -375
- package/src/architecture/renderer.ts +0 -611
- package/src/architecture/types.ts +0 -113
- package/src/ascii/ansi.ts +0 -423
- package/src/ascii/architecture.ts +0 -210
- package/src/ascii/canvas.ts +0 -443
- package/src/ascii/class-diagram.ts +0 -697
- package/src/ascii/converter.ts +0 -475
- package/src/ascii/draw.ts +0 -1693
- package/src/ascii/edge-bundling.ts +0 -328
- package/src/ascii/edge-routing.ts +0 -330
- package/src/ascii/er-diagram.ts +0 -439
- package/src/ascii/gantt.ts +0 -276
- package/src/ascii/grid.ts +0 -747
- package/src/ascii/index.ts +0 -196
- package/src/ascii/journey.ts +0 -169
- package/src/ascii/meta.ts +0 -244
- package/src/ascii/multiline-utils.ts +0 -80
- package/src/ascii/pathfinder.ts +0 -301
- package/src/ascii/pie.ts +0 -67
- package/src/ascii/quadrant.ts +0 -158
- package/src/ascii/reverse.ts +0 -216
- package/src/ascii/sequence.ts +0 -499
- package/src/ascii/shapes/circle.ts +0 -27
- package/src/ascii/shapes/corners.ts +0 -143
- package/src/ascii/shapes/diamond.ts +0 -27
- package/src/ascii/shapes/hexagon.ts +0 -27
- package/src/ascii/shapes/index.ts +0 -106
- package/src/ascii/shapes/rectangle.ts +0 -168
- package/src/ascii/shapes/rounded.ts +0 -27
- package/src/ascii/shapes/special.ts +0 -332
- package/src/ascii/shapes/stadium.ts +0 -110
- package/src/ascii/shapes/state.ts +0 -192
- package/src/ascii/shapes/types.ts +0 -73
- package/src/ascii/timeline.ts +0 -141
- package/src/ascii/types.ts +0 -312
- package/src/ascii/validate.ts +0 -120
- package/src/ascii/width.ts +0 -81
- package/src/ascii/wrap.ts +0 -131
- package/src/ascii/xychart.ts +0 -919
- package/src/browser.ts +0 -29
- package/src/class/layout.ts +0 -240
- package/src/class/parser.ts +0 -332
- package/src/class/renderer.ts +0 -638
- package/src/class/types.ts +0 -129
- package/src/cli/agent-instructions.ts +0 -1
- package/src/cli/am-bin.ts +0 -6
- package/src/cli/exit-codes.ts +0 -18
- package/src/cli/index.ts +0 -1132
- package/src/cli/init-agent.ts +0 -147
- package/src/cli/run-entrypoint.ts +0 -10
- package/src/color-resolver.ts +0 -152
- package/src/elk-instance.ts +0 -114
- package/src/er/layout.ts +0 -187
- package/src/er/parser.ts +0 -226
- package/src/er/renderer.ts +0 -792
- package/src/er/types.ts +0 -99
- package/src/family-rubric.ts +0 -425
- package/src/font-manifest.ts +0 -31
- package/src/gantt/layout.ts +0 -365
- package/src/gantt/parser.ts +0 -282
- package/src/gantt/pipeline.ts +0 -27
- package/src/gantt/renderer.ts +0 -414
- package/src/gantt/schedule.ts +0 -513
- package/src/gantt/types.ts +0 -255
- package/src/index.ts +0 -368
- package/src/journey/layout.ts +0 -554
- package/src/journey/parse-core.ts +0 -251
- package/src/journey/parser.ts +0 -101
- package/src/journey/renderer.ts +0 -917
- package/src/journey/types.ts +0 -144
- package/src/layout/geometry.ts +0 -182
- package/src/layout/pass.ts +0 -142
- package/src/layout/passes/index.ts +0 -3916
- package/src/layout-engine.ts +0 -1565
- package/src/layout-rubric.ts +0 -659
- package/src/mcp/artifacts.ts +0 -137
- package/src/mcp/dynamic-harness.ts +0 -31
- package/src/mcp/dynamic-modules.d.ts +0 -7
- package/src/mcp/facade.ts +0 -608
- package/src/mcp/harness-runtime.ts +0 -203
- package/src/mcp/hosted-server.ts +0 -468
- package/src/mcp/mcp-bin.ts +0 -3
- package/src/mcp/mcp-cli.ts +0 -82
- package/src/mcp/protocol.ts +0 -13
- package/src/mcp/sandbox.ts +0 -195
- package/src/mcp/sdk-decl.ts +0 -391
- package/src/mcp/server.ts +0 -362
- package/src/mcp/tool-surface.ts +0 -146
- package/src/mermaid-source.ts +0 -655
- package/src/multiline-utils.ts +0 -228
- package/src/parser.ts +0 -1031
- package/src/pie/layout.ts +0 -168
- package/src/pie/parser.ts +0 -136
- package/src/pie/renderer.ts +0 -234
- package/src/pie/types.ts +0 -82
- package/src/quadrant/layout.ts +0 -191
- package/src/quadrant/parser.ts +0 -183
- package/src/quadrant/renderer.ts +0 -322
- package/src/quadrant/types.ts +0 -113
- package/src/render-family-hooks.ts +0 -244
- package/src/renderer.ts +0 -1099
- package/src/route-contracts.ts +0 -2723
- package/src/scene/backend.ts +0 -76
- package/src/scene/fidelity.ts +0 -183
- package/src/scene/hybrid-backend.ts +0 -174
- package/src/scene/ir.ts +0 -181
- package/src/scene/marks.ts +0 -127
- package/src/scene/rough-backend.ts +0 -378
- package/src/scene/seed.ts +0 -46
- package/src/scene/style-registry.ts +0 -556
- package/src/sequence/layout.ts +0 -456
- package/src/sequence/parser.ts +0 -255
- package/src/sequence/renderer.ts +0 -604
- package/src/sequence/types.ts +0 -154
- package/src/shape-clipping.ts +0 -351
- package/src/shared/accessibility-directives.ts +0 -22
- package/src/shared/batched.ts +0 -61
- package/src/shared/color-math.ts +0 -93
- package/src/shared/svg-a11y.ts +0 -29
- package/src/shared/syntax-error.ts +0 -17
- package/src/shared/unicode-ranges.ts +0 -135
- package/src/styles.ts +0 -318
- package/src/svg-paths.ts +0 -28
- package/src/text-metrics.ts +0 -248
- package/src/theme.ts +0 -710
- package/src/timeline/layout.ts +0 -381
- package/src/timeline/parser.ts +0 -215
- package/src/timeline/renderer.ts +0 -538
- package/src/timeline/types.ts +0 -105
- package/src/types.ts +0 -462
- package/src/website-worker-modules.d.ts +0 -18
- package/src/xychart/axis-utils.ts +0 -109
- package/src/xychart/colors.ts +0 -124
- package/src/xychart/config.ts +0 -88
- package/src/xychart/layout.ts +0 -656
- package/src/xychart/parser.ts +0 -504
- package/src/xychart/renderer.ts +0 -640
- package/src/xychart/types.ts +0 -282
package/src/text-metrics.ts
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Text Metrics — Variable-width character measurement for SVG layout
|
|
3
|
-
// ============================================================================
|
|
4
|
-
//
|
|
5
|
-
// Provides font-agnostic text width estimation using character class buckets.
|
|
6
|
-
// More accurate than uniform character width for proportional fonts.
|
|
7
|
-
//
|
|
8
|
-
// Width ratios are normalized where 1.0 = average lowercase letter.
|
|
9
|
-
// Final pixel width = sum(charWidths) * fontSize * baseRatio
|
|
10
|
-
//
|
|
11
|
-
// CJK / emoji / combining-mark codepoint range tables live in
|
|
12
|
-
// src/shared/unicode-ranges.ts (shared with the ASCII renderer so both
|
|
13
|
-
// modules agree on which codepoints are wide).
|
|
14
|
-
// ============================================================================
|
|
15
|
-
|
|
16
|
-
import { isWideRange, isZeroWidth, VS_EMOJI, VS_TEXT } from './shared/unicode-ranges.ts'
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Narrow characters - visually thin glyphs.
|
|
20
|
-
* Note: '1' is included because in proportional fonts (like Inter), it's
|
|
21
|
-
* significantly narrower than other digits which use tabular/uniform width.
|
|
22
|
-
*/
|
|
23
|
-
const NARROW_CHARS = new Set(['i', 'l', 't', 'f', 'j', 'I', '1', '!', '|', '.', ',', ':', ';', "'"])
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Wide characters - visually wide glyphs
|
|
27
|
-
*/
|
|
28
|
-
const WIDE_CHARS = new Set(['W', 'M', 'w', 'm', '@', '%'])
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Very wide characters - widest Latin glyphs
|
|
32
|
-
*/
|
|
33
|
-
const VERY_WIDE_CHARS = new Set(['W', 'M'])
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Semi-narrow punctuation - brackets and slashes are narrower than letters
|
|
37
|
-
* but wider than narrow chars like dots/commas
|
|
38
|
-
*/
|
|
39
|
-
const SEMI_NARROW_PUNCT = new Set(['(', ')', '[', ']', '{', '}', '/', '\\', '-', '"', '`'])
|
|
40
|
-
|
|
41
|
-
// Combining-mark / zero-width and CJK-fullwidth checks delegate to the
|
|
42
|
-
// shared range tables so the ASCII renderer and the SVG metrics module
|
|
43
|
-
// can never drift on what counts as wide / zero-width.
|
|
44
|
-
function isCombiningMark(code: number): boolean {
|
|
45
|
-
return isZeroWidth(code) || code === VS_TEXT || code === VS_EMOJI
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function isFullwidth(code: number): boolean {
|
|
49
|
-
return isWideRange(code)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Regex for emoji detection using Unicode property escapes.
|
|
54
|
-
* Uses Emoji_Presentation and Extended_Pictographic (not just Emoji)
|
|
55
|
-
* because \p{Emoji} includes digits and # which we don't want as fullwidth.
|
|
56
|
-
*/
|
|
57
|
-
const EMOJI_REGEX = /\p{Emoji_Presentation}|\p{Extended_Pictographic}/u
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Check if a character is an emoji (fullwidth)
|
|
61
|
-
*/
|
|
62
|
-
function isEmoji(char: string): boolean {
|
|
63
|
-
return EMOJI_REGEX.test(char)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Get the relative width of a single character.
|
|
68
|
-
*
|
|
69
|
-
* Returns a normalized width ratio where:
|
|
70
|
-
* - 0.0 = zero-width (combining marks)
|
|
71
|
-
* - 0.3 = space
|
|
72
|
-
* - 0.4 = narrow (i, l, t, f, j, I, 1)
|
|
73
|
-
* - 0.8 = semi-narrow (r)
|
|
74
|
-
* - 1.0 = average lowercase
|
|
75
|
-
* - 1.2 = wide lowercase / uppercase
|
|
76
|
-
* - 1.5 = very wide (W, M)
|
|
77
|
-
* - 2.0 = fullwidth (CJK, emoji)
|
|
78
|
-
*/
|
|
79
|
-
export function getCharWidth(char: string): number {
|
|
80
|
-
const code = char.codePointAt(0)
|
|
81
|
-
if (code === undefined) return 0
|
|
82
|
-
|
|
83
|
-
// Zero-width: combining diacritical marks
|
|
84
|
-
if (isCombiningMark(code)) return 0
|
|
85
|
-
|
|
86
|
-
// Fullwidth: CJK, emoji
|
|
87
|
-
if (isFullwidth(code) || isEmoji(char)) return 2.0
|
|
88
|
-
|
|
89
|
-
// Space
|
|
90
|
-
if (char === ' ') return 0.3
|
|
91
|
-
|
|
92
|
-
// Very wide Latin
|
|
93
|
-
if (VERY_WIDE_CHARS.has(char)) return 1.5
|
|
94
|
-
|
|
95
|
-
// Wide Latin
|
|
96
|
-
if (WIDE_CHARS.has(char)) return 1.2
|
|
97
|
-
|
|
98
|
-
// Narrow Latin
|
|
99
|
-
if (NARROW_CHARS.has(char)) return 0.4
|
|
100
|
-
|
|
101
|
-
// Semi-narrow punctuation (brackets, slashes, hyphens)
|
|
102
|
-
if (SEMI_NARROW_PUNCT.has(char)) return 0.5
|
|
103
|
-
|
|
104
|
-
// Semi-narrow letter
|
|
105
|
-
if (char === 'r') return 0.8
|
|
106
|
-
|
|
107
|
-
// Uppercase (slightly wider than lowercase on average)
|
|
108
|
-
if (code >= 65 && code <= 90) return 1.2
|
|
109
|
-
|
|
110
|
-
// Digits (uniform width in most fonts)
|
|
111
|
-
if (code >= 48 && code <= 57) return 1.0
|
|
112
|
-
|
|
113
|
-
// Default: average lowercase width
|
|
114
|
-
return 1.0
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Measure the pixel width of a text string.
|
|
119
|
-
*
|
|
120
|
-
* Uses character class buckets for more accurate width estimation
|
|
121
|
-
* than uniform character width assumptions.
|
|
122
|
-
*
|
|
123
|
-
* @param text - The text to measure
|
|
124
|
-
* @param fontSize - Font size in pixels
|
|
125
|
-
* @param fontWeight - Font weight (affects width slightly)
|
|
126
|
-
* @returns Estimated width in pixels
|
|
127
|
-
*/
|
|
128
|
-
export interface TextMeasurementContract {
|
|
129
|
-
version: 1
|
|
130
|
-
unit: 'px'
|
|
131
|
-
fontCalibration: 'Inter-compatible proportional estimate'
|
|
132
|
-
wideCodepoints: 'src/shared/unicode-ranges.ts'
|
|
133
|
-
emojiDetection: 'Emoji_Presentation-or-Extended_Pictographic'
|
|
134
|
-
ambiguousWidth: 'single-cell'
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export const TEXT_MEASUREMENT_CONTRACT: TextMeasurementContract = {
|
|
138
|
-
version: 1,
|
|
139
|
-
unit: 'px',
|
|
140
|
-
fontCalibration: 'Inter-compatible proportional estimate',
|
|
141
|
-
wideCodepoints: 'src/shared/unicode-ranges.ts',
|
|
142
|
-
emojiDetection: 'Emoji_Presentation-or-Extended_Pictographic',
|
|
143
|
-
ambiguousWidth: 'single-cell',
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export interface TextMeasurementInput {
|
|
147
|
-
text: string
|
|
148
|
-
fontSize: number
|
|
149
|
-
fontWeight: number
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export interface TextMeasurementResult {
|
|
153
|
-
contract: TextMeasurementContract
|
|
154
|
-
width: number
|
|
155
|
-
charWidthUnits: number
|
|
156
|
-
codePointCount: number
|
|
157
|
-
fontSize: number
|
|
158
|
-
fontWeight: number
|
|
159
|
-
baseRatio: number
|
|
160
|
-
minPadding: number
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function textBaseRatio(fontWeight: number): number {
|
|
164
|
-
// Base ratio calibrated for Inter font family.
|
|
165
|
-
// Heavier weights are slightly wider.
|
|
166
|
-
// Includes the +0.02 buffer that prevents edge truncation at line ends.
|
|
167
|
-
return fontWeight >= 600 ? 0.60 : fontWeight >= 500 ? 0.57 : 0.54
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export function measureText(input: TextMeasurementInput): TextMeasurementResult {
|
|
171
|
-
const baseRatio = textBaseRatio(input.fontWeight)
|
|
172
|
-
let charWidthUnits = 0
|
|
173
|
-
let codePointCount = 0
|
|
174
|
-
// Iterate over code points (handles surrogate pairs for emoji/CJK)
|
|
175
|
-
for (const char of input.text) {
|
|
176
|
-
charWidthUnits += getCharWidth(char)
|
|
177
|
-
codePointCount++
|
|
178
|
-
}
|
|
179
|
-
const minPadding = input.fontSize * 0.15
|
|
180
|
-
return {
|
|
181
|
-
contract: TEXT_MEASUREMENT_CONTRACT,
|
|
182
|
-
width: charWidthUnits * input.fontSize * baseRatio + minPadding,
|
|
183
|
-
charWidthUnits,
|
|
184
|
-
codePointCount,
|
|
185
|
-
fontSize: input.fontSize,
|
|
186
|
-
fontWeight: input.fontWeight,
|
|
187
|
-
baseRatio,
|
|
188
|
-
minPadding,
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export function measureTextWidth(text: string, fontSize: number, fontWeight: number): number {
|
|
193
|
-
return measureText({ text, fontSize, fontWeight }).width
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// ============================================================================
|
|
197
|
-
// Multi-line Text Measurement
|
|
198
|
-
// ============================================================================
|
|
199
|
-
|
|
200
|
-
/** Standard line height ratio for multi-line text (1.3 = 130% of font size) */
|
|
201
|
-
export const LINE_HEIGHT_RATIO = 1.3
|
|
202
|
-
|
|
203
|
-
/** Metrics for multi-line text measurement */
|
|
204
|
-
export interface MultilineMetrics {
|
|
205
|
-
/** Maximum line width in pixels */
|
|
206
|
-
width: number
|
|
207
|
-
/** Total height in pixels (lines × lineHeight) */
|
|
208
|
-
height: number
|
|
209
|
-
/** Individual lines after splitting */
|
|
210
|
-
lines: string[]
|
|
211
|
-
/** Computed line height in pixels */
|
|
212
|
-
lineHeight: number
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Measure multi-line text dimensions.
|
|
217
|
-
*
|
|
218
|
-
* Splits text on newlines and returns the maximum width across all lines,
|
|
219
|
-
* total height based on line count, and the split lines for rendering.
|
|
220
|
-
*
|
|
221
|
-
* @param text - The text to measure (may contain \n)
|
|
222
|
-
* @param fontSize - Font size in pixels
|
|
223
|
-
* @param fontWeight - Font weight (affects width slightly)
|
|
224
|
-
* @returns Metrics including width, height, lines array, and lineHeight
|
|
225
|
-
*/
|
|
226
|
-
export function measureMultilineText(
|
|
227
|
-
text: string,
|
|
228
|
-
fontSize: number,
|
|
229
|
-
fontWeight: number
|
|
230
|
-
): MultilineMetrics {
|
|
231
|
-
const lines = text.split('\n')
|
|
232
|
-
const lineHeight = fontSize * LINE_HEIGHT_RATIO
|
|
233
|
-
|
|
234
|
-
// Width = max of all line widths
|
|
235
|
-
let maxWidth = 0
|
|
236
|
-
for (const line of lines) {
|
|
237
|
-
const plain = line.replace(/<\/?(?:b|strong|i|em|u|s|del)\s*>/gi, '')
|
|
238
|
-
const w = measureTextWidth(plain, fontSize, fontWeight)
|
|
239
|
-
if (w > maxWidth) maxWidth = w
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return {
|
|
243
|
-
width: maxWidth,
|
|
244
|
-
height: lines.length * lineHeight,
|
|
245
|
-
lines,
|
|
246
|
-
lineHeight,
|
|
247
|
-
}
|
|
248
|
-
}
|