@heroiclands/package-build 23.12.5 → 23.12.7
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 +22 -0
- package/assets/styles/content-base.css +25 -0
- package/bin/content-commands.mjs +264 -3
- package/bin/package-build.mjs +25 -0
- package/content-config.mjs +98 -0
- package/docs/api.md +18 -14
- package/docs/authoring/dates-and-calendars.md +29 -1
- package/docs/authoring/links-and-markup.md +442 -50
- package/docs/commands.md +127 -0
- package/docs/configuration.md +90 -3
- package/docs/content-format.md +3 -1
- package/docs/guides/book.md +11 -0
- package/docs/guides/module-project.md +17 -1
- package/docs/guides/site.md +19 -0
- package/docs/reference/format-details.md +434 -23
- package/docs/reference/note-types.md +45 -21
- package/engine/base-compiler.mjs +2 -0
- package/engine/bundles.mjs +7 -2
- package/engine/compile-corpus.mjs +3 -1
- package/engine/content-charset.mjs +21 -2
- package/engine/content-format.yaml +1 -2
- package/engine/content-index.mjs +33 -2
- package/engine/content-links.mjs +3 -0
- package/engine/content-poetry.mjs +22 -4
- package/engine/content-scripts.mjs +1219 -0
- package/engine/content-session.mjs +2 -0
- package/engine/data-keys.mjs +5 -1
- package/engine/dictionary-layout.mjs +18 -12
- package/engine/dictionary-rows.mjs +11 -2
- package/engine/event-rows.mjs +63 -18
- package/engine/event-views.mjs +28 -15
- package/engine/helpers.mjs +8 -2
- package/engine/index-records.mjs +2 -0
- package/engine/infobox.mjs +4 -8
- package/engine/journals.mjs +14 -3
- package/engine/language-data.mjs +137 -1
- package/engine/language-tag.mjs +103 -0
- package/engine/language-terms.mjs +16 -4
- package/engine/map-lexicon.mjs +762 -0
- package/engine/markdown-expressions.mjs +72 -17
- package/engine/markup-validation.mjs +17 -3
- package/engine/names.mjs +1 -1
- package/engine/note-calendars.mjs +314 -0
- package/engine/note-vocabulary.mjs +124 -16
- package/engine/pdf-build.mjs +28 -10
- package/engine/pdf-render.mjs +116 -11
- package/engine/reckoning-markers.mjs +63 -3
- package/engine/run-rows.mjs +119 -0
- package/engine/run-words.mjs +266 -0
- package/engine/scenes.mjs +1 -0
- package/engine/script-font-delivery.mjs +321 -0
- package/engine/script-fonts.mjs +172 -0
- package/engine/site-build.mjs +10 -3
- package/engine/site-config.mjs +5 -0
- package/engine/site-root.mjs +19 -5
- package/engine/sql-tables.mjs +120 -4
- package/engine/text-maps.mjs +607 -0
- package/engine/verb-pattern.mjs +99 -0
- package/engine/word-pattern.mjs +27 -0
- package/engine/word-rows.mjs +8 -2
- package/engine/word-terms.mjs +44 -0
- package/hugo-theme/layouts/_default/baseof.html +3 -0
- package/hugo-theme/static/css/style.css +10 -0
- package/manifest.mjs +9 -1
- package/package.json +1 -1
- package/types/content-config.d.mts +38 -0
- package/types/engine/compile-corpus.d.mts +2 -1
- package/types/engine/content-index.d.mts +17 -3
- package/types/engine/content-poetry.d.mts +7 -0
- package/types/engine/content-scripts.d.mts +226 -0
- package/types/engine/data-keys.d.mts +2 -2
- package/types/engine/dictionary-rows.d.mts +15 -0
- package/types/engine/event-rows.d.mts +16 -1
- package/types/engine/event-views.d.mts +2 -1
- package/types/engine/helpers.d.mts +5 -1
- package/types/engine/journals.d.mts +7 -2
- package/types/engine/language-data.d.mts +2 -0
- package/types/engine/language-tag.d.mts +8 -0
- package/types/engine/language-terms.d.mts +2 -1
- package/types/engine/map-lexicon.d.mts +274 -0
- package/types/engine/markup-validation.d.mts +7 -2
- package/types/engine/note-calendars.d.mts +60 -0
- package/types/engine/pdf-build.d.mts +26 -3
- package/types/engine/pdf-render.d.mts +17 -2
- package/types/engine/reckoning-markers.d.mts +32 -1
- package/types/engine/run-rows.d.mts +89 -0
- package/types/engine/run-words.d.mts +86 -0
- package/types/engine/script-font-delivery.d.mts +134 -0
- package/types/engine/script-fonts.d.mts +168 -0
- package/types/engine/site-build.d.mts +2 -1
- package/types/engine/site-root.d.mts +11 -3
- package/types/engine/text-maps.d.mts +175 -0
- package/types/engine/verb-pattern.d.mts +65 -0
- package/types/engine/word-pattern.d.mts +13 -0
- package/types/engine/word-terms.d.mts +6 -0
|
@@ -21,6 +21,7 @@ import * as sql from "./sql-tables.mjs";
|
|
|
21
21
|
import { captureContentSources } from "./content-sources.mjs";
|
|
22
22
|
import { buildLinkIndex } from "./content-links.mjs";
|
|
23
23
|
import { buildContentLinkIndex } from "./helpers.mjs";
|
|
24
|
+
import { runContext } from "./content-scripts.mjs";
|
|
24
25
|
import { loadForeignIndexes } from "./metadata-index.mjs";
|
|
25
26
|
import { loadPackConfig } from "./pack-config.mjs";
|
|
26
27
|
import { routerFor } from "./pack-router.mjs";
|
|
@@ -199,6 +200,7 @@ export async function createContentSession({
|
|
|
199
200
|
return memo("compile", async () => {
|
|
200
201
|
const sqlTables = await session.sqlTables("all");
|
|
201
202
|
return {
|
|
203
|
+
runs: runContext(config, records),
|
|
202
204
|
records,
|
|
203
205
|
parsed,
|
|
204
206
|
problems,
|
package/engine/data-keys.mjs
CHANGED
|
@@ -62,7 +62,7 @@ import { nearest } from "./near-miss.mjs";
|
|
|
62
62
|
* @property {string} name - The key as an author writes it.
|
|
63
63
|
* @property {string} [kind] - Its value's shape: `string`, `date`, `number`,
|
|
64
64
|
* `integer`, `boolean`, `address`, `list`, `map`, `scalar-or-map`,
|
|
65
|
-
* `string-or-list`,
|
|
65
|
+
* `string-or-list`, `string-list-or-map` or `list-or-map` — see {@link fitsKind}. Absent, the check
|
|
66
66
|
* makes no claim about the value.
|
|
67
67
|
* @property {readonly string[]} [allowed] - The closed set a present value is
|
|
68
68
|
* one of, where the key takes a fixed word.
|
|
@@ -156,6 +156,10 @@ export function fitsKind(value, kind) {
|
|
|
156
156
|
// described by `values`.
|
|
157
157
|
case "string-list-or-map":
|
|
158
158
|
return fitsKind(value, "string-or-list") || fitsKind(value, "map");
|
|
159
|
+
// A list described by `entries`, or a map with the keys `fields` names:
|
|
160
|
+
// a mapping row is `[from, to]` or `{from, to, before?, at?}`.
|
|
161
|
+
case "list-or-map":
|
|
162
|
+
return Array.isArray(value) || fitsKind(value, "map");
|
|
159
163
|
default:
|
|
160
164
|
return true;
|
|
161
165
|
}
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
|
|
49
49
|
import { parseAddress, readCanonicalKey, renderAddress, splitAnchor } from "./address.mjs";
|
|
50
50
|
import { dictionaryEntryAnchor, dictionaryLetter } from "./dictionary-rows.mjs";
|
|
51
|
-
import {
|
|
51
|
+
import { verbPattern } from "./verb-pattern.mjs";
|
|
52
52
|
import {
|
|
53
53
|
WORD_CLASS_BASES,
|
|
54
54
|
WORD_CLASS_MODIFIERS,
|
|
@@ -191,16 +191,22 @@ function verbTables(rows, opts) {
|
|
|
191
191
|
patterns.set(opts.pageLanguage ?? "", { verbs: opts.verbs });
|
|
192
192
|
const tables = [];
|
|
193
193
|
for (const [language, { verbs, label }] of [...patterns].sort(([a], [b]) => compare(a, b))) {
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
194
|
+
const pattern = verbPattern(verbs);
|
|
195
|
+
if (!pattern) continue;
|
|
196
|
+
const replaced = pattern.replaces === undefined ? undefined : `-${pattern.replaces}`;
|
|
197
|
+
const endings =
|
|
198
|
+
pattern.invariant ?
|
|
199
|
+
[["All persons", "unchanged"]]
|
|
200
|
+
: pattern.endings.map(({ label, ending }) => [
|
|
201
|
+
label,
|
|
202
|
+
ending ? `-${ending}`
|
|
203
|
+
: replaced ? `${replaced} dropped`
|
|
204
|
+
: "unchanged",
|
|
205
|
+
]);
|
|
206
|
+
const heading =
|
|
207
|
+
replaced && !pattern.invariant ?
|
|
208
|
+
`Ending in place of a closing ${replaced}`
|
|
209
|
+
: "Ending added to citation form";
|
|
204
210
|
const title =
|
|
205
211
|
patterns.size > 1 ?
|
|
206
212
|
`**Verbs (${String(opts.labelOf?.(language) ?? label ?? language).replace(/[\r\n]+/g, " ")})**`
|
|
@@ -209,7 +215,7 @@ function verbTables(rows, opts) {
|
|
|
209
215
|
[
|
|
210
216
|
title,
|
|
211
217
|
"",
|
|
212
|
-
|
|
218
|
+
`| Person | ${verbCell(heading)} |`,
|
|
213
219
|
"| --- | --- |",
|
|
214
220
|
...endings.map((ending) => `| ${ending.map(verbCell).join(" | ")} |`),
|
|
215
221
|
].join("\n"),
|
|
@@ -130,8 +130,17 @@ const isSingleWord = (title) => !/[\s\-‐‑]/u.test(title);
|
|
|
130
130
|
/** Whether a glossary entry states a definition. */
|
|
131
131
|
const defines = (entry) => typeof entry.definition === "string" && entry.definition.trim() !== "";
|
|
132
132
|
|
|
133
|
-
/**
|
|
134
|
-
|
|
133
|
+
/**
|
|
134
|
+
* A noun's stated plural, or the first literal ending replacement that fits.
|
|
135
|
+
*
|
|
136
|
+
* @param {{form: string, class?: unknown, plural?: unknown}} word - A word of
|
|
137
|
+
* `data.words`.
|
|
138
|
+
* @param {Record<string, any>|undefined} grammar - Its language's
|
|
139
|
+
* `data.grammar`.
|
|
140
|
+
* @returns {string|null} The plural, or `null` for a word that is no noun or
|
|
141
|
+
* that no rule fits.
|
|
142
|
+
*/
|
|
143
|
+
export function pluralOf(word, grammar) {
|
|
135
144
|
if (parseWordClass(word.class).base !== "n") return null;
|
|
136
145
|
if (typeof word.plural === "string" && word.plural) return word.plural;
|
|
137
146
|
for (const rule of Array.isArray(grammar?.plural) ? grammar.plural : []) {
|
package/engine/event-rows.mjs
CHANGED
|
@@ -25,10 +25,19 @@
|
|
|
25
25
|
* `resolvedDates.events`, position for position with `data.events`, and the
|
|
26
26
|
* sort keys are read from it rather than parsed again here.
|
|
27
27
|
*
|
|
28
|
-
* **`whenText` and `untilText` are the dates as a reader
|
|
29
|
-
* as the `dateformat` helper prints a date, in the
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* **`whenText` and `untilText` are the dates as a reader of the owning note
|
|
29
|
+
* sees them**: printed as the `dateformat` helper prints a date, in the
|
|
30
|
+
* calendar the owning note resolves to — see
|
|
31
|
+
* {@link module:engine/note-calendars} — else in the era the date itself
|
|
32
|
+
* names. A date that does not resolve keeps its authored text.
|
|
33
|
+
*
|
|
34
|
+
* **`whenIn` and `untilIn` are the same dates in every calendar**: a struct
|
|
35
|
+
* keyed by each calendar note's canonical Address, every calendar the corpus
|
|
36
|
+
* holds or any note resolves to, so a query reads a date in the calendar it
|
|
37
|
+
* chooses — `e.whenIn['thalorna-note-lore-commoncal']`. Every row carries every
|
|
38
|
+
* key; a date a calendar cannot print reads there as it would in `whenText`
|
|
39
|
+
* with no calendar, and an absent date is `NULL`. The event views select the
|
|
40
|
+
* key of the page's calendar.
|
|
32
41
|
*
|
|
33
42
|
* @module
|
|
34
43
|
*/
|
|
@@ -36,6 +45,7 @@
|
|
|
36
45
|
import { ownDocumentSystem, renderAddress } from "./address.mjs";
|
|
37
46
|
import { printEventDateIn } from "./event-fields.mjs";
|
|
38
47
|
import { EVENT_ENTRY_KEYS } from "./note-event-terms.mjs";
|
|
48
|
+
import { calendarAddress } from "./reckoning-markers.mjs";
|
|
39
49
|
|
|
40
50
|
/** The columns a row carries besides the event's own keys, before them. */
|
|
41
51
|
const IDENTITY_COLUMNS = Object.freeze(["note", "address"]);
|
|
@@ -46,12 +56,16 @@ export const EVENT_SORT_COLUMNS = Object.freeze(["whenSort", "whenYear", "untilS
|
|
|
46
56
|
/** The printed dates a row carries, after the sort keys. */
|
|
47
57
|
export const EVENT_TEXT_COLUMNS = Object.freeze(["whenText", "untilText"]);
|
|
48
58
|
|
|
59
|
+
/** The dates a row carries in every calendar, after the printed ones. */
|
|
60
|
+
export const EVENT_CALENDAR_COLUMNS = Object.freeze(["whenIn", "untilIn"]);
|
|
61
|
+
|
|
49
62
|
/** Every column of the `events` relation, in order. */
|
|
50
63
|
export const EVENT_COLUMNS = Object.freeze([
|
|
51
64
|
...IDENTITY_COLUMNS,
|
|
52
65
|
...EVENT_ENTRY_KEYS,
|
|
53
66
|
...EVENT_SORT_COLUMNS,
|
|
54
67
|
...EVENT_TEXT_COLUMNS,
|
|
68
|
+
...EVENT_CALENDAR_COLUMNS,
|
|
55
69
|
]);
|
|
56
70
|
|
|
57
71
|
/** The keys whose authored value is a date, printed as the text it was written as. */
|
|
@@ -95,18 +109,35 @@ function axisKeys(date) {
|
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
112
|
+
* Every calendar a set of records can print an event's date in: each calendar
|
|
113
|
+
* note's canonical Address, and every calendar a note resolves to.
|
|
100
114
|
*
|
|
101
|
-
* @param {Record<string, any
|
|
102
|
-
*
|
|
115
|
+
* @param {Iterable<Record<string, any>>} records - Index records, every
|
|
116
|
+
* Address a string.
|
|
117
|
+
* @returns {string[]} The calendars, sorted.
|
|
103
118
|
*/
|
|
104
|
-
function
|
|
119
|
+
export function eventCalendars(records) {
|
|
120
|
+
const calendars = new Set();
|
|
105
121
|
for (const record of records) {
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
const resolved = calendarAddress(record?.resolvedCalendar);
|
|
123
|
+
if (resolved) calendars.add(resolved);
|
|
124
|
+
if (
|
|
125
|
+
record?.type === "lore" &&
|
|
126
|
+
String(record.subType ?? "").toLowerCase() === "calendar" &&
|
|
127
|
+
record.package &&
|
|
128
|
+
record.shortcode &&
|
|
129
|
+
!record.documents
|
|
130
|
+
)
|
|
131
|
+
calendars.add(
|
|
132
|
+
renderAddress({
|
|
133
|
+
package: String(record.package),
|
|
134
|
+
system: "note",
|
|
135
|
+
type: "lore",
|
|
136
|
+
shortcode: String(record.shortcode),
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
108
139
|
}
|
|
109
|
-
return
|
|
140
|
+
return [...calendars].sort();
|
|
110
141
|
}
|
|
111
142
|
|
|
112
143
|
/**
|
|
@@ -115,12 +146,16 @@ function worldCalendar(records) {
|
|
|
115
146
|
* @param {Iterable<Record<string, any>>} records - Index records, every
|
|
116
147
|
* Address a string, already narrowed to the audience being published to.
|
|
117
148
|
* @param {object} [dates] - The reckoning context the dates are printed in.
|
|
149
|
+
* @param {readonly string[]} [calendars] - The calendars `whenIn` and
|
|
150
|
+
* `untilIn` are keyed by: {@link eventCalendars} of every record of the
|
|
151
|
+
* corpus, whatever the audience, so every surface's relation has the same
|
|
152
|
+
* keys. Absent, those of `records`.
|
|
118
153
|
* @returns {object[]} One row per event entry, in record then entry order.
|
|
119
154
|
*/
|
|
120
|
-
export function eventRows(records, dates) {
|
|
155
|
+
export function eventRows(records, dates, calendars) {
|
|
121
156
|
const rows = [];
|
|
122
157
|
const all = [...records];
|
|
123
|
-
const
|
|
158
|
+
const keys = calendars ?? eventCalendars(all);
|
|
124
159
|
for (const record of all) {
|
|
125
160
|
const events = record?.data?.events;
|
|
126
161
|
if (!Array.isArray(events) || !events.length) continue;
|
|
@@ -128,7 +163,7 @@ export function eventRows(records, dates) {
|
|
|
128
163
|
if (!note) continue;
|
|
129
164
|
const resolved =
|
|
130
165
|
Array.isArray(record.resolvedDates?.events) ? record.resolvedDates.events : [];
|
|
131
|
-
const calendar =
|
|
166
|
+
const calendar = calendarAddress(record.resolvedCalendar);
|
|
132
167
|
events.forEach((entry, position) => {
|
|
133
168
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return;
|
|
134
169
|
const row = {
|
|
@@ -149,10 +184,20 @@ export function eventRows(records, dates) {
|
|
|
149
184
|
row.untilSort = until.sort;
|
|
150
185
|
row.untilYear = until.year;
|
|
151
186
|
for (const key of DATE_KEYS) {
|
|
187
|
+
const date = resolved[position]?.[key];
|
|
152
188
|
row[`${key}Text`] =
|
|
153
|
-
row[key] === null ?
|
|
154
|
-
|
|
155
|
-
|
|
189
|
+
row[key] === null ? null : printEventDateIn(row[key], date, dates, calendar);
|
|
190
|
+
row[`${key}In`] =
|
|
191
|
+
keys.length ?
|
|
192
|
+
Object.fromEntries(
|
|
193
|
+
keys.map((each) => [
|
|
194
|
+
each,
|
|
195
|
+
row[key] === null ?
|
|
196
|
+
null
|
|
197
|
+
: printEventDateIn(row[key], date, dates, each),
|
|
198
|
+
]),
|
|
199
|
+
)
|
|
200
|
+
: null;
|
|
156
201
|
}
|
|
157
202
|
rows.push(row);
|
|
158
203
|
});
|
package/engine/event-views.mjs
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
import { ownDocumentSystem, renderAddress } from "./address.mjs";
|
|
43
43
|
import { authoredSection, sectionHeading } from "./generated-sections.mjs";
|
|
44
|
+
import { calendarAddress } from "./reckoning-markers.mjs";
|
|
44
45
|
|
|
45
46
|
/** A literal SQL string. */
|
|
46
47
|
const literal = (text) => `'${String(text).replace(/'/g, "''")}'`;
|
|
@@ -48,6 +49,16 @@ const literal = (text) => `'${String(text).replace(/'/g, "''")}'`;
|
|
|
48
49
|
/** A list of SQL string literals, for an `IN (…)`. */
|
|
49
50
|
const literals = (texts) => texts.map(literal).join(", ");
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* The `When` column of a view on a page whose dates print in `calendar`: the
|
|
54
|
+
* event's date in that calendar, or — on a page with no calendar — in its
|
|
55
|
+
* owning note's.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} [calendar] - The page's calendar, a canonical Address.
|
|
58
|
+
* @returns {string} The column's expression.
|
|
59
|
+
*/
|
|
60
|
+
const whenColumn = (calendar) => (calendar ? `e.whenIn[${literal(calendar)}]` : "e.whenText");
|
|
61
|
+
|
|
51
62
|
/** The order every view sorts its rows in: by date, undated last. */
|
|
52
63
|
const BY_DATE = "ORDER BY e.whenSort NULLS LAST, e.address";
|
|
53
64
|
|
|
@@ -65,7 +76,7 @@ const BY_DATE = "ORDER BY e.whenSort NULLS LAST, e.address";
|
|
|
65
76
|
*
|
|
66
77
|
* @type {ReadonlyArray<Readonly<{slug: string, heading: string,
|
|
67
78
|
* applies: (record: object) => boolean,
|
|
68
|
-
* query: (self: string[], shape: CorpusShape, columns?: {felt?: boolean}) => {fence: string, own?: string}|null,
|
|
79
|
+
* query: (self: string[], shape: CorpusShape, columns?: {felt?: boolean, calendar?: string}) => {fence: string, own?: string}|null,
|
|
69
80
|
* owners?: (db: object, selves: string[][], shape: CorpusShape) => Promise<Set<string>>,
|
|
70
81
|
* felt?: (db: object, selves: string[][], shape: CorpusShape) => Promise<Set<string>>}>>}
|
|
71
82
|
*/
|
|
@@ -87,13 +98,13 @@ export const EVENT_VIEWS = Object.freeze([
|
|
|
87
98
|
(record.type === "lore" && record.subType === "culture"),
|
|
88
99
|
owners: (db, selves, shape) =>
|
|
89
100
|
namedOwners(db, selves, "SELECT unnest(who).ref AS ref FROM events", shape.who),
|
|
90
|
-
query: (self, shape) => {
|
|
101
|
+
query: (self, shape, columns = {}) => {
|
|
91
102
|
if (!shape.who) return null;
|
|
92
103
|
const named = `list_filter(e.who, w -> w.ref IN (${literals(self)}))`;
|
|
93
104
|
return {
|
|
94
105
|
fence: [
|
|
95
106
|
"SELECT e.note AS _ref,",
|
|
96
|
-
|
|
107
|
+
` ${whenColumn(columns.calendar)} AS "When",`,
|
|
97
108
|
' e.summary AS "What happened",',
|
|
98
109
|
` array_to_string(list_transform(${named}, w -> w.role), ', ') AS "Role"`,
|
|
99
110
|
"FROM events e",
|
|
@@ -117,12 +128,12 @@ export const EVENT_VIEWS = Object.freeze([
|
|
|
117
128
|
'SELECT account."by" AS ref FROM (SELECT unnest(accounts) AS account FROM events)',
|
|
118
129
|
shape.accounts,
|
|
119
130
|
),
|
|
120
|
-
query: (self, shape) => {
|
|
131
|
+
query: (self, shape, columns = {}) => {
|
|
121
132
|
if (!shape.accounts) return null;
|
|
122
133
|
return {
|
|
123
134
|
fence: [
|
|
124
135
|
"SELECT e.note AS _ref,",
|
|
125
|
-
|
|
136
|
+
` ${whenColumn(columns.calendar)} AS "When",`,
|
|
126
137
|
' e.summary AS "Event",',
|
|
127
138
|
' e.account.says AS "What they say",',
|
|
128
139
|
' e.account.agrees AS "Agrees"',
|
|
@@ -145,7 +156,7 @@ export const EVENT_VIEWS = Object.freeze([
|
|
|
145
156
|
"FROM (SELECT unnest(follows) AS edge FROM events)",
|
|
146
157
|
shape.follows,
|
|
147
158
|
),
|
|
148
|
-
query: (self, shape) => {
|
|
159
|
+
query: (self, shape, columns = {}) => {
|
|
149
160
|
if (!shape.follows) return null;
|
|
150
161
|
const edges =
|
|
151
162
|
`list_filter(e.follows, f -> split_part(f.event, '#', 1) ` +
|
|
@@ -153,7 +164,7 @@ export const EVENT_VIEWS = Object.freeze([
|
|
|
153
164
|
return {
|
|
154
165
|
fence: [
|
|
155
166
|
"SELECT e.note AS _ref,",
|
|
156
|
-
|
|
167
|
+
` ${whenColumn(columns.calendar)} AS "When",`,
|
|
157
168
|
' e.summary AS "What followed",',
|
|
158
169
|
` array_to_string(list_transform(${edges}, f -> f.how), ', ') AS "How"`,
|
|
159
170
|
"FROM events e",
|
|
@@ -307,8 +318,9 @@ async function chronologyRoots(db, selves, shape, conditions) {
|
|
|
307
318
|
*
|
|
308
319
|
* @param {string[]} self - The place's Address, in each form.
|
|
309
320
|
* @param {CorpusShape} shape - What the corpus states.
|
|
310
|
-
* @param {{felt?: boolean}} [columns] - `felt: false` when
|
|
311
|
-
* place is felt there, which leaves the `Felt here` column
|
|
321
|
+
* @param {{felt?: boolean, calendar?: string}} [columns] - `felt: false` when
|
|
322
|
+
* no row of this place is felt there, which leaves the `Felt here` column
|
|
323
|
+
* out; `calendar`, the page's calendar, which the `When` column prints in.
|
|
312
324
|
* @returns {{fence: string, own: string}|null} The query, and the one that
|
|
313
325
|
* counts the place's own rows.
|
|
314
326
|
*/
|
|
@@ -332,7 +344,7 @@ function chronologyQuery(self, shape, columns = {}) {
|
|
|
332
344
|
...below,
|
|
333
345
|
"here AS (SELECT list(place) AS places FROM below)",
|
|
334
346
|
"SELECT e.note AS _ref,",
|
|
335
|
-
|
|
347
|
+
` ${whenColumn(columns.calendar)} AS "When",`,
|
|
336
348
|
shape.reach && columns.felt !== false ?
|
|
337
349
|
' e.summary AS "What happened",'
|
|
338
350
|
: ' e.summary AS "What happened"',
|
|
@@ -453,11 +465,12 @@ export async function eventViewSections(db, records, fileOf, only) {
|
|
|
453
465
|
const sections = new Map();
|
|
454
466
|
for (const view of EVENT_VIEWS) {
|
|
455
467
|
if (!view.applies(record) || authoredSection(record, view.slug)) continue;
|
|
456
|
-
const query = view.query(
|
|
457
|
-
self,
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
468
|
+
const query = view.query(self, shape, {
|
|
469
|
+
...(felt.has(view.slug) ? { felt: felt.get(view.slug).has(self[0]) } : {}),
|
|
470
|
+
...(calendarAddress(record.resolvedCalendar) ?
|
|
471
|
+
{ calendar: calendarAddress(record.resolvedCalendar) }
|
|
472
|
+
: {}),
|
|
473
|
+
});
|
|
461
474
|
if (!query) continue;
|
|
462
475
|
const has =
|
|
463
476
|
owned.has(view.slug) ?
|
package/engine/helpers.mjs
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { renderAlerts } from "./content-alerts.mjs";
|
|
29
|
+
import { renderRuns } from "./content-scripts.mjs";
|
|
29
30
|
import { buildReferenceTargets } from "./reference-targets.mjs";
|
|
30
31
|
import { standingsDigest } from "./standings.mjs";
|
|
31
32
|
|
|
@@ -224,10 +225,15 @@ md.renderer.rules.footnote_open = (tokens, idx, options, env, renderer) => {
|
|
|
224
225
|
* @param {(address: string) => string|undefined} [resolveRole] - From a
|
|
225
226
|
* picture's address to the role its asset declares — see
|
|
226
227
|
* {@link module:engine/content-figures.scanFigures}.
|
|
228
|
+
* @param {import("./content-scripts.mjs").RunContext} [runs] - The package's
|
|
229
|
+
* languages, faces and maps. Given, text marked with `lang=`, `font=` and
|
|
230
|
+
* `map=` is rewritten first, so it is tagged for its language, mapped and
|
|
231
|
+
* set in its font.
|
|
227
232
|
* @returns {string} The rendered HTML.
|
|
228
233
|
*/
|
|
229
|
-
export function renderFoundryMarkdown(body, figures, footnoteNumbers, docId, resolveRole) {
|
|
230
|
-
const
|
|
234
|
+
export function renderFoundryMarkdown(body, figures, footnoteNumbers, docId, resolveRole, runs) {
|
|
235
|
+
const marked = runs ? renderRuns(body, runs, undefined, { target: "foundry" }).markdown : body;
|
|
236
|
+
const figured = renderFigureBlocks(marked, (block) => md.render(block), figures, {
|
|
231
237
|
resolveRole,
|
|
232
238
|
});
|
|
233
239
|
const blocks = renderBlocks(figured.markdown, "foundry");
|
package/engine/index-records.mjs
CHANGED
package/engine/infobox.mjs
CHANGED
|
@@ -128,6 +128,7 @@ import {
|
|
|
128
128
|
standingLead,
|
|
129
129
|
} from "./standings.mjs";
|
|
130
130
|
import { AddressEntries } from "./address-values.mjs";
|
|
131
|
+
import { noteCalendar } from "./note-calendars.mjs";
|
|
131
132
|
|
|
132
133
|
/**
|
|
133
134
|
* How a section arranges what it holds.
|
|
@@ -1066,15 +1067,10 @@ function noteBox(
|
|
|
1066
1067
|
: parsed?.qualifier ? dates.eras?.get(parsed.qualifier)
|
|
1067
1068
|
: null;
|
|
1068
1069
|
let printable = null;
|
|
1069
|
-
|
|
1070
|
+
const calendar = noteCalendar(fm, dates);
|
|
1071
|
+
if (parsed?.known && calendar) {
|
|
1070
1072
|
try {
|
|
1071
|
-
printable = formatDateInCalendar(
|
|
1072
|
-
parsed,
|
|
1073
|
-
isAddressTuple(data.calendar) ?
|
|
1074
|
-
renderAddress(data.calendar)
|
|
1075
|
-
: data.calendar,
|
|
1076
|
-
dates,
|
|
1077
|
-
);
|
|
1073
|
+
printable = formatDateInCalendar(parsed, calendar, dates);
|
|
1078
1074
|
} catch {
|
|
1079
1075
|
// The calendar reference is checked at the authored field.
|
|
1080
1076
|
}
|
package/engine/journals.mjs
CHANGED
|
@@ -485,10 +485,12 @@ function soleFigurePicture(figure, page) {
|
|
|
485
485
|
* @param {Map<string, number>} footnoteNumbers - Shared across the note.
|
|
486
486
|
* @param {(address: string) => string|undefined} [resolveRole] - See
|
|
487
487
|
* {@link splitPages}.
|
|
488
|
+
* @param {import("./content-scripts.mjs").RunContext} [runs] - The package's
|
|
489
|
+
* languages, faces and maps — see {@link module:engine/helpers.renderFoundryMarkdown}.
|
|
488
490
|
* @returns {{name: string, type: "image", src: string, image: {caption?: string}}
|
|
489
491
|
* |{name: string, type: "text", text: {format: number, content: string}}}
|
|
490
492
|
*/
|
|
491
|
-
function buildFigurePageFields(figure, page, pageId, footnoteNumbers, resolveRole) {
|
|
493
|
+
function buildFigurePageFields(figure, page, pageId, footnoteNumbers, resolveRole, runs) {
|
|
492
494
|
const picture = soleFigurePicture(figure, page);
|
|
493
495
|
if (picture && !captionCarriesMarkup(figure.caption)) {
|
|
494
496
|
let src = picture;
|
|
@@ -515,6 +517,7 @@ function buildFigurePageFields(figure, page, pageId, footnoteNumbers, resolveRol
|
|
|
515
517
|
footnoteNumbers,
|
|
516
518
|
pageId,
|
|
517
519
|
resolveRole,
|
|
520
|
+
runs,
|
|
518
521
|
);
|
|
519
522
|
return {
|
|
520
523
|
name: figure.label || captionPageName(figure.caption),
|
|
@@ -547,13 +550,15 @@ function buildFigurePageFields(figure, page, pageId, footnoteNumbers, resolveRol
|
|
|
547
550
|
* carries its own figure already, from {@link splitPages}.
|
|
548
551
|
* @param {(address: string) => string|undefined} [resolveRole] - See
|
|
549
552
|
* {@link splitPages}.
|
|
553
|
+
* @param {import("./content-scripts.mjs").RunContext} [runs] - The package's
|
|
554
|
+
* languages, faces and maps — see {@link module:engine/helpers.renderFoundryMarkdown}.
|
|
550
555
|
* @returns {Array<{_id: string, name: string, type: string,
|
|
551
556
|
* title: {show: boolean, level: number},
|
|
552
557
|
* text: {format: number, content: string}, _key: string,
|
|
553
558
|
* ownership?: {default: number}}>} The page documents, in order.
|
|
554
559
|
* @throws {Error} When the note has no content at all, or repeats an anchor.
|
|
555
560
|
*/
|
|
556
|
-
export function buildPages(rawPages, entryId, noteName, figures, resolveRole) {
|
|
561
|
+
export function buildPages(rawPages, entryId, noteName, figures, resolveRole, runs) {
|
|
557
562
|
if (rawPages.length === 0) {
|
|
558
563
|
throw new Error(
|
|
559
564
|
`note "${noteName}" has no Introduction content and no H1 headings — nothing to compile`,
|
|
@@ -573,7 +578,7 @@ export function buildPages(rawPages, entryId, noteName, figures, resolveRole) {
|
|
|
573
578
|
const withheld = (page.classes ?? []).includes(WITHHELD_CLASS);
|
|
574
579
|
const fields =
|
|
575
580
|
page.figure ?
|
|
576
|
-
buildFigurePageFields(page.figure, page, pageId, footnoteNumbers, resolveRole)
|
|
581
|
+
buildFigurePageFields(page.figure, page, pageId, footnoteNumbers, resolveRole, runs)
|
|
577
582
|
: {
|
|
578
583
|
name: page.name,
|
|
579
584
|
type: "text",
|
|
@@ -587,6 +592,7 @@ export function buildPages(rawPages, entryId, noteName, figures, resolveRole) {
|
|
|
587
592
|
footnoteNumbers,
|
|
588
593
|
pageId,
|
|
589
594
|
resolveRole,
|
|
595
|
+
runs,
|
|
590
596
|
)
|
|
591
597
|
: "",
|
|
592
598
|
},
|
|
@@ -638,6 +644,8 @@ export function buildPages(rawPages, entryId, noteName, figures, resolveRole) {
|
|
|
638
644
|
* name reads `Map 1` rather than `Figure 1` where it is due — see
|
|
639
645
|
* {@link module:engine/content-figures.scanFigures}.
|
|
640
646
|
* @param {import("./content-session.mjs").ContentSyntax} [params.syntax] - Session scanner projections.
|
|
647
|
+
* @param {import("./content-scripts.mjs").RunContext} [params.runs] - The package's
|
|
648
|
+
* languages, faces and maps — see {@link module:engine/helpers.renderFoundryMarkdown}.
|
|
641
649
|
* @returns {object} The JournalEntry document, keyed for the pack.
|
|
642
650
|
*/
|
|
643
651
|
export function buildJournalEntry({
|
|
@@ -651,6 +659,7 @@ export function buildJournalEntry({
|
|
|
651
659
|
notice = "",
|
|
652
660
|
resolveRole,
|
|
653
661
|
syntax,
|
|
662
|
+
runs,
|
|
654
663
|
}) {
|
|
655
664
|
const rawPages = splitPages(markdown, leadName, resolveRole, syntax);
|
|
656
665
|
const pages = buildPages(
|
|
@@ -659,6 +668,7 @@ export function buildJournalEntry({
|
|
|
659
668
|
name,
|
|
660
669
|
scanFigures(markdown, { resolveRole }).figures,
|
|
661
670
|
resolveRole,
|
|
671
|
+
runs,
|
|
662
672
|
);
|
|
663
673
|
for (const box of infoboxes) {
|
|
664
674
|
const pageId = makeId("journal-infobox-page", `${id}:${box.id}`);
|
|
@@ -844,6 +854,7 @@ export class Journals extends BasePackCompiler {
|
|
|
844
854
|
.filter(Boolean)
|
|
845
855
|
.join("\n\n"),
|
|
846
856
|
resolveRole: (pathname) => roles.get(pathname),
|
|
857
|
+
runs: this.corpus?.runs,
|
|
847
858
|
infoboxes: boxes.map((box) => ({
|
|
848
859
|
id: box.id,
|
|
849
860
|
name: `${
|