@weasel-js/text 1.2.0 → 1.3.0-pre.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/dist/{chunk-4OGXTESY.js → chunk-FCHCIQYT.js} +219 -23
- package/dist/chunk-FCHCIQYT.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -70
- package/dist/index.js.map +1 -1
- package/dist/{test-seams-nUU2u_Pt.d.ts → test-seams-ClWSA091.d.ts} +117 -18
- package/dist/test-seams.d.ts +1 -1
- package/dist/test-seams.js +1 -1
- package/package.json +7 -4
- package/dist/chunk-4OGXTESY.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cachedLayoutRuns } from './chunk-
|
|
2
|
-
export { LAYOUT_CACHE_STRUCTURAL_LIMIT, LAYOUT_CACHE_VARIANT_LIMIT, cachedLayoutRuns, layoutRuns } from './chunk-
|
|
1
|
+
import { resolveTextStyle, cachedLayoutRuns } from './chunk-FCHCIQYT.js';
|
|
2
|
+
export { DEFAULT_TEXT_STYLE, LAYOUT_CACHE_STRUCTURAL_LIMIT, LAYOUT_CACHE_VARIANT_LIMIT, cachedLayoutRuns, fontString, layoutRuns, resolveAlign, resolveTextStyle } from './chunk-FCHCIQYT.js';
|
|
3
3
|
|
|
4
4
|
// src/runs.ts
|
|
5
5
|
function toRuns(input) {
|
|
@@ -118,73 +118,6 @@ function markdownToRuns(input, grammar = MARKDOWN_RUN_GRAMMAR) {
|
|
|
118
118
|
return runs;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
// src/textStyle.ts
|
|
122
|
-
var DEFAULT_FILL = { fill: "solid", color: "#000" };
|
|
123
|
-
function paintColor(p) {
|
|
124
|
-
return "color" in p ? p.color : "#000";
|
|
125
|
-
}
|
|
126
|
-
function defaultSelectionBackground(textColor) {
|
|
127
|
-
return `color-mix(in srgb, ${textColor} 25%, transparent)`;
|
|
128
|
-
}
|
|
129
|
-
var DEFAULT_TEXT_STYLE = {
|
|
130
|
-
fontSize: 16,
|
|
131
|
-
fontFamily: "sans-serif",
|
|
132
|
-
fontWeight: 400,
|
|
133
|
-
fontStyle: "normal",
|
|
134
|
-
align: "left",
|
|
135
|
-
lineHeight: 1.2,
|
|
136
|
-
fill: DEFAULT_FILL,
|
|
137
|
-
caretColor: paintColor(DEFAULT_FILL),
|
|
138
|
-
selectionBackground: defaultSelectionBackground(paintColor(DEFAULT_FILL)),
|
|
139
|
-
selectionColor: null,
|
|
140
|
-
letterSpacing: 0,
|
|
141
|
-
underline: false,
|
|
142
|
-
strikethrough: false,
|
|
143
|
-
overline: false
|
|
144
|
-
};
|
|
145
|
-
function resolveTextStyle(style, paint) {
|
|
146
|
-
const fill = paint?.fill ?? DEFAULT_TEXT_STYLE.fill;
|
|
147
|
-
const stroke = paint?.stroke ?? void 0;
|
|
148
|
-
if (!style) {
|
|
149
|
-
return fill === DEFAULT_TEXT_STYLE.fill && stroke === void 0 ? DEFAULT_TEXT_STYLE : {
|
|
150
|
-
...DEFAULT_TEXT_STYLE,
|
|
151
|
-
fill,
|
|
152
|
-
caretColor: paintColor(fill),
|
|
153
|
-
selectionBackground: defaultSelectionBackground(paintColor(fill)),
|
|
154
|
-
...stroke !== void 0 ? { stroke } : {}
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
const caretColor = style.caretColor ?? paintColor(fill);
|
|
158
|
-
let selectionBackground;
|
|
159
|
-
if (style.selectionBackground === "none") {
|
|
160
|
-
selectionBackground = null;
|
|
161
|
-
} else if (style.selectionBackground != null) {
|
|
162
|
-
selectionBackground = style.selectionBackground;
|
|
163
|
-
} else {
|
|
164
|
-
selectionBackground = defaultSelectionBackground(caretColor);
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
fontSize: style.fontSize ?? DEFAULT_TEXT_STYLE.fontSize,
|
|
168
|
-
fontFamily: style.fontFamily ?? DEFAULT_TEXT_STYLE.fontFamily,
|
|
169
|
-
fontWeight: style.fontWeight ?? DEFAULT_TEXT_STYLE.fontWeight,
|
|
170
|
-
fontStyle: style.fontStyle ?? DEFAULT_TEXT_STYLE.fontStyle,
|
|
171
|
-
align: style.align ?? DEFAULT_TEXT_STYLE.align,
|
|
172
|
-
lineHeight: style.lineHeight ?? DEFAULT_TEXT_STYLE.lineHeight,
|
|
173
|
-
fill,
|
|
174
|
-
caretColor,
|
|
175
|
-
selectionBackground,
|
|
176
|
-
selectionColor: style.selectionColor ?? null,
|
|
177
|
-
letterSpacing: style.letterSpacing ?? DEFAULT_TEXT_STYLE.letterSpacing,
|
|
178
|
-
underline: style.underline ?? DEFAULT_TEXT_STYLE.underline,
|
|
179
|
-
strikethrough: style.strikethrough ?? DEFAULT_TEXT_STYLE.strikethrough,
|
|
180
|
-
overline: style.overline ?? DEFAULT_TEXT_STYLE.overline,
|
|
181
|
-
...stroke !== void 0 ? { stroke } : {}
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
function fontString(s) {
|
|
185
|
-
return `${s.fontStyle} ${s.fontWeight} ${s.fontSize}px ${s.fontFamily}`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
121
|
// src/runs/resolveRuns.ts
|
|
189
122
|
var SCRIPT_METRICS = Object.freeze({
|
|
190
123
|
super: Object.freeze({ size: 0.583, shift: 0.333 }),
|
|
@@ -431,6 +364,6 @@ function createMarkdownRenderer(ctx, text, fontSize, maxWidth = Infinity, fontOp
|
|
|
431
364
|
return { renderer, strokeRenderer, width: layout.width, height: layout.height };
|
|
432
365
|
}
|
|
433
366
|
|
|
434
|
-
export {
|
|
367
|
+
export { MARKDOWN_RUN_GRAMMAR, SCRIPT_METRICS, createMarkdownRenderer, layoutMarkdown, markdownToRuns, measureText, measureTextBounds, measuredWidth, resolveRuns, runsToMarkdown, runsToPlainText, textLineBoxes, toRuns, verticalAlignOffset };
|
|
435
368
|
//# sourceMappingURL=index.js.map
|
|
436
369
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runs.ts","../src/textStyle.ts","../src/runs/resolveRuns.ts","../src/measure/measureText.ts","../src/measure/measureTextBounds.ts","../src/measure/verticalAlign.ts","../src/measure/lineBoxes.ts","../src/markdownText.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Canonical inline-styling primitive for text nodes. A node's text is\n * either a plain `string` (treated as a single-run, default-styled fragment)\n * or `StyledRun[]` for rich content. `toRuns` is the funnel that normalizes\n * either form into the array shape used by the renderer.\n *\n * Every field except `text` is optional; missing fields fall back to the\n * node-level `TextStyle`. `bold`/`italic` are toggles; richer weight axes\n * (300/500/900) are out of scope for slice 1.\n */\n\nimport type { FillStyle, Stroke } from '@weasel-js/paint';\nimport type { TextStyle } from './textStyle';\n\n/** A span of text with its own styling, as authored. Fields left absent\n * inherit from the node's text style — this is the difference between a run\n * and a fully resolved one. */\nexport interface StyledRun {\n text: string;\n bold?: boolean;\n italic?: boolean;\n fontFamily?: string;\n fontSize?: number;\n fill?: FillStyle;\n /** Outline over this run's glyphs. Overrides the node style's stroke;\n * absent inherits it. Only painted on the outline tier — see\n * {@link TextStyle.stroke}. */\n stroke?: Stroke;\n letterSpacing?: number;\n underline?: boolean;\n strikethrough?: boolean;\n /** Draw a rule above this run's ascent. Additive over the node style. */\n overline?: boolean;\n /**\n * Set this run as a superscript or subscript — a raised or lowered baseline\n * and a smaller size together, which is the pair `<sup>` and `<sub>` imply.\n *\n * A preset over the two primitives below, not a third mechanism: it supplies\n * a `baselineShift` and a `fontScale`, and naming either of those directly\n * overrides that half while leaving the other alone. The numbers are in\n * {@link SCRIPT_METRICS}.\n *\n * There is no node-level counterpart. A whole text node set as a superscript\n * is a smaller node moved up, which the pose already says better.\n */\n script?: 'super' | 'sub';\n /**\n * Raise (positive) or lower (negative) this run off the line's shared\n * baseline, in ems of the *inherited* font size — so a run's rise does not\n * shrink along with the run when `fontScale` also applies.\n */\n baselineShift?: number;\n /**\n * Multiplier on the inherited font size. The relative counterpart to\n * `fontSize`, which pins an absolute size and wins over this when both are\n * present.\n */\n fontScale?: number;\n}\n\n/** Normalize the two accepted spellings of text content — a plain string or\n * an array of runs — to runs. Throws on a run with no string `text`. */\nexport function toRuns(input: string | StyledRun[]): StyledRun[] {\n if (typeof input === 'string') {\n return input.length === 0 ? [] : [{ text: input }];\n }\n for (let i = 0; i < input.length; i++) {\n const r = input[i];\n if (typeof r?.text !== 'string') {\n throw new Error(`toRuns: run at index ${i} is missing string \\`text\\``);\n }\n }\n return input;\n}\n\n/** Concatenate runs, dropping all styling. */\nexport function runsToPlainText(runs: readonly StyledRun[]): string {\n let out = '';\n for (const r of runs) out += r.text;\n return out;\n}\n\n/** A run flag an inline marker can toggle. The additive style toggles on\n * `StyledRun`; the valued fields (family, size, paint, `script`) have no\n * inline spelling and are not addressable this way. */\nexport type RunFlag = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'overline';\n\n/** One inline marker: a delimiter repeated `repeat` times, and the flags it\n * turns on between its opening and closing occurrence. */\nexport interface RunMarker {\n delimiter: string;\n repeat: number;\n flags: readonly RunFlag[];\n}\n\n/**\n * The inline grammar `runsToMarkdown` writes and `markdownToRuns` reads.\n *\n * Markers sharing a delimiter are matched longest-first, which is what lets\n * `***` mean something other than `**` followed by `*`. A backslash escapes\n * any delimiter character and itself, in both directions.\n */\nexport interface RunGrammar {\n markers: readonly RunMarker[];\n}\n\n/**\n * The markdown subset the kit reads and writes: `**bold**`, `*italic*`,\n * `***both***`.\n *\n * Deliberately not the whole of markdown, and deliberately silent on\n * `underline` / `strikethrough` — the two run flags with no entry here are\n * dropped by `runsToMarkdown`, as they always have been. A grammar that wants\n * `~~struck~~` adds one marker; nothing else has to change.\n */\nexport const MARKDOWN_RUN_GRAMMAR: RunGrammar = {\n markers: [\n { delimiter: '*', repeat: 3, flags: ['bold', 'italic'] },\n { delimiter: '*', repeat: 2, flags: ['bold'] },\n { delimiter: '*', repeat: 1, flags: ['italic'] },\n ],\n};\n\n/** Delimiter characters of a grammar, plus the backslash that escapes them. */\nfunction escapees(grammar: RunGrammar): Set<string> {\n const out = new Set<string>(['\\\\']);\n for (const m of grammar.markers) out.add(m.delimiter);\n return out;\n}\n\nfunction escapeFor(text: string, chars: ReadonlySet<string>): string {\n let out = '';\n for (const ch of text) out += chars.has(ch) ? `\\\\${ch}` : ch;\n return out;\n}\n\n/** The flags a run turns on, in the grammar's own marker order so two runs\n * with the same flags always serialize identically. */\nfunction flagsOf(run: StyledRun, grammar: RunGrammar): Set<RunFlag> {\n const on = new Set<RunFlag>();\n for (const m of grammar.markers) {\n for (const f of m.flags) if (run[f]) on.add(f);\n }\n return on;\n}\n\nfunction sameFlags(a: ReadonlySet<RunFlag>, b: readonly RunFlag[]): boolean {\n return a.size === b.length && b.every((f) => a.has(f));\n}\n\n/**\n * Render runs in `grammar`, defaulting to the markdown subset. The flavor\n * written to the clipboard alongside the plain-text one.\n *\n * A run whose flags exactly match one marker takes it; otherwise the markers\n * that cover them nest. Flags no marker spells are dropped — the text still\n * round-trips, without that styling.\n */\nexport function runsToMarkdown(\n runs: readonly StyledRun[],\n grammar: RunGrammar = MARKDOWN_RUN_GRAMMAR,\n): string {\n const chars = escapees(grammar);\n let out = '';\n for (const r of runs) {\n const escaped = escapeFor(r.text, chars);\n const on = flagsOf(r, grammar);\n if (on.size === 0) { out += escaped; continue; }\n\n const exact = grammar.markers.find((m) => sameFlags(on, m.flags));\n if (exact) {\n const d = exact.delimiter.repeat(exact.repeat);\n out += `${d}${escaped}${d}`;\n continue;\n }\n // No single marker says all of it: nest the ones that each say part.\n const covering = grammar.markers.filter(\n (m) => m.flags.length === 1 && on.has(m.flags[0]),\n );\n const open = covering.map((m) => m.delimiter.repeat(m.repeat)).join('');\n const close = [...covering].reverse().map((m) => m.delimiter.repeat(m.repeat)).join('');\n out += `${open}${escaped}${close}`;\n }\n return out;\n}\n\n/**\n * Parse `input` in `grammar`, defaulting to the markdown subset, into styled\n * runs. Newlines are preserved as literal characters inside a run — they are\n * not run-boundary markers in this format.\n */\nexport function markdownToRuns(\n input: string,\n grammar: RunGrammar = MARKDOWN_RUN_GRAMMAR,\n): StyledRun[] {\n const chars = escapees(grammar);\n // Longest-first per delimiter, so `***` is tried before `**` before `*`.\n const byDelimiter = new Map<string, RunMarker[]>();\n for (const m of grammar.markers) {\n const list = byDelimiter.get(m.delimiter) ?? [];\n list.push(m);\n byDelimiter.set(m.delimiter, list);\n }\n for (const list of byDelimiter.values()) list.sort((a, b) => b.repeat - a.repeat);\n\n const runs: StyledRun[] = [];\n const active = new Set<RunFlag>();\n let buf = '';\n let i = 0;\n\n function flush(): void {\n if (buf.length === 0) return;\n const run: StyledRun = { text: buf };\n for (const f of active) run[f] = true;\n runs.push(run);\n buf = '';\n }\n\n while (i < input.length) {\n const ch = input[i];\n\n if (ch === '\\\\' && i + 1 < input.length && chars.has(input[i + 1])) {\n buf += input[i + 1];\n i += 2;\n continue;\n }\n\n const markers = byDelimiter.get(ch);\n if (markers) {\n let count = 0;\n while (i + count < input.length && input[i + count] === ch) count++;\n // The longest marker the delimiter run can pay for. A run shorter than\n // every marker still matches the shortest, which is how a lone `*`\n // toggles italic where `***` toggles both.\n const marker = markers.find((m) => m.repeat <= count) ?? markers[markers.length - 1];\n flush();\n for (const f of marker.flags) {\n if (active.has(f)) active.delete(f);\n else active.add(f);\n }\n i += marker.repeat;\n continue;\n }\n\n buf += ch;\n i++;\n }\n\n flush();\n return runs;\n}\n","/**\n * Typography for `TextPose` and friends. Every field is optional; consumers\n * pass `{}` or override the few they care about. Defaults live in\n * `DEFAULT_TEXT_STYLE` and are applied at render/measure time, never written\n * back to the pose.\n *\n * Paint is not typography and does not live here. A text node carries its\n * fill and stroke in `data.fill` / `data.stroke`, the slots every other node\n * kind uses; `resolveTextStyle` takes them as its second argument and\n * `StyledRun.fill` / `.stroke` override them per range.\n */\n\nimport type { FillStyle, Stroke } from '@weasel-js/paint';\n\n/** User-facing text style. All fields optional; defaults applied at render time via `resolveTextStyle`. */\nexport interface TextStyle {\n /** Font size in world units. Default 16. */\n fontSize?: number;\n /** Default `'sans-serif'`. */\n fontFamily?: string;\n /** Default 400. */\n fontWeight?: number | string;\n /** Default `'normal'`. */\n fontStyle?: 'normal' | 'italic';\n /** Default `'left'`. */\n align?: 'left' | 'center' | 'right';\n /** Multiplier applied to `fontSize`. Default 1.2. */\n lineHeight?: number;\n /**\n * Caret color used by the edit overlay. Defaults to the node's fill when\n * that fill is solid; falls back to `#000` for non-solid paints.\n */\n caretColor?: string;\n /**\n * Selection background color used by the edit overlay's `::selection`\n * pseudo-element. Defaults to a 25%-opacity tint of `caretColor` via CSS\n * `color-mix`. Pass `'none'` to fall back to the browser-native highlight.\n */\n selectionBackground?: string;\n /** Selection text color paired with `selectionBackground`. Default: inherits text color. */\n selectionColor?: string;\n /** Extra advance added after each glyph, in world units. Default 0. */\n letterSpacing?: number;\n /** Default `false`. */\n underline?: boolean;\n /** Default `false`. */\n strikethrough?: boolean;\n /** Default `false`. */\n overline?: boolean;\n}\n\n/** `TextStyle` with all fields filled in from defaults — what the renderer actually consumes. */\nexport interface ResolvedTextStyle {\n fontSize: number;\n fontFamily: string;\n fontWeight: number | string;\n fontStyle: 'normal' | 'italic';\n align: 'left' | 'center' | 'right';\n lineHeight: number;\n fill: FillStyle;\n caretColor: string;\n selectionBackground: string | null;\n selectionColor: string | null;\n letterSpacing: number;\n underline: boolean;\n strikethrough: boolean;\n overline: boolean;\n /** Absent means no outline — unlike the other fields, this one has no\n * default to fall back to. See {@link TextPaint.stroke}. */\n stroke?: Stroke;\n}\n\nconst DEFAULT_FILL: FillStyle = { fill: 'solid', color: '#000' };\n\nfunction paintColor(p: FillStyle): string {\n return 'color' in p ? p.color : '#000';\n}\n\n/**\n * Default selection background derived from `textColor` — a 25% tint via\n * CSS `color-mix`. Caller passes `'none'` to opt out.\n */\nfunction defaultSelectionBackground(textColor: string): string {\n return `color-mix(in srgb, ${textColor} 25%, transparent)`;\n}\n\n/** Default resolved style used when a `TextPose` omits `style`. */\nexport const DEFAULT_TEXT_STYLE: ResolvedTextStyle = {\n fontSize: 16,\n fontFamily: 'sans-serif',\n fontWeight: 400,\n fontStyle: 'normal',\n align: 'left',\n lineHeight: 1.2,\n fill: DEFAULT_FILL,\n caretColor: paintColor(DEFAULT_FILL),\n selectionBackground: defaultSelectionBackground(paintColor(DEFAULT_FILL)),\n selectionColor: null,\n letterSpacing: 0,\n underline: false,\n strikethrough: false,\n overline: false,\n};\n\n/**\n * The paint a text node hands its glyphs — `data.fill` and `data.stroke`,\n * read straight off the node. Runs inherit these when they name none of\n * their own.\n *\n * `fill: null` is not yet distinguishable from absent: a `ResolvedRun` must\n * name a concrete fill, so unfilled-but-stroked text has nowhere to say so\n * and falls back to the default black. See `docs/TODO.md`.\n */\nexport interface TextPaint {\n fill?: FillStyle | null;\n /**\n * Outline painted over the glyph fill. Absent means no outline — there is\n * no such thing as a default text stroke.\n *\n * Only glyphs on the outline tier are stroked: above\n * `textOutlineMinScreenSize` a glyph is a real `PolygonPath`, so it gets\n * the ordinary tessellated ribbon with real joins, caps and miters, in any\n * paint. Below it a glyph is a sampled distance field with no geometry to\n * stroke, and it renders unstroked rather than approximated. `width` is in\n * world units, like every other stroke in the kit — it does not scale with\n * `fontSize`.\n */\n stroke?: Stroke | null;\n}\n\n/** Fill in a partial `TextStyle` with defaults from `DEFAULT_TEXT_STYLE`,\n * taking the glyph paint from the node rather than from the style. */\nexport function resolveTextStyle(\n style?: TextStyle,\n paint?: TextPaint,\n): ResolvedTextStyle {\n const fill = paint?.fill ?? DEFAULT_TEXT_STYLE.fill;\n const stroke = paint?.stroke ?? undefined;\n if (!style) {\n return fill === DEFAULT_TEXT_STYLE.fill && stroke === undefined\n ? DEFAULT_TEXT_STYLE\n : {\n ...DEFAULT_TEXT_STYLE,\n fill,\n caretColor: paintColor(fill),\n selectionBackground: defaultSelectionBackground(paintColor(fill)),\n ...(stroke !== undefined ? { stroke } : {}),\n };\n }\n const caretColor = style.caretColor ?? paintColor(fill);\n let selectionBackground: string | null;\n if (style.selectionBackground === 'none') {\n selectionBackground = null;\n } else if (style.selectionBackground != null) {\n selectionBackground = style.selectionBackground;\n } else {\n selectionBackground = defaultSelectionBackground(caretColor);\n }\n return {\n fontSize: style.fontSize ?? DEFAULT_TEXT_STYLE.fontSize,\n fontFamily: style.fontFamily ?? DEFAULT_TEXT_STYLE.fontFamily,\n fontWeight: style.fontWeight ?? DEFAULT_TEXT_STYLE.fontWeight,\n fontStyle: style.fontStyle ?? DEFAULT_TEXT_STYLE.fontStyle,\n align: style.align ?? DEFAULT_TEXT_STYLE.align,\n lineHeight: style.lineHeight ?? DEFAULT_TEXT_STYLE.lineHeight,\n fill,\n caretColor,\n selectionBackground,\n selectionColor: style.selectionColor ?? null,\n letterSpacing: style.letterSpacing ?? DEFAULT_TEXT_STYLE.letterSpacing,\n underline: style.underline ?? DEFAULT_TEXT_STYLE.underline,\n strikethrough: style.strikethrough ?? DEFAULT_TEXT_STYLE.strikethrough,\n overline: style.overline ?? DEFAULT_TEXT_STYLE.overline,\n ...(stroke !== undefined ? { stroke } : {}),\n };\n}\n\n/** Build a CSS `font` shorthand suitable for `ctx.font`. */\nexport function fontString(s: ResolvedTextStyle): string {\n return `${s.fontStyle} ${s.fontWeight} ${s.fontSize}px ${s.fontFamily}`;\n}\n","/**\n * Apply node-level `ResolvedTextStyle` defaults to each `StyledRun`,\n * producing a fully-resolved run with every styling field set. Downstream\n * layout and draw never re-resolve defaults — `ResolvedRun` is the\n * canonical shape the renderer consumes.\n *\n * `bold`/`italic` toggles on a run are folded into `fontWeight`/`fontStyle`:\n * `bold: true` → fontWeight 700, `italic: true` → fontStyle 'italic'.\n * Explicit `fontFamily` / `fontSize` / `fill` / `letterSpacing` on the run\n * override the node-level value (`letterSpacing: 0` on a run is an override,\n * not an absence — it zeroes inherited tracking).\n *\n * `underline` / `strikethrough` / `overline` are *additive*, like\n * `bold`/`italic`: a run can turn a decoration on but never off, so they\n * resolve as `run.x || style.x` and not `run.x ?? style.x`. See the header of\n * `runs/rangeStyle.ts` for why the model collapses the tri-state.\n *\n * `script` is folded the same way the toggles are, into the two primitives it\n * is a preset over: `baselineShift` and `fontScale`. Both come out as one\n * world-unit `baselineShift` and a final `fontSize`, so layout never learns\n * that superscripts exist — it places a run against a baseline and an offset.\n */\n\nimport type { FillStyle, Stroke } from '@weasel-js/paint';\nimport type { StyledRun } from '../runs';\nimport type { ResolvedTextStyle } from '../textStyle';\n\n/** A run with every style resolved against the node's text style — no\n * optional inheritance left. This is what layout and painting consume. */\nexport interface ResolvedRun {\n text: string;\n fontFamily: string;\n fontSize: number;\n fontWeight: number;\n fontStyle: 'normal' | 'italic';\n fill: FillStyle;\n /** Outline over this run's glyphs, or absent for none. Painted only on the\n * outline tier — a distance field has no geometry to stroke. */\n stroke?: Stroke;\n /** Extra advance added after each glyph of this run, in world units. */\n letterSpacing: number;\n /** Draw a rule below this run's baseline. Additive over the node style. */\n underline: boolean;\n /** Draw a rule through this run's x-height. Additive over the node style. */\n strikethrough: boolean;\n /** Draw a rule above this run's ascent. Additive over the node style. */\n overline: boolean;\n /**\n * How far this run sits off the line's shared baseline, in world units;\n * positive raises. 0 for ordinary text.\n *\n * Already multiplied out against the inherited font size, and already\n * carrying whatever `script` asked for — layout adds it to a baseline and\n * asks nothing about where it came from.\n */\n baselineShift: number;\n}\n\n/**\n * What `script: 'super'` and `script: 'sub'` expand to, as fractions of the\n * inherited font size: `size` scales it, `shift` raises (positive) or lowers\n * (negative) the baseline.\n *\n * These are Adobe's defaults — InDesign and Illustrator ship 58.3% size and\n * 33.3% position for both — chosen because a drawing tool's users have those\n * numbers in their fingers already. They are not read from the font: the\n * `OS/2` table carries real `ySuperscript*` / `ySubscript*` metrics, but the\n * baked atlas tier has no slot for them (see `layoutRuns`'s note on the\n * decoration constants, which are derived for the same reason), and metrics\n * that applied on one glyph tier and not the other would reflow text as it\n * crossed the size threshold.\n *\n * Exported so a consumer building a character panel can show the percentages\n * it is about to apply. Override either half per run with `baselineShift` /\n * `fontScale`.\n */\nexport const SCRIPT_METRICS: Readonly<\n Record<'super' | 'sub', Readonly<{ size: number; shift: number }>>\n> = Object.freeze({\n super: Object.freeze({ size: 0.583, shift: 0.333 }),\n sub: Object.freeze({ size: 0.583, shift: -0.333 }),\n});\n\nfunction numericWeight(w: number | string): number {\n if (typeof w === 'number') return w;\n if (w === 'bold') return 700;\n if (w === 'normal') return 400;\n const parsed = Number(w);\n return Number.isFinite(parsed) ? parsed : 400;\n}\n\n/** Resolve each run's styling against the node's text style, filling in\n * everything the run left inherited. */\nexport function resolveRuns(\n runs: readonly StyledRun[],\n style: ResolvedTextStyle,\n): ResolvedRun[] {\n const out: ResolvedRun[] = [];\n const baseWeight = numericWeight(style.fontWeight);\n for (const run of runs) {\n const script = run.script ? SCRIPT_METRICS[run.script] : undefined;\n // Against the inherited size, not the run's own: a superscript that also\n // shrank its rise would climb less the smaller it got.\n const shiftEm = run.baselineShift ?? script?.shift ?? 0;\n const scale = run.fontScale ?? script?.size ?? 1;\n out.push({\n text: run.text,\n fontFamily: run.fontFamily ?? style.fontFamily,\n // An absolute size wins over a relative one; naming both is a consumer\n // saying \"this size exactly\", which a multiplier cannot improve on.\n fontSize: run.fontSize ?? style.fontSize * scale,\n fontWeight: run.bold ? 700 : baseWeight,\n fontStyle: run.italic ? 'italic' : style.fontStyle,\n fill: run.fill ?? style.fill,\n // Unlike the decorations below, a run's stroke *replaces* the node's\n // rather than adding to it — there is only one outline to paint.\n ...((run.stroke ?? style.stroke) !== undefined\n ? { stroke: run.stroke ?? style.stroke }\n : {}),\n letterSpacing: run.letterSpacing ?? style.letterSpacing,\n // `||`, not `??`: run-level decorations are additive over the node style.\n underline: run.underline || style.underline,\n strikethrough: run.strikethrough || style.strikethrough,\n overline: run.overline || style.overline,\n baselineShift: shiftEm * style.fontSize,\n });\n }\n return out;\n}\n","/**\n * Wrap-aware text measurement. Greedy word-wrap against `maxWidth` using\n * `ctx.measureText`. Lines explicitly broken by `\\n` are preserved; long\n * single words that exceed `maxWidth` are emitted on their own line without\n * mid-word breaking (caller can decide to clip).\n *\n * Returns the laid-out lines, the total block height in world units\n * (`lines.length * fontSize * lineHeight`), and per-line `lineStarts` —\n * the offset of each line's first character in the original `text`. The\n * starts are used by `caretIndexAt` to map a clicked (x, y) back to a\n * character offset in the source string. Trailing whitespace consumed by\n * the wrap is not included in `lines[i]` but is implicit in the gap\n * between `lineStarts[i] + lines[i].length` and `lineStarts[i + 1]`.\n *\n * The caller owns the `ctx.font` setup — pass a context whose `font`\n * already matches `style` (use `fontString(style)`).\n */\n\nimport type { ResolvedTextStyle } from '../textStyle';\n\n/**\n * Advance width of `text` in world units, tracking included.\n *\n * `letter-spacing` is not part of the CSS `font` shorthand, so a context\n * whose `font` was set from `fontString(style)` measures glyphs only. The\n * GL path (`layoutRuns`) adds `letterSpacing` after **every** code point\n * including the last, matching CSS, so this does too — and every 2D-side\n * width has to go through here or the two paths disagree about where a line\n * breaks.\n */\nexport function measuredWidth(\n ctx: CanvasRenderingContext2D,\n text: string,\n style: ResolvedTextStyle,\n): number {\n return ctx.measureText(text).width + text.length * style.letterSpacing;\n}\n\n/** Result of `measureText`: wrapped lines, per-line source offsets, and total block height. */\nexport interface MeasuredText {\n lines: string[];\n lineStarts: number[];\n height: number;\n}\n\n/** Greedy word-wrap text measurement against `maxWidth`; preserves explicit `\\n` breaks. */\nexport function measureText(\n ctx: CanvasRenderingContext2D,\n text: string,\n maxWidth: number,\n style: ResolvedTextStyle,\n): MeasuredText {\n const lines: string[] = [];\n const lineStarts: number[] = [];\n const paragraphs = text.split('\\n');\n\n let pos = 0;\n for (let pi = 0; pi < paragraphs.length; pi++) {\n if (pi > 0) pos += 1; // step past the '\\n' between paragraphs\n const paragraph = paragraphs[pi];\n if (paragraph === '') {\n lines.push('');\n lineStarts.push(pos);\n continue;\n }\n const words = paragraph.split(/(\\s+)/).filter((w) => w.length > 0);\n let current = '';\n let currentStart = pos;\n let consumed = 0; // chars of `paragraph` we've already accounted for\n for (const word of words) {\n if (current === '') currentStart = pos + consumed;\n const candidate = current + word;\n if (current === '' || measuredWidth(ctx, candidate, style) <= maxWidth) {\n current = candidate;\n } else {\n lines.push(current.trimEnd());\n lineStarts.push(currentStart);\n if (/^\\s+$/.test(word)) {\n current = '';\n } else {\n current = word;\n currentStart = pos + consumed;\n }\n }\n consumed += word.length;\n }\n lines.push(current.trimEnd());\n lineStarts.push(currentStart);\n pos += paragraph.length;\n }\n\n const height = lines.length * style.fontSize * style.lineHeight;\n return { lines, lineStarts, height };\n}\n","import type { TextStyle } from '../textStyle';\nimport { resolveTextStyle } from '../textStyle';\nimport { resolveRuns } from '../runs/resolveRuns';\nimport { cachedLayoutRuns } from '../layout/layoutCache';\n\n/** Options for `measureTextBounds`. */\nexport interface MeasureTextBoundsOpts {\n /** Wrap width; words exceeding it start a new line. Default `Infinity` (no wrap). */\n maxWidth?: number;\n /** Overrides `style`'s `lineHeight` multiplier for this measurement. */\n lineHeight?: number;\n}\n\n/**\n * Measure how the GL/MSDF renderer will lay out a single plain-text string,\n * using the registered font atlas metrics. Mirrors {@link textCommand} exactly\n * (`resolveTextStyle` → `resolveRuns` → `cachedLayoutRuns`), so the returned\n * bounds match what actually gets drawn — use it to size backgrounds/pills, place\n * labels, or test overlap without guessing widths.\n *\n * Pass `opts.maxWidth` to measure word-wrapped height (e.g. for a fixed-width\n * text box); omitting it measures the unwrapped single-line width. `opts.lineHeight`\n * overrides the style's multiplier without changing `style` itself.\n *\n * Returns `{ width, height }` in the same units as `style.fontSize`. The font\n * must already be registered via `registerFont`; an unregistered family falls\n * back to the atlas fallback glyph (and warns), so register fonts at app boot\n * before measuring.\n */\nexport function measureTextBounds(\n text: string,\n style?: TextStyle,\n opts?: MeasureTextBoundsOpts,\n): { width: number; height: number } {\n const resolved = resolveTextStyle(style);\n const runs = resolveRuns([{ text }], resolved);\n const { bounds } = cachedLayoutRuns(runs, {\n maxWidth: opts?.maxWidth ?? Infinity,\n lineHeight: opts?.lineHeight ?? resolved.lineHeight,\n align: resolved.align,\n });\n // Copied: the layout behind it is shared, and handing a caller a mutable\n // reference into a cache entry is how one measurement corrupts the next.\n return { width: bounds.width, height: bounds.height };\n}\n","/**\n * Box vertical alignment for text draw commands. Given the command's box\n * `height` and the laid-out text block's height, returns the Y offset to\n * apply to every quad. `'top'` (or an undefined `align`, or a missing box\n * `height`) is the legacy behavior: offset 0.\n */\nexport type TextVerticalAlign = 'top' | 'center' | 'bottom';\n\n/** How far to shift laid-out text down inside a box of `boxHeight` to satisfy\n * a vertical alignment. Zero when the box height is unknown or alignment is\n * top. */\nexport function verticalAlignOffset(\n align: TextVerticalAlign | undefined,\n boxHeight: number | undefined,\n textHeight: number,\n): number {\n if (align === undefined || align === 'top' || boxHeight === undefined) return 0;\n const slack = boxHeight - textHeight;\n return align === 'center' ? slack / 2 : slack;\n}\n","/**\n * Where a text node's lines actually sit inside its pose box.\n *\n * A text pose is a *wrap box*, not a bounding box: `\"Away\"` in a 309-unit-wide\n * box leaves most of the box empty, and anything that treats the pose as the\n * node's extent — picking, lasso, clipping, SVG export — claims that empty\n * space. `textLineBoxes` returns the per-line rectangles instead.\n *\n * The numbers come from `cachedLayoutRuns`, the same memoized walk that\n * positions the glyphs, through the same `resolveTextStyle` → `resolveRuns`\n * chain as `textCommand` — so the boxes cannot drift from what is painted. In\n * particular they honor `align` (a centered line reports its own span, not the\n * wrap width) and `verticalAlign` (the block shifts inside `[y, y + height]`\n * exactly as `drawText` shifts the quads).\n *\n * These are line boxes, not ink boxes: each is `fontSize * lineHeight` tall\n * from the pen's line top. Ink can escape vertically at a `lineHeight` low\n * enough — see the `bounds` note at the end of `layoutRuns` — which is the\n * right trade for hit-testing, where a box that hugged the ink would make\n * an `x` harder to click than an `X`.\n */\n\nimport type { Rect } from '@weasel-js/geom';\nimport { resolveTextStyle } from '../textStyle';\nimport { resolveRuns } from '../runs/resolveRuns';\nimport { toRuns } from '../runs';\nimport { cachedLayoutRuns } from '../layout/layoutCache';\nimport { verticalAlignOffset } from './verticalAlign';\nimport type { TextPose } from '../pose';\n\n/** Options for {@link textLineBoxes}. */\nexport interface TextLineBoxesOpts {\n /** Grow every box by this much on all four sides (world units). Default 0.\n * Picking wants a little slack so a single hairline row of text is still\n * grabbable; clipping and export want none. */\n padding?: number;\n /** Keep boxes for blank lines (zero width). Default `false` — a blank line\n * covers no area, so for hit-testing and silhouettes it is noise. Pass\n * `true` when the indices have to line up with the wrapped lines. */\n includeEmpty?: boolean;\n /**\n * Wrap width. Default `pose.width`, which is what `createTextLayer` passes\n * and what `TextPose` means by its box.\n *\n * Pass `Infinity` for a node painted by the built-in `kit:text` painter:\n * that painter deliberately does **not** forward `maxWidth` (see\n * `NodeShape.ts`), so its text does not wrap, and boxes computed with a\n * finite width would wrap where the paint did not.\n */\n maxWidth?: number;\n}\n\n/** Per-line rectangles for a text pose, in world units, in layout order. */\nexport function textLineBoxes(pose: TextPose, opts: TextLineBoxesOpts = {}): Rect[] {\n const padding = opts.padding ?? 0;\n const style = resolveTextStyle(pose.style);\n // `runs` wins when non-empty, matching every painter: empty runs are not a\n // styling, so they fall back to the plain string rather than measure nothing.\n const source = pose.runs && pose.runs.length > 0 ? pose.runs : pose.text;\n const runs = resolveRuns(toRuns(source), style);\n const laid = cachedLayoutRuns(runs, {\n maxWidth: opts.maxWidth ?? pose.width,\n lineHeight: style.lineHeight,\n align: style.align,\n });\n // The same translate `drawText` applies to the quads — `layoutRuns` is\n // origin-relative, so the pose's position lands here — plus the\n // `verticalAlign` shift, so the boxes stay on the text under every setting\n // rather than only the default 'top'.\n const dx = pose.x;\n const dy = pose.y + verticalAlignOffset(pose.verticalAlign, pose.height, laid.bounds.height);\n\n const out: Rect[] = [];\n for (const line of laid.lines) {\n if (!opts.includeEmpty && line.x1 <= line.x0) continue;\n out.push({\n x: line.x0 + dx - padding,\n y: line.y0 + dy - padding,\n width: line.x1 - line.x0 + padding * 2,\n height: line.y1 - line.y0 + padding * 2,\n });\n }\n return out;\n}\n","import { markdownToRuns, type StyledRun } from './runs';\n\nexport type { StyledRun };\n\n/** Width-measurement strategy for `layoutMarkdown`; canvas-backed default supplied by `createMarkdownRenderer`. */\nexport type MeasureFn = (text: string, fontSize: number, bold: boolean, italic: boolean) => number;\n\n/** A `StyledRun` with its computed x-offset relative to the start of its line. */\nexport interface PositionedRun extends StyledRun {\n x: number;\n}\n\n/** A single laid-out line of text: its positioned runs, total width, and computed line height. */\nexport interface LayoutLine {\n runs: PositionedRun[];\n width: number;\n height: number;\n}\n\n/** Output of `layoutMarkdown`: per-line breakdown plus overall block dimensions. */\nexport interface LayoutResult {\n lines: LayoutLine[];\n width: number;\n height: number;\n}\n\n/** Word-wrap parsed runs into lines bounded by `maxWidth`; pass `Infinity` for single-line layout. */\nexport function layoutMarkdown(\n runs: StyledRun[],\n maxWidth: number,\n fontSize: number,\n measure: MeasureFn,\n lineHeightFactor: number = 1.3,\n): LayoutResult {\n if (runs.length === 0) return { lines: [], width: 0, height: 0 };\n\n const lines: LayoutLine[] = [];\n let currentRuns: PositionedRun[] = [];\n let lineX = 0;\n let lineMaxSize = 0;\n\n function commitLine() {\n const effectiveFontSize = lineMaxSize > 0 ? lineMaxSize : fontSize;\n const lineHeight = effectiveFontSize * lineHeightFactor;\n lines.push({ runs: currentRuns, width: lineX, height: lineHeight });\n currentRuns = [];\n lineX = 0;\n lineMaxSize = 0;\n }\n\n function processSegment(segRun: StyledRun) {\n const effectiveSize = segRun.fontSize ?? fontSize;\n lineMaxSize = Math.max(lineMaxSize, effectiveSize);\n\n if (maxWidth === Infinity) {\n const w = measure(segRun.text, effectiveSize, segRun.bold ?? false, segRun.italic ?? false);\n currentRuns.push({ ...segRun, x: lineX });\n lineX += w;\n return;\n }\n\n // Word-wrap: split run text by spaces\n const words = segRun.text.split(/ /);\n let wordBuf = '';\n\n for (let wi = 0; wi < words.length; wi++) {\n const word = words[wi];\n const candidate = wordBuf.length > 0 ? wordBuf + ' ' + word : word;\n const candidateW = measure(candidate, effectiveSize, segRun.bold ?? false, segRun.italic ?? false);\n\n if (lineX + candidateW > maxWidth && (lineX > 0 || wordBuf.length > 0)) {\n // Flush current wordBuf as a run on the current line\n if (wordBuf.length > 0) {\n const w = measure(wordBuf, effectiveSize, segRun.bold ?? false, segRun.italic ?? false);\n currentRuns.push({ ...segRun, text: wordBuf, x: lineX });\n lineX += w;\n }\n commitLine();\n lineMaxSize = Math.max(lineMaxSize, effectiveSize);\n wordBuf = word;\n } else {\n // Either fits, or is an oversized single word starting a fresh line — accept it\n wordBuf = candidate;\n }\n }\n\n // Flush remaining wordBuf\n if (wordBuf.length > 0) {\n const w = measure(wordBuf, effectiveSize, segRun.bold ?? false, segRun.italic ?? false);\n currentRuns.push({ ...segRun, text: wordBuf, x: lineX });\n lineX += w;\n }\n }\n\n for (const run of runs) {\n // markdownToRuns embeds newlines inside runs; split on '\\n' so each\n // segment becomes its own line via commitLine().\n const segments = run.text.split('\\n');\n for (let si = 0; si < segments.length; si++) {\n if (si > 0) commitLine();\n const seg = segments[si];\n if (seg.length > 0) {\n processSegment({ ...run, text: seg });\n }\n }\n }\n\n // Commit final line\n if (currentRuns.length > 0) {\n commitLine();\n }\n\n const width = Math.max(...lines.map((l) => l.width));\n const height = lines.reduce((sum, l) => sum + l.height, 0);\n return { lines, width, height };\n}\n\n/** Pluggable text-painting strategy. The default fills white at `(x, y)`; markdown renderers replace this. */\nexport type TextRenderer = (\n ctx: CanvasRenderingContext2D,\n text: string,\n x: number,\n y: number,\n) => void;\n\n/** Font styling options threaded through `createMarkdownRenderer`. */\nexport interface MarkdownFontOptions {\n /** Font-family spec (e.g. `'\"Iowan Old Style\", Georgia, serif'`). Defaults to `sans-serif`. */\n family?: string;\n /** Numeric weight applied to non-bold runs. Bold runs always use `bold`. Default `normal`. */\n weight?: string | number;\n /** Override fill color. When set, used for all runs (italic and bold). */\n color?: string;\n /** Multiplier applied to font size for line height. Default 1.3. */\n lineHeight?: number;\n}\n\nfunction buildFont(\n fontSize: number,\n bold: boolean,\n italic: boolean,\n opts: MarkdownFontOptions = {},\n): string {\n const family = opts.family ?? 'sans-serif';\n const weight = bold ? 'bold' : (opts.weight !== undefined ? String(opts.weight) : 'normal');\n const parts: string[] = [];\n if (italic) parts.push('italic');\n parts.push(weight);\n parts.push(`${fontSize}px ${family}`);\n return parts.join(' ');\n}\n\nfunction canvasMeasure(ctx: CanvasRenderingContext2D, opts: MarkdownFontOptions = {}): MeasureFn {\n return (text, fontSize, bold, italic) => {\n ctx.font = buildFont(fontSize, bold, italic, opts);\n return ctx.measureText(text).width;\n };\n}\n\n/** Build a fill+stroke `TextRenderer` pair for a markdown string at the given size; pre-computes layout once. */\nexport function createMarkdownRenderer(\n ctx: CanvasRenderingContext2D,\n text: string,\n fontSize: number,\n maxWidth: number = Infinity,\n fontOpts: MarkdownFontOptions = {},\n): { renderer: TextRenderer; strokeRenderer: TextRenderer; width: number; height: number } {\n const measure = canvasMeasure(ctx, fontOpts);\n const parsed = markdownToRuns(text);\n const layout = layoutMarkdown(parsed, maxWidth, fontSize, measure, fontOpts.lineHeight);\n\n const renderer: TextRenderer = (_ctx, _text, x, y) => {\n let lineY = y;\n for (const line of layout.lines) {\n for (const run of line.runs) {\n const effSize = run.fontSize ?? fontSize;\n _ctx.font = buildFont(effSize, run.bold ?? false, run.italic ?? false, fontOpts);\n _ctx.fillStyle = fontOpts.color\n ?? (run.italic && !run.bold ? 'rgba(255, 255, 255, 0.7)' : '#FFFFFF');\n const lineOffset = (layout.width - line.width) / 2;\n _ctx.fillText(run.text, x + lineOffset + run.x, lineY);\n }\n lineY += line.height;\n }\n };\n\n const strokeRenderer: TextRenderer = (_ctx, _text, x, y) => {\n let lineY = y;\n for (const line of layout.lines) {\n for (const run of line.runs) {\n const effSize = run.fontSize ?? fontSize;\n _ctx.font = buildFont(effSize, run.bold ?? false, run.italic ?? false, fontOpts);\n const lineOffset = (layout.width - line.width) / 2;\n _ctx.strokeText(run.text, x + lineOffset + run.x, lineY);\n }\n lineY += line.height;\n }\n };\n\n return { renderer, strokeRenderer, width: layout.width, height: layout.height };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/runs.ts","../src/runs/resolveRuns.ts","../src/measure/measureText.ts","../src/measure/measureTextBounds.ts","../src/measure/verticalAlign.ts","../src/measure/lineBoxes.ts","../src/markdownText.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Canonical inline-styling primitive for text nodes. A node's text is\n * either a plain `string` (treated as a single-run, default-styled fragment)\n * or `StyledRun[]` for rich content. `toRuns` is the funnel that normalizes\n * either form into the array shape used by the renderer.\n *\n * Every field except `text` is optional; missing fields fall back to the\n * node-level `TextStyle`. `bold`/`italic` are toggles; richer weight axes\n * (300/500/900) are out of scope for slice 1.\n */\n\nimport type { FillStyle, Stroke } from '@weasel-js/paint';\nimport type { TextStyle } from './textStyle';\n\n/** A span of text with its own styling, as authored. Fields left absent\n * inherit from the node's text style — this is the difference between a run\n * and a fully resolved one. */\nexport interface StyledRun {\n text: string;\n bold?: boolean;\n italic?: boolean;\n fontFamily?: string;\n fontSize?: number;\n fill?: FillStyle;\n /** Outline over this run's glyphs. Overrides the node style's stroke;\n * absent inherits it. Only painted on the outline tier — see\n * {@link TextStyle.stroke}. */\n stroke?: Stroke;\n letterSpacing?: number;\n underline?: boolean;\n strikethrough?: boolean;\n /** Draw a rule above this run's ascent. Additive over the node style. */\n overline?: boolean;\n /**\n * Set this run as a superscript or subscript — a raised or lowered baseline\n * and a smaller size together, which is the pair `<sup>` and `<sub>` imply.\n *\n * A preset over the two primitives below, not a third mechanism: it supplies\n * a `baselineShift` and a `fontScale`, and naming either of those directly\n * overrides that half while leaving the other alone. The numbers are in\n * {@link SCRIPT_METRICS}.\n *\n * There is no node-level counterpart. A whole text node set as a superscript\n * is a smaller node moved up, which the pose already says better.\n */\n script?: 'super' | 'sub';\n /**\n * Raise (positive) or lower (negative) this run off the line's shared\n * baseline, in ems of the *inherited* font size — so a run's rise does not\n * shrink along with the run when `fontScale` also applies.\n */\n baselineShift?: number;\n /**\n * Multiplier on the inherited font size. The relative counterpart to\n * `fontSize`, which pins an absolute size and wins over this when both are\n * present.\n */\n fontScale?: number;\n}\n\n/** Normalize the two accepted spellings of text content — a plain string or\n * an array of runs — to runs. Throws on a run with no string `text`. */\nexport function toRuns(input: string | StyledRun[]): StyledRun[] {\n if (typeof input === 'string') {\n return input.length === 0 ? [] : [{ text: input }];\n }\n for (let i = 0; i < input.length; i++) {\n const r = input[i];\n if (typeof r?.text !== 'string') {\n throw new Error(`toRuns: run at index ${i} is missing string \\`text\\``);\n }\n }\n return input;\n}\n\n/** Concatenate runs, dropping all styling. */\nexport function runsToPlainText(runs: readonly StyledRun[]): string {\n let out = '';\n for (const r of runs) out += r.text;\n return out;\n}\n\n/** A run flag an inline marker can toggle. The additive style toggles on\n * `StyledRun`; the valued fields (family, size, paint, `script`) have no\n * inline spelling and are not addressable this way. */\nexport type RunFlag = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'overline';\n\n/** One inline marker: a delimiter repeated `repeat` times, and the flags it\n * turns on between its opening and closing occurrence. */\nexport interface RunMarker {\n delimiter: string;\n repeat: number;\n flags: readonly RunFlag[];\n}\n\n/**\n * The inline grammar `runsToMarkdown` writes and `markdownToRuns` reads.\n *\n * Markers sharing a delimiter are matched longest-first, which is what lets\n * `***` mean something other than `**` followed by `*`. A backslash escapes\n * any delimiter character and itself, in both directions.\n */\nexport interface RunGrammar {\n markers: readonly RunMarker[];\n}\n\n/**\n * The markdown subset the kit reads and writes: `**bold**`, `*italic*`,\n * `***both***`.\n *\n * Deliberately not the whole of markdown, and deliberately silent on\n * `underline` / `strikethrough` — the two run flags with no entry here are\n * dropped by `runsToMarkdown`, as they always have been. A grammar that wants\n * `~~struck~~` adds one marker; nothing else has to change.\n */\nexport const MARKDOWN_RUN_GRAMMAR: RunGrammar = {\n markers: [\n { delimiter: '*', repeat: 3, flags: ['bold', 'italic'] },\n { delimiter: '*', repeat: 2, flags: ['bold'] },\n { delimiter: '*', repeat: 1, flags: ['italic'] },\n ],\n};\n\n/** Delimiter characters of a grammar, plus the backslash that escapes them. */\nfunction escapees(grammar: RunGrammar): Set<string> {\n const out = new Set<string>(['\\\\']);\n for (const m of grammar.markers) out.add(m.delimiter);\n return out;\n}\n\nfunction escapeFor(text: string, chars: ReadonlySet<string>): string {\n let out = '';\n for (const ch of text) out += chars.has(ch) ? `\\\\${ch}` : ch;\n return out;\n}\n\n/** The flags a run turns on, in the grammar's own marker order so two runs\n * with the same flags always serialize identically. */\nfunction flagsOf(run: StyledRun, grammar: RunGrammar): Set<RunFlag> {\n const on = new Set<RunFlag>();\n for (const m of grammar.markers) {\n for (const f of m.flags) if (run[f]) on.add(f);\n }\n return on;\n}\n\nfunction sameFlags(a: ReadonlySet<RunFlag>, b: readonly RunFlag[]): boolean {\n return a.size === b.length && b.every((f) => a.has(f));\n}\n\n/**\n * Render runs in `grammar`, defaulting to the markdown subset. The flavor\n * written to the clipboard alongside the plain-text one.\n *\n * A run whose flags exactly match one marker takes it; otherwise the markers\n * that cover them nest. Flags no marker spells are dropped — the text still\n * round-trips, without that styling.\n */\nexport function runsToMarkdown(\n runs: readonly StyledRun[],\n grammar: RunGrammar = MARKDOWN_RUN_GRAMMAR,\n): string {\n const chars = escapees(grammar);\n let out = '';\n for (const r of runs) {\n const escaped = escapeFor(r.text, chars);\n const on = flagsOf(r, grammar);\n if (on.size === 0) { out += escaped; continue; }\n\n const exact = grammar.markers.find((m) => sameFlags(on, m.flags));\n if (exact) {\n const d = exact.delimiter.repeat(exact.repeat);\n out += `${d}${escaped}${d}`;\n continue;\n }\n // No single marker says all of it: nest the ones that each say part.\n const covering = grammar.markers.filter(\n (m) => m.flags.length === 1 && on.has(m.flags[0]),\n );\n const open = covering.map((m) => m.delimiter.repeat(m.repeat)).join('');\n const close = [...covering].reverse().map((m) => m.delimiter.repeat(m.repeat)).join('');\n out += `${open}${escaped}${close}`;\n }\n return out;\n}\n\n/**\n * Parse `input` in `grammar`, defaulting to the markdown subset, into styled\n * runs. Newlines are preserved as literal characters inside a run — they are\n * not run-boundary markers in this format.\n */\nexport function markdownToRuns(\n input: string,\n grammar: RunGrammar = MARKDOWN_RUN_GRAMMAR,\n): StyledRun[] {\n const chars = escapees(grammar);\n // Longest-first per delimiter, so `***` is tried before `**` before `*`.\n const byDelimiter = new Map<string, RunMarker[]>();\n for (const m of grammar.markers) {\n const list = byDelimiter.get(m.delimiter) ?? [];\n list.push(m);\n byDelimiter.set(m.delimiter, list);\n }\n for (const list of byDelimiter.values()) list.sort((a, b) => b.repeat - a.repeat);\n\n const runs: StyledRun[] = [];\n const active = new Set<RunFlag>();\n let buf = '';\n let i = 0;\n\n function flush(): void {\n if (buf.length === 0) return;\n const run: StyledRun = { text: buf };\n for (const f of active) run[f] = true;\n runs.push(run);\n buf = '';\n }\n\n while (i < input.length) {\n const ch = input[i];\n\n if (ch === '\\\\' && i + 1 < input.length && chars.has(input[i + 1])) {\n buf += input[i + 1];\n i += 2;\n continue;\n }\n\n const markers = byDelimiter.get(ch);\n if (markers) {\n let count = 0;\n while (i + count < input.length && input[i + count] === ch) count++;\n // The longest marker the delimiter run can pay for. A run shorter than\n // every marker still matches the shortest, which is how a lone `*`\n // toggles italic where `***` toggles both.\n const marker = markers.find((m) => m.repeat <= count) ?? markers[markers.length - 1];\n flush();\n for (const f of marker.flags) {\n if (active.has(f)) active.delete(f);\n else active.add(f);\n }\n i += marker.repeat;\n continue;\n }\n\n buf += ch;\n i++;\n }\n\n flush();\n return runs;\n}\n","/**\n * Apply node-level `ResolvedTextStyle` defaults to each `StyledRun`,\n * producing a fully-resolved run with every styling field set. Downstream\n * layout and draw never re-resolve defaults — `ResolvedRun` is the\n * canonical shape the renderer consumes.\n *\n * `bold`/`italic` toggles on a run are folded into `fontWeight`/`fontStyle`:\n * `bold: true` → fontWeight 700, `italic: true` → fontStyle 'italic'.\n * Explicit `fontFamily` / `fontSize` / `fill` / `letterSpacing` on the run\n * override the node-level value (`letterSpacing: 0` on a run is an override,\n * not an absence — it zeroes inherited tracking).\n *\n * `underline` / `strikethrough` / `overline` are *additive*, like\n * `bold`/`italic`: a run can turn a decoration on but never off, so they\n * resolve as `run.x || style.x` and not `run.x ?? style.x`. See the header of\n * `runs/rangeStyle.ts` for why the model collapses the tri-state.\n *\n * `script` is folded the same way the toggles are, into the two primitives it\n * is a preset over: `baselineShift` and `fontScale`. Both come out as one\n * world-unit `baselineShift` and a final `fontSize`, so layout never learns\n * that superscripts exist — it places a run against a baseline and an offset.\n */\n\nimport type { FillStyle, Stroke } from '@weasel-js/paint';\nimport type { StyledRun } from '../runs';\nimport type { ResolvedTextStyle } from '../textStyle';\n\n/** A run with every style resolved against the node's text style — no\n * optional inheritance left. This is what layout and painting consume. */\nexport interface ResolvedRun {\n text: string;\n fontFamily: string;\n fontSize: number;\n fontWeight: number;\n fontStyle: 'normal' | 'italic';\n fill: FillStyle;\n /** Outline over this run's glyphs, or absent for none. Painted only on the\n * outline tier — a distance field has no geometry to stroke. */\n stroke?: Stroke;\n /** Extra advance added after each glyph of this run, in world units. */\n letterSpacing: number;\n /** Draw a rule below this run's baseline. Additive over the node style. */\n underline: boolean;\n /** Draw a rule through this run's x-height. Additive over the node style. */\n strikethrough: boolean;\n /** Draw a rule above this run's ascent. Additive over the node style. */\n overline: boolean;\n /**\n * How far this run sits off the line's shared baseline, in world units;\n * positive raises. 0 for ordinary text.\n *\n * Already multiplied out against the inherited font size, and already\n * carrying whatever `script` asked for — layout adds it to a baseline and\n * asks nothing about where it came from.\n */\n baselineShift: number;\n}\n\n/**\n * What `script: 'super'` and `script: 'sub'` expand to, as fractions of the\n * inherited font size: `size` scales it, `shift` raises (positive) or lowers\n * (negative) the baseline.\n *\n * These are Adobe's defaults — InDesign and Illustrator ship 58.3% size and\n * 33.3% position for both — chosen because a drawing tool's users have those\n * numbers in their fingers already. They are not read from the font: the\n * `OS/2` table carries real `ySuperscript*` / `ySubscript*` metrics, but the\n * baked atlas tier has no slot for them (see `layoutRuns`'s note on the\n * decoration constants, which are derived for the same reason), and metrics\n * that applied on one glyph tier and not the other would reflow text as it\n * crossed the size threshold.\n *\n * Exported so a consumer building a character panel can show the percentages\n * it is about to apply. Override either half per run with `baselineShift` /\n * `fontScale`.\n */\nexport const SCRIPT_METRICS: Readonly<\n Record<'super' | 'sub', Readonly<{ size: number; shift: number }>>\n> = Object.freeze({\n super: Object.freeze({ size: 0.583, shift: 0.333 }),\n sub: Object.freeze({ size: 0.583, shift: -0.333 }),\n});\n\nfunction numericWeight(w: number | string): number {\n if (typeof w === 'number') return w;\n if (w === 'bold') return 700;\n if (w === 'normal') return 400;\n const parsed = Number(w);\n return Number.isFinite(parsed) ? parsed : 400;\n}\n\n/** Resolve each run's styling against the node's text style, filling in\n * everything the run left inherited. */\nexport function resolveRuns(\n runs: readonly StyledRun[],\n style: ResolvedTextStyle,\n): ResolvedRun[] {\n const out: ResolvedRun[] = [];\n const baseWeight = numericWeight(style.fontWeight);\n for (const run of runs) {\n const script = run.script ? SCRIPT_METRICS[run.script] : undefined;\n // Against the inherited size, not the run's own: a superscript that also\n // shrank its rise would climb less the smaller it got.\n const shiftEm = run.baselineShift ?? script?.shift ?? 0;\n const scale = run.fontScale ?? script?.size ?? 1;\n out.push({\n text: run.text,\n fontFamily: run.fontFamily ?? style.fontFamily,\n // An absolute size wins over a relative one; naming both is a consumer\n // saying \"this size exactly\", which a multiplier cannot improve on.\n fontSize: run.fontSize ?? style.fontSize * scale,\n fontWeight: run.bold ? 700 : baseWeight,\n fontStyle: run.italic ? 'italic' : style.fontStyle,\n fill: run.fill ?? style.fill,\n // Unlike the decorations below, a run's stroke *replaces* the node's\n // rather than adding to it — there is only one outline to paint.\n ...((run.stroke ?? style.stroke) !== undefined\n ? { stroke: run.stroke ?? style.stroke }\n : {}),\n letterSpacing: run.letterSpacing ?? style.letterSpacing,\n // `||`, not `??`: run-level decorations are additive over the node style.\n underline: run.underline || style.underline,\n strikethrough: run.strikethrough || style.strikethrough,\n overline: run.overline || style.overline,\n baselineShift: shiftEm * style.fontSize,\n });\n }\n return out;\n}\n","/**\n * Wrap-aware text measurement. Greedy word-wrap against `maxWidth` using\n * `ctx.measureText`. Lines explicitly broken by `\\n` are preserved; long\n * single words that exceed `maxWidth` are emitted on their own line without\n * mid-word breaking (caller can decide to clip).\n *\n * Returns the laid-out lines, the total block height in world units\n * (`lines.length * fontSize * lineHeight`), and per-line `lineStarts` —\n * the offset of each line's first character in the original `text`. The\n * starts are used by `caretIndexAt` to map a clicked (x, y) back to a\n * character offset in the source string. Trailing whitespace consumed by\n * the wrap is not included in `lines[i]` but is implicit in the gap\n * between `lineStarts[i] + lines[i].length` and `lineStarts[i + 1]`.\n *\n * The caller owns the `ctx.font` setup — pass a context whose `font`\n * already matches `style` (use `fontString(style)`).\n */\n\nimport type { ResolvedTextStyle } from '../textStyle';\n\n/**\n * Advance width of `text` in world units, tracking included.\n *\n * `letter-spacing` is not part of the CSS `font` shorthand, so a context\n * whose `font` was set from `fontString(style)` measures glyphs only. The\n * GL path (`layoutRuns`) adds `letterSpacing` after **every** code point\n * including the last, matching CSS, so this does too — and every 2D-side\n * width has to go through here or the two paths disagree about where a line\n * breaks.\n */\nexport function measuredWidth(\n ctx: CanvasRenderingContext2D,\n text: string,\n style: ResolvedTextStyle,\n): number {\n return ctx.measureText(text).width + text.length * style.letterSpacing;\n}\n\n/** Result of `measureText`: wrapped lines, per-line source offsets, and total block height. */\nexport interface MeasuredText {\n lines: string[];\n lineStarts: number[];\n height: number;\n}\n\n/** Greedy word-wrap text measurement against `maxWidth`; preserves explicit `\\n` breaks. */\nexport function measureText(\n ctx: CanvasRenderingContext2D,\n text: string,\n maxWidth: number,\n style: ResolvedTextStyle,\n): MeasuredText {\n const lines: string[] = [];\n const lineStarts: number[] = [];\n const paragraphs = text.split('\\n');\n\n let pos = 0;\n for (let pi = 0; pi < paragraphs.length; pi++) {\n if (pi > 0) pos += 1; // step past the '\\n' between paragraphs\n const paragraph = paragraphs[pi];\n if (paragraph === '') {\n lines.push('');\n lineStarts.push(pos);\n continue;\n }\n const words = paragraph.split(/(\\s+)/).filter((w) => w.length > 0);\n let current = '';\n let currentStart = pos;\n let consumed = 0; // chars of `paragraph` we've already accounted for\n for (const word of words) {\n if (current === '') currentStart = pos + consumed;\n const candidate = current + word;\n if (current === '' || measuredWidth(ctx, candidate, style) <= maxWidth) {\n current = candidate;\n } else {\n lines.push(current.trimEnd());\n lineStarts.push(currentStart);\n if (/^\\s+$/.test(word)) {\n current = '';\n } else {\n current = word;\n currentStart = pos + consumed;\n }\n }\n consumed += word.length;\n }\n lines.push(current.trimEnd());\n lineStarts.push(currentStart);\n pos += paragraph.length;\n }\n\n const height = lines.length * style.fontSize * style.lineHeight;\n return { lines, lineStarts, height };\n}\n","import type { TextStyle } from '../textStyle';\nimport { resolveTextStyle } from '../textStyle';\nimport { resolveRuns } from '../runs/resolveRuns';\nimport { cachedLayoutRuns } from '../layout/layoutCache';\n\n/** Options for `measureTextBounds`. */\nexport interface MeasureTextBoundsOpts {\n /** Wrap width; words exceeding it start a new line. Default `Infinity` (no wrap). */\n maxWidth?: number;\n /** Overrides `style`'s `lineHeight` multiplier for this measurement. */\n lineHeight?: number;\n}\n\n/**\n * Measure how the GL/MSDF renderer will lay out a single plain-text string,\n * using the registered font atlas metrics. Mirrors {@link textCommand} exactly\n * (`resolveTextStyle` → `resolveRuns` → `cachedLayoutRuns`), so the returned\n * bounds match what actually gets drawn — use it to size backgrounds/pills, place\n * labels, or test overlap without guessing widths.\n *\n * Pass `opts.maxWidth` to measure word-wrapped height (e.g. for a fixed-width\n * text box); omitting it measures the unwrapped single-line width. `opts.lineHeight`\n * overrides the style's multiplier without changing `style` itself.\n *\n * Returns `{ width, height }` in the same units as `style.fontSize`. The font\n * must already be registered via `registerFont`; an unregistered family falls\n * back to the atlas fallback glyph (and warns), so register fonts at app boot\n * before measuring.\n */\nexport function measureTextBounds(\n text: string,\n style?: TextStyle,\n opts?: MeasureTextBoundsOpts,\n): { width: number; height: number } {\n const resolved = resolveTextStyle(style);\n const runs = resolveRuns([{ text }], resolved);\n const { bounds } = cachedLayoutRuns(runs, {\n maxWidth: opts?.maxWidth ?? Infinity,\n lineHeight: opts?.lineHeight ?? resolved.lineHeight,\n align: resolved.align,\n });\n // Copied: the layout behind it is shared, and handing a caller a mutable\n // reference into a cache entry is how one measurement corrupts the next.\n return { width: bounds.width, height: bounds.height };\n}\n","/**\n * Box vertical alignment for text draw commands. Given the command's box\n * `height` and the laid-out text block's height, returns the Y offset to\n * apply to every quad. `'top'` (or an undefined `align`, or a missing box\n * `height`) is the legacy behavior: offset 0.\n */\nexport type TextVerticalAlign = 'top' | 'center' | 'bottom';\n\n/** How far to shift laid-out text down inside a box of `boxHeight` to satisfy\n * a vertical alignment. Zero when the box height is unknown or alignment is\n * top. */\nexport function verticalAlignOffset(\n align: TextVerticalAlign | undefined,\n boxHeight: number | undefined,\n textHeight: number,\n): number {\n if (align === undefined || align === 'top' || boxHeight === undefined) return 0;\n const slack = boxHeight - textHeight;\n return align === 'center' ? slack / 2 : slack;\n}\n","/**\n * Where a text node's lines actually sit inside its pose box.\n *\n * A text pose is a *wrap box*, not a bounding box: `\"Away\"` in a 309-unit-wide\n * box leaves most of the box empty, and anything that treats the pose as the\n * node's extent — picking, lasso, clipping, SVG export — claims that empty\n * space. `textLineBoxes` returns the per-line rectangles instead.\n *\n * The numbers come from `cachedLayoutRuns`, the same memoized walk that\n * positions the glyphs, through the same `resolveTextStyle` → `resolveRuns`\n * chain as `textCommand` — so the boxes cannot drift from what is painted. In\n * particular they honor `align` (a centered line reports its own span, not the\n * wrap width) and `verticalAlign` (the block shifts inside `[y, y + height]`\n * exactly as `drawText` shifts the quads).\n *\n * These are line boxes, not ink boxes: each is `fontSize * lineHeight` tall\n * from the pen's line top. Ink can escape vertically at a `lineHeight` low\n * enough — see the `bounds` note at the end of `layoutRuns` — which is the\n * right trade for hit-testing, where a box that hugged the ink would make\n * an `x` harder to click than an `X`.\n */\n\nimport type { Rect } from '@weasel-js/geom';\nimport { resolveTextStyle } from '../textStyle';\nimport { resolveRuns } from '../runs/resolveRuns';\nimport { toRuns } from '../runs';\nimport { cachedLayoutRuns } from '../layout/layoutCache';\nimport { verticalAlignOffset } from './verticalAlign';\nimport type { TextPose } from '../pose';\n\n/** Options for {@link textLineBoxes}. */\nexport interface TextLineBoxesOpts {\n /** Grow every box by this much on all four sides (world units). Default 0.\n * Picking wants a little slack so a single hairline row of text is still\n * grabbable; clipping and export want none. */\n padding?: number;\n /** Keep boxes for blank lines (zero width). Default `false` — a blank line\n * covers no area, so for hit-testing and silhouettes it is noise. Pass\n * `true` when the indices have to line up with the wrapped lines. */\n includeEmpty?: boolean;\n /**\n * Wrap width. Default `pose.width`, which is what `createTextLayer` passes\n * and what `TextPose` means by its box.\n *\n * Pass `Infinity` for a node painted by the built-in `kit:text` painter:\n * that painter deliberately does **not** forward `maxWidth` (see\n * `NodeShape.ts`), so its text does not wrap, and boxes computed with a\n * finite width would wrap where the paint did not.\n */\n maxWidth?: number;\n}\n\n/** Per-line rectangles for a text pose, in world units, in layout order. */\nexport function textLineBoxes(pose: TextPose, opts: TextLineBoxesOpts = {}): Rect[] {\n const padding = opts.padding ?? 0;\n const style = resolveTextStyle(pose.style);\n // `runs` wins when non-empty, matching every painter: empty runs are not a\n // styling, so they fall back to the plain string rather than measure nothing.\n const source = pose.runs && pose.runs.length > 0 ? pose.runs : pose.text;\n const runs = resolveRuns(toRuns(source), style);\n const laid = cachedLayoutRuns(runs, {\n maxWidth: opts.maxWidth ?? pose.width,\n lineHeight: style.lineHeight,\n align: style.align,\n });\n // The same translate `drawText` applies to the quads — `layoutRuns` is\n // origin-relative, so the pose's position lands here — plus the\n // `verticalAlign` shift, so the boxes stay on the text under every setting\n // rather than only the default 'top'.\n const dx = pose.x;\n const dy = pose.y + verticalAlignOffset(pose.verticalAlign, pose.height, laid.bounds.height);\n\n const out: Rect[] = [];\n for (const line of laid.lines) {\n if (!opts.includeEmpty && line.x1 <= line.x0) continue;\n out.push({\n x: line.x0 + dx - padding,\n y: line.y0 + dy - padding,\n width: line.x1 - line.x0 + padding * 2,\n height: line.y1 - line.y0 + padding * 2,\n });\n }\n return out;\n}\n","import { markdownToRuns, type StyledRun } from './runs';\n\nexport type { StyledRun };\n\n/** Width-measurement strategy for `layoutMarkdown`; canvas-backed default supplied by `createMarkdownRenderer`. */\nexport type MeasureFn = (text: string, fontSize: number, bold: boolean, italic: boolean) => number;\n\n/** A `StyledRun` with its computed x-offset relative to the start of its line. */\nexport interface PositionedRun extends StyledRun {\n x: number;\n}\n\n/** A single laid-out line of text: its positioned runs, total width, and computed line height. */\nexport interface LayoutLine {\n runs: PositionedRun[];\n width: number;\n height: number;\n}\n\n/** Output of `layoutMarkdown`: per-line breakdown plus overall block dimensions. */\nexport interface LayoutResult {\n lines: LayoutLine[];\n width: number;\n height: number;\n}\n\n/** Word-wrap parsed runs into lines bounded by `maxWidth`; pass `Infinity` for single-line layout. */\nexport function layoutMarkdown(\n runs: StyledRun[],\n maxWidth: number,\n fontSize: number,\n measure: MeasureFn,\n lineHeightFactor: number = 1.3,\n): LayoutResult {\n if (runs.length === 0) return { lines: [], width: 0, height: 0 };\n\n const lines: LayoutLine[] = [];\n let currentRuns: PositionedRun[] = [];\n let lineX = 0;\n let lineMaxSize = 0;\n\n function commitLine() {\n const effectiveFontSize = lineMaxSize > 0 ? lineMaxSize : fontSize;\n const lineHeight = effectiveFontSize * lineHeightFactor;\n lines.push({ runs: currentRuns, width: lineX, height: lineHeight });\n currentRuns = [];\n lineX = 0;\n lineMaxSize = 0;\n }\n\n function processSegment(segRun: StyledRun) {\n const effectiveSize = segRun.fontSize ?? fontSize;\n lineMaxSize = Math.max(lineMaxSize, effectiveSize);\n\n if (maxWidth === Infinity) {\n const w = measure(segRun.text, effectiveSize, segRun.bold ?? false, segRun.italic ?? false);\n currentRuns.push({ ...segRun, x: lineX });\n lineX += w;\n return;\n }\n\n // Word-wrap: split run text by spaces\n const words = segRun.text.split(/ /);\n let wordBuf = '';\n\n for (let wi = 0; wi < words.length; wi++) {\n const word = words[wi];\n const candidate = wordBuf.length > 0 ? wordBuf + ' ' + word : word;\n const candidateW = measure(candidate, effectiveSize, segRun.bold ?? false, segRun.italic ?? false);\n\n if (lineX + candidateW > maxWidth && (lineX > 0 || wordBuf.length > 0)) {\n // Flush current wordBuf as a run on the current line\n if (wordBuf.length > 0) {\n const w = measure(wordBuf, effectiveSize, segRun.bold ?? false, segRun.italic ?? false);\n currentRuns.push({ ...segRun, text: wordBuf, x: lineX });\n lineX += w;\n }\n commitLine();\n lineMaxSize = Math.max(lineMaxSize, effectiveSize);\n wordBuf = word;\n } else {\n // Either fits, or is an oversized single word starting a fresh line — accept it\n wordBuf = candidate;\n }\n }\n\n // Flush remaining wordBuf\n if (wordBuf.length > 0) {\n const w = measure(wordBuf, effectiveSize, segRun.bold ?? false, segRun.italic ?? false);\n currentRuns.push({ ...segRun, text: wordBuf, x: lineX });\n lineX += w;\n }\n }\n\n for (const run of runs) {\n // markdownToRuns embeds newlines inside runs; split on '\\n' so each\n // segment becomes its own line via commitLine().\n const segments = run.text.split('\\n');\n for (let si = 0; si < segments.length; si++) {\n if (si > 0) commitLine();\n const seg = segments[si];\n if (seg.length > 0) {\n processSegment({ ...run, text: seg });\n }\n }\n }\n\n // Commit final line\n if (currentRuns.length > 0) {\n commitLine();\n }\n\n const width = Math.max(...lines.map((l) => l.width));\n const height = lines.reduce((sum, l) => sum + l.height, 0);\n return { lines, width, height };\n}\n\n/** Pluggable text-painting strategy. The default fills white at `(x, y)`; markdown renderers replace this. */\nexport type TextRenderer = (\n ctx: CanvasRenderingContext2D,\n text: string,\n x: number,\n y: number,\n) => void;\n\n/** Font styling options threaded through `createMarkdownRenderer`. */\nexport interface MarkdownFontOptions {\n /** Font-family spec (e.g. `'\"Iowan Old Style\", Georgia, serif'`). Defaults to `sans-serif`. */\n family?: string;\n /** Numeric weight applied to non-bold runs. Bold runs always use `bold`. Default `normal`. */\n weight?: string | number;\n /** Override fill color. When set, used for all runs (italic and bold). */\n color?: string;\n /** Multiplier applied to font size for line height. Default 1.3. */\n lineHeight?: number;\n}\n\nfunction buildFont(\n fontSize: number,\n bold: boolean,\n italic: boolean,\n opts: MarkdownFontOptions = {},\n): string {\n const family = opts.family ?? 'sans-serif';\n const weight = bold ? 'bold' : (opts.weight !== undefined ? String(opts.weight) : 'normal');\n const parts: string[] = [];\n if (italic) parts.push('italic');\n parts.push(weight);\n parts.push(`${fontSize}px ${family}`);\n return parts.join(' ');\n}\n\nfunction canvasMeasure(ctx: CanvasRenderingContext2D, opts: MarkdownFontOptions = {}): MeasureFn {\n return (text, fontSize, bold, italic) => {\n ctx.font = buildFont(fontSize, bold, italic, opts);\n return ctx.measureText(text).width;\n };\n}\n\n/** Build a fill+stroke `TextRenderer` pair for a markdown string at the given size; pre-computes layout once. */\nexport function createMarkdownRenderer(\n ctx: CanvasRenderingContext2D,\n text: string,\n fontSize: number,\n maxWidth: number = Infinity,\n fontOpts: MarkdownFontOptions = {},\n): { renderer: TextRenderer; strokeRenderer: TextRenderer; width: number; height: number } {\n const measure = canvasMeasure(ctx, fontOpts);\n const parsed = markdownToRuns(text);\n const layout = layoutMarkdown(parsed, maxWidth, fontSize, measure, fontOpts.lineHeight);\n\n const renderer: TextRenderer = (_ctx, _text, x, y) => {\n let lineY = y;\n for (const line of layout.lines) {\n for (const run of line.runs) {\n const effSize = run.fontSize ?? fontSize;\n _ctx.font = buildFont(effSize, run.bold ?? false, run.italic ?? false, fontOpts);\n _ctx.fillStyle = fontOpts.color\n ?? (run.italic && !run.bold ? 'rgba(255, 255, 255, 0.7)' : '#FFFFFF');\n const lineOffset = (layout.width - line.width) / 2;\n _ctx.fillText(run.text, x + lineOffset + run.x, lineY);\n }\n lineY += line.height;\n }\n };\n\n const strokeRenderer: TextRenderer = (_ctx, _text, x, y) => {\n let lineY = y;\n for (const line of layout.lines) {\n for (const run of line.runs) {\n const effSize = run.fontSize ?? fontSize;\n _ctx.font = buildFont(effSize, run.bold ?? false, run.italic ?? false, fontOpts);\n const lineOffset = (layout.width - line.width) / 2;\n _ctx.strokeText(run.text, x + lineOffset + run.x, lineY);\n }\n lineY += line.height;\n }\n };\n\n return { renderer, strokeRenderer, width: layout.width, height: layout.height };\n}\n"]}
|
|
@@ -120,6 +120,21 @@ declare function markdownToRuns(input: string, grammar?: RunGrammar): StyledRun[
|
|
|
120
120
|
* `StyledRun.fill` / `.stroke` override them per range.
|
|
121
121
|
*/
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Horizontal alignment. `start` / `end` resolve against the reading direction;
|
|
125
|
+
* `left` / `right` are absolute. Same five values as CSS `text-align`, with
|
|
126
|
+
* the same split between the relative pair and the absolute pair.
|
|
127
|
+
*/
|
|
128
|
+
type TextAlign = 'left' | 'center' | 'right' | 'start' | 'end';
|
|
129
|
+
/** Reading direction, which is what gives `start` / `end` their meaning. */
|
|
130
|
+
type TextDirection = 'ltr' | 'rtl';
|
|
131
|
+
/**
|
|
132
|
+
* Collapse a possibly reading-order-relative alignment to an absolute edge.
|
|
133
|
+
*
|
|
134
|
+
* Layout works in absolute edges, so this runs once at its entry and `start` /
|
|
135
|
+
* `end` never reach the geometry.
|
|
136
|
+
*/
|
|
137
|
+
declare function resolveAlign(align: TextAlign, direction: TextDirection): 'left' | 'center' | 'right';
|
|
123
138
|
/** User-facing text style. All fields optional; defaults applied at render time via `resolveTextStyle`. */
|
|
124
139
|
interface TextStyle {
|
|
125
140
|
/** Font size in world units. Default 16. */
|
|
@@ -130,8 +145,10 @@ interface TextStyle {
|
|
|
130
145
|
fontWeight?: number | string;
|
|
131
146
|
/** Default `'normal'`. */
|
|
132
147
|
fontStyle?: 'normal' | 'italic';
|
|
133
|
-
/** Default `'left'`. */
|
|
134
|
-
align?:
|
|
148
|
+
/** Default `'left'`. Pass `'start'` to align by reading order instead. */
|
|
149
|
+
align?: TextAlign;
|
|
150
|
+
/** Reading direction, resolving `align: 'start' | 'end'`. Default `'ltr'`. */
|
|
151
|
+
direction?: TextDirection;
|
|
135
152
|
/** Multiplier applied to `fontSize`. Default 1.2. */
|
|
136
153
|
lineHeight?: number;
|
|
137
154
|
/**
|
|
@@ -162,7 +179,9 @@ interface ResolvedTextStyle {
|
|
|
162
179
|
fontFamily: string;
|
|
163
180
|
fontWeight: number | string;
|
|
164
181
|
fontStyle: 'normal' | 'italic';
|
|
165
|
-
|
|
182
|
+
/** Still relative if the author wrote it that way — `direction` resolves it. */
|
|
183
|
+
align: TextAlign;
|
|
184
|
+
direction: TextDirection;
|
|
166
185
|
lineHeight: number;
|
|
167
186
|
fill: FillStyle;
|
|
168
187
|
caretColor: string;
|
|
@@ -288,6 +307,36 @@ declare const SCRIPT_METRICS: Readonly<Record<'super' | 'sub', Readonly<{
|
|
|
288
307
|
* everything the run left inherited. */
|
|
289
308
|
declare function resolveRuns(runs: readonly StyledRun[], style: ResolvedTextStyle): ResolvedRun[];
|
|
290
309
|
|
|
310
|
+
/**
|
|
311
|
+
* The bidi contract, declared here and implemented elsewhere.
|
|
312
|
+
*
|
|
313
|
+
* This package does not depend on `@weasel-js/bidi` and must not: the point of
|
|
314
|
+
* naming the interface on this side is that a consumer who renders no
|
|
315
|
+
* right-to-left text never installs the Unicode tables, and that a different
|
|
316
|
+
* implementation can be substituted without weasel knowing. `@weasel-js/bidi`
|
|
317
|
+
* satisfies this structurally, importing nothing from here.
|
|
318
|
+
*
|
|
319
|
+
* Two calls because the algorithm splits at one place: analysis is per
|
|
320
|
+
* paragraph, reordering is per line. `layoutRuns` wraps between them.
|
|
321
|
+
*/
|
|
322
|
+
/** Opaque to this package — whatever the engine needs to carry between calls. */
|
|
323
|
+
type BidiAnalysis = unknown;
|
|
324
|
+
interface BidiReordering {
|
|
325
|
+
/**
|
|
326
|
+
* Positions in the analysed sequence, in visual order, left to right.
|
|
327
|
+
* A position the engine drops (a formatting control) is simply absent.
|
|
328
|
+
*/
|
|
329
|
+
order: readonly number[];
|
|
330
|
+
/** Resolved embedding level per position in the line's range. */
|
|
331
|
+
levels: ArrayLike<number>;
|
|
332
|
+
}
|
|
333
|
+
interface BidiResolver {
|
|
334
|
+
analyze(codePoints: readonly number[], direction?: 'ltr' | 'rtl' | 'auto'): BidiAnalysis;
|
|
335
|
+
reorder(analysis: BidiAnalysis, start: number, end: number): BidiReordering;
|
|
336
|
+
/** L4 — the mirrored form for a character in a right-to-left run, or null. */
|
|
337
|
+
mirror(cp: number): number | null;
|
|
338
|
+
}
|
|
339
|
+
|
|
291
340
|
/**
|
|
292
341
|
* Runs-aware MSDF layout. Walks `ResolvedRun[]` codepoint-by-codepoint,
|
|
293
342
|
* switching atlas per run via `resolveFontVariant`, applying kerning
|
|
@@ -423,6 +472,42 @@ interface LaidOutDecoration {
|
|
|
423
472
|
y1: number;
|
|
424
473
|
fill: FillStyle;
|
|
425
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* One advance cell: a single code point's slot on a line.
|
|
477
|
+
*
|
|
478
|
+
* A cell spans its code point *plus* the kerning that precedes the next one,
|
|
479
|
+
* so a caret snapped to the midpoint between two `x`s lands where the glyphs
|
|
480
|
+
* were actually painted rather than where an unkerned re-measure would put it.
|
|
481
|
+
*/
|
|
482
|
+
interface LaidOutCell {
|
|
483
|
+
/** UTF-16 offset of this code point in the runs' concatenated text. */
|
|
484
|
+
srcIndex: number;
|
|
485
|
+
/** One past it — `srcIndex + 2` for an astral code point. */
|
|
486
|
+
srcEnd: number;
|
|
487
|
+
cp: number;
|
|
488
|
+
/** Left edge of the cell, post-alignment, origin-relative like `x0`/`x1`.
|
|
489
|
+
*
|
|
490
|
+
* **Not monotonic across `cells`** once a bidi engine reorders a line: cells
|
|
491
|
+
* stay in logical order and their x values do not. Sort on `x` for visual
|
|
492
|
+
* order; never assume `cells[i + 1].x` is this cell's right edge. */
|
|
493
|
+
x: number;
|
|
494
|
+
/** Width of the cell: its glyph's advance plus its run's tracking. */
|
|
495
|
+
advance: number;
|
|
496
|
+
/** Resolved bidi embedding level — even reads left-to-right. 0 with no
|
|
497
|
+
* engine, which is the same as saying the text was laid out logically. */
|
|
498
|
+
level: number;
|
|
499
|
+
/**
|
|
500
|
+
* Whether this code point can put ink on the page. False for a space and
|
|
501
|
+
* for a code point no tier could serve — each of which still holds its
|
|
502
|
+
* slot. A zero-advance combining mark is `true`: it inks without advancing.
|
|
503
|
+
*
|
|
504
|
+
* A property of the character and the face, not of this call: it does not
|
|
505
|
+
* change when a dynamic bake lands or the outline threshold is crossed. A
|
|
506
|
+
* blank-but-servable glyph (NBSP in a face that has one) reports `true`;
|
|
507
|
+
* read the quad or outline geometry for exact ink extents.
|
|
508
|
+
*/
|
|
509
|
+
drawsInk: boolean;
|
|
510
|
+
}
|
|
426
511
|
/**
|
|
427
512
|
* One laid-out line's box, in the same world space as `LaidOutQuad`.
|
|
428
513
|
*
|
|
@@ -445,23 +530,20 @@ interface LaidOutLineBox {
|
|
|
445
530
|
/** The line's baseline, for callers placing carets or rules against it. */
|
|
446
531
|
baselineY: number;
|
|
447
532
|
/**
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
533
|
+
* One cell per code point on this line, left to right. Every code point the
|
|
534
|
+
* line covers has exactly one, drawn or not; only a newline has none, since
|
|
535
|
+
* it separates cells rather than being one. So `cells[i]` is addressable as
|
|
536
|
+
* slot `i` and needs no reconstruction against the source string.
|
|
452
537
|
*
|
|
453
|
-
* A cell
|
|
454
|
-
*
|
|
455
|
-
* glyphs actually were painted rather than where an unkerned re-measure
|
|
456
|
-
* would put them.
|
|
538
|
+
* A caret stop is a cell's `x`, and the stop closing the line is `x1` /
|
|
539
|
+
* `srcEnd` — there is one more stop than cell.
|
|
457
540
|
*/
|
|
458
|
-
|
|
541
|
+
cells: LaidOutCell[];
|
|
459
542
|
/**
|
|
460
|
-
* Source offset
|
|
461
|
-
*
|
|
462
|
-
* serve occupies no cell, and the wrap swallows the break between lines.
|
|
543
|
+
* Source offset just past this line's last cell — the closing caret stop's
|
|
544
|
+
* index, and the only source offset a blank line carries.
|
|
463
545
|
*/
|
|
464
|
-
|
|
546
|
+
srcEnd: number;
|
|
465
547
|
}
|
|
466
548
|
interface LaidOutRuns {
|
|
467
549
|
groups: LaidOutGroup[];
|
|
@@ -481,7 +563,24 @@ interface LaidOutRuns {
|
|
|
481
563
|
interface LayoutRunsOpts {
|
|
482
564
|
maxWidth: number;
|
|
483
565
|
lineHeight: number;
|
|
484
|
-
|
|
566
|
+
/**
|
|
567
|
+
* `start` / `end` resolve against `direction`; `left` / `right` are absolute
|
|
568
|
+
* and ignore it, the way CSS `text-align` treats the same five values.
|
|
569
|
+
*/
|
|
570
|
+
align: TextAlign;
|
|
571
|
+
/**
|
|
572
|
+
* Bidi engine. Omit and the text lays out in logical order, which is correct
|
|
573
|
+
* for left-to-right text and wrong for right-to-left text — see the warning
|
|
574
|
+
* this emits when it detects the latter without one.
|
|
575
|
+
*/
|
|
576
|
+
bidi?: BidiResolver;
|
|
577
|
+
/**
|
|
578
|
+
* Reading direction, for resolving `start` / `end`. Default `'ltr'`.
|
|
579
|
+
*
|
|
580
|
+
* Taken as an input rather than sniffed: this package has no DOM, so a
|
|
581
|
+
* consumer reading `getComputedStyle(box).direction` passes what it found.
|
|
582
|
+
*/
|
|
583
|
+
direction?: TextDirection;
|
|
485
584
|
/**
|
|
486
585
|
* World-space `fontSize` at or above which glyphs are emitted as outline
|
|
487
586
|
* geometry, when the resolved face has outlines registered. Omit (the
|
|
@@ -586,4 +685,4 @@ declare function cachedLayoutRuns(runs: readonly ResolvedRun[], opts: LayoutRuns
|
|
|
586
685
|
/** Test helper. Do not call from product code. */
|
|
587
686
|
declare function _resetLayoutCacheForTests(): void;
|
|
588
687
|
|
|
589
|
-
export { DEFAULT_TEXT_STYLE as D, LAYOUT_CACHE_STRUCTURAL_LIMIT as L, MARKDOWN_RUN_GRAMMAR as M, type ResolvedTextStyle as R, type StyledRun as S, type TextStyle as T, _resetLayoutCacheForTests as _,
|
|
688
|
+
export { runsToMarkdown as A, type BidiAnalysis as B, runsToPlainText as C, DEFAULT_TEXT_STYLE as D, toRuns as E, LAYOUT_CACHE_STRUCTURAL_LIMIT as L, MARKDOWN_RUN_GRAMMAR as M, type ResolvedTextStyle as R, type StyledRun as S, type TextStyle as T, _resetLayoutCacheForTests as _, type BidiReordering as a, type BidiResolver as b, LAYOUT_CACHE_VARIANT_LIMIT as c, type LaidOutCell as d, type LaidOutDecoration as e, type LaidOutGroup as f, type LaidOutLineBox as g, type LaidOutOutlineGlyph as h, type LaidOutQuad as i, type LaidOutRuns as j, type LayoutRunsOpts as k, type ResolvedRun as l, type RunFlag as m, type RunGrammar as n, type RunMarker as o, SCRIPT_METRICS as p, type TextAlign as q, type TextDirection as r, type TextPaint as s, cachedLayoutRuns as t, fontString as u, layoutRuns as v, markdownToRuns as w, resolveAlign as x, resolveRuns as y, resolveTextStyle as z };
|
package/dist/test-seams.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { _ as _resetLayoutCacheForTests } from './test-seams-
|
|
1
|
+
export { _ as _resetLayoutCacheForTests } from './test-seams-ClWSA091.js';
|
|
2
2
|
import '@weasel-js/paint';
|
package/dist/test-seams.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/text",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0-pre.0",
|
|
4
4
|
"description": "Typography for weasel: styled runs, style resolution, kerned glyph layout, wrap and measurement. No scene graph, no React.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"./package.json": "./package.json"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@weasel-js/font": "1.
|
|
24
|
-
"@weasel-js/geom": "1.
|
|
25
|
-
"@weasel-js/paint": "1.
|
|
23
|
+
"@weasel-js/font": "1.3.0-pre.0",
|
|
24
|
+
"@weasel-js/geom": "1.3.0-pre.0",
|
|
25
|
+
"@weasel-js/paint": "1.3.0-pre.0"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"test": "vitest run",
|
|
@@ -49,5 +49,8 @@
|
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public",
|
|
51
51
|
"provenance": true
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@weasel-js/bidi": "1.3.0-pre.0"
|
|
52
55
|
}
|
|
53
56
|
}
|