rapier-markdown-kit 1.1.101 → 1.1.104

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.
Files changed (60) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +213 -53
  3. package/dist/agent/vendor/hyphen-en-us/LICENSE +14 -0
  4. package/dist/agent/vendor/hyphen-en-us/SOURCE.json +33 -0
  5. package/dist/agent/vendor/hyphen-en-us/hyph-en-us.hyp.txt +14 -0
  6. package/dist/agent/vendor/hyphen-en-us/hyph-en-us.pat.txt +4938 -0
  7. package/dist/agent/vendor/hyphen-en-us/patterns.js +25 -0
  8. package/dist/agent/vendor/pretext/LICENSE +208 -0
  9. package/dist/agent/vendor/pretext/NOTICE +26 -13
  10. package/dist/agent/vendor/pretext/SOURCE.json +213 -45
  11. package/dist/agent/vendor/pretext/analysis.js +675 -1224
  12. package/dist/agent/vendor/pretext/entry-geometry.js +183 -0
  13. package/dist/agent/vendor/pretext/gecko-line-breaks.js +688 -0
  14. package/dist/agent/vendor/pretext/generated/engine-break-data.js +57 -0
  15. package/dist/agent/vendor/pretext/generated/webkit-generic-families.js +80 -0
  16. package/dist/agent/vendor/pretext/graphemes.js +98 -0
  17. package/dist/agent/vendor/pretext/han-kerning.js +253 -0
  18. package/dist/agent/vendor/pretext/layout.js +352 -290
  19. package/dist/agent/vendor/pretext/line-break.js +1154 -588
  20. package/dist/agent/vendor/pretext/line-breaks.js +892 -0
  21. package/dist/agent/vendor/pretext/line-text.js +81 -39
  22. package/dist/agent/vendor/pretext/measurement.js +1105 -176
  23. package/dist/agent/vendor/pretext/prepare.js +807 -0
  24. package/dist/agent/vendor/pretext/rich-inline.js +1242 -273
  25. package/dist/agent/will.mjs +2 -0
  26. package/dist/kit/assets.mjs +5 -1
  27. package/dist/kit/conformance/run.mjs +4 -4
  28. package/dist/kit/export-fonts.mjs +26 -11
  29. package/dist/kit/index.mjs +1 -0
  30. package/dist/kit/ledger/carried.mjs +9 -7
  31. package/dist/kit/ledger/data.mjs +73 -13
  32. package/dist/kit/ledger/format.mjs +166 -53
  33. package/dist/kit/ledger/hash.mjs +75 -18
  34. package/dist/kit/ledger/history.mjs +143 -115
  35. package/dist/kit/ledger/inverse-source.mjs +158 -19
  36. package/dist/kit/ledger/journal-records.mjs +292 -67
  37. package/dist/kit/model.mjs +1 -1
  38. package/dist/kit/pdf-glyphs.mjs +4 -4
  39. package/dist/kit/render-markdown.mjs +259 -122
  40. package/dist/kit/render-sanitize.mjs +24 -4
  41. package/dist/kit/render-work.mjs +99 -20
  42. package/dist/kit/render.mjs +101 -65
  43. package/dist/kit/typography.mjs +2 -0
  44. package/dist/layout/hyphenation.mjs +127 -0
  45. package/dist/layout/line-plan.mjs +88 -96
  46. package/dist/layout/model.mjs +61 -80
  47. package/dist/layout/paragraph-fit.mjs +191 -0
  48. package/dist/layout/preparation.mjs +373 -0
  49. package/dist/layout/prepared-paragraph.mjs +223 -0
  50. package/dist/layout/typography.mjs +345 -111
  51. package/dist/packages/rapier-diff/index.mjs +288 -196
  52. package/dist/spec/document-fonts.mjs +13 -4
  53. package/dist/spec/md-assets.mjs +149 -30
  54. package/dist/spec/md-layout.mjs +5 -2
  55. package/dist/spec/md-marks.mjs +218 -3
  56. package/dist/spec/md-source.mjs +122 -0
  57. package/package.json +2 -1
  58. package/style.css +9 -0
  59. package/dist/agent/vendor/pretext/bidi.js +0 -151
  60. package/dist/agent/vendor/pretext/generated/bidi-data.js +0 -3831
package/LICENSE CHANGED
@@ -21,8 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
22
 
23
23
  Files under this licence carry `SPDX-License-Identifier: MIT` in their first lines: the standard's
24
- grammar, marks and reference style (`spec/md-layout.mjs`, `spec/md-marks.mjs`, `spec/flowchart.mjs`, `spec/markdown-style.css`), the layout model and line planner
25
- (`layout/model.mjs`, `layout/line-plan.mjs`, `layout/typography.mjs`),
24
+ grammar, marks and reference style (`spec/md-layout.mjs`, `spec/md-marks.mjs`, `spec/md-source.mjs`, `spec/flowchart.mjs`, `spec/markdown-style.css`), the layout model and line planner
25
+ (`layout/model.mjs`, `layout/line-plan.mjs`, `layout/typography.mjs`, `layout/preparation.mjs`),
26
26
  the portable HTML and document metadata helpers (`spec/html-blocks.mjs`, `spec/frontmatter.mjs`,
27
27
  `spec/document-settings.mjs`, `spec/document-fonts.mjs`), the Will grammar (`agent/will.mjs`), the picture appendix reader and writer
28
28
  (`spec/md-assets.mjs`), the two shared-page reference readers (`tools/read-shared-page.mjs`,
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # rapier-markdown-kit
2
2
 
3
3
  Read, render and write Self-contained Markdown in browsers and Node: one `.md` file with pictures, editable SVG
4
- drawings, layout and colour. Build portable documents for people and agents. Will/1 regions mark what an agent
4
+ drawings, layout and precise inline typography. Build portable documents for people and agents. Will/1 regions mark what an agent
5
5
  may edit, append to or preserve. MIT, no dependencies.
6
6
 
7
- - Everything past CommonMark and GFM is an HTML comment other readers ignore or an ordinary reference
8
- definition, so every Markdown app still reads the file.
7
+ - Typography uses ordinary inline HTML spans with validated size, colour and family. Comments and references
8
+ carry other optional facts; each reader chooses its HTML, style, extension and resource support.
9
9
  - One stylesheet renders the document as the editor and its exported page do.
10
10
  - The line planner flows text around a picture's real shape; a conformance suite of thirteen documents runs
11
11
  any implementation against it.
@@ -26,7 +26,7 @@ npm install rapier-markdown-kit
26
26
 
27
27
  ```js
28
28
  import {parseLayout} from 'rapier-markdown-kit/layout'; // the md-layout comment a picture carries
29
- import {scanColorMarkers, formatPageBreak} from 'rapier-markdown-kit/marks'; // colour runs and page breaks
29
+ import {typography, formatPageBreak} from 'rapier-markdown-kit/marks'; // inline typography and page breaks
30
30
  import {flowLines} from 'rapier-markdown-kit/model'; // the line planner: text around pictures
31
31
  import {parseWill} from 'rapier-markdown-kit/will'; // Will/1 markers: keep, append, edit
32
32
  import {parseAssets} from 'rapier-markdown-kit/assets'; // the picture appendix
@@ -119,17 +119,139 @@ family name share the host's font namespace. Print retains its existing factory
119
119
  | Import | What it is |
120
120
  | --- | --- |
121
121
  | `rapier-markdown-kit/layout` | The `md-layout:v1` comment a picture carries: parse it, validate it, write it back. |
122
- | `rapier-markdown-kit/marks` | Text colour and page-break markers. |
122
+ | `rapier-markdown-kit/marks` | Unified typography values, span writer and parser installer; preserved colour/font comments, ink and page breaks. |
123
123
  | `rapier-markdown-kit/model` | Occupancy profiles and the line planner: the wrapping itself. |
124
+ | `rapier-markdown-kit/typography` | Source-preserving paragraph refinement through `createTypography()`. |
124
125
  | `rapier-markdown-kit/will` | The Will/1 grammar (a separate, optional standard): the markers around an agent's intent regions. |
125
126
  | `rapier-markdown-kit/assets` | The picture appendix: the reference definitions a document's pictures live in. It never decodes a picture. |
126
127
  | `rapier-markdown-kit/style.css` | The reference style: type, lists, tables, tasks, pictures and the document's other markup, scoped to `.md-render`. |
127
128
  | `rapier-markdown-kit/rapier-ledger` | The MIT edit-ledger format, replay, authorship projection and offline merge. |
128
129
  | `rapier-markdown-kit/ledger/carried` | Checked optional parts for pages and saved Markdown. |
129
- | `rapier-markdown-kit` | Named exports from the layout, marks, model, Will, assets and render modules, plus the `ledger` namespace. |
130
+ | `rapier-markdown-kit` | Named exports from the layout, marks, model, typography, Will, assets and render modules, plus the `ledger` namespace. |
130
131
 
131
- Every convention is plain Markdown or an HTML comment other readers ignore. Laying lines out as Rapier does takes
132
- the host's font metrics (below).
132
+ The canonical inline form is ordinary HTML inside Markdown. CSS support varies; the host's font metrics still
133
+ determine line layout (below).
134
+
135
+ ## Refined paragraphs
136
+
137
+ The existing comment expresses the paragraph's intent without storing font-dependent line breaks:
138
+
139
+ ```md
140
+ Words stay in their original source. <!--md-layout:v1 align=justify hyphens=auto hanging=on-->
141
+ ```
142
+
143
+ `align=justify` alone keeps native justification. With `hyphens=auto`, the browser adapter chooses a paragraph-wide
144
+ plan using the same pinned Pretext analysis, shaping and actual-font authority as picture wrapping. It prefers
145
+ whole words, bounds ordinary spaces to 60–180% of their natural advance, permits at most two consecutive
146
+ discretionary endings, and starts the final line with a whole word. A short final line costs more; the final line
147
+ is never stretched. Authored compound hyphens are ordinary break opportunities, distinct from discretionary ones.
148
+ In very narrow measures, bounded spaces can leave some ragged edges. There is no paragraph-length switch.
149
+
150
+ Automatic cuts keep at least three graphemes on each side, with at most one automatic split per word. ASCII words
151
+ under `en` and `en-US` use the included Kuiken/Plain TeX pattern data and exceptions. Other English regions,
152
+ non-ASCII words and other admitted languages ask the browser's offline hyphenator for legal positions. That path
153
+ requires native `hyphenate-limit-chars` support; otherwise the paragraph stays native. Available dictionaries and
154
+ their divisions depend on the host. The included American patterns are a deliberate alternative to native data,
155
+ not a promise of identical opportunities in every browser. No language or font data is downloaded.
156
+
157
+ The projection displays a hyphen-minus from the affected text face, measured by Pretext as part of the whole
158
+ prefix. It never adds a source hyphen. Explicit authored `hyphenate-character` stays native. With `hanging=on`,
159
+ measured punctuation extends only into available unclipped margins, by at most half an em. Refinement adds no
160
+ letter tracking; authored supported letter spacing remains part of its shaping contract.
161
+
162
+ ```js
163
+ import {createTypography} from 'rapier-markdown-kit/typography';
164
+
165
+ const refinement = createTypography();
166
+ await document.fonts.ready;
167
+ function update() {
168
+ if (refinement.apply(root).pending) requestAnimationFrame(update);
169
+ }
170
+ update();
171
+ for (const event of ['copy', 'cut']) document.addEventListener(event, () => {
172
+ if (refinement.clearSelection(root)) requestAnimationFrame(update);
173
+ }, true);
174
+ // On a width change, call update() again. Source/style/font changes are revalidated.
175
+ // Before native editing or consuming this disposable DOM projection:
176
+ refinement.clear(root);
177
+ // When retiring the root:
178
+ refinement.retire(root);
179
+ ```
180
+
181
+ `apply()` works on visible paragraphs and yields at completed preparation phases or word batches after an 8 ms
182
+ allowance. A single native shaping call can exceed that allowance; it is not a hard execution deadline. Pending
183
+ paragraphs remain native and readable. Each continuation revalidates source, style and font authority, and chooses
184
+ lines at the current width only after all required metrics finish. `{all:true}` performs the same complete work
185
+ synchronously for explicit capture or a print host with an established content width. Width changes reuse current measured geometry, and unchanged break
186
+ topology reuses projection nodes. Bind font loading, completion and failure to `invalidateFonts(document)` and
187
+ schedule `apply()`; a changed font generation removes stale projections before new preparation.
188
+
189
+ The adapter retains original text, semantic elements and listeners, backward selections, hard breaks, protected
190
+ code/nowrap atoms, NBSP, SHY and Unicode source coordinates. `clearSelection()` restores selected inline trees
191
+ before native clipboard capture; schedule refinement afterward as above. The editor and complete exported pages
192
+ bind this lifecycle themselves. Active editing/composition stays native. Unsupported
193
+ scripts, direction, shaping controls, initials and picture-flow paragraphs also stay native. The current adapter
194
+ admits horizontal LTR Latin, Greek, Cyrillic and common/inherited characters on Chrome/Chromium; native font
195
+ coverage and language support still apply. A font-family name is never a substitute for a loaded actual face.
196
+
197
+ Complete exported pages carry the same single module graph, CSP nonce, offline language data and font lifecycle.
198
+ Their readiness promise completes after the visible refinement queue; scrolling refines newly visible paragraphs.
199
+ Before print, an authored paper size supplies the fixed content width and all paragraphs finish synchronously.
200
+ Automatic-paper browser printing clears the projection and uses native CSS: `beforeprint` may still report the
201
+ screen viewport rather than the selected paper. Scoped fragments and `scripts:false` output also retain native CSS. The American data's separate permissive notice and exact provenance accompany the kit;
202
+ Pretext retains its own complete notices. These vendor notices are not replaced by the kit's MIT grant.
203
+
204
+ ## One inline typography form
205
+
206
+ ```md
207
+ Plain prose needs no typography markup.
208
+
209
+ A <span style="font-size:13.5pt;color:#175dc4">precise **phrase**</span>.
210
+
211
+ ## <span style="font-size:9.25pt;font-family:serif">Still a semantic H2</span>
212
+
213
+ <span style="font-size:18px;color:#175dc4;font-family:&quot;Coastal Text&quot;">An offline custom run</span>
214
+
215
+ [font:Coastal Text]: data:font/woff2;base64,<font-bytes> '{"family":"Coastal Text","sourceId":"f0123456789abcdef01234567"}'
216
+ ```
217
+
218
+ The font bytes and identity in that last example are placeholders; a host derives identity from admitted bytes.
219
+ A family name alone is not a portable font. Default prose needs no font reference. Custom faces keep the existing
220
+ font-data authority, including a name that collides with a factory face.
221
+
222
+ `typography.normalize({size, color, family})` validates a style. `typography.wrap(style, inlineMarkdown)` writes one
223
+ inline span; split block selections and keep their `#`, list markers and separators outside. `typography.parseStyle`
224
+ returns only admitted facts. `typography.install(md)` installs that interpretation on the caller's markdown-it
225
+ parser, and `typographySourceRanges(source, md)` reports original UTF-16 wrapper ranges through that parser.
226
+ These calls neither mutate the source nor load resources. The installer also reads already-authored colour and
227
+ font comments into the same facts; an ordinary open/save must preserve their bytes, not rewrite them as spans.
228
+
229
+ Sizes accept positive decimal `pt`/`px`, at most 12 fractional digits, up to 1638pt/2184px; 96px = 72pt. New values
230
+ use lowercase units and omit insignificant zeros. Colours become lowercase six-digit RGB hex; opaque integer
231
+ RGB CSSOM values are also read. Families are one validated name, an unquoted CSS generic, or an existing preset
232
+ stack. `sans`, `serif`, `mono`, `system`, `hand` are reserved choice aliases, not quoted custom identities. Styles
233
+ are bounded to 4096 characters, 32 declarations and 64 paired levels. Invalid declarations and unknown properties
234
+ are ignored; invalid/unclosed style strings admit no facts. Inner spans override individual properties. An editor
235
+ clearing a selection removes that property's overrides and preserves the surrounding words' styles.
236
+
237
+ Keep each opener with its first words and each closer with its last words. Soft/hard breaks may stay inside one paragraph; blank lines cannot. An opener alone on a line can become a raw HTML block.
238
+ The old leading colour comment has the same portability fault: it can suppress Markdown parsing or disappear
239
+ with its whole block in a non-HTML conversion. With inline HTML enabled, the new form preserves words, emphasis,
240
+ links and heading rank; GitHub strips the styles, and HTML-disabled readers may show the tags. Rapier's **Copy
241
+ markdown without typography** removes recognized wrappers while retaining semantic Markdown and literal code.
242
+ Its separate Pandoc/Quarto copy option writes bracketed spans deliberately; Pandoc HTML carries the CSS, but
243
+ ordinary LaTeX and native DOCX styling do not follow automatically.
244
+
245
+ Rapier's own Word adapter writes native run properties, rounding to the nearest half-point with positive ties up
246
+ and refusing results outside 1–3276 half-points. The unchanged DOCX source carrier keeps original decimals and units;
247
+ native import reports represented direct-run/character-style sizes without repeating document or paragraph defaults
248
+ as inline marks. It does not reproduce an arbitrary Word style sheet. HTML and print/PDF retain the same admitted facts at the default reference
249
+ unit, independent of editor zoom. No mapping claims identical glyphs or line breaks across applications.
250
+
251
+ See the original [typography guidance](https://github.com/jackskip22/rapier/blob/main/docs/markdown-standard.md#inline-typography),
252
+ [CommonMark raw HTML](https://spec.commonmark.org/0.31.2/#raw-html), [CSS absolute units](https://www.w3.org/TR/css-values-4/#absolute-lengths),
253
+ [Pandoc spans](https://pandoc.org/MANUAL.html#extension-bracketed_spans) and [Quarto spans](https://quarto.org/docs/authoring/markdown-basics.html#divs-and-spans).
254
+ Those specifications retain their own licences.
133
255
 
134
256
  ## Render with the reference style
135
257
 
@@ -142,19 +264,19 @@ are named font families with system fallbacks; no font or external asset is fetc
142
264
  The example below renders trusted Markdown with `markdown-it` and the kit's readers: run
143
265
  `npm install rapier-markdown-kit markdown-it@15`, save it as `render.mjs`, run `node render.mjs` and open
144
266
  `styled.html`, which uses only `style.css`. It shows core Markdown, GFM tables, an aligned paragraph, a sized
145
- picture, a named colour and a page break; tasks, footnotes and diagrams are the caller's plugins. The full markup
267
+ picture, a precise combined run, a semantic heading and a page break; tasks, footnotes and diagrams are the caller's plugins. The full markup
146
268
  contract is [Reference style](https://github.com/jackskip22/rapier/blob/main/docs/markdown-standard.md#reference-style).
147
269
 
148
270
  <!-- reference-style-example -->
149
271
  ```js
150
272
  import MarkdownIt from 'markdown-it';
151
273
  import {parseLayout, formatLayout, imageStyle} from 'rapier-markdown-kit/layout';
152
- import {TEXT_COLOR_NAMES, formatColorRun, formatPageBreak, parseColorOpen, isColorClose, isPageBreakBlock} from 'rapier-markdown-kit/marks';
274
+ import {TEXT_COLOR_NAMES, typography, formatColorRun, formatPageBreak, isPageBreakBlock} from 'rapier-markdown-kit/marks';
153
275
  import {installMarkdownImages} from 'rapier-markdown-kit/assets';
154
276
  import {readFile, writeFile} from 'node:fs/promises';
155
277
  import {pathToFileURL} from 'node:url';
156
278
 
157
- const md = installMarkdownImages(new MarkdownIt({html: true, linkify: true}));
279
+ const md = typography.install(installMarkdownImages(new MarkdownIt({html: true, linkify: true})));
158
280
  md.renderer.rules.table_open = (tokens, index, options, env, renderer) =>
159
281
  '<div class="table-scroll-wrap">\n' + renderer.renderToken(tokens, index, options);
160
282
  md.renderer.rules.table_close = (tokens, index, options, env, renderer) =>
@@ -175,29 +297,6 @@ md.core.ruler.after('inline', 'reference-layout', state => {
175
297
  }
176
298
  }
177
299
  });
178
- md.core.ruler.after('inline', 'reference-colour', state => {
179
- for (const inline of state.tokens) {
180
- if (inline.type !== 'inline') continue;
181
- let open = null, depth = 0;
182
- for (const token of inline.children) {
183
- if (token.type !== 'html_inline') {
184
- depth += token.nesting;
185
- if (open && depth < open.depth) open = null;
186
- continue;
187
- }
188
- if (isColorClose(token.content) && open && depth === open.depth) {
189
- Object.assign(open.token, {type: 'reference_colour_open', tag: 'span', nesting: 1, content: ''});
190
- open.token.attrSet('data-md-color', open.hex);
191
- open.token.attrSet('style', '--md-color:' + open.hex);
192
- Object.assign(token, {type: 'reference_colour_close', tag: 'span', nesting: -1, content: ''});
193
- open = null;
194
- } else {
195
- const hex = parseColorOpen(token.content);
196
- if (hex && !open) open = {token, hex, depth};
197
- }
198
- }
199
- }
200
- });
201
300
  const htmlBlock = md.renderer.rules.html_block;
202
301
  md.renderer.rules.html_block = (tokens, index, options, env, renderer) =>
203
302
  isPageBreakBlock(tokens[index].content)
@@ -213,7 +312,7 @@ export const exampleSource = [
213
312
  '',
214
313
  'One paragraph with **weight**, *emphasis*, `inline code` and a [link](https://example.com).',
215
314
  '',
216
- '## Lists and tables',
315
+ '## ' + typography.wrap({size: '21.5pt', family: 'serif'}, 'Lists and tables'),
217
316
  '',
218
317
  '1. First numbered item',
219
318
  '2. Second numbered item',
@@ -234,7 +333,9 @@ export const exampleSource = [
234
333
  '',
235
334
  'A centred paragraph.' + formatLayout({align: 'center'}),
236
335
  '',
237
- 'A ' + formatColorRun(TEXT_COLOR_NAMES.blue, 'blue phrase') + ' keeps its named colour.',
336
+ 'A ' + formatColorRun(TEXT_COLOR_NAMES.blue, 'blue phrase') + ' keeps its colour.',
337
+ '',
338
+ 'And ' + typography.wrap({size: '13.5pt', color: '#175dc4', family: 'serif'}, '**precise words**') + ' keep their emphasis.',
238
339
  '',
239
340
  '![A square](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aDAkAAAAASUVORK5CYII=)' + formatLayout({width: 45, align: 'center'}),
240
341
  '',
@@ -257,12 +358,12 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
257
358
 
258
359
  ## Example
259
360
 
260
- Save this as `example.mjs` in the folder you installed into and run `node example.mjs`. It prints
261
- `lines: 4 height: 80`.
361
+ Save this as `example.mjs` in the folder you installed into and run `node example.mjs`. It validates
362
+ the fragments' source custody and prints the resulting line count and height for its supplied measurements.
262
363
 
263
364
  ```js
264
365
  import {parseLayout} from 'rapier-markdown-kit/layout';
265
- import {parseProfile, pictureSlices, prepareRun, flowLines, prepareRichInline} from 'rapier-markdown-kit/model';
366
+ import {parseProfile, pictureSlices, createPreparationScope, flowLines, mapFragment} from 'rapier-markdown-kit/model';
266
367
  const {loadDocuments, installMeasurer} = await import(new URL(
267
368
  './dist/kit/conformance/measurer.mjs', import.meta.resolve('rapier-markdown-kit/package.json')));
268
369
 
@@ -281,27 +382,82 @@ const obstacles = pictureSlices(profile, /* x */ 0, /* y */ 0, /* width */ 120,
281
382
 
282
383
  // 3. Plan the lines of a paragraph around those obstacles.
283
384
  const text = 'This anchor paragraph carries enough ordinary prose', font = 'normal 400 17.6px Geist, system-ui, sans-serif';
284
- const run = prepareRun(text, font);
285
- const flow = prepareRichInline([{text: run.raw, font, letterSpacing: 0}]);
385
+ const preparation = createPreparationScope(), paragraph = {};
386
+ const flow = preparation.prepare(paragraph, [{text, font, letterSpacing: 0}], {language: 'en'});
387
+ if (!flow) throw new Error('The host could not prepare this paragraph');
286
388
  const plan = flowLines(flow, /* column width */ 300, /* top */ 0, obstacles, /* line height */ 20, /* min slot */ 40);
287
389
  // Each line carries its own x, y and width, and the text fragments laid into it.
288
- console.log('lines:', plan.lines.length, 'height:', plan.height); // lines: 4 height: 80
390
+ if (!plan || !preparation.current(flow)) throw new Error('No current plan');
391
+ let end = 0;
392
+ for (const line of plan.lines) for (const fragment of line.fragments) {
393
+ const mapped = mapFragment(text, fragment);
394
+ if (!mapped || mapped.start < end || text.slice(end, mapped.start).trim()) throw new Error('Unmapped source');
395
+ end = mapped.end;
396
+ }
397
+ if (text.slice(end).trim()) throw new Error('Unmapped source tail');
398
+ console.log('lines:', plan.lines.length, 'height:', plan.height);
289
399
  ```
290
400
 
291
- A heading, which the style pack balances, takes its font size as an eighth argument
292
- (`flowLines(flow, width, top, obstacles, lineHeight, minSlot, direction, fontSize)`): its lines come out as
293
- Chromium lays `text-wrap: balance`, and greedy wherever an obstacle narrows a line. The block's `text-align` is the ninth
294
- argument (`'start'` by default). A picture moves a block's words only where its outline meets them: when no line of the
401
+ The block's `text-align` is the ninth argument (`'start'` by default). Native balanced headings retain native
402
+ wrapping: the current rich line-range API does not identify every legal balancing candidate. A picture moves a
403
+ block's words only where its outline meets them: when no line of the
295
404
  block, laid and aligned in the whole column as with no picture, meets an obstacle, the plan is that layout (`x` 0, the
296
405
  column's width); otherwise every line flows around the obstacles.
297
406
 
298
407
  ## What the host supplies
299
408
 
300
- `prepareRun` and `prepareRichInline` measure text the way a browser does: they need a global
409
+ Rich preparation measures text through one pinned Pretext graph. It needs a global
301
410
  `OffscreenCanvas` (or a DOM `document`) whose `getContext('2d')` has a settable `font` and a
302
411
  `measureText(text)` returning `{width}`. A browser has one already; in Node, put those two members
303
- in front of whatever shaper you have. The example replays recorded measurements instead, and a
304
- query they never recorded falls back to summing single characters, without kerning or shaping.
412
+ in front of the host's actual shaper and admit its fonts before preparation. The example replays historical
413
+ measurements instead; an unrecorded query falls back to summing single characters. That fallback demonstrates API
414
+ execution only and does not qualify kerning, ligatures, complex scripts or actual browser line selection.
415
+
416
+ Create one `createPreparationScope(document)` for a host document in the same measurement realm. Call
417
+ `scope.prepare(owner, items, {whiteSpace, wordBreak, language})` with a stable paragraph owner and original rich
418
+ items; omitted document support is available for declared Node measurement hosts. Text items accept
419
+ `{text, font, letterSpacing?, break?: 'normal' | 'never', extraWidth?}`; box items accept `{width}`. Paragraph
420
+ `whiteSpace` is `normal` or `pre-wrap`, and `wordBreak` is `normal` or `keep-all`. `language` is the content language,
421
+ not the interface locale. Mixed run languages require native fallback. The scope retains owners weakly and reuses
422
+ preparation across widths. Changes to exact text, item shaping settings or language require a new preparation.
423
+
424
+ The returned handle is opaque. Pass it to `flowLines`, or retain the original ranges from
425
+ `layoutNextRichInlineLineRange` for `materializeRichInlineLineRange`. Materialized fragments give `itemIndex`,
426
+ `sourceStart` and `sourceEnd` in UTF-16 offsets of that item's **original** text; `mapFragment(originalText, fragment)`
427
+ provides the shared display-to-source mapping. `gapItemIndex` names the source style for a collapsed gap. Never decode
428
+ range cursors as old segment/grapheme fields, resegment text to interpret them, JSON-serialize or deep-freeze the
429
+ handle. Storage segments and emergency overflow advances are not a list of legal breaks.
430
+
431
+ `scope.current(handle)` checks the shared actual-font authority before projection. Bind font loading, completion and
432
+ failure to `scope.invalidateFonts()` and schedule reflow. Actual face identity, descriptors and status detect
433
+ same-name replacement and invalidate the vendor's metric cache as well as prepared handles. Width changes do
434
+ neither. Call `scope.forget(owner)` when a block stops participating. One imported graph owns the realm's vendor
435
+ metrics; another bundle or copied vendor tree cannot share that authority. Call `scope.retire()` when replacing or
436
+ retiring a projection scope, so both its handles and the shared vendor metrics are retired. This retains the
437
+ document's known custom-family history, allowing a missing previously declared face to be refused instead of
438
+ silently measured through a fallback. Only the owner replacing the whole document calls
439
+ `scope.retire({documentChanged: true})`, which also resets that document's expected-family history.
440
+
441
+ The browser adapter calls `supportsRichStyle(style, language)` and refuses settings the pinned API cannot represent.
442
+ The API has no independent controls for kerning, OpenType features, variation settings, optical sizing, word spacing
443
+ or mixed item language; a matching font-family string alone is not proof of matching actual font data. Unsupported
444
+ settings, unavailable or failed faces, complex objects, authored `br`, preserved-whitespace DOM paragraphs and
445
+ balanced headings keep selectable native DOM. Direct prepared-text hosts can use `pre-wrap` with original CRLF and
446
+ hard breaks. The reference combines browser-derived break tables and Canvas metrics with JavaScript flow. The DOM
447
+ adapter admits Chrome/Chromium profiles; Firefox, WebKit and Chrome on iOS retain native DOM. It does not invoke live
448
+ DOM line selection or promise identical pixels in other engines.
449
+ Editor soft-break source tokens and generated-content shells stay native. The semantic export resolves verified
450
+ soft-break tokens to ordinary rendered spaces through its existing owner before wrapping; its source carrier keeps
451
+ the original Markdown unchanged.
452
+ Ordinary picture flow keeps its greedy policy instead of the reference sheet's generic `pretty` preference;
453
+ explicit authored inline `text-wrap:pretty` retains native wrapping. No native pretty optimization is claimed.
454
+ A lone Latin, Greek or Cyrillic word prefix, including an authored no-break-space group, may defer to a later
455
+ wider picture-side slot on the same row when the public engine output fits the entire group there. Decorated
456
+ atoms, mixed-item words, punctuation, discretionary breaks and next-row placement retain the engine policy.
457
+
458
+ `md-layout:v1` remains semantic and engine-independent. No plan or font-dependent pixel carrier is saved. Offline
459
+ complete pages load their admitted embedded faces before enhanced wrapping; fragments and `scripts:false` pages
460
+ retain native wrapping. Fonts and codecs remain explicit host capabilities as described above.
305
461
 
306
462
  `alphaProfile(image)` samples a decoded `<img>` through a canvas, so it needs a browser. Without
307
463
  one, read the saved descriptor with `parseProfile`, or sample the picture with your own image
@@ -317,15 +473,19 @@ node node_modules/rapier-markdown-kit/dist/kit/conformance/run.mjs
317
473
  ```
318
474
 
319
475
  Thirteen small documents, each with the line boxes Rapier's own export produced for it. **Preservation**: every
320
- layout comment round-trips exactly (12 of 12 that carry one). **Presentation**: how closely the planned lines match
321
- (3 agree within 2px, 10 are close with a named cause, none differ; `dist/kit/conformance/README.md`).
476
+ layout comment round-trips exactly (12 of 12 that carry one). **Presentation** compares the plan with historical
477
+ measurements and reports real-trace hits separately from character-sum misses (`dist/kit/conformance/README.md`).
478
+ Its historical line counts are not a current-engine or font-shaping acceptance gate.
322
479
  `--impl path/to/module.mjs` runs another implementation against the same documents; `--tolerance 2` sets the pixel
323
480
  tolerance.
324
481
 
325
482
  ## Licence
326
483
 
327
- MIT, the full text in `LICENSE`; Rapier-owned modules and the stylesheet keep their MIT line. Pretext's licence, notice and
328
- pinned source inventory are in `dist/agent/vendor/pretext/`. The history projection uses the MIT diff core;
484
+ MIT, the full text in `LICENSE`; Rapier-owned modules and the stylesheet keep their MIT line. Pretext's complete licence,
485
+ notice and pinned source inventory are in `dist/agent/vendor/pretext/`. The selected upstream is unreleased main
486
+ `bac1774c0f8b7a92e44b9da43b6d5443d55d0d9b`, despite its package version `0.0.9`. Its upstream licence includes
487
+ Chromium/WebKit BSD, Unicode and ICU notices for derived code and generated data; those notices travel with the kit
488
+ and the exported wrapping runtime. The history projection uses the MIT diff core;
329
489
  its full grant is in `dist/packages/rapier-diff/LICENSE`. The Rapier editor is AGPL-3.0-only and
330
490
  none of it is in this package. The kit carries Rapier's release number, written from the one
331
491
  value the editor's release reads.
@@ -0,0 +1,14 @@
1
+ American English hyphenation patterns (hyph-en-us)
2
+ Copyright (C) 1990, 2004, 2005 Gerard D.C. Kuiken
3
+
4
+ Copying and distribution of this file, with or without modification,
5
+ are permitted in any medium without royalty provided the copyright
6
+ notice and this notice are preserved.
7
+
8
+ Source: hyphenation/tex-hyphen, commit 5684c0f51c0b81133db2efbe60a408b4155a3ff5
9
+ hyph-utf8/tex/generic/hyph-utf8/patterns/tex/hyph-en-us.tex
10
+ The accompanying .pat.txt and .hyp.txt inputs are unchanged. patterns.js is a
11
+ mechanical numeric representation generated by tools/compile-hyphens.mjs.
12
+ These data include the original Plain TeX patterns and the additional patterns
13
+ and exceptions described in the upstream file. No hyphenation implementation
14
+ from that repository or Justice is included.
@@ -0,0 +1,33 @@
1
+ {
2
+ "repository": "https://github.com/hyphenation/tex-hyphen",
3
+ "commit": "5684c0f51c0b81133db2efbe60a408b4155a3ff5",
4
+ "upstreamDirectory": "hyph-utf8/tex/generic/hyph-utf8/patterns/txt/",
5
+ "licenseSource": "hyph-utf8/tex/generic/hyph-utf8/patterns/tex/hyph-en-us.tex",
6
+ "licenseSourceGitBlob": "bd9b757d89074e3e11b590311ce13ba6667544aa",
7
+ "license": "LicenseRef-Kuiken",
8
+ "licenseSha256": "0cf036759a4fb63a3d0bdb919f0dacbe137e225cb6f8f268d2928e368a8c1731",
9
+ "inputs": [
10
+ {
11
+ "path": "hyph-en-us.pat.txt",
12
+ "sourceGitBlob": "f69e835d9dacea688fdc51956ea9243b9d042e51",
13
+ "bytes": 31489,
14
+ "sha256": "0f57318b878b132547ae92db39a6e1d1cf2a05d9008874955d6ecb910007a463"
15
+ },
16
+ {
17
+ "path": "hyph-en-us.hyp.txt",
18
+ "sourceGitBlob": "b87163d4425b7b5b7e2d9f170e486131988706c4",
19
+ "bytes": 169,
20
+ "sha256": "5e785c27e151f878c312991d937b5582615b77737ab30bac413d5967d6aa21da"
21
+ }
22
+ ],
23
+ "generator": "tools/compile-hyphens.mjs",
24
+ "generatorSha256": "b5758c3ceac76ac39a8ba0bf2f45bc88854e4779d4f443a9c0957694305571dd",
25
+ "policy": "Complete pinned data in a static prefix trie, with original MIT Rapier traversal and matcher; en and en-US ASCII words. Other regional English, non-ASCII words and other languages use the native offline hyphenator.",
26
+ "files": [
27
+ {
28
+ "output": "patterns.js",
29
+ "outputBytes": 46360,
30
+ "outputSha256": "e0360f1552704657d79a670f1e2e0b263f6eea42e209866b521cd8ec2aff387d"
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,14 @@
1
+ as-so-ciate
2
+ as-so-ciates
3
+ dec-li-na-tion
4
+ oblig-a-tory
5
+ phil-an-thropic
6
+ present
7
+ presents
8
+ project
9
+ projects
10
+ reci-procity
11
+ re-cog-ni-zance
12
+ ref-or-ma-tion
13
+ ret-ri-bu-tion
14
+ ta-ble