@rse/specbook 1.2.10 → 1.2.12
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.
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
the table header and the headings of all enclosing objects, and
|
|
12
12
|
every matched word is highlighted with a <mark> element (an SVG
|
|
13
13
|
<tspan> element underlaid with a <rect> inside a diagram), while
|
|
14
|
-
the table of contents (page and side panel) is filtered along
|
|
14
|
+
the table of contents (page and side panel) is filtered along; the
|
|
15
|
+
explicit anchor id of an object (invisible in the document) is
|
|
16
|
+
searched like its text, a hit marking the anchor symbol instead */
|
|
15
17
|
(function () {
|
|
16
18
|
const tab = document.getElementById("search")
|
|
17
19
|
const toggle = document.getElementById("search-toggle")
|
|
@@ -52,7 +54,9 @@
|
|
|
52
54
|
one not nested inside another unit (like a paragraph or diagram
|
|
53
55
|
in a table cell, which is covered by its table row), where a
|
|
54
56
|
diagram is searched by its SVG text labels, joined by spaces,
|
|
55
|
-
as its raw text content would run them together
|
|
57
|
+
as its raw text content would run them together, and where the
|
|
58
|
+
explicit anchor id of an object heading or table row joins the
|
|
59
|
+
searched text */
|
|
56
60
|
const index = () => {
|
|
57
61
|
if (indexed)
|
|
58
62
|
return
|
|
@@ -66,12 +70,13 @@
|
|
|
66
70
|
return
|
|
67
71
|
parent = parent.parentElement
|
|
68
72
|
}
|
|
69
|
-
const
|
|
73
|
+
const id = (el.dataset.id ?? "").toLowerCase()
|
|
74
|
+
const text = ((el.classList.contains("diagram") ?
|
|
70
75
|
Array.from(el.querySelectorAll("text")).map((t) => t.textContent ?? "").join(" ") :
|
|
71
|
-
(el.textContent ?? "")).toLowerCase()
|
|
76
|
+
(el.textContent ?? "")) + " " + id).toLowerCase()
|
|
72
77
|
const words = new Set(text.split(/[^\p{L}\p{N}]+/u).filter((w) => w !== ""))
|
|
73
78
|
words.forEach((w) => { vocab.add(w) })
|
|
74
|
-
units.push({ el, text, words, keep: false })
|
|
79
|
+
units.push({ el, id, text, words, keep: false })
|
|
75
80
|
})
|
|
76
81
|
})
|
|
77
82
|
|
|
@@ -319,12 +324,24 @@
|
|
|
319
324
|
el.classList.add("search-hide")
|
|
320
325
|
})
|
|
321
326
|
|
|
322
|
-
/* highlight every matched word within the kept units
|
|
327
|
+
/* highlight every matched word within the kept units, plus the
|
|
328
|
+
anchor symbol of a unit whose explicit anchor id matched, as
|
|
329
|
+
the id itself is nowhere visible */
|
|
323
330
|
const words = Array.from(new Set(parts.flat()))
|
|
324
331
|
const regex = buildRegex(words, Array.from(variants))
|
|
325
332
|
units.forEach((unit) => {
|
|
326
|
-
if (unit.el.classList.contains("search-keep"))
|
|
327
|
-
|
|
333
|
+
if (!unit.el.classList.contains("search-keep"))
|
|
334
|
+
return
|
|
335
|
+
highlight(unit.el, regex)
|
|
336
|
+
regex.lastIndex = 0
|
|
337
|
+
const symbol = unit.el.querySelector("span.anchor-symbol")
|
|
338
|
+
if (unit.id === "" || symbol === null || !regex.test(unit.id))
|
|
339
|
+
return
|
|
340
|
+
const mark = document.createElement("mark")
|
|
341
|
+
mark.classList.add("search-hit")
|
|
342
|
+
while (symbol.firstChild !== null)
|
|
343
|
+
mark.appendChild(symbol.firstChild)
|
|
344
|
+
symbol.appendChild(mark)
|
|
328
345
|
})
|
|
329
346
|
|
|
330
347
|
/* filter the table of contents (its page rows and its side
|
|
@@ -282,7 +282,9 @@ body.info-on div.info-switch svg.info-icon {
|
|
|
282
282
|
icon leading each, the kinds bold in the path color and the names
|
|
283
283
|
semi-bold in the description color, as in the document content,
|
|
284
284
|
and a pointer separating the segments), extended for properties
|
|
285
|
-
with the not-filled circle icon and the bold property name,
|
|
285
|
+
with the not-filled circle icon and the bold property name, trailed
|
|
286
|
+
for instances by the anchor id of the object in the description
|
|
287
|
+
color (its "(#...)" wrapping normal, the id itself semi-bold), and
|
|
286
288
|
the description led by a "Schema" or "Specification" origin label
|
|
287
289
|
(its word in normal weight in a rounded box filled with the path
|
|
288
290
|
color), where
|
|
@@ -329,6 +331,13 @@ div.info-popup span.info-name {
|
|
|
329
331
|
div.info-popup.spec span.info-name {
|
|
330
332
|
color: var(--theme-color-specbook-popup-spec-desc);
|
|
331
333
|
}
|
|
334
|
+
div.info-popup.spec span.info-id {
|
|
335
|
+
color: var(--theme-color-specbook-popup-spec-desc);
|
|
336
|
+
font-weight: normal;
|
|
337
|
+
}
|
|
338
|
+
div.info-popup.spec span.info-id-name {
|
|
339
|
+
font-weight: 600;
|
|
340
|
+
}
|
|
332
341
|
div.info-popup span.info-object::before {
|
|
333
342
|
content: "\25CF";
|
|
334
343
|
font-weight: normal;
|
|
@@ -109,7 +109,7 @@ const templates = {
|
|
|
109
109
|
<h1>Table of Contents</h1>
|
|
110
110
|
<table>
|
|
111
111
|
{% for entry in Toc.entries %}
|
|
112
|
-
<tr class="level-{{ entry.level }}"><td><a href="#{{ entry.id }}"><span class="object-kind"{% if entry.info %} data-info="{{ entry.info }}" data-info-path="{{ entry.infopath }}"{% endif %}>{{ entry.kind }}:</span> <span class="object-name"{% if entry.spec %} data-info-spec="{{ entry.spec }}" data-info-path="{{ entry.specpath }}"{% endif %}>{{ entry.name }}</span> <span class="link-symbol">⚭</span></a></td>{% if entry.page %}<td class="page"><a href="#{{ entry.id }}">{{ entry.page }}</a></td>{% endif %}</tr>
|
|
112
|
+
<tr class="level-{{ entry.level }}"><td><a href="#{{ entry.id }}"><span class="object-kind"{% if entry.info %} data-info="{{ entry.info }}" data-info-path="{{ entry.infopath }}"{% endif %}>{{ entry.kind }}:</span> <span class="object-name"{% if entry.spec %} data-info-spec="{{ entry.spec }}" data-info-id="{{ entry.specid }}" data-info-path="{{ entry.specpath }}"{% endif %}>{{ entry.name }}</span> <span class="link-symbol">⚭</span></a></td>{% if entry.page %}<td class="page"><a href="#{{ entry.id }}">{{ entry.page }}</a></td>{% endif %}</tr>
|
|
113
113
|
{% endfor %}
|
|
114
114
|
</table>
|
|
115
115
|
</nav>
|
|
@@ -132,7 +132,7 @@ const templates = {
|
|
|
132
132
|
"TocPanelEntries": textframe `
|
|
133
133
|
<ul>
|
|
134
134
|
{% for entry in Entries %}
|
|
135
|
-
<li><a href="#{{ entry.id }}"><span class="entry"><span class="object-kind"{% if entry.info %} data-info="{{ entry.info }}" data-info-path="{{ entry.infopath }}"{% endif %}>{{ entry.kind }}:</span> <span class="object-name"{% if entry.spec %} data-info-spec="{{ entry.spec }}" data-info-path="{{ entry.specpath }}"{% endif %}>{{ entry.name }}</span></span></a>{{ entry.children }}</li>
|
|
135
|
+
<li><a href="#{{ entry.id }}"><span class="entry"><span class="object-kind"{% if entry.info %} data-info="{{ entry.info }}" data-info-path="{{ entry.infopath }}"{% endif %}>{{ entry.kind }}:</span> <span class="object-name"{% if entry.spec %} data-info-spec="{{ entry.spec }}" data-info-id="{{ entry.specid }}" data-info-path="{{ entry.specpath }}"{% endif %}>{{ entry.name }}</span></span></a>{{ entry.children }}</li>
|
|
136
136
|
{% endfor %}
|
|
137
137
|
</ul>
|
|
138
138
|
`,
|
|
@@ -152,7 +152,7 @@ const templates = {
|
|
|
152
152
|
/* <Object/> */
|
|
153
153
|
"Object": textframe `
|
|
154
154
|
<section>
|
|
155
|
-
<h{{ Object.level }} id="{{ Object.id }}"><span class="object-kind"{% if Object.info %} data-info="{{ Object.info }}" data-info-path="{{ Object.infopath }}"{% endif %}>{{ Object.kind }}:</span> <span class="object-name"{% if Object.spec %} data-info-spec="{{ Object.spec }}" data-info-path="{{ Object.specpath }}"{% endif %}>{{ Object.name }}</span>{% if Object.primary %} <span class="primary-marker">⌘</span>{% endif %}{% if Object.paren %} <span class="anchor-paren">({{ Object.paren }})</span>{% endif %} <a href="#{{ Object.id }}"><span class="anchor-symbol">⚓︎</span></a></h{{ Object.level }}>
|
|
155
|
+
<h{{ Object.level }} id="{{ Object.id }}"{% if Object.anchor %} data-id="{{ Object.anchor }}"{% endif %}><span class="object-kind"{% if Object.info %} data-info="{{ Object.info }}" data-info-path="{{ Object.infopath }}"{% endif %}>{{ Object.kind }}:</span> <span class="object-name"{% if Object.spec %} data-info-spec="{{ Object.spec }}" data-info-id="{{ Object.specid }}" data-info-path="{{ Object.specpath }}"{% endif %}>{{ Object.name }}</span>{% if Object.primary %} <span class="primary-marker">⌘</span>{% endif %}{% if Object.paren %} <span class="anchor-paren">({{ Object.paren }})</span>{% endif %} <a href="#{{ Object.id }}"><span class="anchor-symbol">⚓︎</span></a></h{{ Object.level }}>
|
|
156
156
|
{{ Object.diagram }}
|
|
157
157
|
{{ Object.properties }}
|
|
158
158
|
{{ Object.description }}
|
|
@@ -206,8 +206,8 @@ const templates = {
|
|
|
206
206
|
</thead>
|
|
207
207
|
<tbody>
|
|
208
208
|
{% for row in Table.rows %}
|
|
209
|
-
<tr id="{{ row.id }}"{% if row.even %} class="even"{% endif %}>
|
|
210
|
-
<td><span{% if row.spec %} data-info-spec="{{ row.spec }}" data-info-path="{{ row.specpath }}"{% endif %}>{{ row.name }}</span>{% if row.primary %} <span class="primary-marker">⌘</span>{% endif %}{% if row.paren %} <span class="anchor-paren">({{ row.paren }})</span>{% endif %} <a href="#{{ row.id }}"><span class="anchor-symbol">⚓︎</span></a></td>
|
|
209
|
+
<tr id="{{ row.id }}"{% if row.anchor %} data-id="{{ row.anchor }}"{% endif %}{% if row.even %} class="even"{% endif %}>
|
|
210
|
+
<td><span{% if row.spec %} data-info-spec="{{ row.spec }}" data-info-id="{{ row.specid }}" data-info-path="{{ row.specpath }}"{% endif %}>{{ row.name }}</span>{% if row.primary %} <span class="primary-marker">⌘</span>{% endif %}{% if row.paren %} <span class="anchor-paren">({{ row.paren }})</span>{% endif %} <a href="#{{ row.id }}"><span class="anchor-symbol">⚓︎</span></a></td>
|
|
211
211
|
{%- for value in row.values %}<td>{{ value }}</td>{% endfor %}
|
|
212
212
|
{%- if Table.desc %}<td>{{ row.description }}</td>{% endif %}
|
|
213
213
|
</tr>
|
|
@@ -226,8 +226,8 @@ const templates = {
|
|
|
226
226
|
</thead>
|
|
227
227
|
<tbody>
|
|
228
228
|
{% for row in Table.rows %}
|
|
229
|
-
<tr id="{{ row.id }}"{% if row.even %} class="even"{% endif %}>
|
|
230
|
-
<td><span{% if row.spec %} data-info-spec="{{ row.spec }}" data-info-path="{{ row.specpath }}"{% endif %}>{{ row.name }}</span>{% if row.primary %} <span class="primary-marker">⌘</span>{% endif %}{% if row.paren %} <span class="anchor-paren">({{ row.paren }})</span>{% endif %} <a href="#{{ row.id }}"><span class="anchor-symbol">⚓︎</span></a></td>
|
|
229
|
+
<tr id="{{ row.id }}"{% if row.anchor %} data-id="{{ row.anchor }}"{% endif %}{% if row.even %} class="even"{% endif %}>
|
|
230
|
+
<td><span{% if row.spec %} data-info-spec="{{ row.spec }}" data-info-id="{{ row.specid }}" data-info-path="{{ row.specpath }}"{% endif %}>{{ row.name }}</span>{% if row.primary %} <span class="primary-marker">⌘</span>{% endif %}{% if row.paren %} <span class="anchor-paren">({{ row.paren }})</span>{% endif %} <a href="#{{ row.id }}"><span class="anchor-symbol">⚓︎</span></a></td>
|
|
231
231
|
<td class="chunks">
|
|
232
232
|
<table class="chunks">
|
|
233
233
|
{% for chunk in row.chunks %}
|
|
@@ -866,7 +866,8 @@ const tocPanelScript = textframe `
|
|
|
866
866
|
carrying a "data-info-spec" key pops up the corpus description of
|
|
867
867
|
the object instance (a diagram node box resolves through its
|
|
868
868
|
hyperlinked object anchor), titled with the "data-info-path" object
|
|
869
|
-
path
|
|
869
|
+
path (an instance popup trailing the "data-info-id" anchor id of
|
|
870
|
+
the object) and fed from the injected INFO/SPEC maps of pre-rendered
|
|
870
871
|
description HTML; the popup is capped at 40% viewport width and
|
|
871
872
|
attached above or below, whichever side offers more space; the
|
|
872
873
|
script runs at the end of the body, so a live preview body swap
|
|
@@ -964,6 +965,20 @@ const infoPopupScript = textframe `
|
|
|
964
965
|
}
|
|
965
966
|
title.appendChild(path)
|
|
966
967
|
}
|
|
968
|
+
|
|
969
|
+
/* the anchor id of an object instance (explicit or derived
|
|
970
|
+
from its name, carried by the "data-info-id" key) trails
|
|
971
|
+
the last segment */
|
|
972
|
+
const anchor = source.getAttribute("data-info-id")
|
|
973
|
+
if (spec !== null && anchor !== null) {
|
|
974
|
+
const id = document.createElement("span")
|
|
975
|
+
id.className = "info-id"
|
|
976
|
+
const name = document.createElement("span")
|
|
977
|
+
name.className = "info-id-name"
|
|
978
|
+
name.textContent = anchor
|
|
979
|
+
id.append(" (#", name, ")")
|
|
980
|
+
title.appendChild(id)
|
|
981
|
+
}
|
|
967
982
|
if (prop !== null) {
|
|
968
983
|
title.appendChild(pointer())
|
|
969
984
|
const name = document.createElement("span")
|
|
@@ -1154,11 +1169,12 @@ const infoAttr = (object) => {
|
|
|
1154
1169
|
` data-info="${escapeHtml(key)}" data-info-path="${escapeHtml(infoPathOf(object, false))}"` : "";
|
|
1155
1170
|
};
|
|
1156
1171
|
/* render the corpus description popup key (the fully-qualified anchor
|
|
1157
|
-
path) and name-ending title path of an object as
|
|
1158
|
-
attributes for the manually assembled hyperlink markup (the
|
|
1172
|
+
path), anchor id, and name-ending title path of an object as
|
|
1173
|
+
"data-info-spec" attributes for the manually assembled hyperlink markup (the
|
|
1159
1174
|
instance popup of the compact and full hyperlinks) */
|
|
1160
1175
|
const specAttr = (object) => infoParents !== null ?
|
|
1161
|
-
` data-info-spec="${escapeHtml(anchorOf(object))}" data-info-
|
|
1176
|
+
` data-info-spec="${escapeHtml(anchorOf(object))}" data-info-id="${escapeHtml(object.id)}"` +
|
|
1177
|
+
` data-info-path="${escapeHtml(infoPathOf(object))}"` : "";
|
|
1162
1178
|
/* collect the description popup map and the qualified title paths of
|
|
1163
1179
|
the schema nodes (the artifact kind leading its name, the nested
|
|
1164
1180
|
kinds appended), with the description Markdown of the objects and
|
|
@@ -1460,9 +1476,11 @@ const renderTable = (children, maxColumns) => {
|
|
|
1460
1476
|
width: Math.round(200 / (keys.length + 3)),
|
|
1461
1477
|
rows: children.map((child, i) => scoped(child, () => ({
|
|
1462
1478
|
id: anchorOf(child),
|
|
1479
|
+
anchor: child.anchor,
|
|
1463
1480
|
paren: child.paren,
|
|
1464
1481
|
primary: child.primary,
|
|
1465
1482
|
spec: infoParents !== null ? anchorOf(child) : undefined,
|
|
1483
|
+
specid: child.id,
|
|
1466
1484
|
specpath: infoPathOf(child),
|
|
1467
1485
|
name: inline(child.name),
|
|
1468
1486
|
even: i % 2 === 1,
|
|
@@ -1500,9 +1518,11 @@ const renderTable = (children, maxColumns) => {
|
|
|
1500
1518
|
chunks.unshift([{ key: "Diagram", desc: true, span: size, value: diagram }]);
|
|
1501
1519
|
return {
|
|
1502
1520
|
id: anchorOf(child),
|
|
1521
|
+
anchor: child.anchor,
|
|
1503
1522
|
paren: child.paren,
|
|
1504
1523
|
primary: child.primary,
|
|
1505
1524
|
spec: infoParents !== null ? anchorOf(child) : undefined,
|
|
1525
|
+
specid: child.id,
|
|
1506
1526
|
specpath: infoPathOf(child),
|
|
1507
1527
|
name: inline(child.name),
|
|
1508
1528
|
even: i % 2 === 1,
|
|
@@ -1520,8 +1540,10 @@ const renderObject = (object, level, concise) => {
|
|
|
1520
1540
|
info: infoKeyOf(object),
|
|
1521
1541
|
infopath: infoPathOf(object, false),
|
|
1522
1542
|
spec: infoParents !== null ? anchorOf(object) : undefined,
|
|
1543
|
+
specid: object.id,
|
|
1523
1544
|
specpath: infoPathOf(object),
|
|
1524
1545
|
id: anchorOf(object),
|
|
1546
|
+
anchor: object.anchor,
|
|
1525
1547
|
paren: object.paren,
|
|
1526
1548
|
primary: object.primary,
|
|
1527
1549
|
name: inline(object.name),
|
|
@@ -1590,7 +1612,7 @@ const tocEntries = (objects, pages) => {
|
|
|
1590
1612
|
const id = anchorOf(object);
|
|
1591
1613
|
entries.push({ id, kind: object.kind,
|
|
1592
1614
|
info: infoKeyOf(object), infopath: infoPathOf(object, false),
|
|
1593
|
-
spec: infoParents !== null ? id : undefined, specpath: infoPathOf(object),
|
|
1615
|
+
spec: infoParents !== null ? id : undefined, specid: object.id, specpath: infoPathOf(object),
|
|
1594
1616
|
name: inline(object.name), level: Math.min(level, 6), page: pages?.get(id) });
|
|
1595
1617
|
collect(headingChildren(object), level + 1);
|
|
1596
1618
|
}
|
|
@@ -1610,6 +1632,7 @@ const renderTocPanel = (objects, title, doc) => {
|
|
|
1610
1632
|
info: infoKeyOf(object),
|
|
1611
1633
|
infopath: infoPathOf(object, false),
|
|
1612
1634
|
spec: infoParents !== null ? anchorOf(object) : undefined,
|
|
1635
|
+
specid: object.id,
|
|
1613
1636
|
specpath: infoPathOf(object),
|
|
1614
1637
|
name: inline(object.name),
|
|
1615
1638
|
children: safe(entries(headingChildren(object)))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rse/specbook",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.12",
|
|
4
4
|
"publishConfig": { "access": "public" },
|
|
5
5
|
"description": "Markdown-based Specification Format (API, CLI and MCP service)",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [ "dst" ],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"marked": "18.0.
|
|
20
|
+
"marked": "18.0.13",
|
|
21
21
|
"marked-smartypants": "1.1.12",
|
|
22
|
-
"yaml": "2.9.
|
|
22
|
+
"yaml": "2.9.1",
|
|
23
23
|
"json5": "2.2.3",
|
|
24
24
|
"pdf-lib": "1.17.1",
|
|
25
25
|
"subset-font": "2.7.0",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"glob": "13.0.6",
|
|
41
41
|
"picomatch": "4.0.7",
|
|
42
42
|
"es-toolkit": "1.52.0",
|
|
43
|
-
"fastify": "5.12.
|
|
43
|
+
"fastify": "5.12.4",
|
|
44
44
|
"@fastify/websocket": "11.3.0",
|
|
45
45
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
46
|
-
"zod": "4.6.
|
|
46
|
+
"zod": "4.6.5"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"chokidar-cli": "3.0.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"globals": "17.12.0",
|
|
56
56
|
"markdownlint": "0.41.1",
|
|
57
57
|
"markdownlint-cli2": "0.23.2",
|
|
58
|
-
"@types/node": "26.5.
|
|
58
|
+
"@types/node": "26.5.1",
|
|
59
59
|
"@types/picomatch": "4.0.3",
|
|
60
60
|
"@types/subset-font": "2.3.0",
|
|
61
61
|
"@types/nunjucks": "3.2.6",
|