@rse/specbook 1.2.14 → 1.2.16
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/README.md +98 -11
- package/dst/specbook-api.d.ts +22 -21
- package/dst/specbook-api.js +80 -27
- package/dst/specbook-cli.js +50 -23
- package/dst/specbook-cmd-describe.js +8 -4
- package/dst/specbook-cmd-export.d.ts +4 -1
- package/dst/specbook-cmd-export.js +22 -12
- package/dst/specbook-cmd-init.js +2 -8
- package/dst/specbook-cmd-lint.d.ts +2 -1
- package/dst/specbook-cmd-lint.js +31 -28
- package/dst/specbook-config.d.ts +7 -0
- package/dst/specbook-config.js +6 -1
- package/dst/specbook-coverage.js +3 -4
- package/dst/specbook-diagram.d.ts +3 -1
- package/dst/specbook-diagram.js +78 -27
- package/dst/specbook-export-ast.d.ts +2 -1
- package/dst/specbook-export-ast.js +16 -8
- package/dst/specbook-export-common.d.ts +7 -0
- package/dst/specbook-export-common.js +36 -3
- package/dst/specbook-export-html-search.js +27 -24
- package/dst/specbook-export-html.css +64 -18
- package/dst/specbook-export-html.d.ts +2 -1
- package/dst/specbook-export-html.js +519 -277
- package/dst/specbook-export-image.d.ts +3 -0
- package/dst/specbook-export-image.js +121 -0
- package/dst/specbook-export-md.d.ts +8 -2
- package/dst/specbook-export-md.js +67 -6
- package/dst/specbook-export-pdf.js +7 -4
- package/dst/specbook-format.md +35 -8
- package/dst/specbook-format.yaml +12 -9
- package/dst/specbook-ignore.js +5 -5
- package/dst/specbook-link.js +1 -1
- package/dst/specbook-mcp.js +51 -34
- package/dst/specbook-parse-common.d.ts +4 -1
- package/dst/specbook-parse-common.js +15 -3
- package/dst/specbook-parse-semantic.d.ts +1 -1
- package/dst/specbook-parse-syntax.d.ts +1 -1
- package/dst/specbook-parse-syntax.js +28 -8
- package/dst/specbook-parse.js +6 -2
- package/dst/specbook-project.d.ts +7 -0
- package/dst/specbook-project.js +93 -0
- package/dst/specbook-theme.d.ts +1 -1
- package/dst/specbook-theme.js +9 -8
- package/dst/specbook-verbose.js +4 -9
- package/package.json +14 -6
|
@@ -17,6 +17,24 @@ export const searchScript = () => fs.readFileSync(new URL("specbook-export-html-
|
|
|
17
17
|
SpecBook itself (as a self-contained data: URL, to keep its styles isolated) */
|
|
18
18
|
export const fallbackLogo = (theme) => "data:image/svg+xml;base64," +
|
|
19
19
|
fs.readFileSync(new URL(`specbook-export-logo-${theme}.svg`, import.meta.url)).toString("base64");
|
|
20
|
+
/* the content aspects an export can omit, each matching the set of a
|
|
21
|
+
folding control of the HTML export: the diagrams of a type, the
|
|
22
|
+
diagrams from an object tree nesting level on, and the long texts */
|
|
23
|
+
export const omitAspects = ["diagram:graph", "diagram:hub", "diagram:grid",
|
|
24
|
+
"diagram:1", "diagram:2", "diagram:3", "text:long"];
|
|
25
|
+
/* parse the (comma-separated) aspects to omit, where the
|
|
26
|
+
plain "diagram" is the alias of "diagram:1", hence of all diagrams */
|
|
27
|
+
export const parseOmit = (omit = []) => {
|
|
28
|
+
const aspects = new Set();
|
|
29
|
+
for (const item of omit.flatMap((list) => list.split(",")).map((item) => item.trim())) {
|
|
30
|
+
const aspect = item === "diagram" ? "diagram:1" : item;
|
|
31
|
+
if (!omitAspects.includes(aspect))
|
|
32
|
+
throw new Error(`unknown omit aspect "${item}" ` +
|
|
33
|
+
`(expected "diagram" or one of "${omitAspects.join("\", \"")}")`);
|
|
34
|
+
aspects.add(aspect);
|
|
35
|
+
}
|
|
36
|
+
return aspects;
|
|
37
|
+
};
|
|
20
38
|
/* check whether an object is the specification title object */
|
|
21
39
|
export const isTitleObject = (object) => object.kind === "META" && object.name.toUpperCase() === "TITLE";
|
|
22
40
|
/* determine the title object of the specification, searched at any
|
|
@@ -34,8 +52,14 @@ export const titleObject = (specification) => {
|
|
|
34
52
|
};
|
|
35
53
|
return search(specification.artifacts.flatMap((artifact) => artifact.objects));
|
|
36
54
|
};
|
|
37
|
-
/* determine a property value of the title object
|
|
38
|
-
|
|
55
|
+
/* determine a property value of the title object (a present but empty
|
|
56
|
+
value counting as an absent property, so every consumer falls back
|
|
57
|
+
onto its own default) */
|
|
58
|
+
const titleProperty = (specification, name) => {
|
|
59
|
+
const value = titleObject(specification)
|
|
60
|
+
?.properties.find((property) => property.key === name)?.value;
|
|
61
|
+
return value !== undefined && value.trim() !== "" ? value : undefined;
|
|
62
|
+
};
|
|
39
63
|
/* determine the document language (LANG) from the title object */
|
|
40
64
|
export const documentLang = (specification) => titleProperty(specification, "LANG")?.trim();
|
|
41
65
|
/* determine the document character set (CHARSET) from the title object */
|
|
@@ -76,10 +100,18 @@ export const documentPaperSize = (specification) => {
|
|
|
76
100
|
sized diagram could not share its page with the heading, which
|
|
77
101
|
would defeat the "break-after: avoid" bundling of the stylesheet */
|
|
78
102
|
const headingReserve = 6;
|
|
103
|
+
/* the vertical room (in rem) the "Diagram of Contents" heading claims
|
|
104
|
+
above its diagram: this <h1> opens its page and hence carries its
|
|
105
|
+
full margins (3rem above, 2rem below) plus its own 2.4rem line and
|
|
106
|
+
the 2rem margins of the diagram, which the flat reserve above
|
|
107
|
+
underestimates, so the diagram would be pushed onto the next page */
|
|
108
|
+
const docHeadingReserve = 10;
|
|
79
109
|
/* provide the paper-dependent print stylesheet: a diagram is scaled
|
|
80
110
|
down to still fit onto a single page (the paper height less the
|
|
81
111
|
print margins, the own vertical margins of the diagram, and the
|
|
82
|
-
heading reserve above it) and is never broken across a page boundary
|
|
112
|
+
heading reserve above it) and is never broken across a page boundary,
|
|
113
|
+
where the diagram of the "Diagram of Contents" page gets the larger
|
|
114
|
+
reserve its own heading demands */
|
|
83
115
|
export const paperStylesheet = (paper) => {
|
|
84
116
|
const setup = paperSetup(paper);
|
|
85
117
|
const avail = setup.height - setup.margin.top - setup.margin.bottom;
|
|
@@ -87,6 +119,7 @@ export const paperStylesheet = (paper) => {
|
|
|
87
119
|
"div.diagram { break-inside: avoid; }\n" +
|
|
88
120
|
`div.diagram svg { max-height: calc(${paperLength(setup, avail)} - ${headingReserve}rem);` +
|
|
89
121
|
" width: auto; height: auto; }\n" +
|
|
122
|
+
`nav.doc div.diagram svg { max-height: calc(${paperLength(setup, avail)} - ${docHeadingReserve}rem); }\n` +
|
|
90
123
|
"}\n";
|
|
91
124
|
};
|
|
92
125
|
/* generate a contiguous codepoint range */
|
|
@@ -50,34 +50,37 @@
|
|
|
50
50
|
let indexed = false
|
|
51
51
|
|
|
52
52
|
/* fill the unit and vocabulary indices once on first use: a unit
|
|
53
|
-
is an outermost paragraph-level element within an article
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
searched text */
|
|
53
|
+
is an outermost paragraph-level element within an article or
|
|
54
|
+
the title page abstract (a lone paragraph being the unit itself),
|
|
55
|
+
i.e. one not nested inside another unit (like a paragraph in a
|
|
56
|
+
table cell, covered by its table row), where a diagram is
|
|
57
|
+
searched by its space-joined SVG text labels (its raw text
|
|
58
|
+
content runs them together), and where the explicit anchor id
|
|
59
|
+
of an object heading or table row joins the searched text */
|
|
60
60
|
const index = () => {
|
|
61
61
|
if (indexed)
|
|
62
62
|
return
|
|
63
63
|
indexed = true
|
|
64
|
-
document.querySelectorAll("article").forEach((
|
|
65
|
-
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
})
|
|
64
|
+
document.querySelectorAll("article, div.titlepage > .description").forEach((root) => {
|
|
65
|
+
const els = Array.from(root.querySelectorAll(
|
|
66
|
+
"p, li, tr, pre, blockquote, h1, h2, h3, h4, h5, h6, div.diagram"))
|
|
67
|
+
if (root.tagName === "P")
|
|
68
|
+
els.unshift(root)
|
|
69
|
+
els.forEach((el) => {
|
|
70
|
+
let parent = el.parentElement
|
|
71
|
+
while (parent !== null && parent !== root) {
|
|
72
|
+
if (/^(TR|TD|TH|LI|P|BLOCKQUOTE)$/.test(parent.tagName))
|
|
73
|
+
return
|
|
74
|
+
parent = parent.parentElement
|
|
75
|
+
}
|
|
76
|
+
const id = (el.dataset.id ?? "").toLowerCase()
|
|
77
|
+
const text = ((el.classList.contains("diagram") ?
|
|
78
|
+
Array.from(el.querySelectorAll("text")).map((t) => t.textContent ?? "").join(" ") :
|
|
79
|
+
(el.textContent ?? "")) + " " + id).toLowerCase()
|
|
80
|
+
const words = new Set(text.split(/[^\p{L}\p{N}]+/u).filter((w) => w !== ""))
|
|
81
|
+
words.forEach((w) => { vocab.add(w) })
|
|
82
|
+
units.push({ el, id, text, words, keep: false })
|
|
83
|
+
})
|
|
81
84
|
})
|
|
82
85
|
|
|
83
86
|
/* let a hyperlink whose target the filtering hid leave the
|
|
@@ -73,8 +73,8 @@ html.theme-light {
|
|
|
73
73
|
--theme-color-specbook-tint-deep: var(--theme-color-accent-28);
|
|
74
74
|
--theme-color-specbook-accent: var(--theme-color-accent-12);
|
|
75
75
|
--theme-color-specbook-accent-soft: var(--theme-color-accent-18);
|
|
76
|
-
--theme-color-specbook-mark-text: var(--theme-color-
|
|
77
|
-
--theme-color-specbook-mark-back: var(--theme-color-
|
|
76
|
+
--theme-color-specbook-mark-text: var(--theme-color-signal-32);
|
|
77
|
+
--theme-color-specbook-mark-back: var(--theme-color-signal-16);
|
|
78
78
|
--theme-color-specbook-field: var(--theme-color-accent-8);
|
|
79
79
|
--theme-color-specbook-error: #c00;
|
|
80
80
|
--theme-color-specbook-popup-schema-path: var(--theme-color-accent-30);
|
|
@@ -84,6 +84,15 @@ html.theme-light {
|
|
|
84
84
|
--theme-color-specbook-progress-done: var(--theme-color-accent-12);
|
|
85
85
|
--theme-color-specbook-progress-todo: var(--theme-color-accent-31);
|
|
86
86
|
--gradia-font-family: "Source Sans 3";
|
|
87
|
+
/* the font sizes the diagram layout was sized for (kept in sync
|
|
88
|
+
with "diagramPresets" of specbook-diagram.ts, which feeds the
|
|
89
|
+
layout): an override here keeps that layout and hence has to
|
|
90
|
+
stay close to them */
|
|
91
|
+
--gradia-size-font-node: 30px;
|
|
92
|
+
--gradia-size-font-type: 19px;
|
|
93
|
+
--gradia-size-font-prop: 22px;
|
|
94
|
+
--gradia-size-font-edge: 19px;
|
|
95
|
+
--gradia-size-font-arity: 19px;
|
|
87
96
|
--gradia-color-node-regular-name: var(--theme-color-accent-12);
|
|
88
97
|
--gradia-color-node-regular-box: var(--theme-color-accent-30);
|
|
89
98
|
--gradia-color-node-regular-border: var(--theme-color-accent-26);
|
|
@@ -93,6 +102,9 @@ html.theme-light {
|
|
|
93
102
|
--gradia-color-node-ghost-name: var(--theme-color-base-22);
|
|
94
103
|
--gradia-color-node-ghost-box: var(--theme-color-base-31);
|
|
95
104
|
--gradia-color-node-ghost-border: var(--theme-color-base-24);
|
|
105
|
+
--gradia-color-node-self-name: var(--theme-color-base-14);
|
|
106
|
+
--gradia-color-node-self-box: var(--theme-color-base-28);
|
|
107
|
+
--gradia-color-node-self-border: var(--theme-color-base-18);
|
|
96
108
|
--gradia-color-group-name: var(--theme-color-accent-18);
|
|
97
109
|
--gradia-color-group-box: var(--theme-color-accent-32);
|
|
98
110
|
--gradia-color-group-border: var(--theme-color-accent-26);
|
|
@@ -117,27 +129,30 @@ html.theme-light {
|
|
|
117
129
|
--theme-color-specbook-tint: var(--theme-color-accent-1);
|
|
118
130
|
--theme-color-specbook-tint-stripe: var(--theme-color-accent-3);
|
|
119
131
|
--theme-color-specbook-tint-deep: var(--theme-color-accent-5);
|
|
120
|
-
--theme-color-specbook-accent: var(--theme-color-accent-
|
|
132
|
+
--theme-color-specbook-accent: var(--theme-color-accent-22);
|
|
121
133
|
--theme-color-specbook-accent-soft: var(--theme-color-accent-16);
|
|
122
|
-
--theme-color-specbook-mark-text: var(--theme-color-
|
|
123
|
-
--theme-color-specbook-mark-back: var(--theme-color-
|
|
134
|
+
--theme-color-specbook-mark-text: var(--theme-color-signal-32);
|
|
135
|
+
--theme-color-specbook-mark-back: var(--theme-color-signal-4);
|
|
124
136
|
--theme-color-specbook-field: var(--theme-color-accent-16);
|
|
125
137
|
--theme-color-specbook-error: #c00;
|
|
126
138
|
--theme-color-specbook-popup-schema-path: var(--theme-color-accent-4);
|
|
127
139
|
--theme-color-specbook-popup-schema-desc: var(--theme-color-base-7);
|
|
128
|
-
--theme-color-specbook-popup-spec-path: var(--theme-color-accent-
|
|
140
|
+
--theme-color-specbook-popup-spec-path: var(--theme-color-accent-22);
|
|
129
141
|
--theme-color-specbook-popup-spec-desc: var(--theme-color-base-29);
|
|
130
|
-
--theme-color-specbook-progress-done: var(--theme-color-accent-
|
|
142
|
+
--theme-color-specbook-progress-done: var(--theme-color-accent-22);
|
|
131
143
|
--theme-color-specbook-progress-todo: var(--theme-color-accent-4);
|
|
132
144
|
--gradia-color-node-regular-name: var(--theme-color-accent-21);
|
|
133
145
|
--gradia-color-node-regular-box: var(--theme-color-accent-3);
|
|
134
146
|
--gradia-color-node-regular-border: var(--theme-color-accent-7);
|
|
135
147
|
--gradia-color-node-primary-name: var(--theme-color-base-1);
|
|
136
|
-
--gradia-color-node-primary-box: var(--theme-color-accent-
|
|
137
|
-
--gradia-color-node-primary-border: var(--theme-color-accent-
|
|
148
|
+
--gradia-color-node-primary-box: var(--theme-color-accent-20);
|
|
149
|
+
--gradia-color-node-primary-border: var(--theme-color-accent-24);
|
|
138
150
|
--gradia-color-node-ghost-name: var(--theme-color-base-24);
|
|
139
151
|
--gradia-color-node-ghost-box: var(--theme-color-base-12);
|
|
140
152
|
--gradia-color-node-ghost-border: var(--theme-color-base-16);
|
|
153
|
+
--gradia-color-node-self-name: var(--theme-color-base-28);
|
|
154
|
+
--gradia-color-node-self-box: var(--theme-color-base-15);
|
|
155
|
+
--gradia-color-node-self-border: var(--theme-color-base-22);
|
|
141
156
|
--gradia-color-group-name: var(--theme-color-accent-15);
|
|
142
157
|
--gradia-color-group-box: var(--theme-color-accent-1);
|
|
143
158
|
--gradia-color-group-border: var(--theme-color-accent-7);
|
|
@@ -413,12 +428,13 @@ div.info-popup.spec a.link-compact::before {
|
|
|
413
428
|
/* the folding tab (screen-only): the tab riding right of the brand
|
|
414
429
|
bar between the search tab and the table of contents tab (as wide
|
|
415
430
|
as those, fixed independent of the content scrolling), showing a
|
|
416
|
-
fold icon (drawn like the search icon), which slides the
|
|
431
|
+
fold icon (drawn like the search icon), which slides the
|
|
417
432
|
controls out to the right (and back in), where the tab follows the
|
|
418
433
|
bar when the table of contents panel shifts it aside; the controls
|
|
419
|
-
fold and unfold all diagrams
|
|
420
|
-
|
|
421
|
-
|
|
434
|
+
fold and unfold all diagrams of a type, all diagrams from a nesting
|
|
435
|
+
level on, and all over-long cell texts of the content at once and
|
|
436
|
+
carry, like the fold icon itself, the mark colors in a rounded box
|
|
437
|
+
while anything of their kind is folded */
|
|
422
438
|
div.fold-switch {
|
|
423
439
|
position: fixed;
|
|
424
440
|
top: 10.66rem;
|
|
@@ -465,7 +481,7 @@ div.fold-switch div.fold-controls {
|
|
|
465
481
|
transition: max-width 0.3s ease, padding 0.3s ease;
|
|
466
482
|
}
|
|
467
483
|
div.fold-switch.open div.fold-controls {
|
|
468
|
-
max-width:
|
|
484
|
+
max-width: 15rem;
|
|
469
485
|
padding: 0 0.5rem 0 0.2rem;
|
|
470
486
|
}
|
|
471
487
|
div.fold-switch div.fold-controls > div {
|
|
@@ -1155,10 +1171,18 @@ table.objects table.chunks th:not(.property-name) {
|
|
|
1155
1171
|
}
|
|
1156
1172
|
/* the diagram leading the chunks of an object, kept in one piece */
|
|
1157
1173
|
table.objects table.chunks div.diagram {
|
|
1158
|
-
margin: 0.
|
|
1174
|
+
margin: 0.15rem 0;
|
|
1159
1175
|
page-break-inside: avoid;
|
|
1160
1176
|
break-inside: avoid;
|
|
1161
1177
|
}
|
|
1178
|
+
/* bundle a chunk header row with its value row, so neither the
|
|
1179
|
+
"Diagram" header nor the property names are orphaned at a page end */
|
|
1180
|
+
@media print {
|
|
1181
|
+
table.objects table.chunks tr:has(> th) {
|
|
1182
|
+
page-break-after: avoid;
|
|
1183
|
+
break-after: avoid;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1162
1186
|
/* keep the cells of empty property values one text line high */
|
|
1163
1187
|
table.objects td:empty::after {
|
|
1164
1188
|
content: "\00A0";
|
|
@@ -1182,6 +1206,11 @@ table.chunks {
|
|
|
1182
1206
|
table-layout: fixed;
|
|
1183
1207
|
width: 100%;
|
|
1184
1208
|
}
|
|
1209
|
+
table.objects table.chunks th {
|
|
1210
|
+
font-size: 80%;
|
|
1211
|
+
padding-top: 0.27rem;
|
|
1212
|
+
padding-bottom: 0.27rem;
|
|
1213
|
+
}
|
|
1185
1214
|
table.objects table.chunks tr td {
|
|
1186
1215
|
background-color: transparent;
|
|
1187
1216
|
}
|
|
@@ -1298,13 +1327,19 @@ div.diagram {
|
|
|
1298
1327
|
margin: 1rem 0;
|
|
1299
1328
|
}
|
|
1300
1329
|
div.diagram svg {
|
|
1330
|
+
display: block;
|
|
1301
1331
|
max-width: 100%;
|
|
1302
1332
|
height: auto;
|
|
1303
|
-
}
|
|
1304
|
-
div.diagram svg.hub {
|
|
1305
|
-
display: block;
|
|
1306
1333
|
margin: 0 auto;
|
|
1307
1334
|
}
|
|
1335
|
+
/* the halo of the diagram edge labels is dropped for print, as Chromium
|
|
1336
|
+
prints a stroked text as the curve outlines of its glyphs instead of
|
|
1337
|
+
as text, which alone made up nearly half of the size of the PDF */
|
|
1338
|
+
@media print {
|
|
1339
|
+
div.diagram svg text {
|
|
1340
|
+
stroke: none;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1308
1343
|
span.rationale {
|
|
1309
1344
|
color: var(--theme-color-specbook-muted);
|
|
1310
1345
|
}
|
|
@@ -1579,6 +1614,17 @@ nav.doc h1 {
|
|
|
1579
1614
|
color: var(--theme-color-specbook-accent);
|
|
1580
1615
|
font-weight: normal;
|
|
1581
1616
|
}
|
|
1617
|
+
/* in print, both front matter headings and the first artifact one
|
|
1618
|
+
open a fresh page, where their top margin is kept after the forced
|
|
1619
|
+
page break */
|
|
1620
|
+
@media print {
|
|
1621
|
+
nav.toc h1,
|
|
1622
|
+
nav.doc h1,
|
|
1623
|
+
article:first-of-type > section:first-child,
|
|
1624
|
+
article:first-of-type > section:first-child > h1 {
|
|
1625
|
+
margin-top: 0;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1582
1628
|
@keyframes realtime-blink {
|
|
1583
1629
|
from {
|
|
1584
1630
|
background-color: var(--theme-color-specbook-accent-soft);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Spec, SpecObject } from "./specbook-format-spec.js";
|
|
2
2
|
import type { Schema } from "./specbook-format-schema.js";
|
|
3
|
+
import { type ExportOptions } from "./specbook-export-common.js";
|
|
3
4
|
import type { Verbose } from "./specbook-verbose.js";
|
|
4
5
|
export type OutlineEntry = {
|
|
5
6
|
title: string;
|
|
@@ -9,4 +10,4 @@ export type OutlineEntry = {
|
|
|
9
10
|
export declare const htmlOutline: (specification: Spec, config?: Schema) => OutlineEntry[];
|
|
10
11
|
export declare const titlePageObject: (specification: Spec) => SpecObject | undefined;
|
|
11
12
|
export declare const renderPlaceholder: (message: string) => string;
|
|
12
|
-
export declare const renderHtml: (specification: Spec, config?: Schema, tocPages?: Map<string, number>, css?: string,
|
|
13
|
+
export declare const renderHtml: (specification: Spec, config?: Schema, tocPages?: Map<string, number>, css?: string, options?: ExportOptions, verbose?: Verbose, print?: boolean) => Promise<string>;
|