@heroiclands/package-build 22.15.1 → 22.16.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/CHANGELOG.md +95 -0
- package/README.md +39 -678
- package/bin/{content-build.mjs → content-commands.mjs} +197 -118
- package/bin/package-build.mjs +303 -42
- package/bin/report.mjs +1 -1
- package/ci/ci-docker.mjs +6 -3
- package/ci/ci-steps.mjs +6 -4
- package/ci/content-format-reference.mjs +106 -0
- package/config.mjs +22 -2
- package/content-config.mjs +74 -134
- package/docs/api.md +294 -234
- package/docs/authoring/assets.md +31 -0
- package/docs/authoring/dates-and-calendars.md +33 -0
- package/docs/authoring/first-note.md +48 -0
- package/docs/authoring/frontmatter.md +40 -0
- package/docs/authoring/links-and-markup.md +137 -0
- package/docs/commands.md +631 -162
- package/docs/configuration.md +176 -140
- package/docs/content-format.md +50 -4229
- package/docs/design.md +32 -0
- package/docs/diagnostics.md +29 -22
- package/docs/getting-started.md +21 -877
- package/docs/guides/book.md +153 -0
- package/docs/guides/e2e.md +143 -0
- package/docs/guides/icons.md +126 -0
- package/docs/guides/localization.md +120 -0
- package/docs/guides/module-project.md +915 -0
- package/docs/guides/packs.md +167 -0
- package/docs/guides/reference-pages.md +148 -0
- package/docs/guides/release-and-deploy.md +114 -0
- package/docs/guides/site.md +152 -0
- package/docs/guides/system-project.md +45 -0
- package/docs/index.md +16 -0
- package/docs/project-setup.md +54 -35
- package/docs/reference/format-details.md +4420 -0
- package/docs/reference/note-types.md +531 -0
- package/engine/address-render.mjs +26 -0
- package/engine/address.mjs +3 -20
- package/engine/anchors.mjs +15 -6
- package/engine/asset-bindings.mjs +6 -2
- package/engine/base-compiler.mjs +47 -11
- package/engine/being-measurements.mjs +70 -0
- package/engine/calendar-artifacts.mjs +1 -1
- package/engine/calendar-choice.mjs +32 -0
- package/engine/calendar-human.mjs +71 -0
- package/engine/calendar-notes.mjs +41 -3
- package/engine/calendars.mjs +8 -1
- package/engine/changelog-lint.mjs +2 -2
- package/engine/code-fences.mjs +23 -97
- package/engine/compile-corpus.mjs +6 -7
- package/engine/content-admonitions.mjs +114 -0
- package/engine/content-captions.mjs +181 -0
- package/engine/content-embeds.mjs +2 -2
- package/engine/content-footnotes.mjs +34 -0
- package/engine/content-format-check.mjs +2 -2
- package/engine/content-format.mjs +127 -73
- package/engine/content-format.yaml +745 -0
- package/engine/content-html.mjs +1 -1
- package/engine/content-icons.mjs +45 -92
- package/engine/content-images.mjs +22 -74
- package/engine/content-index.mjs +12 -3
- package/engine/content-links.mjs +21 -26
- package/engine/content-lint.mjs +1 -5
- package/engine/content-secrets.mjs +3 -1
- package/engine/content-tables.mjs +47 -1257
- package/engine/culture-choice.mjs +32 -0
- package/engine/date-conversion.mjs +47 -0
- package/engine/declaration-check.mjs +87 -0
- package/engine/docs-checks.mjs +140 -0
- package/engine/documentation-links.mjs +31 -0
- package/engine/exported-scene.mjs +1 -1
- package/engine/extension-attributes.mjs +74 -0
- package/engine/field-reference.mjs +1 -1
- package/engine/field-spec.mjs +4 -0
- package/engine/foreign-catalog.mjs +5 -5
- package/engine/format-generated.mjs +15 -0
- package/engine/foundry-entries.mjs +3 -1
- package/engine/frontmatter-lint.mjs +5 -1
- package/engine/frontmatter.mjs +10 -0
- package/engine/generate.mjs +2 -2
- package/engine/helpers.mjs +71 -117
- package/engine/homepage.mjs +2 -3
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +18 -3
- package/engine/infobox-registry.mjs +3 -1
- package/engine/infobox.mjs +38 -20
- package/engine/item-compiler.mjs +10 -8
- package/engine/item-docs.mjs +7 -6
- package/engine/journals.mjs +51 -10
- package/engine/macros.mjs +1 -1
- package/engine/map-build.mjs +1 -1
- package/engine/map-dot.mjs +1 -1
- package/engine/markdown-expressions.mjs +117 -0
- package/engine/metadata-index.mjs +7 -3
- package/engine/note-addresses.mjs +1 -0
- package/engine/note-dates.mjs +283 -61
- package/engine/note-package.mjs +5 -25
- package/engine/note-state.mjs +6 -2
- package/engine/note-vocabulary.mjs +74 -19
- package/engine/number-words.mjs +75 -0
- package/engine/office-holders.mjs +188 -0
- package/engine/pack-config.mjs +2 -15
- package/engine/pack-router.mjs +24 -5
- package/engine/packages.mjs +3 -2
- package/engine/pathnames.mjs +21 -24
- package/engine/pdf-build.mjs +217 -55
- package/engine/pdf-fonts.mjs +2 -2
- package/engine/pdf-images.mjs +82 -0
- package/engine/pdf-render.mjs +174 -22
- package/engine/populations.mjs +3 -2
- package/engine/project-init.mjs +499 -0
- package/engine/prose-config.mjs +1 -1
- package/engine/prose-lint.mjs +7 -6
- package/engine/readability-lint.mjs +86 -0
- package/engine/reckoning-markers.mjs +205 -93
- package/engine/scenes.mjs +5 -2
- package/engine/schema-check.mjs +3 -3
- package/engine/site-build.mjs +87 -37
- package/engine/site-config.mjs +8 -8
- package/engine/site-index.mjs +4 -0
- package/engine/site-maps.mjs +1 -1
- package/engine/site-preview.mjs +19 -8
- package/engine/social-ties.mjs +5 -1
- package/engine/sql-tables.mjs +92 -22
- package/engine/web-wikilinks.mjs +15 -31
- package/engine/wikilink-syntax.mjs +4 -1
- package/engine/wikilinks.mjs +12 -14
- package/hm3/item-fields.mjs +1 -1
- package/hm3/items.mjs +12 -45
- package/index.mjs +2 -14
- package/manifest.mjs +31 -4
- package/markdownlint-config.mjs +1 -1
- package/package.json +32 -11
- package/prettier-config.mjs +1 -1
- package/sohl/being-info.mjs +3 -145
- package/sohl/index.mjs +1 -1
- package/sohl/infobox.mjs +2 -1
- package/sohl/item-builders.mjs +3 -27
- package/sohl/item-fields.mjs +61 -22
- package/sohl/kb-passes.mjs +2 -2
- package/sohl/note-schemas.mjs +0 -8
- package/stage.mjs +19 -0
- package/types/config.d.mts +12 -3
- package/types/content-config.d.mts +15 -81
- package/types/engine/address-render.d.mts +13 -0
- package/types/engine/address.d.mts +2 -11
- package/types/engine/anchors.d.mts +3 -4
- package/types/engine/asset-bindings.d.mts +9 -2
- package/types/engine/base-compiler.d.mts +1 -2
- package/types/engine/being-measurements.d.mts +16 -0
- package/types/engine/calendar-choice.d.mts +14 -0
- package/types/engine/calendar-human.d.mts +4 -0
- package/types/engine/calendar-notes.d.mts +2 -0
- package/types/engine/calendars.d.mts +5 -3
- package/types/engine/code-fences.d.mts +6 -72
- package/types/engine/compile-corpus.d.mts +1 -3
- package/types/engine/content-admonitions.d.mts +24 -0
- package/types/engine/content-captions.d.mts +45 -0
- package/types/engine/content-footnotes.d.mts +10 -0
- package/types/engine/content-format-check.d.mts +17 -4
- package/types/engine/content-format.d.mts +24 -11
- package/types/engine/content-icons.d.mts +14 -23
- package/types/engine/content-images.d.mts +2 -2
- package/types/engine/content-index.d.mts +9 -2
- package/types/engine/content-links.d.mts +3 -5
- package/types/engine/content-tables.d.mts +11 -140
- package/types/engine/culture-choice.d.mts +14 -0
- package/types/engine/date-conversion.d.mts +4 -0
- package/types/engine/declaration-check.d.mts +24 -0
- package/types/engine/docs-checks.d.mts +28 -0
- package/types/engine/documentation-links.d.mts +7 -0
- package/types/engine/exported-scene.d.mts +6 -2
- package/types/engine/extension-attributes.d.mts +14 -0
- package/types/engine/field-spec.d.mts +9 -0
- package/types/engine/format-generated.d.mts +7 -0
- package/types/engine/frontmatter.d.mts +14 -4
- package/types/engine/generate.d.mts +1 -1
- package/types/engine/helpers.d.mts +37 -92
- package/types/engine/homepage.d.mts +2 -3
- package/types/engine/index.d.mts +5 -0
- package/types/engine/infobox-registry.d.mts +3 -1
- package/types/engine/infobox.d.mts +17 -6
- package/types/engine/item-compiler.d.mts +4 -4
- package/types/engine/item-docs.d.mts +4 -3
- package/types/engine/journals.d.mts +10 -7
- package/types/engine/map-graphviz.d.mts +1 -1
- package/types/engine/markdown-expressions.d.mts +17 -0
- package/types/engine/note-dates.d.mts +47 -1
- package/types/engine/note-package.d.mts +3 -19
- package/types/engine/note-state.d.mts +2 -1
- package/types/engine/note-vocabulary.d.mts +9 -0
- package/types/engine/number-words.d.mts +4 -0
- package/types/engine/office-holders.d.mts +14 -0
- package/types/engine/pack-config.d.mts +1 -1
- package/types/engine/packages.d.mts +3 -2
- package/types/engine/pdf-images.d.mts +38 -0
- package/types/engine/populations.d.mts +2 -2
- package/types/engine/project-init.d.mts +17 -0
- package/types/engine/prose-config.d.mts +1 -1
- package/types/engine/prose-lint.d.mts +3 -2
- package/types/engine/readability-lint.d.mts +22 -0
- package/types/engine/reckoning-markers.d.mts +4 -2
- package/types/engine/schema-check.d.mts +3 -3
- package/types/engine/site-build.d.mts +38 -28
- package/types/engine/site-config.d.mts +2 -2
- package/types/engine/site-index.d.mts +4 -0
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/social-ties.d.mts +8 -2
- package/types/engine/sql-tables.d.mts +13 -2
- package/types/engine/web-wikilinks.d.mts +2 -1
- package/types/engine/wikilinks.d.mts +7 -1
- package/types/hm3/items.d.mts +5 -6
- package/types/labels.d.mts +40 -0
- package/types/manifest.d.mts +3 -3
- package/types/sohl/actors.d.mts +2 -2
- package/types/sohl/being-info.d.mts +3 -43
- package/types/sohl/index.d.mts +1 -1
- package/types/sohl/infobox.d.mts +5 -2
- package/types/sohl/item-fields.d.mts +4 -13
- package/types/stage.d.mts +9 -0
- package/CONTENT.md +0 -3239
package/engine/pdf-render.mjs
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
*
|
|
52
52
|
* ## Icons
|
|
53
53
|
*
|
|
54
|
-
* `:icon
|
|
54
|
+
* `:icon star-outline:` is parsed by the *same* {@link module:engine/content-icons.iconPlugin}
|
|
55
55
|
* the journals and the website use — one rule, three surfaces — and only the
|
|
56
56
|
* output differs. The glyph is resolved from the font file the consumer named,
|
|
57
57
|
* because the registry deliberately holds no codepoints; when no font is
|
|
@@ -62,9 +62,11 @@
|
|
|
62
62
|
*/
|
|
63
63
|
|
|
64
64
|
import MarkdownIt from "markdown-it";
|
|
65
|
+
import footnotePlugin from "markdown-it-footnote";
|
|
66
|
+
import deflistPlugin from "markdown-it-deflist";
|
|
65
67
|
|
|
66
68
|
import { bookDraftNoticePreamble } from "./draft-notice.mjs";
|
|
67
|
-
import { iconPlugin, ICON_PATTERN } from "./content-icons.mjs";
|
|
69
|
+
import { iconPlugin, ICON_PATTERN, ICON_SIZES } from "./content-icons.mjs";
|
|
68
70
|
import { IMAGE_CLASSES, IMAGE_FLOATS, imagePlugin } from "./content-images.mjs";
|
|
69
71
|
|
|
70
72
|
/** Requested print width for every named image size. */
|
|
@@ -77,6 +79,9 @@ export const BOOK_IMAGE_WIDTHS = Object.freeze({
|
|
|
77
79
|
"full-width": '"full-width"',
|
|
78
80
|
});
|
|
79
81
|
import { slugify } from "./content-slug.mjs";
|
|
82
|
+
import { scanCaptions } from "./content-captions.mjs";
|
|
83
|
+
import { scanAdmonitions } from "./content-admonitions.mjs";
|
|
84
|
+
import { separateFootnotes } from "./content-footnotes.mjs";
|
|
80
85
|
|
|
81
86
|
/**
|
|
82
87
|
* Characters that mean something to Typst's markup parser.
|
|
@@ -154,6 +159,8 @@ export function labelFor(anchor) {
|
|
|
154
159
|
*/
|
|
155
160
|
export function createParser(registry) {
|
|
156
161
|
const md = new MarkdownIt({ html: false, linkify: false, typographer: false });
|
|
162
|
+
md.use(footnotePlugin);
|
|
163
|
+
md.use(deflistPlugin);
|
|
157
164
|
md.use(iconPlugin(registry));
|
|
158
165
|
// The same plugin the HTML surfaces use, so one directive is read once and
|
|
159
166
|
// three renderers read the same `meta` off the same token.
|
|
@@ -192,19 +199,110 @@ export function markdownToTypst(markdown, opts = {}) {
|
|
|
192
199
|
headingOffset = 0,
|
|
193
200
|
anchorPrefix = "",
|
|
194
201
|
} = opts;
|
|
195
|
-
const
|
|
202
|
+
const separated =
|
|
203
|
+
opts.footnoteDefinitions === undefined ? separateFootnotes(String(markdown ?? "")) : null;
|
|
204
|
+
const source = separated?.markdown ?? String(markdown ?? "");
|
|
205
|
+
const definitions = opts.footnoteDefinitions ?? separated?.definitions ?? "";
|
|
206
|
+
const footnoteState = opts.footnoteState ?? { seen: new Set() };
|
|
207
|
+
const sharedOptions = { ...opts, footnoteDefinitions: definitions, footnoteState };
|
|
208
|
+
if (!opts.insideAdmonition) {
|
|
209
|
+
const { blocks } = scanAdmonitions(source);
|
|
210
|
+
if (blocks.length) {
|
|
211
|
+
const lines = source.split("\n");
|
|
212
|
+
const output = [];
|
|
213
|
+
let cursor = 0;
|
|
214
|
+
for (const block of blocks) {
|
|
215
|
+
output.push(
|
|
216
|
+
markdownToTypst(lines.slice(cursor, block.start).join("\n"), {
|
|
217
|
+
...sharedOptions,
|
|
218
|
+
insideAdmonition: true,
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
const color = block.kind === "warn" ? "#9a6700" : "#2f6f9f";
|
|
222
|
+
const background = block.kind === "warn" ? "#fff5db" : "#eef6fb";
|
|
223
|
+
const label = block.kind === "warn" ? "Warning" : "Info";
|
|
224
|
+
const symbol = block.kind === "warn" ? "!" : "i";
|
|
225
|
+
const content = markdownToTypst(block.body, {
|
|
226
|
+
...sharedOptions,
|
|
227
|
+
insideAdmonition: true,
|
|
228
|
+
});
|
|
229
|
+
output.push(
|
|
230
|
+
`\n#block(width: 100%, fill: rgb("${background}"), stroke: (left: 2pt + rgb("${color}")), inset: 8pt, above: 0.7em, below: 0.7em)[#text(fill: rgb("${color}"), weight: "bold")[${symbol} ${label}]\n\n${content}]\n`,
|
|
231
|
+
);
|
|
232
|
+
cursor = block.end + 1;
|
|
233
|
+
}
|
|
234
|
+
output.push(
|
|
235
|
+
markdownToTypst(lines.slice(cursor).join("\n"), {
|
|
236
|
+
...sharedOptions,
|
|
237
|
+
insideAdmonition: true,
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
240
|
+
return output.join("\n");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
196
243
|
// One map for the whole body, not one per block: a heading inside a
|
|
197
244
|
// blockquote or a list item shares the entry's anchor namespace with every
|
|
198
245
|
// other heading in the same body, because `sectionLabel` scopes by entry
|
|
199
246
|
// rather than by container.
|
|
200
|
-
|
|
247
|
+
const ctx = {
|
|
248
|
+
md,
|
|
201
249
|
links,
|
|
202
250
|
glyphs,
|
|
203
251
|
images,
|
|
204
252
|
headingOffset,
|
|
205
253
|
anchorPrefix,
|
|
206
254
|
seen: new Map(),
|
|
207
|
-
|
|
255
|
+
footnoteState,
|
|
256
|
+
footnotePrefix: opts.footnotePrefix ?? `footnote-${slugify(anchorPrefix || "body")}`,
|
|
257
|
+
};
|
|
258
|
+
const lines = source.split("\n");
|
|
259
|
+
const localCaptions = scanCaptions(source).captions;
|
|
260
|
+
const numbered = new Map(
|
|
261
|
+
(opts.captions ?? localCaptions).map((caption) => [caption.id, caption]),
|
|
262
|
+
);
|
|
263
|
+
const captions = localCaptions.map((caption) => numbered.get(caption.id) ?? caption);
|
|
264
|
+
if (!captions.length) return renderMarkdownSegment(source, md, ctx, definitions);
|
|
265
|
+
const out = [];
|
|
266
|
+
let cursor = 0;
|
|
267
|
+
for (const caption of captions) {
|
|
268
|
+
out.push(
|
|
269
|
+
renderMarkdownSegment(
|
|
270
|
+
lines.slice(cursor, caption.line - 1).join("\n"),
|
|
271
|
+
md,
|
|
272
|
+
ctx,
|
|
273
|
+
definitions,
|
|
274
|
+
),
|
|
275
|
+
);
|
|
276
|
+
const block = lines.slice(caption.blockStart, caption.blockEnd).join("\n");
|
|
277
|
+
out.push(renderMarkdownSegment(block, md, { ...ctx, caption }, definitions));
|
|
278
|
+
if (caption.kind !== "table" && caption.kind !== "figure") {
|
|
279
|
+
out.push(
|
|
280
|
+
`\n#block(below: 0.6em)[#text(size: 7.6pt, style: "italic")[${captionMarkup(caption, ctx)}]] <${sectionLabel(anchorPrefix, slugify(caption.id))}>\n\n`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
cursor = caption.blockEnd;
|
|
284
|
+
}
|
|
285
|
+
out.push(renderMarkdownSegment(lines.slice(cursor).join("\n"), md, ctx, definitions));
|
|
286
|
+
return out.filter(Boolean).join("\n\n");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Render one fragment with the note's footnote definitions available. */
|
|
290
|
+
function renderMarkdownSegment(source, md, ctx, definitions) {
|
|
291
|
+
const input = definitions ? `${source}\n\n${definitions}` : source;
|
|
292
|
+
const tokens = md.parse(input, {});
|
|
293
|
+
const footnoteStart = tokens.findIndex((token) => token.type === "footnote_block_open");
|
|
294
|
+
const visible = footnoteStart < 0 ? tokens : tokens.slice(0, footnoteStart);
|
|
295
|
+
const footnotes = new Map();
|
|
296
|
+
if (footnoteStart >= 0) {
|
|
297
|
+
for (let at = footnoteStart + 1; at < tokens.length; at++) {
|
|
298
|
+
if (tokens[at].type !== "footnote_open") continue;
|
|
299
|
+
const id = tokens[at].meta.id;
|
|
300
|
+
const start = at + 1;
|
|
301
|
+
while (at < tokens.length && tokens[at].type !== "footnote_close") at++;
|
|
302
|
+
footnotes.set(id, tokens.slice(start, at));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return renderTokens(visible, { ...ctx, footnotes });
|
|
208
306
|
}
|
|
209
307
|
|
|
210
308
|
/**
|
|
@@ -293,6 +391,29 @@ function renderBlock(tokens, i, out, ctx) {
|
|
|
293
391
|
out.push(`\n#${fn}(${items.map((it) => `[${it}]`).join(", ")})\n\n`);
|
|
294
392
|
return end - i + 1;
|
|
295
393
|
}
|
|
394
|
+
case "dl_open": {
|
|
395
|
+
const end = matching(tokens, i, "dl_open", "dl_close");
|
|
396
|
+
const entries = [];
|
|
397
|
+
let at = i + 1;
|
|
398
|
+
while (at < end) {
|
|
399
|
+
if (tokens[at].type !== "dt_open") {
|
|
400
|
+
at++;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const termEnd = matching(tokens, at, "dt_open", "dt_close");
|
|
404
|
+
const term = renderTokens(tokens.slice(at + 1, termEnd), ctx);
|
|
405
|
+
at = termEnd + 1;
|
|
406
|
+
const definitions = [];
|
|
407
|
+
while (at < end && tokens[at].type === "dd_open") {
|
|
408
|
+
const definitionEnd = matching(tokens, at, "dd_open", "dd_close");
|
|
409
|
+
definitions.push(renderTokens(tokens.slice(at + 1, definitionEnd), ctx));
|
|
410
|
+
at = definitionEnd + 1;
|
|
411
|
+
}
|
|
412
|
+
entries.push(`terms.item([${term}],[${definitions.join("\n\n")}])`);
|
|
413
|
+
}
|
|
414
|
+
out.push(`\n#terms(${entries.join(", ")})\n\n`);
|
|
415
|
+
return end - i + 1;
|
|
416
|
+
}
|
|
296
417
|
case "table_open": {
|
|
297
418
|
const end = matching(tokens, i, "table_open", "table_close");
|
|
298
419
|
out.push(renderTable(tokens.slice(i, end + 1), ctx));
|
|
@@ -429,6 +550,12 @@ function listItems(tokens, start, end, ctx) {
|
|
|
429
550
|
return items;
|
|
430
551
|
}
|
|
431
552
|
|
|
553
|
+
/** Caption text with the same inline Markdown handling as the surrounding prose. */
|
|
554
|
+
function captionMarkup(caption, ctx) {
|
|
555
|
+
const inline = ctx.md.parseInline(caption.text, {})[0];
|
|
556
|
+
return `${escapeTypst(caption.label)}: ${renderInline(inline, ctx)}`;
|
|
557
|
+
}
|
|
558
|
+
|
|
432
559
|
/**
|
|
433
560
|
* A markdown table as a Typst `#table`.
|
|
434
561
|
*
|
|
@@ -502,11 +629,17 @@ function renderTable(tokens, ctx) {
|
|
|
502
629
|
)}),`
|
|
503
630
|
: "";
|
|
504
631
|
const drawn = `#table(\n columns: ${columns},${alignment}${header}\n${body}\n)`;
|
|
632
|
+
const caption = ctx.caption;
|
|
633
|
+
const labelled =
|
|
634
|
+
caption ?
|
|
635
|
+
`#text(size: 7.6pt, style: "italic")[${captionMarkup(caption, ctx)}] <${sectionLabel(ctx.anchorPrefix, slugify(caption.id))}>\n${drawn}`
|
|
636
|
+
: drawn;
|
|
505
637
|
// Wide content is given an explicit span rather than left to overflow the
|
|
506
638
|
// measure: past three columns a table is set across the page, and
|
|
507
639
|
// `book-wide` decides between a float and pages of its own by measuring it.
|
|
508
|
-
if (columns > WIDE_TABLE_COLUMNS)
|
|
509
|
-
|
|
640
|
+
if (columns > WIDE_TABLE_COLUMNS)
|
|
641
|
+
return `${caption ? "" : "\n#pagebreak(weak: true)\n"}\n#book-wide[\n${labelled}\n]\n\n`;
|
|
642
|
+
return `\n${labelled}\n\n`;
|
|
510
643
|
}
|
|
511
644
|
|
|
512
645
|
/**
|
|
@@ -586,6 +719,21 @@ function renderInline(token, ctx) {
|
|
|
586
719
|
case "heroiclands_icon":
|
|
587
720
|
out.push(renderIcon(child, ctx));
|
|
588
721
|
break;
|
|
722
|
+
case "footnote_ref": {
|
|
723
|
+
const label = child.meta?.label ?? String(child.meta?.id ?? "");
|
|
724
|
+
const key = `${ctx.footnotePrefix}-${slugify(label)}`;
|
|
725
|
+
const body = ctx.footnotes?.get(child.meta?.id);
|
|
726
|
+
if (!body) {
|
|
727
|
+
out.push(escapeTypst(`[^${label}]`));
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
if (ctx.footnoteState.seen.has(key)) out.push(`#footnote(<${key}>)`);
|
|
731
|
+
else {
|
|
732
|
+
ctx.footnoteState.seen.add(key);
|
|
733
|
+
out.push(`#footnote[${renderTokens(body, ctx)}] <${key}>`);
|
|
734
|
+
}
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
589
737
|
case "link_open": {
|
|
590
738
|
const close = childMatching(children, i, "link_open", "link_close");
|
|
591
739
|
const inner = renderInline({ children: children.slice(i + 1, close) }, ctx);
|
|
@@ -632,10 +780,7 @@ function childMatching(children, i, open, close) {
|
|
|
632
780
|
* @returns {string} Typst markup.
|
|
633
781
|
*/
|
|
634
782
|
function inlineRaw(content) {
|
|
635
|
-
|
|
636
|
-
const longest = (text.match(/`+/g) ?? []).reduce((n, run) => Math.max(n, run.length), 0);
|
|
637
|
-
const ticks = "`".repeat(Math.max(1, longest + 1));
|
|
638
|
-
return `${ticks}${text}${ticks}`;
|
|
783
|
+
return `#raw("${escapeTypstString(content)}", block: false)`;
|
|
639
784
|
}
|
|
640
785
|
|
|
641
786
|
/**
|
|
@@ -655,6 +800,8 @@ function inlineRaw(content) {
|
|
|
655
800
|
function renderLink(href, inner, ctx) {
|
|
656
801
|
const url = String(href ?? "");
|
|
657
802
|
const fragment = /#([^?]*)/.exec(url)?.[1] ?? "";
|
|
803
|
+
if (url.startsWith("#") && fragment)
|
|
804
|
+
return `#link(<${sectionLabel(ctx.anchorPrefix, fragment)}>)[${inner}]`;
|
|
658
805
|
const slug =
|
|
659
806
|
url
|
|
660
807
|
.replace(/[#?].*$/, "")
|
|
@@ -692,7 +839,7 @@ function renderLink(href, inner, ctx) {
|
|
|
692
839
|
* the figure at the top of the fresh one, where nothing is above it to displace
|
|
693
840
|
* it and nothing that follows it can print first.
|
|
694
841
|
*
|
|
695
|
-
* A `.full-width` image that **also states a `float
|
|
842
|
+
* A `.full-width` image that **also states a `float`** is asking for a float,
|
|
696
843
|
* and keeps one — deferral is the honest consequence of the request.
|
|
697
844
|
*
|
|
698
845
|
* ## A float occupies the measure
|
|
@@ -716,15 +863,18 @@ function renderLink(href, inner, ctx) {
|
|
|
716
863
|
*/
|
|
717
864
|
function renderImage(token, ctx) {
|
|
718
865
|
const alt = token.content || token.attrGet?.("alt") || "";
|
|
719
|
-
const
|
|
866
|
+
const captionText = ctx.caption ? captionMarkup(ctx.caption, ctx) : escapeTypst(alt);
|
|
867
|
+
const caption = captionText ? `[${captionText}]` : "none";
|
|
868
|
+
const anchor =
|
|
869
|
+
ctx.caption ? ` <${sectionLabel(ctx.anchorPrefix, slugify(ctx.caption.id))}>` : "";
|
|
720
870
|
const staged = ctx.images.get(token.attrGet?.("src") ?? "");
|
|
721
871
|
if (!staged)
|
|
722
|
-
return
|
|
723
|
-
`\n#block(below: 0.6em)[#text(size: 7.6pt, style: "italic", fill: luma(45%))${caption}]\n\n`
|
|
872
|
+
return captionText ?
|
|
873
|
+
`\n#block(below: 0.6em)[#text(size: 7.6pt, style: "italic", fill: luma(45%))${caption}]${anchor}\n\n`
|
|
724
874
|
: "";
|
|
725
875
|
|
|
726
876
|
const size = BOOK_IMAGE_WIDTHS[token.meta?.size] ?? BOOK_IMAGE_WIDTHS.auto;
|
|
727
|
-
const figure = `#book-image("${escapeTypstString(staged)}", requested: ${size}, caption: ${caption})`;
|
|
877
|
+
const figure = `#book-image("${escapeTypstString(staged)}", requested: ${size}, caption: ${caption})${anchor}`;
|
|
728
878
|
|
|
729
879
|
const width = token.meta?.classes?.[0];
|
|
730
880
|
const scope =
|
|
@@ -749,8 +899,10 @@ function renderImage(token, ctx) {
|
|
|
749
899
|
function renderIcon(token, ctx) {
|
|
750
900
|
const name = token?.meta?.name ?? "";
|
|
751
901
|
const glyph = ctx.glyphs.get(name);
|
|
752
|
-
if (!glyph) return escapeTypst(`:icon
|
|
753
|
-
|
|
902
|
+
if (!glyph) return escapeTypst(`:icon ${name}:`);
|
|
903
|
+
const scale = ICON_SIZES[token?.meta?.attrs?.size]?.scale;
|
|
904
|
+
const size = scale ? `, size: ${scale}em` : "";
|
|
905
|
+
return `#text(font: "${escapeTypstString(glyph.font)}"${size})[\\u{${glyph.codepoint.toString(16)}}]`;
|
|
754
906
|
}
|
|
755
907
|
|
|
756
908
|
/**
|
|
@@ -964,11 +1116,11 @@ export function bookTypstPreamble() {
|
|
|
964
1116
|
" }\n" +
|
|
965
1117
|
"}",
|
|
966
1118
|
"#let book-place-map(title, path) = {\n" +
|
|
967
|
-
" page(columns: 1)[\n" +
|
|
1119
|
+
" page(columns: 1, flipped: true)[\n" +
|
|
968
1120
|
" #set par(justify: false, first-line-indent: 0em)\n" +
|
|
969
1121
|
' #text(size: 18pt, weight: "bold", fill: book-head)[#title]\n' +
|
|
970
1122
|
" #v(0.4cm)\n" +
|
|
971
|
-
' #image(path, width: 100%, height: book-text-
|
|
1123
|
+
' #image(path, width: 100%, height: book-text-width - 2cm, fit: "contain")\n' +
|
|
972
1124
|
" ]\n" +
|
|
973
1125
|
"}",
|
|
974
1126
|
// Wide content spans the page, and how it spans depends on how tall it
|
|
@@ -1122,6 +1274,7 @@ export function renderBook({
|
|
|
1122
1274
|
out.push(
|
|
1123
1275
|
`#set text(font: "${escapeTypstString(serif)}", size: 9.6pt, fill: book-ink, lang: "en")`,
|
|
1124
1276
|
);
|
|
1277
|
+
out.push("#show footnote.entry: set text(size: 7.8pt)");
|
|
1125
1278
|
out.push("#set par(justify: true, leading: 0.55em, first-line-indent: 1.2em)");
|
|
1126
1279
|
// The first-line indent is what separates one paragraph of running prose
|
|
1127
1280
|
// from the next, and inside a list item the marker already does that. An
|
|
@@ -1247,8 +1400,7 @@ export function renderBook({
|
|
|
1247
1400
|
out.push(body);
|
|
1248
1401
|
out.push("");
|
|
1249
1402
|
}
|
|
1250
|
-
const map
|
|
1251
|
-
if (map) {
|
|
1403
|
+
for (const map of maps.get(entry.anchor) ?? []) {
|
|
1252
1404
|
out.push(
|
|
1253
1405
|
`#book-place-map([${escapeTypst(map.title)}], "${escapeTypstString(map.path)}")`,
|
|
1254
1406
|
);
|
package/engine/populations.mjs
CHANGED
|
@@ -217,8 +217,8 @@ function localNode(note) {
|
|
|
217
217
|
/**
|
|
218
218
|
* Read a fetched index entry into a node, or `null` where it takes no part.
|
|
219
219
|
*
|
|
220
|
-
* A fetched entry carries
|
|
221
|
-
*
|
|
220
|
+
* A fetched entry carries its author's figure when the published index has
|
|
221
|
+
* one. An older index without a figure contributes no population.
|
|
222
222
|
*
|
|
223
223
|
* @param {string} canonical - The entry's address.
|
|
224
224
|
* @param {object} entry - The entry.
|
|
@@ -234,6 +234,7 @@ export function foreignNode(canonical, entry) {
|
|
|
234
234
|
type,
|
|
235
235
|
subType: String(entry.subType ?? ""),
|
|
236
236
|
title: String(entry.name ?? shortcode),
|
|
237
|
+
population: figureOf(entry.population),
|
|
237
238
|
parents: shortcodesOf(entry.parents),
|
|
238
239
|
domains: type === "affiliation" ? shortcodesOf(entry.domains) : [],
|
|
239
240
|
};
|