@heroiclands/package-build 25.2.1 → 25.4.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1 -1
  3. package/assets/styles/content-base.css +97 -116
  4. package/docs/api.md +30 -6
  5. package/docs/authoring/links-and-markup.md +214 -126
  6. package/docs/commands.md +2 -2
  7. package/docs/content-format.md +31 -6
  8. package/docs/diagnostics.md +27 -27
  9. package/docs/guides/writing-in-invented-scripts.md +89 -83
  10. package/docs/reference/format-details.md +6 -7
  11. package/docs/reference/note-types.md +15 -15
  12. package/docs/reference/theme-pages.md +38 -57
  13. package/engine/anchors.mjs +14 -0
  14. package/engine/code-fences.mjs +15 -6
  15. package/engine/content-blocks.mjs +7 -4
  16. package/engine/content-figures.mjs +19 -5
  17. package/engine/content-footnotes.mjs +60 -1
  18. package/engine/content-index.mjs +1 -1
  19. package/engine/content-render.mjs +66 -48
  20. package/engine/content-scripts.mjs +357 -101
  21. package/engine/content-spans.mjs +76 -10
  22. package/engine/content-verse.mjs +687 -358
  23. package/engine/first-use.mjs +43 -12
  24. package/engine/heading-attributes.mjs +2 -1
  25. package/engine/journals.mjs +8 -1
  26. package/engine/markdown-blocks.mjs +3 -2
  27. package/engine/markup-validation.mjs +17 -0
  28. package/engine/name-markup.mjs +1 -0
  29. package/engine/note-vocabulary.mjs +2 -2
  30. package/engine/pdf-render.mjs +89 -130
  31. package/engine/prose-lint.mjs +14 -2
  32. package/engine/roman-verse.mjs +68 -57
  33. package/engine/run-rows.mjs +1 -1
  34. package/engine/sql-tables.mjs +1 -1
  35. package/engine/verse-format.mjs +77 -0
  36. package/engine/verse-metre.mjs +49 -47
  37. package/engine/verse-regions.mjs +181 -0
  38. package/engine/wikilink-pattern.mjs +31 -0
  39. package/engine/wikilink-syntax.mjs +2 -16
  40. package/package.json +1 -1
  41. package/site-theme/static/css/style.css +68 -96
  42. package/types/engine/code-fences.d.mts +1 -3
  43. package/types/engine/content-blocks.d.mts +3 -1
  44. package/types/engine/content-footnotes.d.mts +23 -0
  45. package/types/engine/content-scripts.d.mts +12 -7
  46. package/types/engine/content-spans.d.mts +3 -1
  47. package/types/engine/content-verse.d.mts +263 -82
  48. package/types/engine/name-markup.d.mts +5 -0
  49. package/types/engine/roman-verse.d.mts +7 -6
  50. package/types/engine/verse-format.d.mts +9 -0
  51. package/types/engine/verse-metre.d.mts +4 -4
  52. package/types/engine/verse-regions.d.mts +78 -0
  53. package/types/engine/wikilink-pattern.d.mts +17 -0
  54. package/types/engine/wikilink-syntax.d.mts +1 -41
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 25.4.0
4
+
5
+ **Verse**
6
+
7
+ - Verse is written as a `.verse` div, or quoted inside prose as a `[…]{.verse}` span. An encoded div sets script beside Roman; an encoded quotation flows in the prose as script only.
8
+ - A canto break can carry a heading and an anchor.
9
+ - The formatter leaves verse untouched.
10
+ - Footnotes in an encoded work appear in its Roman column only, with one number and one definition each.
11
+ - A footnote inside any encoded passage appears after its script text, in the ordinary font.
12
+
13
+ **Upgrading**
14
+
15
+ - Rewrite each ```` ```verse ```` fence as `::: {.verse …}` (or `:::verse {…}`) with the same attributes, closed by `:::`. A verse fence is an error.
16
+ - Replace each typed `***` with `--- {.canto}` on its own line after a blank line, with a stanza on both sides. A typed `***` is an error.
17
+
18
+ ## 25.3.0
19
+
20
+ **Verse**
21
+
22
+ - Versions of a work share automatic stanza labels and a check that their stanza shapes agree. An encoded original is written once and appears as aligned script and Roman text. Directions and speaker labels carry plain presentation hooks.
23
+
24
+ **Upgrading**
25
+
26
+ - Verse fences join source newlines: add `/` at intended line endings. Blank lines or `//` end stanzas, and standalone `***` ends a canto.
27
+ - Remove `numstep=` and `cols=` from verse fences and remove column prefixes. Write each original once with its fence-level `lang=` and `enc=`; update the package and convert its content together.
28
+
3
29
  ## 25.2.1
4
30
 
5
31
  **Rendering and sites**
package/README.md CHANGED
@@ -92,7 +92,7 @@ Every icon and image is covered by a provenance record stating `attribution`, `l
92
92
 
93
93
  `package-build cdn publish --set <file> --name <display name>` uploads every icon and image the package has, shipped or not, to `<site.assets>/<contentPackage>/` in that flat layout, and writes the named set listing the pictures the package leaves out. It reads the bucket from `CDN_ENDPOINT`, `CDN_BUCKET`, `CDN_ACCESS_KEY_ID` and `CDN_SECRET_ACCESS_KEY`, in the environment or `.env.local`. See [publish pictures to the CDN](docs/guides/release-and-deploy.md#publish-pictures-to-the-cdn).
94
94
 
95
- Leading captions, generic fenced divs, inline spans, and verse code fences are described in [links and markup](docs/authoring/links-and-markup.md).
95
+ Leading captions, fenced divs, inline spans, and verse quotations are described in [links and markup](docs/authoring/links-and-markup.md).
96
96
 
97
97
  Cypress end-to-end suites use pinned Chrome for Testing 155.0.8059.84 by default
98
98
  on macOS arm64 and Linux x64, downloaded once into a reusable cache. Select an
@@ -124,168 +124,149 @@
124
124
  * the two markers are also the editor preview's stylesheet, with the content
125
125
  * roots replaced by the preview's own. */
126
126
 
127
- /* Verse groups are paragraphs; explicit stanza separators add their own gap. */
128
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .verse {
129
- margin: 1.5rem 0;
130
- line-height: 1.7;
127
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
128
+ .verse
129
+ .verse-stanza
130
+ p {
131
+ margin: 0;
131
132
  }
132
133
 
133
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .verse p {
134
+ /* A quotation stays within its prose paragraph; canto titles retain heading type. */
135
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
136
+ .verse.verse-inline {
137
+ display: inline-block;
138
+ vertical-align: baseline;
139
+ max-inline-size: 100%;
134
140
  margin: 0;
141
+ container-type: normal;
135
142
  }
136
-
137
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .verse p + p {
138
- margin-top: 1em;
143
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
144
+ .verse.verse-inline
145
+ .verse-stanza {
146
+ display: block;
147
+ margin: 0;
148
+ }
149
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
150
+ .verse.verse-inline
151
+ .verse-stanza
152
+ + .verse-stanza {
153
+ margin-top: 0.5em;
154
+ }
155
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
156
+ .verse
157
+ .verse-canto-heading {
158
+ display: block;
159
+ margin: 0.5em 0;
160
+ font-family: sans-serif;
161
+ font-size: 1.1em;
162
+ font-weight: bold;
163
+ line-height: 1.3;
139
164
  }
140
165
 
141
- .verse .verse-line {
166
+ /* Logical stanzas share labels across native and Roman presentations. */
167
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .verse {
168
+ margin: 1.5rem 0;
169
+ line-height: 1.7;
170
+ container-type: inline-size;
171
+ }
172
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
173
+ .verse
174
+ .verse-stanza {
175
+ position: relative;
176
+ margin: 0 0 1em;
177
+ }
178
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
179
+ .verse-labelled
180
+ .verse-stanza {
181
+ padding-inline-start: 3em;
182
+ }
183
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
184
+ .verse
185
+ .verse-label {
186
+ position: absolute;
187
+ inset-inline-start: 0;
188
+ width: 2.5em;
189
+ text-align: end;
190
+ font-size: 0.75em;
191
+ color: color-mix(in srgb, currentColor 60%, transparent);
192
+ }
193
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .verse .verse-line {
142
194
  display: inline-block;
143
195
  margin-inline-start: var(--verse-indent, 0em);
144
196
  white-space: pre-wrap;
145
197
  }
146
-
147
- .verse :is(.verse-line, .verse-part).i1 {
198
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
199
+ .verse
200
+ .verse-line.i1 {
148
201
  --verse-indent: 1.25em;
149
202
  }
150
- .verse :is(.verse-line, .verse-part).i2 {
203
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
204
+ .verse
205
+ .verse-line.i2 {
151
206
  --verse-indent: 2.5em;
152
207
  }
153
- .verse :is(.verse-line, .verse-part).i3 {
208
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
209
+ .verse
210
+ .verse-line.i3 {
154
211
  --verse-indent: 3.75em;
155
212
  }
156
- .verse :is(.verse-line, .verse-part).i4 {
213
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
214
+ .verse
215
+ .verse-line.i4 {
157
216
  --verse-indent: 5em;
158
217
  }
159
- .verse :is(.verse-line, .verse-part).i5 {
160
- --verse-indent: 6.25em;
161
- }
162
- .verse :is(.verse-line, .verse-part).i6 {
163
- --verse-indent: 7.5em;
164
- }
165
- .verse :is(.verse-line, .verse-part).i7 {
166
- --verse-indent: 8.75em;
167
- }
168
- .verse :is(.verse-line, .verse-part).i8 {
169
- --verse-indent: 10em;
170
- }
171
-
172
- .verse .verse-stanza-break {
173
- display: block;
174
- height: 0.5em;
175
- }
176
-
177
- /* Numbered verse carries its numbers in a margin. A verse in columns is one
178
- * grid per verse line: its number, then each column's parts stacked, the
179
- * first in the normal tone and the rest muted, every part in the serif unless
180
- * its face sets it. A narrow journal stacks the columns of each line. */
181
218
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
182
- .verse.verse-numbered {
183
- position: relative;
184
- padding-inline-start: 3em;
219
+ .verse
220
+ .verse-line.i5 {
221
+ --verse-indent: 6.25em;
185
222
  }
186
-
187
223
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
188
224
  .verse
189
- .verse-number {
190
- color: color-mix(in srgb, currentColor 60%, transparent);
191
- font-size: 0.8em;
225
+ .verse-line.i6 {
226
+ --verse-indent: 7.5em;
192
227
  }
193
-
194
228
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
195
- .verse-numbered
196
- .verse-line
197
- .verse-number {
198
- position: absolute;
199
- inset-inline-start: 0;
200
- width: 2.2em;
201
- text-align: end;
229
+ .verse
230
+ .verse-line.i7 {
231
+ --verse-indent: 8.75em;
202
232
  }
203
-
204
233
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
205
- .verse.verse-columns {
206
- container-type: inline-size;
234
+ .verse
235
+ .verse-line.i8 {
236
+ --verse-indent: 10em;
207
237
  }
208
-
209
238
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
210
- .verse-numbered.verse-columns {
211
- padding-inline-start: 0;
239
+ .verse
240
+ .verse-canto-break {
241
+ display: block;
242
+ height: 0.5em;
212
243
  }
213
-
214
244
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
215
- .verse-columns
245
+ .verse-paired
216
246
  .verse-row {
217
247
  display: grid;
218
- grid-auto-flow: column;
219
- grid-auto-columns: 1fr;
220
248
  direction: ltr;
221
- grid-template-columns: repeat(var(--verse-columns, 2), minmax(0, 1fr));
249
+ grid-template-columns: repeat(2, minmax(0, 1fr));
222
250
  column-gap: 1.5rem;
223
- margin-bottom: 0.5em;
224
- }
225
-
226
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
227
- .verse-numbered.verse-columns
228
- .verse-row {
229
- grid-template-columns: 2.5em repeat(var(--verse-columns, 2), minmax(0, 1fr));
230
- }
231
-
232
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
233
- .verse-columns
234
- .verse-number {
235
- text-align: end;
236
- padding-top: 0.2em;
251
+ align-items: start;
237
252
  }
238
-
239
253
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
240
- .verse-columns
241
- :is(.verse-column, .verse-part) {
242
- display: block;
254
+ .verse-paired
255
+ :is(.verse-native, .verse-roman) {
256
+ min-width: 0;
243
257
  white-space: pre-wrap;
244
258
  }
245
-
246
259
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
247
- .verse-columns
248
- .verse-part {
260
+ .verse-paired
261
+ .verse-roman {
249
262
  font-family: serif;
250
- padding-inline-start: var(--verse-indent, 0em);
251
- }
252
-
253
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
254
- .verse-columns
255
- .verse-secondary {
256
- color: color-mix(in srgb, currentColor 60%, transparent);
257
263
  }
258
-
259
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
260
- .verse-columns
261
- .verse-secondary
262
- * {
263
- color: inherit;
264
- }
265
-
266
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
267
- .verse-columns
268
- .verse-part[dir="rtl"] {
269
- text-align: start;
270
- }
271
-
272
264
  @container (max-width: 32rem) {
273
265
  :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
274
- .verse-columns
266
+ .verse-paired
275
267
  .verse-row {
276
- grid-auto-flow: row;
277
268
  grid-template-columns: 1fr;
278
269
  }
279
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
280
- .verse-numbered.verse-columns
281
- .verse-row {
282
- grid-template-columns: 2.5em 1fr;
283
- }
284
- :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
285
- .verse-numbered.verse-columns
286
- .verse-column {
287
- grid-column: 2;
288
- }
289
270
  }
290
271
 
291
272
  /* Text set in one of the faces. The font family and features come from the
package/docs/api.md CHANGED
@@ -1339,6 +1339,29 @@ index for checking page links.
1339
1339
 
1340
1340
  `createRenderer(target, options?)` builds the one markdown-it configuration every target renders through. `target` is `"foundry"`, `"web"`, `"preview"` or `"book"`. The renderer holds the configured parser (`md`), the names of the plugins installed (`plugins`), and three functions: `prepare` writes the constructs markdown-it has no syntax for — marked text, captioned items, named blocks, verse and alerts — as HTML between blank lines; `render` prepares and renders to HTML; `renderInline` renders one inline run. The `book` target parses only, and its Typst stage reads the tokens.
1341
1341
 
1342
+ Verse blocks are `::: {.verse ...}` or `:::verse {...}`; their raw bodies are
1343
+ parsed with inline Markdown through the shared verse structure. Inline
1344
+ `[text/..line//]{.verse}` quotations retain span wrappers inside prose. A blank
1345
+ line followed by `--- {.canto #id title="Heading"}` supplies a titled canto
1346
+ boundary without opening a journal page. `verseDivPlugin` from
1347
+ `engine/verse-regions.mjs` emits `verse_div` tokens before Markdown block
1348
+ interpretation. `renderVerseQuotes` emits inline-safe HTML from the same logical
1349
+ model; the book's `verse_inline` token parses quoted lines in the note's footnote
1350
+ environment. A canto title is heading metadata, while its encoder receives the
1351
+ standard Roman canto control. Encoded verse divs produce paired
1352
+ script and Roman presentations. Encoded verse spans produce ordinary script-only
1353
+ inline text with encoded verse controls, while unencoded quotations retain their
1354
+ Roman line and stanza layout. Defined footnote references are excluded before
1355
+ encoding: divs retain the references only in their Roman presentation; all
1356
+ encoded spans restore references after their script wrapper. Display generation
1357
+ adds no duplicate reference, number, definition or backlink.
1358
+
1359
+ `parseVerse(body, {footnotes})` accepts the same-note set of defined footnote
1360
+ labels when parsing a detached body; `scanVerse` obtains that context from the
1361
+ complete source. Reference-only positions contribute no work shape. A wholly
1362
+ reference-only body carries its references in `model.notes`, rendered once
1363
+ outside native-font wrappers without a verse row or label.
1364
+
1342
1365
  | Export | Signature | Returns | Use it when |
1343
1366
  | ---------------- | ---------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
1344
1367
  | `createRenderer` | `createRenderer(target, options?)` | `{ target, md, plugins, prepare, render, renderInline }` | Rendering a note body for one target with the rules every target shares. |
@@ -2497,12 +2520,13 @@ Not a JavaScript module — this subpath entry exists so tooling (bundlers, `imp
2497
2520
 
2498
2521
  The engine wildcard subpath also exposes the body parsers below. Their figure exports operate on leading captions.
2499
2522
 
2500
- | Module | Exports | Contract |
2501
- | ---------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2502
- | `engine/content-figures.mjs` | `FIGURE_NAMES`, `FIGURE_CLASSES`, `scanFigures`, `numberFigures`, `renderFigureBlocks` | Scan leading captions and their next supported blocks; `numbered` distinguishes `:` and `:@`. `type` may override inference; `example` is supported. `FIGURE_CLASSES` retains built-in styled classes, not a closed author vocabulary. |
2503
- | `engine/content-blocks.mjs` | `BLOCK_NAMES`, `BLOCK_CONTAINERS`, `scanBlocks`, `renderBlocks`, `renderWithheldSections` | Read div and secret fences; generic div records have `name: "div"` and no generated title. Scanning includes verse errors. |
2504
- | `engine/content-verse.mjs` | `verseLines`, `verseStanzas`, `inlineVerse`, `verseMarkdown`, `scanVerse`, `renderVerse` | Read `verse` code fences, validate authored metadata and tabs, and preserve relative indent levels and stanzas. |
2505
- | `engine/content-spans.mjs` | `spanMarkdownPlugin`, `scanSpans`, `renderSpans` | Parse bracket spans after recognized links. The scanner returns spans, the exact `.name` inventory and located errors, retaining visible text and attribute positions; rendering preserves inline Markdown and permitted attributes. |
2523
+ | Module | Exports | Contract |
2524
+ | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2525
+ | `engine/content-figures.mjs` | `FIGURE_NAMES`, `FIGURE_CLASSES`, `scanFigures`, `numberFigures`, `renderFigureBlocks` | Scan leading captions and their next supported blocks; `numbered` distinguishes `:` and `:@`. `type` may override inference; `example` is supported. `FIGURE_CLASSES` retains built-in styled classes, not a closed author vocabulary. |
2526
+ | `engine/content-blocks.mjs` | `BLOCK_NAMES`, `BLOCK_CONTAINERS`, `scanBlocks`, `renderBlocks`, `renderWithheldSections` | Read div and secret fences; generic div records have `name: "div"` and no generated title. Scanning includes verse errors. |
2527
+ | `engine/content-verse.mjs` | `verseLines`, `verseStanzas`, `inlineVerse`, `parseVerse`, `verseMarkdown`, `scanVerse`, `renderVerse`, `renderVerseQuotes` | Read `.verse` divs and quotations, validate metadata and source positions, and share logical lines, stanza labels and titled canto headings across targets. |
2528
+ | `engine/verse-regions.mjs` | `verseRegions`, `verseDivPlugin` | Locate opaque raw verse div regions and install their block token rule before ordinary Markdown interpretation. |
2529
+ | `engine/content-spans.mjs` | `spanMarkdownPlugin`, `scanSpans`, `renderSpans` | Parse bracket spans after recognized links. The scanner returns spans, the exact `.name` inventory and located errors, retaining visible text and attribute positions; rendering preserves inline Markdown and permitted attributes. |
2506
2530
 
2507
2531
  Inline `.secret` spans preserve their inline content, name classes and
2508
2532
  attributes. Site rendering uses a separate show/hide button and content span,