@fuzdev/fuz_ui 0.198.0 → 0.199.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.
Files changed (108) hide show
  1. package/README.md +5 -3
  2. package/dist/ApiIndex.svelte +2 -2
  3. package/dist/ApiModule.svelte +1 -1
  4. package/dist/LibraryDetail.svelte +14 -16
  5. package/dist/LibraryDetail.svelte.d.ts.map +1 -1
  6. package/dist/LibrarySummary.svelte +12 -12
  7. package/dist/Mdz.svelte +2 -2
  8. package/dist/MdzNodeView.svelte +8 -6
  9. package/dist/MdzNodeView.svelte.d.ts.map +1 -1
  10. package/dist/MdzRoot.svelte +30 -0
  11. package/dist/MdzRoot.svelte.d.ts +12 -0
  12. package/dist/MdzRoot.svelte.d.ts.map +1 -0
  13. package/dist/MdzStream.svelte +32 -0
  14. package/dist/MdzStream.svelte.d.ts +12 -0
  15. package/dist/MdzStream.svelte.d.ts.map +1 -0
  16. package/dist/MdzStreamNodeView.svelte +106 -0
  17. package/dist/MdzStreamNodeView.svelte.d.ts +9 -0
  18. package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -0
  19. package/dist/ProjectLinks.svelte +2 -0
  20. package/dist/ProjectLinks.svelte.d.ts.map +1 -1
  21. package/dist/declaration.svelte.js +2 -2
  22. package/dist/library.svelte.d.ts +9 -70
  23. package/dist/library.svelte.d.ts.map +1 -1
  24. package/dist/library.svelte.js +24 -18
  25. package/dist/library_helpers.d.ts +3 -3
  26. package/dist/library_helpers.js +3 -3
  27. package/dist/logos.d.ts +7 -0
  28. package/dist/logos.d.ts.map +1 -1
  29. package/dist/logos.js +9 -0
  30. package/dist/mdz.d.ts.map +1 -1
  31. package/dist/mdz.js +37 -29
  32. package/dist/mdz_components.d.ts +37 -23
  33. package/dist/mdz_components.d.ts.map +1 -1
  34. package/dist/mdz_components.js +16 -4
  35. package/dist/mdz_helpers.d.ts +46 -14
  36. package/dist/mdz_helpers.d.ts.map +1 -1
  37. package/dist/mdz_helpers.js +189 -56
  38. package/dist/mdz_lexer.d.ts.map +1 -1
  39. package/dist/mdz_lexer.js +18 -22
  40. package/dist/mdz_opcodes.d.ts +174 -0
  41. package/dist/mdz_opcodes.d.ts.map +1 -0
  42. package/dist/mdz_opcodes.js +14 -0
  43. package/dist/mdz_opcodes_to_nodes.d.ts +20 -0
  44. package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -0
  45. package/dist/mdz_opcodes_to_nodes.js +332 -0
  46. package/dist/mdz_stream_parser.d.ts +80 -0
  47. package/dist/mdz_stream_parser.d.ts.map +1 -0
  48. package/dist/mdz_stream_parser.js +354 -0
  49. package/dist/mdz_stream_parser_block.d.ts +76 -0
  50. package/dist/mdz_stream_parser_block.d.ts.map +1 -0
  51. package/dist/mdz_stream_parser_block.js +458 -0
  52. package/dist/mdz_stream_parser_inline.d.ts +52 -0
  53. package/dist/mdz_stream_parser_inline.d.ts.map +1 -0
  54. package/dist/mdz_stream_parser_inline.js +378 -0
  55. package/dist/mdz_stream_parser_link.d.ts +22 -0
  56. package/dist/mdz_stream_parser_link.d.ts.map +1 -0
  57. package/dist/mdz_stream_parser_link.js +215 -0
  58. package/dist/mdz_stream_parser_state.d.ts +187 -0
  59. package/dist/mdz_stream_parser_state.d.ts.map +1 -0
  60. package/dist/mdz_stream_parser_state.js +398 -0
  61. package/dist/mdz_stream_parser_text.d.ts +14 -0
  62. package/dist/mdz_stream_parser_text.d.ts.map +1 -0
  63. package/dist/mdz_stream_parser_text.js +50 -0
  64. package/dist/mdz_stream_parser_url.d.ts +60 -0
  65. package/dist/mdz_stream_parser_url.d.ts.map +1 -0
  66. package/dist/mdz_stream_parser_url.js +307 -0
  67. package/dist/mdz_stream_state.svelte.d.ts +44 -0
  68. package/dist/mdz_stream_state.svelte.d.ts.map +1 -0
  69. package/dist/mdz_stream_state.svelte.js +357 -0
  70. package/dist/mdz_token_parser.d.ts.map +1 -1
  71. package/dist/mdz_token_parser.js +8 -39
  72. package/dist/module.svelte.js +1 -1
  73. package/dist/site.svelte.d.ts +13 -0
  74. package/dist/site.svelte.d.ts.map +1 -1
  75. package/dist/site.svelte.js +8 -2
  76. package/dist/tsdoc_mdz.d.ts +2 -2
  77. package/dist/tsdoc_mdz.js +2 -2
  78. package/dist/vite_plugin_pkg_json.d.ts +63 -0
  79. package/dist/vite_plugin_pkg_json.d.ts.map +1 -0
  80. package/dist/vite_plugin_pkg_json.js +126 -0
  81. package/package.json +6 -5
  82. package/src/lib/declaration.svelte.ts +2 -2
  83. package/src/lib/library.svelte.ts +37 -20
  84. package/src/lib/library_helpers.ts +3 -3
  85. package/src/lib/logos.ts +10 -0
  86. package/src/lib/mdz.ts +38 -29
  87. package/src/lib/mdz_components.ts +40 -19
  88. package/src/lib/mdz_helpers.ts +199 -56
  89. package/src/lib/mdz_lexer.ts +18 -20
  90. package/src/lib/mdz_opcodes.ts +205 -0
  91. package/src/lib/mdz_opcodes_to_nodes.ts +375 -0
  92. package/src/lib/mdz_stream_parser.ts +415 -0
  93. package/src/lib/mdz_stream_parser_block.ts +532 -0
  94. package/src/lib/mdz_stream_parser_inline.ts +414 -0
  95. package/src/lib/mdz_stream_parser_link.ts +271 -0
  96. package/src/lib/mdz_stream_parser_state.ts +539 -0
  97. package/src/lib/mdz_stream_parser_text.ts +77 -0
  98. package/src/lib/mdz_stream_parser_url.ts +365 -0
  99. package/src/lib/mdz_stream_state.svelte.ts +387 -0
  100. package/src/lib/mdz_token_parser.ts +13 -40
  101. package/src/lib/module.svelte.ts +1 -1
  102. package/src/lib/site.svelte.ts +17 -2
  103. package/src/lib/tsdoc_mdz.ts +2 -2
  104. package/src/lib/vite_plugin_pkg_json.ts +142 -0
  105. package/dist/package_helpers.d.ts +0 -150
  106. package/dist/package_helpers.d.ts.map +0 -1
  107. package/dist/package_helpers.js +0 -179
  108. package/src/lib/package_helpers.ts +0 -186
@@ -0,0 +1,357 @@
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
+ import { DEV } from 'esm-env';
11
+ /**
12
+ * A reactive node in the stream renderer tree.
13
+ * Fields are `$state` so Svelte re-renders only what changes.
14
+ */
15
+ export class MdzStreamNode {
16
+ id;
17
+ type;
18
+ content = $state('');
19
+ children = $state([]);
20
+ // metadata
21
+ level;
22
+ reference = $state();
23
+ link_type = $state();
24
+ name;
25
+ lang;
26
+ heading_id = $state();
27
+ constructor(id, type) {
28
+ this.id = id;
29
+ this.type = type;
30
+ }
31
+ }
32
+ /**
33
+ * Reactive state manager for streaming mdz content.
34
+ * Apply opcodes to incrementally build and update the render tree.
35
+ */
36
+ export class MdzStreamState {
37
+ root = $state([]);
38
+ /** Node lookup by ID. */
39
+ #nodes = new Map();
40
+ /** Parent tracking for re-parenting during revert. */
41
+ #parents = new Map();
42
+ /** Stack of open container IDs (for knowing current parent). */
43
+ #stack = [];
44
+ /**
45
+ * Register a new node and attach it to its current parent (innermost open
46
+ * container, or root). Shared by `open`, `text`, and `void` paths.
47
+ */
48
+ #attach(node) {
49
+ this.#nodes.set(node.id, node);
50
+ const parent_id = this.#stack.length > 0 ? this.#stack[this.#stack.length - 1] : null;
51
+ this.#parents.set(node.id, parent_id);
52
+ if (parent_id !== null) {
53
+ const parent = this.#nodes.get(parent_id);
54
+ if (DEV && !parent) {
55
+ throw new Error(`MdzStreamState: parent id ${parent_id} on stack but missing from nodes`);
56
+ }
57
+ if (parent)
58
+ parent.children.push(node);
59
+ }
60
+ else {
61
+ this.root.push(node);
62
+ }
63
+ }
64
+ /**
65
+ * Apply a single opcode.
66
+ */
67
+ apply(opcode) {
68
+ switch (opcode.type) {
69
+ case 'open': {
70
+ if (DEV && this.#nodes.has(opcode.id)) {
71
+ throw new Error(`MdzStreamState: duplicate open for id ${opcode.id} (node_type '${opcode.node_type}')`);
72
+ }
73
+ const node = new MdzStreamNode(opcode.id, opcode.node_type);
74
+ if (opcode.level !== undefined)
75
+ node.level = opcode.level;
76
+ if (opcode.name !== undefined)
77
+ node.name = opcode.name;
78
+ if (opcode.lang !== undefined)
79
+ node.lang = opcode.lang;
80
+ this.#attach(node);
81
+ this.#stack.push(opcode.id);
82
+ break;
83
+ }
84
+ case 'close': {
85
+ if (DEV) {
86
+ if (!this.#nodes.has(opcode.id)) {
87
+ throw new Error(`MdzStreamState: close for unknown id ${opcode.id}`);
88
+ }
89
+ if (!this.#stack.includes(opcode.id)) {
90
+ throw new Error(`MdzStreamState: close for id ${opcode.id} which is not on the stack`);
91
+ }
92
+ }
93
+ const node = this.#nodes.get(opcode.id);
94
+ if (node) {
95
+ if (opcode.discard) {
96
+ // drop the node and its descendants from its parent's children
97
+ const parent_id = this.#parents.get(opcode.id);
98
+ const parent_children = parent_id !== null && parent_id !== undefined
99
+ ? this.#nodes.get(parent_id)?.children
100
+ : this.root;
101
+ if (parent_children) {
102
+ const idx = parent_children.indexOf(node);
103
+ if (idx !== -1)
104
+ parent_children.splice(idx, 1);
105
+ }
106
+ }
107
+ else {
108
+ // apply deferred metadata
109
+ if (opcode.reference !== undefined)
110
+ node.reference = opcode.reference;
111
+ if (opcode.link_type !== undefined)
112
+ node.link_type = opcode.link_type;
113
+ if (opcode.heading_id !== undefined)
114
+ node.heading_id = opcode.heading_id;
115
+ // Code/Codeblock: collapse text children into `content` so renderers
116
+ // can read it directly without walking children every render.
117
+ if (node.type === 'Code' || node.type === 'Codeblock') {
118
+ let content = '';
119
+ for (const child of node.children)
120
+ content += child.content;
121
+ node.content = content;
122
+ }
123
+ }
124
+ }
125
+ // pop from stack
126
+ const idx = this.#stack.lastIndexOf(opcode.id);
127
+ if (idx !== -1)
128
+ this.#stack.splice(idx, 1);
129
+ // cleanup: closed nodes have no further opcode interactions.
130
+ // children are retained in the render tree via children arrays
131
+ // (discarded nodes' subtrees also need full cleanup, which the
132
+ // traversal in `#cleanup_node` already handles).
133
+ this.#cleanup_node(opcode.id);
134
+ break;
135
+ }
136
+ case 'text': {
137
+ if (DEV && this.#nodes.has(opcode.id)) {
138
+ throw new Error(`MdzStreamState: duplicate text for id ${opcode.id} (text_type '${opcode.text_type}')`);
139
+ }
140
+ const node = new MdzStreamNode(opcode.id, opcode.text_type);
141
+ node.content = opcode.content;
142
+ this.#attach(node);
143
+ break;
144
+ }
145
+ case 'append_text': {
146
+ if (DEV && !this.#nodes.has(opcode.id)) {
147
+ throw new Error(`MdzStreamState: append_text for unknown id ${opcode.id}`);
148
+ }
149
+ const node = this.#nodes.get(opcode.id);
150
+ if (node) {
151
+ node.content += opcode.content;
152
+ }
153
+ break;
154
+ }
155
+ case 'trim_text': {
156
+ if (DEV && !this.#nodes.has(opcode.id)) {
157
+ throw new Error(`MdzStreamState: trim_text for unknown id ${opcode.id}`);
158
+ }
159
+ const node = this.#nodes.get(opcode.id);
160
+ if (node) {
161
+ node.content = node.content.slice(0, node.content.length - opcode.count);
162
+ if (node.content.length === 0) {
163
+ const parent_id = this.#parents.get(opcode.id);
164
+ const parent_children = parent_id !== null && parent_id !== undefined
165
+ ? this.#nodes.get(parent_id)?.children
166
+ : this.root;
167
+ if (parent_children) {
168
+ const idx = parent_children.indexOf(node);
169
+ if (idx !== -1)
170
+ parent_children.splice(idx, 1);
171
+ }
172
+ this.#nodes.delete(opcode.id);
173
+ this.#parents.delete(opcode.id);
174
+ }
175
+ }
176
+ break;
177
+ }
178
+ case 'void': {
179
+ if (DEV && this.#nodes.has(opcode.id)) {
180
+ throw new Error(`MdzStreamState: duplicate void for id ${opcode.id} (node_type '${opcode.node_type}')`);
181
+ }
182
+ const node = new MdzStreamNode(opcode.id, opcode.node_type);
183
+ this.#attach(node);
184
+ break;
185
+ }
186
+ case 'revert': {
187
+ if (DEV) {
188
+ if (!this.#nodes.has(opcode.id)) {
189
+ throw new Error(`MdzStreamState: revert for unknown id ${opcode.id}`);
190
+ }
191
+ if (!this.#stack.includes(opcode.id)) {
192
+ throw new Error(`MdzStreamState: revert for id ${opcode.id} which is not on the stack`);
193
+ }
194
+ }
195
+ const node = this.#nodes.get(opcode.id);
196
+ if (!node)
197
+ break;
198
+ const parent_id = this.#parents.get(opcode.id);
199
+ const parent_children = parent_id !== null && parent_id !== undefined
200
+ ? this.#nodes.get(parent_id)?.children
201
+ : this.root;
202
+ if (!parent_children) {
203
+ if (DEV) {
204
+ throw new Error(`MdzStreamState: revert for id ${opcode.id} but parent has no children array`);
205
+ }
206
+ break;
207
+ }
208
+ // find the node in parent's children
209
+ let node_idx = -1;
210
+ for (let i = 0; i < parent_children.length; i++) {
211
+ if (parent_children[i].id === opcode.id) {
212
+ node_idx = i;
213
+ break;
214
+ }
215
+ }
216
+ if (node_idx === -1) {
217
+ if (DEV) {
218
+ throw new Error(`MdzStreamState: revert for id ${opcode.id} but node not found in parent's children`);
219
+ }
220
+ break;
221
+ }
222
+ // collect replacement content: literal delimiter text + re-parented children
223
+ const replacement_nodes = [];
224
+ if (opcode.replacement_text) {
225
+ const text_node = new MdzStreamNode(opcode.id, 'Text');
226
+ text_node.content = opcode.replacement_text;
227
+ replacement_nodes.push(text_node);
228
+ }
229
+ for (const child of node.children) {
230
+ replacement_nodes.push(child);
231
+ }
232
+ if (opcode.wrap_node_type != null && opcode.wrap_id != null) {
233
+ if (DEV && this.#nodes.has(opcode.wrap_id)) {
234
+ throw new Error(`MdzStreamState: revert wrap_id ${opcode.wrap_id} already exists in nodes`);
235
+ }
236
+ // block-level revert: wrap content in a new container and push onto stack
237
+ const wrapper = new MdzStreamNode(opcode.wrap_id, opcode.wrap_node_type);
238
+ for (const rn of replacement_nodes) {
239
+ wrapper.children.push(rn);
240
+ }
241
+ for (const child of node.children) {
242
+ this.#parents.set(child.id, opcode.wrap_id);
243
+ }
244
+ this.#nodes.set(opcode.wrap_id, wrapper);
245
+ this.#parents.set(opcode.wrap_id, parent_id ?? null);
246
+ parent_children.splice(node_idx, 1, wrapper);
247
+ this.#stack.push(opcode.wrap_id);
248
+ }
249
+ else {
250
+ // inline revert: re-parent directly to grandparent
251
+ for (const child of node.children) {
252
+ this.#parents.set(child.id, parent_id ?? null);
253
+ }
254
+ parent_children.splice(node_idx, 1, ...replacement_nodes);
255
+ }
256
+ // remove reverted node from stack
257
+ const stack_idx = this.#stack.lastIndexOf(opcode.id);
258
+ if (stack_idx !== -1)
259
+ this.#stack.splice(stack_idx, 1);
260
+ // cleanup
261
+ this.#nodes.delete(opcode.id);
262
+ this.#parents.delete(opcode.id);
263
+ break;
264
+ }
265
+ case 'wrap': {
266
+ if (DEV) {
267
+ if (!this.#nodes.has(opcode.target_id)) {
268
+ throw new Error(`MdzStreamState: wrap target_id ${opcode.target_id} not in nodes`);
269
+ }
270
+ if (this.#nodes.has(opcode.id)) {
271
+ throw new Error(`MdzStreamState: wrap id ${opcode.id} already exists in nodes`);
272
+ }
273
+ }
274
+ const target = this.#nodes.get(opcode.target_id);
275
+ if (!target)
276
+ break;
277
+ const parent_id = this.#parents.get(opcode.target_id);
278
+ const parent_children = parent_id !== null && parent_id !== undefined
279
+ ? this.#nodes.get(parent_id)?.children
280
+ : this.root;
281
+ if (!parent_children)
282
+ break;
283
+ // find target in parent's children
284
+ let target_idx = -1;
285
+ for (let i = 0; i < parent_children.length; i++) {
286
+ if (parent_children[i].id === opcode.target_id) {
287
+ target_idx = i;
288
+ break;
289
+ }
290
+ }
291
+ if (target_idx === -1)
292
+ break;
293
+ // handle trailing punctuation trim
294
+ let trimmed_node = null;
295
+ if (opcode.trim_end && opcode.trim_end > 0 && opcode.trim_id != null) {
296
+ const trimmed_content = target.content.slice(target.content.length - opcode.trim_end);
297
+ target.content = target.content.slice(0, target.content.length - opcode.trim_end);
298
+ trimmed_node = new MdzStreamNode(opcode.trim_id, 'Text');
299
+ trimmed_node.content = trimmed_content;
300
+ this.#nodes.set(opcode.trim_id, trimmed_node);
301
+ this.#parents.set(opcode.trim_id, parent_id ?? null);
302
+ }
303
+ // create Link wrapper containing target
304
+ const link = new MdzStreamNode(opcode.id, opcode.node_type);
305
+ link.reference = opcode.reference;
306
+ link.link_type = opcode.link_type;
307
+ link.children.push(target);
308
+ this.#nodes.set(opcode.id, link);
309
+ this.#parents.set(opcode.id, parent_id ?? null);
310
+ this.#parents.set(opcode.target_id, opcode.id);
311
+ // replace target with [Link, trimmed?] in parent's children
312
+ if (trimmed_node) {
313
+ parent_children.splice(target_idx, 1, link, trimmed_node);
314
+ }
315
+ else {
316
+ parent_children[target_idx] = link;
317
+ }
318
+ // cleanup: Link is complete (no close opcode coming).
319
+ // trim_id (if any) stays in maps — cleaned when parent closes.
320
+ this.#cleanup_node(opcode.id);
321
+ break;
322
+ }
323
+ }
324
+ }
325
+ /**
326
+ * Remove a node and its descendants from the lookup maps. Iterative to avoid
327
+ * stack overflow on deep trees.
328
+ *
329
+ * Parser invariant: once a container closes, no future opcode targets that
330
+ * subtree (no `append_text` to a closed Text, no nested `wrap`/`revert`).
331
+ * Children stay reachable via `parent.children` arrays — only the lookup
332
+ * maps are pruned. If this invariant is violated, the misbehaving opcode
333
+ * triggers a DEV throw in `apply()` and silently no-ops in production.
334
+ */
335
+ #cleanup_node(id) {
336
+ const queue = [id];
337
+ while (queue.length > 0) {
338
+ const current_id = queue.pop();
339
+ const node = this.#nodes.get(current_id);
340
+ if (node) {
341
+ for (const child of node.children) {
342
+ queue.push(child.id);
343
+ }
344
+ }
345
+ this.#nodes.delete(current_id);
346
+ this.#parents.delete(current_id);
347
+ }
348
+ }
349
+ /**
350
+ * Apply a batch of opcodes.
351
+ */
352
+ apply_batch(opcodes) {
353
+ for (const op of opcodes) {
354
+ this.apply(op);
355
+ }
356
+ }
357
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_token_parser.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_token_parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACX,OAAO,EASP,MAAM,UAAU,CAAC;AAgBlB;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,KAAK,CAAC,OAAO,CAG3D,CAAC"}
1
+ {"version":3,"file":"mdz_token_parser.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_token_parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACX,OAAO,EASP,MAAM,UAAU,CAAC;AAqBlB;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,KAAK,CAAC,OAAO,CAG3D,CAAC"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import { extract_single_tag, mdz_heading_id } from './mdz_helpers.js';
9
+ import { extract_single_tag, mdz_heading_id, mdz_merge_adjacent_text, mdz_push_merging_text, } from './mdz_helpers.js';
10
10
  import { MdzLexer, } from './mdz_lexer.js';
11
11
  /**
12
12
  * Parses text to an array of `MdzNode` using a two-phase lexer+parser approach.
@@ -91,7 +91,7 @@ class MdzTokenParser {
91
91
  if (node)
92
92
  children.push(node);
93
93
  }
94
- const merged = this.#merge_adjacent_text(children);
94
+ const merged = mdz_merge_adjacent_text(children);
95
95
  const end = merged.length > 0 ? merged[merged.length - 1].end : start + level + 1;
96
96
  return { type: 'Heading', level, id: mdz_heading_id(merged), children: merged, start, end };
97
97
  }
@@ -173,7 +173,7 @@ class MdzTokenParser {
173
173
  }
174
174
  const node = this.#parse_inline();
175
175
  if (node)
176
- children.push(node);
176
+ mdz_push_merging_text(children, node);
177
177
  }
178
178
  // Unclosed - treat as text
179
179
  return { type: 'Text', content: '**', start, end: start + 2 };
@@ -197,7 +197,7 @@ class MdzTokenParser {
197
197
  }
198
198
  const node = this.#parse_inline();
199
199
  if (node)
200
- children.push(node);
200
+ mdz_push_merging_text(children, node);
201
201
  }
202
202
  return { type: 'Text', content: '_', start, end: start + 1 };
203
203
  }
@@ -220,7 +220,7 @@ class MdzTokenParser {
220
220
  }
221
221
  const node = this.#parse_inline();
222
222
  if (node)
223
- children.push(node);
223
+ mdz_push_merging_text(children, node);
224
224
  }
225
225
  return { type: 'Text', content: '~', start, end: start + 1 };
226
226
  }
@@ -257,7 +257,7 @@ class MdzTokenParser {
257
257
  }
258
258
  const node = this.#parse_inline();
259
259
  if (node)
260
- children.push(node);
260
+ mdz_push_merging_text(children, node);
261
261
  }
262
262
  return { type: 'Text', content: '[', start, end: start + 1 };
263
263
  }
@@ -288,7 +288,7 @@ class MdzTokenParser {
288
288
  }
289
289
  const node = this.#parse_inline();
290
290
  if (node)
291
- children.push(node);
291
+ mdz_push_merging_text(children, node);
292
292
  }
293
293
  // Unclosed tag
294
294
  return { type: 'Text', content: '<', start, end: start + 1 };
@@ -330,7 +330,7 @@ class MdzTokenParser {
330
330
  return single_tag;
331
331
  }
332
332
  // Merge adjacent text nodes
333
- const merged = this.#merge_adjacent_text(paragraph_children.slice());
333
+ const merged = mdz_merge_adjacent_text(paragraph_children.slice());
334
334
  paragraph_children.length = 0;
335
335
  if (merged.length === 0)
336
336
  return null;
@@ -341,35 +341,4 @@ class MdzTokenParser {
341
341
  end: merged[merged.length - 1].end,
342
342
  };
343
343
  }
344
- #merge_adjacent_text(nodes) {
345
- if (nodes.length <= 1)
346
- return nodes;
347
- const merged = [];
348
- let pending_text = null;
349
- for (const node of nodes) {
350
- if (node.type === 'Text') {
351
- if (pending_text) {
352
- pending_text = {
353
- type: 'Text',
354
- content: pending_text.content + node.content,
355
- start: pending_text.start,
356
- end: node.end,
357
- };
358
- }
359
- else {
360
- pending_text = { ...node };
361
- }
362
- }
363
- else {
364
- if (pending_text) {
365
- merged.push(pending_text);
366
- pending_text = null;
367
- }
368
- merged.push(node);
369
- }
370
- }
371
- if (pending_text)
372
- merged.push(pending_text);
373
- return merged;
374
- }
375
344
  }
@@ -1,5 +1,5 @@
1
1
  import { Declaration } from './declaration.svelte.js';
2
- import { url_github_file } from './package_helpers.js';
2
+ import { url_github_file } from '@fuzdev/fuz_util/package_helpers.js';
3
3
  /**
4
4
  * Rich runtime representation of a module with computed properties.
5
5
  *
@@ -1,4 +1,5 @@
1
1
  import type { Url } from '@fuzdev/fuz_util/url.js';
2
+ import type { PkgJson } from '@fuzdev/fuz_util/pkg_json.js';
2
3
  import type { SvgData } from './svg.js';
3
4
  export interface SiteStateOptions {
4
5
  /**
@@ -7,18 +8,30 @@ export interface SiteStateOptions {
7
8
  icon?: SvgData | null;
8
9
  /**
9
10
  * The site's glyph, a single unicode character identifying the project.
11
+ * Falls back to `pkg_json.glyph`.
10
12
  */
11
13
  glyph?: string | null;
12
14
  /**
13
15
  * The site's source repository url, e.g. for `DocsFooter` links.
16
+ * Falls back to the url parsed from `pkg_json.repository`.
14
17
  */
15
18
  repo_url?: Url | null;
19
+ /**
20
+ * Curated `package.json`, typically the default export of `virtual:pkg.json`.
21
+ * `glyph` and `repo_url` derive from it when not given explicitly; explicit
22
+ * props always win.
23
+ */
24
+ pkg_json?: PkgJson | null;
16
25
  }
17
26
  /**
18
27
  * Light site-identity values for app chrome - the icon, glyph, and repo url used
19
28
  * by components like `Breadcrumb` and `DocsFooter`. Set once per app at the root
20
29
  * layout. Distinct from `library_context`, which carries the heavy
21
30
  * module/declaration metadata that only docs and API pages need.
31
+ *
32
+ * Pass `pkg_json` (from `virtual:pkg.json`) to derive `glyph`/`repo_url` from the
33
+ * package's own metadata instead of hardcoding them; `icon` stays an explicit
34
+ * logo import since it's structured `SvgData`, not package metadata.
22
35
  */
23
36
  export declare class SiteState {
24
37
  icon: SvgData | null;
@@ -1 +1 @@
1
- {"version":3,"file":"site.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/site.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,yBAAyB,CAAC;AAGjD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,UAAU,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,SAAS;IACrB,IAAI,EAAE,OAAO,GAAG,IAAI,CAAoB;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAoB;IACxC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAoB;gBAE5B,OAAO,GAAE,gBAAqB;CAK1C;AAED,eAAO,MAAM,YAAY;;;;CAA8B,CAAC"}
1
+ {"version":3,"file":"site.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/site.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,yBAAyB,CAAC;AACjD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,8BAA8B,CAAC;AAI1D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,UAAU,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,qBAAa,SAAS;IACrB,IAAI,EAAE,OAAO,GAAG,IAAI,CAAoB;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAoB;IACxC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAoB;gBAE5B,OAAO,GAAE,gBAAqB;CAM1C;AAED,eAAO,MAAM,YAAY;;;;CAA8B,CAAC"}
@@ -1,9 +1,14 @@
1
1
  import { create_context } from './context_helpers.js';
2
+ import { repo_url_parse } from '@fuzdev/fuz_util/package_helpers.js';
2
3
  /**
3
4
  * Light site-identity values for app chrome - the icon, glyph, and repo url used
4
5
  * by components like `Breadcrumb` and `DocsFooter`. Set once per app at the root
5
6
  * layout. Distinct from `library_context`, which carries the heavy
6
7
  * module/declaration metadata that only docs and API pages need.
8
+ *
9
+ * Pass `pkg_json` (from `virtual:pkg.json`) to derive `glyph`/`repo_url` from the
10
+ * package's own metadata instead of hardcoding them; `icon` stays an explicit
11
+ * logo import since it's structured `SvgData`, not package metadata.
7
12
  */
8
13
  export class SiteState {
9
14
  icon = $state.raw(null);
@@ -11,8 +16,9 @@ export class SiteState {
11
16
  repo_url = $state.raw(null);
12
17
  constructor(options = {}) {
13
18
  this.icon = options.icon ?? null;
14
- this.glyph = options.glyph ?? null;
15
- this.repo_url = options.repo_url ?? null;
19
+ this.glyph = options.glyph ?? options.pkg_json?.glyph ?? null;
20
+ this.repo_url =
21
+ options.repo_url ?? (options.pkg_json ? repo_url_parse(options.pkg_json.repository) : null);
16
22
  }
17
23
  }
18
24
  export const site_context = create_context();
@@ -31,8 +31,8 @@
31
31
  * tsdoc_see_to_mdz('{@link SomeType}')
32
32
  * // → '`SomeType`'
33
33
  *
34
- * tsdoc_see_to_mdz('https://example.com')
35
- * // → 'https://example.com'
34
+ * tsdoc_see_to_mdz('https://fuz.dev')
35
+ * // → 'https://fuz.dev'
36
36
  *
37
37
  * tsdoc_see_to_mdz('[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library')
38
38
  * // → '[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library'
package/dist/tsdoc_mdz.js CHANGED
@@ -34,8 +34,8 @@ const format_reference = (ref) => (mdz_is_url(ref) ? ref : `\`${ref}\``);
34
34
  * tsdoc_see_to_mdz('{@link SomeType}')
35
35
  * // → '`SomeType`'
36
36
  *
37
- * tsdoc_see_to_mdz('https://example.com')
38
- * // → 'https://example.com'
37
+ * tsdoc_see_to_mdz('https://fuz.dev')
38
+ * // → 'https://fuz.dev'
39
39
  *
40
40
  * tsdoc_see_to_mdz('[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library')
41
41
  * // → '[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library'
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Vite plugin serving a curated, publish-safe `package.json` as the virtual
3
+ * module `virtual:pkg.json`.
4
+ *
5
+ * Consumers' `src/routes/library.ts` (and root layouts) need package identity —
6
+ * name, version, the Fuz extension fields — in the client. Importing the root
7
+ * `package.json` directly inlines the *whole* file into the client bundle
8
+ * (`scripts`, `dependencies`, `devDependencies`, private config) and trips
9
+ * SvelteKit's `server.fs.allow` on cold HMR reloads. This plugin reads the
10
+ * project's `package.json` at build time, strips it to `pkg_json_keys`, and
11
+ * serves only that subset. Consumers combine it with `virtual:svelte-docinfo`'s
12
+ * analyzed `modules` via `library_json_from_modules` to build a `LibraryJson`
13
+ * (see `src/routes/library.ts` for the canonical pattern).
14
+ *
15
+ * The `.json` suffix on the virtual id is load-bearing, mirroring how
16
+ * `vite_plugin_fuz_css`'s `virtual:fuz.css` relies on `.css`: `load()` returns
17
+ * raw JSON text and Vite's built-in `vite:json` plugin transforms it into an ES
18
+ * module (default export plus named exports), so consumers write
19
+ * `import package_json from 'virtual:pkg.json'`. Do *not* return a JS module
20
+ * here — the `.json` id would double-transform it.
21
+ *
22
+ * ```ts
23
+ * // vite.config.ts
24
+ * import {vite_plugin_pkg_json} from './vite_plugin_pkg_json.js';
25
+ * export default defineConfig({plugins: [vite_plugin_pkg_json(), sveltekit()]});
26
+ * ```
27
+ *
28
+ * The kept field set defaults to `pkg_json_keys`. To expose extra publish-safe
29
+ * fields, pass a wider `keys` list — typically composed from the default with a
30
+ * spread (`` keys: [...pkg_json_keys, 'keywords'] ``). Because `library_json_from_modules`
31
+ * re-strips at runtime, the *same* list must reach that call (and the consumer's
32
+ * `virtual:pkg.json` ambient type) for the extras to survive end to end — share
33
+ * one const across all three sites:
34
+ *
35
+ * ```ts
36
+ * // src/routes/pkg_json_keys.ts
37
+ * import {pkg_json_keys} from '@fuzdev/fuz_util/pkg_json.js';
38
+ * export const pkg_json_keys_custom = [...pkg_json_keys, 'keywords'] as const;
39
+ *
40
+ * // vite.config.ts → vite_plugin_pkg_json({keys: pkg_json_keys_custom})
41
+ * // src/routes/library.ts → library_json_from_modules(pkg_json, modules, pkg_json_keys_custom)
42
+ * ```
43
+ *
44
+ * @module
45
+ */
46
+ import type { Plugin } from 'vite';
47
+ import type { PackageJson } from '@fuzdev/fuz_util/package_json.js';
48
+ /** Options for `vite_plugin_pkg_json`. */
49
+ export interface VitePluginPkgJsonOptions {
50
+ /**
51
+ * The `package.json` fields to keep in the served subset. Defaults to the
52
+ * curated, publish-safe `pkg_json_keys`. Pass a wider list — usually
53
+ * `` [...pkg_json_keys, 'extra'] `` — to expose more fields; the same list
54
+ * must reach `library_json_from_modules` (see the module comment).
55
+ */
56
+ keys?: ReadonlyArray<keyof PackageJson>;
57
+ }
58
+ /**
59
+ * Creates the `virtual:pkg.json` plugin. Zero-config for canonical fuz usage —
60
+ * the publish-safe field set defaults to `pkg_json_keys`; widen it via `keys`.
61
+ */
62
+ export declare const vite_plugin_pkg_json: (options?: VitePluginPkgJsonOptions) => Plugin;
63
+ //# sourceMappingURL=vite_plugin_pkg_json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite_plugin_pkg_json.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/vite_plugin_pkg_json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAIH,OAAO,KAAK,EAAC,MAAM,EAAS,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kCAAkC,CAAC;AAkBlE,0CAA0C;AAC1C,MAAM,WAAW,wBAAwB;IACxC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,WAAW,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,UAAS,wBAA6B,KAAG,MA2D7E,CAAC"}