@heroiclands/package-build 22.22.0 → 22.24.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 +46 -0
- package/README.md +1 -0
- package/bin/content-commands.mjs +51 -14
- package/ci/content-format-reference.mjs +1 -1
- package/content-config.mjs +80 -2
- package/docs/api.md +85 -72
- package/docs/authoring/assets.md +29 -0
- package/docs/authoring/dates-and-calendars.md +37 -1
- package/docs/authoring/links-and-markup.md +112 -31
- package/docs/commands.md +30 -29
- package/docs/configuration.md +92 -19
- package/docs/content-format.md +1 -1
- package/docs/guides/book.md +5 -4
- package/docs/guides/module-project.md +2 -2
- package/docs/guides/site.md +41 -0
- package/docs/project-setup.md +4 -4
- package/docs/reference/format-details.md +323 -60
- package/docs/reference/note-types.md +9 -9
- package/docs/reference/theme-pages.md +496 -0
- package/engine/anchors.mjs +16 -10
- package/engine/art-fields.mjs +118 -0
- package/engine/art-slots.mjs +9 -2
- package/engine/asset-index.mjs +160 -10
- package/engine/asset-replacement.mjs +93 -0
- package/engine/asset-types.mjs +240 -0
- package/engine/base-compiler.mjs +54 -2
- package/engine/bundles.mjs +13 -1
- package/engine/content-blocks.mjs +4 -4
- package/engine/content-embeds.mjs +19 -0
- package/engine/content-figures.mjs +468 -0
- package/engine/content-footnotes.mjs +10 -2
- package/engine/content-format.yaml +1 -1
- package/engine/content-images.mjs +126 -25
- package/engine/content-index.mjs +36 -10
- package/engine/field-reference.mjs +31 -10
- package/engine/field-spec.mjs +10 -8
- package/engine/foreign-catalog.mjs +145 -19
- package/engine/frontmatter-lint.mjs +73 -1
- package/engine/heading-attributes.mjs +31 -18
- package/engine/helpers.mjs +102 -33
- package/engine/infobox-registry.mjs +4 -1
- package/engine/infobox.mjs +47 -8
- package/engine/itinerary-scenes.mjs +4 -10
- package/engine/journals.mjs +264 -39
- package/engine/lore-events.mjs +197 -0
- package/engine/markdown-expressions.mjs +257 -7
- package/engine/note-dates.mjs +305 -12
- package/engine/note-vocabulary.mjs +11 -5
- package/engine/pathnames.mjs +36 -0
- package/engine/pdf-build.mjs +357 -34
- package/engine/pdf-images.mjs +64 -0
- package/engine/pdf-render.mjs +160 -36
- package/engine/scenes.mjs +16 -4
- package/engine/site-build.mjs +127 -13
- package/engine/site-config.mjs +19 -24
- package/engine/site-index.mjs +72 -1
- package/engine/site-preview.mjs +2 -0
- package/engine/system-block.mjs +44 -16
- package/hm3/actors.mjs +2 -2
- package/hugo-theme/data/banners.yaml +69 -0
- package/hugo-theme/layouts/404.html +85 -0
- package/hugo-theme/layouts/_default/baseof.html +64 -0
- package/hugo-theme/layouts/_default/list.html +139 -0
- package/hugo-theme/layouts/_default/single.html +4 -0
- package/hugo-theme/layouts/blog/list.html +54 -0
- package/hugo-theme/layouts/blog-post/single.html +36 -0
- package/hugo-theme/layouts/index.html +75 -0
- package/hugo-theme/layouts/partials/banner-url.html +117 -0
- package/hugo-theme/layouts/partials/cdn-url.html +36 -0
- package/hugo-theme/layouts/partials/draft-notice.html +41 -0
- package/hugo-theme/layouts/partials/draft-state.html +26 -0
- package/hugo-theme/layouts/partials/footer.html +29 -0
- package/hugo-theme/layouts/partials/header.html +35 -0
- package/hugo-theme/layouts/partials/hero-banner.html +86 -0
- package/hugo-theme/layouts/partials/holdings/entry.html +21 -0
- package/hugo-theme/layouts/partials/holdings/kind-groups.html +71 -0
- package/hugo-theme/layouts/partials/holdings.html +61 -0
- package/hugo-theme/layouts/partials/infobox/entry.html +15 -0
- package/hugo-theme/layouts/partials/infobox/items.html +26 -0
- package/hugo-theme/layouts/partials/infobox/section.html +56 -0
- package/hugo-theme/layouts/partials/infobox/value.html +43 -0
- package/hugo-theme/layouts/partials/infobox.html +57 -0
- package/hugo-theme/layouts/partials/page-body.html +154 -0
- package/hugo-theme/layouts/partials/place-map.html +9 -0
- package/hugo-theme/layouts/partials/related.html +121 -0
- package/hugo-theme/layouts/partials/search.html +84 -0
- package/hugo-theme/layouts/partials/site-url.html +32 -0
- package/hugo-theme/layouts/partials/tags.html +30 -0
- package/hugo-theme/layouts/partials/toc.html +28 -0
- package/hugo-theme/layouts/partials/warn-missing-banner.html +3 -0
- package/hugo-theme/layouts/shortcodes/cartouche.html +64 -0
- package/hugo-theme/layouts/shortcodes/exturl.html +12 -0
- package/hugo-theme/layouts/shortcodes/glyph.html +43 -0
- package/hugo-theme/layouts/shortcodes/pagelist.html +63 -0
- package/hugo-theme/static/css/sohl-icons.css +57 -0
- package/hugo-theme/static/css/style.css +2953 -0
- package/hugo-theme/static/fonts/game-icons-kb.woff2 +0 -0
- package/hugo-theme/static/img/banners/takheperu-hieroglyphs.svg +1 -0
- package/hugo-theme/static/img/cartouches/takheperu.svg +1 -0
- package/hugo-theme/theme.toml +11 -0
- package/manifest.mjs +30 -24
- package/package.json +2 -1
- package/packagebuild-metadata.jsonl +13 -13
- package/sohl/item-fields.mjs +4 -3
- package/types/content-config.d.mts +48 -1
- package/types/engine/anchors.d.mts +9 -4
- package/types/engine/art-fields.d.mts +64 -0
- package/types/engine/art-slots.d.mts +2 -1
- package/types/engine/asset-index.d.mts +43 -0
- package/types/engine/asset-replacement.d.mts +22 -0
- package/types/engine/asset-types.d.mts +18 -0
- package/types/engine/base-compiler.d.mts +0 -17
- package/types/engine/content-embeds.d.mts +16 -0
- package/types/engine/content-figures.d.mts +101 -0
- package/types/engine/content-footnotes.d.mts +7 -0
- package/types/engine/content-images.d.mts +78 -12
- package/types/engine/content-index.d.mts +8 -2
- package/types/engine/field-reference.d.mts +21 -0
- package/types/engine/field-spec.d.mts +20 -16
- package/types/engine/foreign-catalog.d.mts +39 -0
- package/types/engine/heading-attributes.d.mts +13 -8
- package/types/engine/helpers.d.mts +28 -10
- package/types/engine/infobox-registry.d.mts +4 -1
- package/types/engine/infobox.d.mts +6 -0
- package/types/engine/journals.d.mts +31 -9
- package/types/engine/lore-events.d.mts +14 -0
- package/types/engine/markdown-expressions.d.mts +23 -2
- package/types/engine/note-dates.d.mts +64 -1
- package/types/engine/pathnames.d.mts +26 -0
- package/types/engine/pdf-build.d.mts +28 -6
- package/types/engine/pdf-images.d.mts +52 -0
- package/types/engine/pdf-render.d.mts +12 -0
- package/types/engine/site-build.d.mts +14 -3
- package/types/engine/site-config.d.mts +9 -9
- package/types/engine/site-index.d.mts +54 -0
- package/types/engine/system-block.d.mts +26 -2
- package/types/hm3/actors.d.mts +18 -0
- package/types/manifest.d.mts +19 -23
- package/engine/content-captions.mjs +0 -230
- package/types/engine/content-captions.d.mts +0 -58
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import MarkdownIt from "markdown-it";
|
|
6
|
-
import footnotePlugin from "markdown-it-footnote";
|
|
7
|
-
import deflistPlugin from "markdown-it-deflist";
|
|
8
|
-
|
|
9
|
-
import { slugify } from "./content-slug.mjs";
|
|
10
|
-
import { parseExtensionAttributes } from "./extension-attributes.mjs";
|
|
11
|
-
import { HEADING_LINE, parseHeadingLine, splitHeadingAttributes } from "./heading-attributes.mjs";
|
|
12
|
-
import { imagesIn } from "./content-images.mjs";
|
|
13
|
-
|
|
14
|
-
const parser = new MarkdownIt({ html: true }).use(footnotePlugin).use(deflistPlugin);
|
|
15
|
-
const OPEN = /^:::caption\s+(\{[^}\n]*\})\s*$/;
|
|
16
|
-
const CAPTION_LINE = /^:::caption\b/;
|
|
17
|
-
const CLOSE = /^:::\s*$/;
|
|
18
|
-
|
|
19
|
-
/** A caption's automatically determined kind. */
|
|
20
|
-
export const CAPTION_NAMES = Object.freeze({
|
|
21
|
-
code: "Code",
|
|
22
|
-
table: "Table",
|
|
23
|
-
figure: "Figure",
|
|
24
|
-
prose: "Prose",
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
/** Number a sequence of captions with counters shared across documents. */
|
|
28
|
-
export function numberCaptions(captions, counts) {
|
|
29
|
-
return captions.map((caption) => {
|
|
30
|
-
const number = (counts[caption.kind] ?? 0) + 1;
|
|
31
|
-
counts[caption.kind] = number;
|
|
32
|
-
return { ...caption, number, label: `${CAPTION_NAMES[caption.kind]} ${number}` };
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Read caption directives and the Markdown block immediately after each one.
|
|
38
|
-
* SQL fences are expanded before this pass, so their output is a table.
|
|
39
|
-
*
|
|
40
|
-
* @param {string} source - A note body with expanded tables.
|
|
41
|
-
* @returns {{captions: Array<{id: string, text: string, kind: string, number: number,
|
|
42
|
-
* label: string, line: number, blockStart: number, blockEnd: number,
|
|
43
|
-
* directiveEnd: number}>, errors: Array<{line: number, column: number, message: string}>}}
|
|
44
|
-
*/
|
|
45
|
-
export function scanCaptions(source) {
|
|
46
|
-
const lines = String(source ?? "").split("\n");
|
|
47
|
-
const captions = [];
|
|
48
|
-
const errors = [];
|
|
49
|
-
const counts = { code: 0, table: 0, figure: 0, prose: 0 };
|
|
50
|
-
const ids = new Set();
|
|
51
|
-
const headingIds = [];
|
|
52
|
-
let codeFence = null;
|
|
53
|
-
|
|
54
|
-
for (let i = 0; i < lines.length; i++) {
|
|
55
|
-
const line = lines[i];
|
|
56
|
-
const fence = /^ {0,3}(`{3,}|~{3,})/.exec(line);
|
|
57
|
-
if (codeFence) {
|
|
58
|
-
if (fence && fence[1][0] === codeFence[0] && fence[1].length >= codeFence.length)
|
|
59
|
-
codeFence = null;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (fence) {
|
|
63
|
-
codeFence = fence[1];
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
const heading = HEADING_LINE.exec(line);
|
|
67
|
-
const headingId = heading ? splitHeadingAttributes(heading[2]).id : "";
|
|
68
|
-
if (headingId) headingIds.push({ id: slugify(headingId), line: i + 1 });
|
|
69
|
-
if (!CAPTION_LINE.test(line)) continue;
|
|
70
|
-
const open = OPEN.exec(line);
|
|
71
|
-
if (!open) {
|
|
72
|
-
errors.push({ line: i + 1, column: 1, message: "caption needs {#anchor} attributes" });
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const attributes = parseExtensionAttributes(open[1].slice(1, -1));
|
|
76
|
-
if (
|
|
77
|
-
attributes.problems.length ||
|
|
78
|
-
!attributes.id ||
|
|
79
|
-
attributes.classes.length ||
|
|
80
|
-
Object.keys(attributes.values).length
|
|
81
|
-
) {
|
|
82
|
-
errors.push({
|
|
83
|
-
line: i + 1,
|
|
84
|
-
column: 1,
|
|
85
|
-
message: attributes.problems[0] ?? "caption accepts only {#anchor}",
|
|
86
|
-
});
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
const id = attributes.id;
|
|
90
|
-
const start = i;
|
|
91
|
-
let close = i + 1;
|
|
92
|
-
while (close < lines.length && !CLOSE.test(lines[close])) close++;
|
|
93
|
-
if (close === lines.length) {
|
|
94
|
-
errors.push({ line: start + 1, column: 1, message: "caption needs a closing :::" });
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
const caption = lines
|
|
98
|
-
.slice(i + 1, close)
|
|
99
|
-
.join("\n")
|
|
100
|
-
.trim();
|
|
101
|
-
if (!caption) errors.push({ line: start + 1, column: 1, message: "caption text is empty" });
|
|
102
|
-
// An H1, or an anchored heading at any level, starts a Foundry
|
|
103
|
-
// journal page — see `engine/content-blocks.mjs`'s identical refusal
|
|
104
|
-
// for a named block.
|
|
105
|
-
for (let at = i + 1; at < close; at++) {
|
|
106
|
-
if (!parseHeadingLine(lines[at])?.startsPage) continue;
|
|
107
|
-
errors.push({
|
|
108
|
-
line: at + 1,
|
|
109
|
-
column: 1,
|
|
110
|
-
message:
|
|
111
|
-
"a heading that starts a page cannot be written inside a caption — " +
|
|
112
|
-
"keep an H1 or an anchored heading at the top level, or drop the " +
|
|
113
|
-
"anchor and the level to stay inside it",
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
if (ids.has(slugify(id)))
|
|
117
|
-
errors.push({ line: start + 1, column: 1, message: `duplicate caption id "${id}"` });
|
|
118
|
-
ids.add(slugify(id));
|
|
119
|
-
|
|
120
|
-
let next = close + 1;
|
|
121
|
-
while (next < lines.length && !lines[next].trim()) next++;
|
|
122
|
-
if (next === lines.length || CAPTION_LINE.test(lines[next])) {
|
|
123
|
-
errors.push({ line: start + 1, column: 1, message: "caption needs a following block" });
|
|
124
|
-
i = close;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
const tokens = parser.parse(lines.slice(next).join("\n"), {});
|
|
128
|
-
const first = tokens.find((token) => token.level === 0 && token.map);
|
|
129
|
-
if (!first) {
|
|
130
|
-
errors.push({ line: start + 1, column: 1, message: "caption needs a following block" });
|
|
131
|
-
i = close;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
const blockEnd = next + first.map[1];
|
|
135
|
-
const block = lines.slice(next, blockEnd).join("\n");
|
|
136
|
-
const trimmedBlock = block.trim();
|
|
137
|
-
// A markdown image is only a figure when it stands alone — nothing
|
|
138
|
-
// else in the block, which is also what decides whether the image
|
|
139
|
-
// plugin wraps it in a `<figure>` at all. A trailing footnote
|
|
140
|
-
// reference, or any other trailing text, disqualifies it exactly as
|
|
141
|
-
// it disqualifies `standsAlone`; such a block is prose with an inline
|
|
142
|
-
// image in it, left as Markdown so a reference inside resolves
|
|
143
|
-
// rather than classified as a figure whose own render strips nothing.
|
|
144
|
-
const aloneImage =
|
|
145
|
-
/^(?:!\[|!\[\[)/.test(trimmedBlock) &&
|
|
146
|
-
imagesIn(trimmedBlock).some((image) => image.block);
|
|
147
|
-
const kind =
|
|
148
|
-
first.type === "table_open" ? "table"
|
|
149
|
-
: first.type === "fence" && /^\s*sql\b/i.test(first.info ?? "") ? "table"
|
|
150
|
-
: first.type === "fence" || first.type === "code_block" ? "code"
|
|
151
|
-
: aloneImage || /^(?:<figure\b|<img\b)/.test(trimmedBlock) ? "figure"
|
|
152
|
-
: "prose";
|
|
153
|
-
const number = ++counts[kind];
|
|
154
|
-
captions.push({
|
|
155
|
-
id,
|
|
156
|
-
text: caption,
|
|
157
|
-
kind,
|
|
158
|
-
number,
|
|
159
|
-
label: `${CAPTION_NAMES[kind]} ${number}`,
|
|
160
|
-
line: start + 1,
|
|
161
|
-
blockStart: next,
|
|
162
|
-
blockEnd,
|
|
163
|
-
directiveEnd: close + 1,
|
|
164
|
-
});
|
|
165
|
-
i = close;
|
|
166
|
-
}
|
|
167
|
-
for (const heading of headingIds) {
|
|
168
|
-
if (ids.has(heading.id))
|
|
169
|
-
errors.push({
|
|
170
|
-
line: heading.line,
|
|
171
|
-
column: 1,
|
|
172
|
-
message: `heading and caption declare the same anchor "${heading.id}"`,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
return { captions, errors };
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Render captioned blocks as HTML, leaving other Markdown untouched.
|
|
180
|
-
*
|
|
181
|
-
* A code, table or prose caption's block is left as Markdown inside the
|
|
182
|
-
* wrapper, blank-line separated from its tags exactly as
|
|
183
|
-
* {@link module:engine/content-blocks.renderBlocks} leaves a named block's
|
|
184
|
-
* body — so the surrounding render sees it as part of its own document and a
|
|
185
|
-
* footnote reference inside it resolves against the note's own definitions.
|
|
186
|
-
*
|
|
187
|
-
* A figure's block is still rendered here, because its `<figcaption>` has to
|
|
188
|
-
* be stripped from HTML this pass controls directly; the surrounding render
|
|
189
|
-
* never sees it as Markdown, so a footnote reference inside a figure caption
|
|
190
|
-
* is not resolved, the same gap a block's own title carries.
|
|
191
|
-
*/
|
|
192
|
-
export function renderCaptionBlocks(source, renderMarkdown = parser.render.bind(parser), numbers) {
|
|
193
|
-
const { captions, errors } = scanCaptions(source);
|
|
194
|
-
if (errors.length) return { markdown: source, errors };
|
|
195
|
-
const byId = numbers ? new Map(numbers.map((entry) => [entry.id, entry])) : new Map();
|
|
196
|
-
const lines = String(source ?? "").split("\n");
|
|
197
|
-
const output = [];
|
|
198
|
-
let cursor = 0;
|
|
199
|
-
const escape = (value) =>
|
|
200
|
-
String(value)
|
|
201
|
-
.replace(/&/g, "&")
|
|
202
|
-
.replace(/</g, "<")
|
|
203
|
-
.replace(/>/g, ">")
|
|
204
|
-
.replace(/"/g, """);
|
|
205
|
-
for (const caption of captions) {
|
|
206
|
-
output.push(...lines.slice(cursor, caption.line - 1));
|
|
207
|
-
const numbered = byId.get(caption.id) ?? caption;
|
|
208
|
-
const block = lines.slice(caption.blockStart, caption.blockEnd).join("\n");
|
|
209
|
-
const captionHtml = parser.renderInline(caption.text);
|
|
210
|
-
output.push(
|
|
211
|
-
`<div id="${escape(slugify(caption.id))}" class="content-caption content-caption-${caption.kind}">`,
|
|
212
|
-
);
|
|
213
|
-
if (caption.kind === "figure") {
|
|
214
|
-
const html = renderMarkdown(block).replace(
|
|
215
|
-
/<figcaption\b[^>]*>[\s\S]*?<\/figcaption>/i,
|
|
216
|
-
"",
|
|
217
|
-
);
|
|
218
|
-
output.push(html.trim());
|
|
219
|
-
} else {
|
|
220
|
-
output.push("", block, "");
|
|
221
|
-
}
|
|
222
|
-
output.push(
|
|
223
|
-
`<p class="content-caption-label">${escape(numbered.label)}: ${captionHtml}</p>`,
|
|
224
|
-
"</div>",
|
|
225
|
-
);
|
|
226
|
-
cursor = caption.blockEnd;
|
|
227
|
-
}
|
|
228
|
-
output.push(...lines.slice(cursor));
|
|
229
|
-
return { markdown: output.join("\n"), errors: [] };
|
|
230
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/** Number a sequence of captions with counters shared across documents. */
|
|
2
|
-
export function numberCaptions(captions: any, counts: any): any;
|
|
3
|
-
/**
|
|
4
|
-
* Read caption directives and the Markdown block immediately after each one.
|
|
5
|
-
* SQL fences are expanded before this pass, so their output is a table.
|
|
6
|
-
*
|
|
7
|
-
* @param {string} source - A note body with expanded tables.
|
|
8
|
-
* @returns {{captions: Array<{id: string, text: string, kind: string, number: number,
|
|
9
|
-
* label: string, line: number, blockStart: number, blockEnd: number,
|
|
10
|
-
* directiveEnd: number}>, errors: Array<{line: number, column: number, message: string}>}}
|
|
11
|
-
*/
|
|
12
|
-
export function scanCaptions(source: string): {
|
|
13
|
-
captions: Array<{
|
|
14
|
-
id: string;
|
|
15
|
-
text: string;
|
|
16
|
-
kind: string;
|
|
17
|
-
number: number;
|
|
18
|
-
label: string;
|
|
19
|
-
line: number;
|
|
20
|
-
blockStart: number;
|
|
21
|
-
blockEnd: number;
|
|
22
|
-
directiveEnd: number;
|
|
23
|
-
}>;
|
|
24
|
-
errors: Array<{
|
|
25
|
-
line: number;
|
|
26
|
-
column: number;
|
|
27
|
-
message: string;
|
|
28
|
-
}>;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Render captioned blocks as HTML, leaving other Markdown untouched.
|
|
32
|
-
*
|
|
33
|
-
* A code, table or prose caption's block is left as Markdown inside the
|
|
34
|
-
* wrapper, blank-line separated from its tags exactly as
|
|
35
|
-
* {@link module:engine/content-blocks.renderBlocks} leaves a named block's
|
|
36
|
-
* body — so the surrounding render sees it as part of its own document and a
|
|
37
|
-
* footnote reference inside it resolves against the note's own definitions.
|
|
38
|
-
*
|
|
39
|
-
* A figure's block is still rendered here, because its `<figcaption>` has to
|
|
40
|
-
* be stripped from HTML this pass controls directly; the surrounding render
|
|
41
|
-
* never sees it as Markdown, so a footnote reference inside a figure caption
|
|
42
|
-
* is not resolved, the same gap a block's own title carries.
|
|
43
|
-
*/
|
|
44
|
-
export function renderCaptionBlocks(source: any, renderMarkdown: ((src: string, env?: import("markdown-it").Env) => string) | undefined, numbers: any): {
|
|
45
|
-
markdown: any;
|
|
46
|
-
errors: {
|
|
47
|
-
line: number;
|
|
48
|
-
column: number;
|
|
49
|
-
message: string;
|
|
50
|
-
}[];
|
|
51
|
-
};
|
|
52
|
-
/** A caption's automatically determined kind. */
|
|
53
|
-
export const CAPTION_NAMES: Readonly<{
|
|
54
|
-
code: "Code";
|
|
55
|
-
table: "Table";
|
|
56
|
-
figure: "Figure";
|
|
57
|
-
prose: "Prose";
|
|
58
|
-
}>;
|