@fuzdev/fuz_ui 0.198.1 → 0.200.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/dist/ApiDeclarationList.svelte +2 -1
- package/dist/ApiDeclarationList.svelte.d.ts.map +1 -1
- package/dist/ApiIndex.svelte +2 -2
- package/dist/ApiModule.svelte +3 -3
- package/dist/DeclarationDetail.svelte +145 -90
- package/dist/DeclarationDetail.svelte.d.ts.map +1 -1
- package/dist/LibraryDetail.svelte +14 -16
- package/dist/LibraryDetail.svelte.d.ts.map +1 -1
- package/dist/LibrarySummary.svelte +12 -12
- package/dist/Mdz.svelte +2 -2
- package/dist/MdzNodeView.svelte +8 -6
- package/dist/MdzNodeView.svelte.d.ts.map +1 -1
- package/dist/MdzRoot.svelte +30 -0
- package/dist/MdzRoot.svelte.d.ts +12 -0
- package/dist/MdzRoot.svelte.d.ts.map +1 -0
- package/dist/MdzStream.svelte +32 -0
- package/dist/MdzStream.svelte.d.ts +12 -0
- package/dist/MdzStream.svelte.d.ts.map +1 -0
- package/dist/MdzStreamNodeView.svelte +106 -0
- package/dist/MdzStreamNodeView.svelte.d.ts +9 -0
- package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -0
- package/dist/ProjectLinks.svelte +1 -1
- package/dist/api_search.svelte.d.ts.map +1 -1
- package/dist/api_search.svelte.js +4 -2
- package/dist/declaration.svelte.d.ts +11 -0
- package/dist/declaration.svelte.d.ts.map +1 -1
- package/dist/declaration.svelte.js +15 -3
- package/dist/library.svelte.d.ts +9 -70
- package/dist/library.svelte.d.ts.map +1 -1
- package/dist/library.svelte.js +24 -18
- package/dist/library_helpers.d.ts +3 -3
- package/dist/library_helpers.js +3 -3
- package/dist/mdz.d.ts.map +1 -1
- package/dist/mdz.js +37 -29
- package/dist/mdz_components.d.ts +37 -23
- package/dist/mdz_components.d.ts.map +1 -1
- package/dist/mdz_components.js +16 -4
- package/dist/mdz_helpers.d.ts +46 -14
- package/dist/mdz_helpers.d.ts.map +1 -1
- package/dist/mdz_helpers.js +189 -56
- package/dist/mdz_lexer.d.ts.map +1 -1
- package/dist/mdz_lexer.js +18 -22
- package/dist/mdz_opcodes.d.ts +174 -0
- package/dist/mdz_opcodes.d.ts.map +1 -0
- package/dist/mdz_opcodes.js +14 -0
- package/dist/mdz_opcodes_to_nodes.d.ts +20 -0
- package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -0
- package/dist/mdz_opcodes_to_nodes.js +332 -0
- package/dist/mdz_stream_parser.d.ts +80 -0
- package/dist/mdz_stream_parser.d.ts.map +1 -0
- package/dist/mdz_stream_parser.js +354 -0
- package/dist/mdz_stream_parser_block.d.ts +76 -0
- package/dist/mdz_stream_parser_block.d.ts.map +1 -0
- package/dist/mdz_stream_parser_block.js +458 -0
- package/dist/mdz_stream_parser_inline.d.ts +52 -0
- package/dist/mdz_stream_parser_inline.d.ts.map +1 -0
- package/dist/mdz_stream_parser_inline.js +378 -0
- package/dist/mdz_stream_parser_link.d.ts +22 -0
- package/dist/mdz_stream_parser_link.d.ts.map +1 -0
- package/dist/mdz_stream_parser_link.js +215 -0
- package/dist/mdz_stream_parser_state.d.ts +187 -0
- package/dist/mdz_stream_parser_state.d.ts.map +1 -0
- package/dist/mdz_stream_parser_state.js +398 -0
- package/dist/mdz_stream_parser_text.d.ts +14 -0
- package/dist/mdz_stream_parser_text.d.ts.map +1 -0
- package/dist/mdz_stream_parser_text.js +50 -0
- package/dist/mdz_stream_parser_url.d.ts +60 -0
- package/dist/mdz_stream_parser_url.d.ts.map +1 -0
- package/dist/mdz_stream_parser_url.js +307 -0
- package/dist/mdz_stream_state.svelte.d.ts +44 -0
- package/dist/mdz_stream_state.svelte.d.ts.map +1 -0
- package/dist/mdz_stream_state.svelte.js +357 -0
- package/dist/mdz_token_parser.d.ts.map +1 -1
- package/dist/mdz_token_parser.js +8 -39
- package/dist/module.svelte.d.ts +2 -0
- package/dist/module.svelte.d.ts.map +1 -1
- package/dist/module.svelte.js +3 -1
- package/dist/site.svelte.d.ts +13 -0
- package/dist/site.svelte.d.ts.map +1 -1
- package/dist/site.svelte.js +8 -2
- package/dist/tsdoc_mdz.d.ts +2 -2
- package/dist/tsdoc_mdz.js +2 -2
- package/dist/vite_plugin_pkg_json.d.ts +63 -0
- package/dist/vite_plugin_pkg_json.d.ts.map +1 -0
- package/dist/vite_plugin_pkg_json.js +126 -0
- package/package.json +8 -7
- package/src/lib/api_search.svelte.ts +4 -2
- package/src/lib/declaration.svelte.ts +18 -3
- package/src/lib/library.svelte.ts +37 -20
- package/src/lib/library_helpers.ts +3 -3
- package/src/lib/mdz.ts +38 -29
- package/src/lib/mdz_components.ts +40 -19
- package/src/lib/mdz_helpers.ts +199 -56
- package/src/lib/mdz_lexer.ts +18 -20
- package/src/lib/mdz_opcodes.ts +205 -0
- package/src/lib/mdz_opcodes_to_nodes.ts +375 -0
- package/src/lib/mdz_stream_parser.ts +415 -0
- package/src/lib/mdz_stream_parser_block.ts +532 -0
- package/src/lib/mdz_stream_parser_inline.ts +414 -0
- package/src/lib/mdz_stream_parser_link.ts +271 -0
- package/src/lib/mdz_stream_parser_state.ts +539 -0
- package/src/lib/mdz_stream_parser_text.ts +77 -0
- package/src/lib/mdz_stream_parser_url.ts +365 -0
- package/src/lib/mdz_stream_state.svelte.ts +387 -0
- package/src/lib/mdz_token_parser.ts +13 -40
- package/src/lib/module.svelte.ts +5 -1
- package/src/lib/site.svelte.ts +17 -2
- package/src/lib/tsdoc_mdz.ts +2 -2
- package/src/lib/vite_plugin_pkg_json.ts +142 -0
- package/dist/package_helpers.d.ts +0 -150
- package/dist/package_helpers.d.ts.map +0 -1
- package/dist/package_helpers.js +0 -179
- package/src/lib/package_helpers.ts +0 -186
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive state for the mdz streaming renderer.
|
|
3
|
+
*
|
|
4
|
+
* Maintains a reactive tree of `MdzStreamNode` objects that Svelte 5 can
|
|
5
|
+
* efficiently update. Opcodes from `MdzStreamParser` are applied via
|
|
6
|
+
* `apply()` or `apply_batch()`, mutating the tree in place.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {DEV} from 'esm-env';
|
|
12
|
+
|
|
13
|
+
import type {MdzNodeId, MdzNodeType, MdzOpcode} from './mdz_opcodes.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A reactive node in the stream renderer tree.
|
|
17
|
+
* Fields are `$state` so Svelte re-renders only what changes.
|
|
18
|
+
*/
|
|
19
|
+
export class MdzStreamNode {
|
|
20
|
+
id: MdzNodeId;
|
|
21
|
+
type: MdzNodeType;
|
|
22
|
+
content: string = $state('');
|
|
23
|
+
children: Array<MdzStreamNode> = $state([]);
|
|
24
|
+
// metadata
|
|
25
|
+
level?: number;
|
|
26
|
+
reference?: string = $state();
|
|
27
|
+
link_type?: string = $state();
|
|
28
|
+
name?: string;
|
|
29
|
+
lang?: string | null;
|
|
30
|
+
heading_id?: string = $state();
|
|
31
|
+
|
|
32
|
+
constructor(id: MdzNodeId, type: MdzNodeType) {
|
|
33
|
+
this.id = id;
|
|
34
|
+
this.type = type;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Reactive state manager for streaming mdz content.
|
|
40
|
+
* Apply opcodes to incrementally build and update the render tree.
|
|
41
|
+
*/
|
|
42
|
+
export class MdzStreamState {
|
|
43
|
+
root: Array<MdzStreamNode> = $state([]);
|
|
44
|
+
|
|
45
|
+
/** Node lookup by ID. */
|
|
46
|
+
#nodes = new Map<MdzNodeId, MdzStreamNode>();
|
|
47
|
+
|
|
48
|
+
/** Parent tracking for re-parenting during revert. */
|
|
49
|
+
#parents = new Map<MdzNodeId, MdzNodeId | null>();
|
|
50
|
+
|
|
51
|
+
/** Stack of open container IDs (for knowing current parent). */
|
|
52
|
+
#stack: Array<MdzNodeId> = [];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Register a new node and attach it to its current parent (innermost open
|
|
56
|
+
* container, or root). Shared by `open`, `text`, and `void` paths.
|
|
57
|
+
*/
|
|
58
|
+
#attach(node: MdzStreamNode): void {
|
|
59
|
+
this.#nodes.set(node.id, node);
|
|
60
|
+
const parent_id = this.#stack.length > 0 ? this.#stack[this.#stack.length - 1]! : null;
|
|
61
|
+
this.#parents.set(node.id, parent_id);
|
|
62
|
+
if (parent_id !== null) {
|
|
63
|
+
const parent = this.#nodes.get(parent_id);
|
|
64
|
+
if (DEV && !parent) {
|
|
65
|
+
throw new Error(`MdzStreamState: parent id ${parent_id} on stack but missing from nodes`);
|
|
66
|
+
}
|
|
67
|
+
if (parent) parent.children.push(node);
|
|
68
|
+
} else {
|
|
69
|
+
this.root.push(node);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Apply a single opcode.
|
|
75
|
+
*/
|
|
76
|
+
apply(opcode: MdzOpcode): void {
|
|
77
|
+
switch (opcode.type) {
|
|
78
|
+
case 'open': {
|
|
79
|
+
if (DEV && this.#nodes.has(opcode.id)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`MdzStreamState: duplicate open for id ${opcode.id} (node_type '${opcode.node_type}')`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const node = new MdzStreamNode(opcode.id, opcode.node_type);
|
|
85
|
+
if (opcode.level !== undefined) node.level = opcode.level;
|
|
86
|
+
if (opcode.name !== undefined) node.name = opcode.name;
|
|
87
|
+
if (opcode.lang !== undefined) node.lang = opcode.lang;
|
|
88
|
+
this.#attach(node);
|
|
89
|
+
this.#stack.push(opcode.id);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
case 'close': {
|
|
94
|
+
if (DEV) {
|
|
95
|
+
if (!this.#nodes.has(opcode.id)) {
|
|
96
|
+
throw new Error(`MdzStreamState: close for unknown id ${opcode.id}`);
|
|
97
|
+
}
|
|
98
|
+
if (!this.#stack.includes(opcode.id)) {
|
|
99
|
+
throw new Error(`MdzStreamState: close for id ${opcode.id} which is not on the stack`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const node = this.#nodes.get(opcode.id);
|
|
103
|
+
if (node) {
|
|
104
|
+
if (opcode.discard) {
|
|
105
|
+
// drop the node and its descendants from its parent's children
|
|
106
|
+
const parent_id = this.#parents.get(opcode.id);
|
|
107
|
+
const parent_children =
|
|
108
|
+
parent_id !== null && parent_id !== undefined
|
|
109
|
+
? this.#nodes.get(parent_id)?.children
|
|
110
|
+
: this.root;
|
|
111
|
+
if (parent_children) {
|
|
112
|
+
const idx = parent_children.indexOf(node);
|
|
113
|
+
if (idx !== -1) parent_children.splice(idx, 1);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
// apply deferred metadata
|
|
117
|
+
if (opcode.reference !== undefined) node.reference = opcode.reference;
|
|
118
|
+
if (opcode.link_type !== undefined) node.link_type = opcode.link_type;
|
|
119
|
+
if (opcode.heading_id !== undefined) node.heading_id = opcode.heading_id;
|
|
120
|
+
// Code/Codeblock: collapse text children into `content` so renderers
|
|
121
|
+
// can read it directly without walking children every render.
|
|
122
|
+
if (node.type === 'Code' || node.type === 'Codeblock') {
|
|
123
|
+
let content = '';
|
|
124
|
+
for (const child of node.children) content += child.content;
|
|
125
|
+
node.content = content;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// pop from stack
|
|
130
|
+
const idx = this.#stack.lastIndexOf(opcode.id);
|
|
131
|
+
if (idx !== -1) this.#stack.splice(idx, 1);
|
|
132
|
+
// cleanup: closed nodes have no further opcode interactions.
|
|
133
|
+
// children are retained in the render tree via children arrays
|
|
134
|
+
// (discarded nodes' subtrees also need full cleanup, which the
|
|
135
|
+
// traversal in `#cleanup_node` already handles).
|
|
136
|
+
this.#cleanup_node(opcode.id);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
case 'text': {
|
|
141
|
+
if (DEV && this.#nodes.has(opcode.id)) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`MdzStreamState: duplicate text for id ${opcode.id} (text_type '${opcode.text_type}')`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
const node = new MdzStreamNode(opcode.id, opcode.text_type);
|
|
147
|
+
node.content = opcode.content;
|
|
148
|
+
this.#attach(node);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case 'append_text': {
|
|
153
|
+
if (DEV && !this.#nodes.has(opcode.id)) {
|
|
154
|
+
throw new Error(`MdzStreamState: append_text for unknown id ${opcode.id}`);
|
|
155
|
+
}
|
|
156
|
+
const node = this.#nodes.get(opcode.id);
|
|
157
|
+
if (node) {
|
|
158
|
+
node.content += opcode.content;
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
case 'trim_text': {
|
|
164
|
+
if (DEV && !this.#nodes.has(opcode.id)) {
|
|
165
|
+
throw new Error(`MdzStreamState: trim_text for unknown id ${opcode.id}`);
|
|
166
|
+
}
|
|
167
|
+
const node = this.#nodes.get(opcode.id);
|
|
168
|
+
if (node) {
|
|
169
|
+
node.content = node.content.slice(0, node.content.length - opcode.count);
|
|
170
|
+
if (node.content.length === 0) {
|
|
171
|
+
const parent_id = this.#parents.get(opcode.id);
|
|
172
|
+
const parent_children =
|
|
173
|
+
parent_id !== null && parent_id !== undefined
|
|
174
|
+
? this.#nodes.get(parent_id)?.children
|
|
175
|
+
: this.root;
|
|
176
|
+
if (parent_children) {
|
|
177
|
+
const idx = parent_children.indexOf(node);
|
|
178
|
+
if (idx !== -1) parent_children.splice(idx, 1);
|
|
179
|
+
}
|
|
180
|
+
this.#nodes.delete(opcode.id);
|
|
181
|
+
this.#parents.delete(opcode.id);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
case 'void': {
|
|
188
|
+
if (DEV && this.#nodes.has(opcode.id)) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`MdzStreamState: duplicate void for id ${opcode.id} (node_type '${opcode.node_type}')`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
const node = new MdzStreamNode(opcode.id, opcode.node_type);
|
|
194
|
+
this.#attach(node);
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
case 'revert': {
|
|
199
|
+
if (DEV) {
|
|
200
|
+
if (!this.#nodes.has(opcode.id)) {
|
|
201
|
+
throw new Error(`MdzStreamState: revert for unknown id ${opcode.id}`);
|
|
202
|
+
}
|
|
203
|
+
if (!this.#stack.includes(opcode.id)) {
|
|
204
|
+
throw new Error(`MdzStreamState: revert for id ${opcode.id} which is not on the stack`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const node = this.#nodes.get(opcode.id);
|
|
208
|
+
if (!node) break;
|
|
209
|
+
|
|
210
|
+
const parent_id = this.#parents.get(opcode.id);
|
|
211
|
+
const parent_children =
|
|
212
|
+
parent_id !== null && parent_id !== undefined
|
|
213
|
+
? this.#nodes.get(parent_id)?.children
|
|
214
|
+
: this.root;
|
|
215
|
+
|
|
216
|
+
if (!parent_children) {
|
|
217
|
+
if (DEV) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`MdzStreamState: revert for id ${opcode.id} but parent has no children array`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// find the node in parent's children
|
|
226
|
+
let node_idx = -1;
|
|
227
|
+
for (let i = 0; i < parent_children.length; i++) {
|
|
228
|
+
if (parent_children[i]!.id === opcode.id) {
|
|
229
|
+
node_idx = i;
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (node_idx === -1) {
|
|
234
|
+
if (DEV) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`MdzStreamState: revert for id ${opcode.id} but node not found in parent's children`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// collect replacement content: literal delimiter text + re-parented children
|
|
243
|
+
const replacement_nodes: Array<MdzStreamNode> = [];
|
|
244
|
+
if (opcode.replacement_text) {
|
|
245
|
+
const text_node = new MdzStreamNode(opcode.id, 'Text');
|
|
246
|
+
text_node.content = opcode.replacement_text;
|
|
247
|
+
replacement_nodes.push(text_node);
|
|
248
|
+
}
|
|
249
|
+
for (const child of node.children) {
|
|
250
|
+
replacement_nodes.push(child);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (opcode.wrap_node_type != null && opcode.wrap_id != null) {
|
|
254
|
+
if (DEV && this.#nodes.has(opcode.wrap_id)) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`MdzStreamState: revert wrap_id ${opcode.wrap_id} already exists in nodes`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
// block-level revert: wrap content in a new container and push onto stack
|
|
260
|
+
const wrapper = new MdzStreamNode(opcode.wrap_id, opcode.wrap_node_type);
|
|
261
|
+
for (const rn of replacement_nodes) {
|
|
262
|
+
wrapper.children.push(rn);
|
|
263
|
+
}
|
|
264
|
+
for (const child of node.children) {
|
|
265
|
+
this.#parents.set(child.id, opcode.wrap_id);
|
|
266
|
+
}
|
|
267
|
+
this.#nodes.set(opcode.wrap_id, wrapper);
|
|
268
|
+
this.#parents.set(opcode.wrap_id, parent_id ?? null);
|
|
269
|
+
parent_children.splice(node_idx, 1, wrapper);
|
|
270
|
+
this.#stack.push(opcode.wrap_id);
|
|
271
|
+
} else {
|
|
272
|
+
// inline revert: re-parent directly to grandparent
|
|
273
|
+
for (const child of node.children) {
|
|
274
|
+
this.#parents.set(child.id, parent_id ?? null);
|
|
275
|
+
}
|
|
276
|
+
parent_children.splice(node_idx, 1, ...replacement_nodes);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// remove reverted node from stack
|
|
280
|
+
const stack_idx = this.#stack.lastIndexOf(opcode.id);
|
|
281
|
+
if (stack_idx !== -1) this.#stack.splice(stack_idx, 1);
|
|
282
|
+
|
|
283
|
+
// cleanup
|
|
284
|
+
this.#nodes.delete(opcode.id);
|
|
285
|
+
this.#parents.delete(opcode.id);
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
case 'wrap': {
|
|
290
|
+
if (DEV) {
|
|
291
|
+
if (!this.#nodes.has(opcode.target_id)) {
|
|
292
|
+
throw new Error(`MdzStreamState: wrap target_id ${opcode.target_id} not in nodes`);
|
|
293
|
+
}
|
|
294
|
+
if (this.#nodes.has(opcode.id)) {
|
|
295
|
+
throw new Error(`MdzStreamState: wrap id ${opcode.id} already exists in nodes`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const target = this.#nodes.get(opcode.target_id);
|
|
299
|
+
if (!target) break;
|
|
300
|
+
|
|
301
|
+
const parent_id = this.#parents.get(opcode.target_id);
|
|
302
|
+
const parent_children =
|
|
303
|
+
parent_id !== null && parent_id !== undefined
|
|
304
|
+
? this.#nodes.get(parent_id)?.children
|
|
305
|
+
: this.root;
|
|
306
|
+
|
|
307
|
+
if (!parent_children) break;
|
|
308
|
+
|
|
309
|
+
// find target in parent's children
|
|
310
|
+
let target_idx = -1;
|
|
311
|
+
for (let i = 0; i < parent_children.length; i++) {
|
|
312
|
+
if (parent_children[i]!.id === opcode.target_id) {
|
|
313
|
+
target_idx = i;
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (target_idx === -1) break;
|
|
318
|
+
|
|
319
|
+
// handle trailing punctuation trim
|
|
320
|
+
let trimmed_node: MdzStreamNode | null = null;
|
|
321
|
+
if (opcode.trim_end && opcode.trim_end > 0 && opcode.trim_id != null) {
|
|
322
|
+
const trimmed_content = target.content.slice(target.content.length - opcode.trim_end);
|
|
323
|
+
target.content = target.content.slice(0, target.content.length - opcode.trim_end);
|
|
324
|
+
trimmed_node = new MdzStreamNode(opcode.trim_id, 'Text');
|
|
325
|
+
trimmed_node.content = trimmed_content;
|
|
326
|
+
this.#nodes.set(opcode.trim_id, trimmed_node);
|
|
327
|
+
this.#parents.set(opcode.trim_id, parent_id ?? null);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// create Link wrapper containing target
|
|
331
|
+
const link = new MdzStreamNode(opcode.id, opcode.node_type);
|
|
332
|
+
link.reference = opcode.reference;
|
|
333
|
+
link.link_type = opcode.link_type;
|
|
334
|
+
link.children.push(target);
|
|
335
|
+
this.#nodes.set(opcode.id, link);
|
|
336
|
+
this.#parents.set(opcode.id, parent_id ?? null);
|
|
337
|
+
this.#parents.set(opcode.target_id, opcode.id);
|
|
338
|
+
|
|
339
|
+
// replace target with [Link, trimmed?] in parent's children
|
|
340
|
+
if (trimmed_node) {
|
|
341
|
+
parent_children.splice(target_idx, 1, link, trimmed_node);
|
|
342
|
+
} else {
|
|
343
|
+
parent_children[target_idx] = link;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// cleanup: Link is complete (no close opcode coming).
|
|
347
|
+
// trim_id (if any) stays in maps — cleaned when parent closes.
|
|
348
|
+
this.#cleanup_node(opcode.id);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Remove a node and its descendants from the lookup maps. Iterative to avoid
|
|
356
|
+
* stack overflow on deep trees.
|
|
357
|
+
*
|
|
358
|
+
* Parser invariant: once a container closes, no future opcode targets that
|
|
359
|
+
* subtree (no `append_text` to a closed Text, no nested `wrap`/`revert`).
|
|
360
|
+
* Children stay reachable via `parent.children` arrays — only the lookup
|
|
361
|
+
* maps are pruned. If this invariant is violated, the misbehaving opcode
|
|
362
|
+
* triggers a DEV throw in `apply()` and silently no-ops in production.
|
|
363
|
+
*/
|
|
364
|
+
#cleanup_node(id: MdzNodeId): void {
|
|
365
|
+
const queue: Array<MdzNodeId> = [id];
|
|
366
|
+
while (queue.length > 0) {
|
|
367
|
+
const current_id = queue.pop()!;
|
|
368
|
+
const node = this.#nodes.get(current_id);
|
|
369
|
+
if (node) {
|
|
370
|
+
for (const child of node.children) {
|
|
371
|
+
queue.push(child.id);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
this.#nodes.delete(current_id);
|
|
375
|
+
this.#parents.delete(current_id);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Apply a batch of opcodes.
|
|
381
|
+
*/
|
|
382
|
+
apply_batch(opcodes: Array<MdzOpcode>): void {
|
|
383
|
+
for (const op of opcodes) {
|
|
384
|
+
this.apply(op);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
@@ -18,7 +18,12 @@ import type {
|
|
|
18
18
|
MdzElementNode,
|
|
19
19
|
MdzComponentNode,
|
|
20
20
|
} from './mdz.js';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
extract_single_tag,
|
|
23
|
+
mdz_heading_id,
|
|
24
|
+
mdz_merge_adjacent_text,
|
|
25
|
+
mdz_push_merging_text,
|
|
26
|
+
} from './mdz_helpers.js';
|
|
22
27
|
import {
|
|
23
28
|
MdzLexer,
|
|
24
29
|
type MdzToken,
|
|
@@ -124,7 +129,7 @@ class MdzTokenParser {
|
|
|
124
129
|
if (node) children.push(node);
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
const merged =
|
|
132
|
+
const merged = mdz_merge_adjacent_text(children);
|
|
128
133
|
const end = merged.length > 0 ? merged[merged.length - 1]!.end : start + level + 1;
|
|
129
134
|
|
|
130
135
|
return {type: 'Heading', level, id: mdz_heading_id(merged), children: merged, start, end};
|
|
@@ -228,7 +233,7 @@ class MdzTokenParser {
|
|
|
228
233
|
break;
|
|
229
234
|
}
|
|
230
235
|
const node = this.#parse_inline();
|
|
231
|
-
if (node) children
|
|
236
|
+
if (node) mdz_push_merging_text(children, node);
|
|
232
237
|
}
|
|
233
238
|
|
|
234
239
|
// Unclosed - treat as text
|
|
@@ -257,7 +262,7 @@ class MdzTokenParser {
|
|
|
257
262
|
break;
|
|
258
263
|
}
|
|
259
264
|
const node = this.#parse_inline();
|
|
260
|
-
if (node) children
|
|
265
|
+
if (node) mdz_push_merging_text(children, node);
|
|
261
266
|
}
|
|
262
267
|
|
|
263
268
|
return {type: 'Text', content: '_', start, end: start + 1};
|
|
@@ -285,7 +290,7 @@ class MdzTokenParser {
|
|
|
285
290
|
break;
|
|
286
291
|
}
|
|
287
292
|
const node = this.#parse_inline();
|
|
288
|
-
if (node) children
|
|
293
|
+
if (node) mdz_push_merging_text(children, node);
|
|
289
294
|
}
|
|
290
295
|
|
|
291
296
|
return {type: 'Text', content: '~', start, end: start + 1};
|
|
@@ -329,7 +334,7 @@ class MdzTokenParser {
|
|
|
329
334
|
break;
|
|
330
335
|
}
|
|
331
336
|
const node = this.#parse_inline();
|
|
332
|
-
if (node) children
|
|
337
|
+
if (node) mdz_push_merging_text(children, node);
|
|
333
338
|
}
|
|
334
339
|
|
|
335
340
|
return {type: 'Text', content: '[', start, end: start + 1};
|
|
@@ -364,7 +369,7 @@ class MdzTokenParser {
|
|
|
364
369
|
return {type: node_type, name: tag_name, children, start, end: t.end};
|
|
365
370
|
}
|
|
366
371
|
const node = this.#parse_inline();
|
|
367
|
-
if (node) children
|
|
372
|
+
if (node) mdz_push_merging_text(children, node);
|
|
368
373
|
}
|
|
369
374
|
|
|
370
375
|
// Unclosed tag
|
|
@@ -414,7 +419,7 @@ class MdzTokenParser {
|
|
|
414
419
|
}
|
|
415
420
|
|
|
416
421
|
// Merge adjacent text nodes
|
|
417
|
-
const merged =
|
|
422
|
+
const merged = mdz_merge_adjacent_text(paragraph_children.slice());
|
|
418
423
|
paragraph_children.length = 0;
|
|
419
424
|
|
|
420
425
|
if (merged.length === 0) return null;
|
|
@@ -426,36 +431,4 @@ class MdzTokenParser {
|
|
|
426
431
|
end: merged[merged.length - 1]!.end,
|
|
427
432
|
};
|
|
428
433
|
}
|
|
429
|
-
|
|
430
|
-
#merge_adjacent_text(nodes: Array<MdzNode>): Array<MdzNode> {
|
|
431
|
-
if (nodes.length <= 1) return nodes;
|
|
432
|
-
|
|
433
|
-
const merged: Array<MdzNode> = [];
|
|
434
|
-
let pending_text: MdzTextNode | null = null;
|
|
435
|
-
|
|
436
|
-
for (const node of nodes) {
|
|
437
|
-
if (node.type === 'Text') {
|
|
438
|
-
if (pending_text) {
|
|
439
|
-
pending_text = {
|
|
440
|
-
type: 'Text',
|
|
441
|
-
content: pending_text.content + node.content,
|
|
442
|
-
start: pending_text.start,
|
|
443
|
-
end: node.end,
|
|
444
|
-
};
|
|
445
|
-
} else {
|
|
446
|
-
pending_text = {...node};
|
|
447
|
-
}
|
|
448
|
-
} else {
|
|
449
|
-
if (pending_text) {
|
|
450
|
-
merged.push(pending_text);
|
|
451
|
-
pending_text = null;
|
|
452
|
-
}
|
|
453
|
-
merged.push(node);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (pending_text) merged.push(pending_text);
|
|
458
|
-
|
|
459
|
-
return merged;
|
|
460
|
-
}
|
|
461
434
|
}
|
package/src/lib/module.svelte.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type {ModuleJsonInput} from 'svelte-docinfo/types.js';
|
|
|
2
2
|
|
|
3
3
|
import {Declaration} from './declaration.svelte.js';
|
|
4
4
|
import type {Library} from './library.svelte.js';
|
|
5
|
-
import {url_github_file} from '
|
|
5
|
+
import {url_github_file} from '@fuzdev/fuz_util/package_helpers.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Rich runtime representation of a module with computed properties.
|
|
@@ -90,6 +90,10 @@ export class Module {
|
|
|
90
90
|
*/
|
|
91
91
|
dependents = $derived(this.module_json.dependents ?? []);
|
|
92
92
|
|
|
93
|
+
has_dependencies: boolean = $derived(this.dependencies.length > 0);
|
|
94
|
+
|
|
95
|
+
has_dependents: boolean = $derived(this.dependents.length > 0);
|
|
96
|
+
|
|
93
97
|
constructor(library: Library, module_json: ModuleJsonInput) {
|
|
94
98
|
this.library = library;
|
|
95
99
|
this.module_json = module_json;
|
package/src/lib/site.svelte.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type {Url} from '@fuzdev/fuz_util/url.js';
|
|
2
|
+
import type {PkgJson} from '@fuzdev/fuz_util/pkg_json.js';
|
|
2
3
|
|
|
3
4
|
import {create_context} from './context_helpers.js';
|
|
5
|
+
import {repo_url_parse} from '@fuzdev/fuz_util/package_helpers.js';
|
|
4
6
|
import type {SvgData} from './svg.js';
|
|
5
7
|
|
|
6
8
|
export interface SiteStateOptions {
|
|
@@ -10,12 +12,20 @@ export interface SiteStateOptions {
|
|
|
10
12
|
icon?: SvgData | null;
|
|
11
13
|
/**
|
|
12
14
|
* The site's glyph, a single unicode character identifying the project.
|
|
15
|
+
* Falls back to `pkg_json.glyph`.
|
|
13
16
|
*/
|
|
14
17
|
glyph?: string | null;
|
|
15
18
|
/**
|
|
16
19
|
* The site's source repository url, e.g. for `DocsFooter` links.
|
|
20
|
+
* Falls back to the url parsed from `pkg_json.repository`.
|
|
17
21
|
*/
|
|
18
22
|
repo_url?: Url | null;
|
|
23
|
+
/**
|
|
24
|
+
* Curated `package.json`, typically the default export of `virtual:pkg.json`.
|
|
25
|
+
* `glyph` and `repo_url` derive from it when not given explicitly; explicit
|
|
26
|
+
* props always win.
|
|
27
|
+
*/
|
|
28
|
+
pkg_json?: PkgJson | null;
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
/**
|
|
@@ -23,6 +33,10 @@ export interface SiteStateOptions {
|
|
|
23
33
|
* by components like `Breadcrumb` and `DocsFooter`. Set once per app at the root
|
|
24
34
|
* layout. Distinct from `library_context`, which carries the heavy
|
|
25
35
|
* module/declaration metadata that only docs and API pages need.
|
|
36
|
+
*
|
|
37
|
+
* Pass `pkg_json` (from `virtual:pkg.json`) to derive `glyph`/`repo_url` from the
|
|
38
|
+
* package's own metadata instead of hardcoding them; `icon` stays an explicit
|
|
39
|
+
* logo import since it's structured `SvgData`, not package metadata.
|
|
26
40
|
*/
|
|
27
41
|
export class SiteState {
|
|
28
42
|
icon: SvgData | null = $state.raw(null);
|
|
@@ -31,8 +45,9 @@ export class SiteState {
|
|
|
31
45
|
|
|
32
46
|
constructor(options: SiteStateOptions = {}) {
|
|
33
47
|
this.icon = options.icon ?? null;
|
|
34
|
-
this.glyph = options.glyph ?? null;
|
|
35
|
-
this.repo_url =
|
|
48
|
+
this.glyph = options.glyph ?? options.pkg_json?.glyph ?? null;
|
|
49
|
+
this.repo_url =
|
|
50
|
+
options.repo_url ?? (options.pkg_json ? repo_url_parse(options.pkg_json.repository) : null);
|
|
36
51
|
}
|
|
37
52
|
}
|
|
38
53
|
|
package/src/lib/tsdoc_mdz.ts
CHANGED
|
@@ -37,8 +37,8 @@ const format_reference = (ref: string): string => (mdz_is_url(ref) ? ref : `\`${
|
|
|
37
37
|
* tsdoc_see_to_mdz('{@link SomeType}')
|
|
38
38
|
* // → '`SomeType`'
|
|
39
39
|
*
|
|
40
|
-
* tsdoc_see_to_mdz('https://
|
|
41
|
-
* // → 'https://
|
|
40
|
+
* tsdoc_see_to_mdz('https://fuz.dev')
|
|
41
|
+
* // → 'https://fuz.dev'
|
|
42
42
|
*
|
|
43
43
|
* tsdoc_see_to_mdz('[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library')
|
|
44
44
|
* // → '[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library'
|