@fuzdev/fuz_ui 0.204.0 → 0.205.1

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 (123) hide show
  1. package/dist/ApiModule.svelte +14 -1
  2. package/dist/ApiModule.svelte.d.ts.map +1 -1
  3. package/dist/Breadcrumb.svelte +2 -2
  4. package/dist/Card.svelte +1 -7
  5. package/dist/ColorSchemeInput.svelte +1 -2
  6. package/dist/ContextmenuEntry.svelte +1 -3
  7. package/dist/ContextmenuLinkEntry.svelte +1 -4
  8. package/dist/ContextmenuSeparator.svelte +1 -1
  9. package/dist/ContextmenuSubmenu.svelte +1 -2
  10. package/dist/CopyToClipboard.svelte +5 -5
  11. package/dist/CopyToClipboard.svelte.d.ts.map +1 -1
  12. package/dist/DeclarationDetail.svelte +15 -4
  13. package/dist/DeclarationDetail.svelte.d.ts.map +1 -1
  14. package/dist/Dialog.svelte +1 -2
  15. package/dist/DialogContent.svelte +1 -1
  16. package/dist/DocsList.svelte +1 -1
  17. package/dist/DocsMenu.svelte +1 -1
  18. package/dist/DocsPageLinks.svelte +9 -8
  19. package/dist/DocsPageLinks.svelte.d.ts.map +1 -1
  20. package/dist/DocsPrimaryNav.svelte +1 -1
  21. package/dist/DocsSecondaryNav.svelte +1 -1
  22. package/dist/EcosystemLinks.svelte +1 -1
  23. package/dist/LibraryDetail.svelte +10 -7
  24. package/dist/LibraryDetail.svelte.d.ts.map +1 -1
  25. package/dist/LibrarySummary.svelte +1 -2
  26. package/dist/PendingAnimation.svelte +6 -4
  27. package/dist/PendingAnimation.svelte.d.ts.map +1 -1
  28. package/dist/PendingButton.svelte +1 -2
  29. package/dist/StyleVariableButton.svelte +1 -2
  30. package/dist/Svg.svelte +1 -1
  31. package/dist/ThemeInput.svelte +1 -2
  32. package/dist/TomeSectionHeader.svelte +1 -1
  33. package/package.json +14 -38
  34. package/dist/Mdz.svelte +0 -34
  35. package/dist/Mdz.svelte.d.ts +0 -11
  36. package/dist/Mdz.svelte.d.ts.map +0 -1
  37. package/dist/MdzNodeView.svelte +0 -107
  38. package/dist/MdzNodeView.svelte.d.ts +0 -9
  39. package/dist/MdzNodeView.svelte.d.ts.map +0 -1
  40. package/dist/MdzPrecompiled.svelte +0 -30
  41. package/dist/MdzPrecompiled.svelte.d.ts +0 -11
  42. package/dist/MdzPrecompiled.svelte.d.ts.map +0 -1
  43. package/dist/MdzRoot.svelte +0 -30
  44. package/dist/MdzRoot.svelte.d.ts +0 -12
  45. package/dist/MdzRoot.svelte.d.ts.map +0 -1
  46. package/dist/MdzStream.svelte +0 -32
  47. package/dist/MdzStream.svelte.d.ts +0 -12
  48. package/dist/MdzStream.svelte.d.ts.map +0 -1
  49. package/dist/MdzStreamNodeView.svelte +0 -106
  50. package/dist/MdzStreamNodeView.svelte.d.ts +0 -9
  51. package/dist/MdzStreamNodeView.svelte.d.ts.map +0 -1
  52. package/dist/mdz.d.ts +0 -112
  53. package/dist/mdz.d.ts.map +0 -1
  54. package/dist/mdz.js +0 -1186
  55. package/dist/mdz_components.d.ts +0 -63
  56. package/dist/mdz_components.d.ts.map +0 -1
  57. package/dist/mdz_components.js +0 -32
  58. package/dist/mdz_helpers.d.ts +0 -164
  59. package/dist/mdz_helpers.d.ts.map +0 -1
  60. package/dist/mdz_helpers.js +0 -424
  61. package/dist/mdz_lexer.d.ts +0 -93
  62. package/dist/mdz_lexer.d.ts.map +0 -1
  63. package/dist/mdz_lexer.js +0 -732
  64. package/dist/mdz_opcodes.d.ts +0 -174
  65. package/dist/mdz_opcodes.d.ts.map +0 -1
  66. package/dist/mdz_opcodes.js +0 -14
  67. package/dist/mdz_opcodes_to_nodes.d.ts +0 -20
  68. package/dist/mdz_opcodes_to_nodes.d.ts.map +0 -1
  69. package/dist/mdz_opcodes_to_nodes.js +0 -332
  70. package/dist/mdz_stream_parser.d.ts +0 -80
  71. package/dist/mdz_stream_parser.d.ts.map +0 -1
  72. package/dist/mdz_stream_parser.js +0 -354
  73. package/dist/mdz_stream_parser_block.d.ts +0 -76
  74. package/dist/mdz_stream_parser_block.d.ts.map +0 -1
  75. package/dist/mdz_stream_parser_block.js +0 -458
  76. package/dist/mdz_stream_parser_inline.d.ts +0 -52
  77. package/dist/mdz_stream_parser_inline.d.ts.map +0 -1
  78. package/dist/mdz_stream_parser_inline.js +0 -378
  79. package/dist/mdz_stream_parser_link.d.ts +0 -22
  80. package/dist/mdz_stream_parser_link.d.ts.map +0 -1
  81. package/dist/mdz_stream_parser_link.js +0 -215
  82. package/dist/mdz_stream_parser_state.d.ts +0 -187
  83. package/dist/mdz_stream_parser_state.d.ts.map +0 -1
  84. package/dist/mdz_stream_parser_state.js +0 -398
  85. package/dist/mdz_stream_parser_text.d.ts +0 -14
  86. package/dist/mdz_stream_parser_text.d.ts.map +0 -1
  87. package/dist/mdz_stream_parser_text.js +0 -50
  88. package/dist/mdz_stream_parser_url.d.ts +0 -60
  89. package/dist/mdz_stream_parser_url.d.ts.map +0 -1
  90. package/dist/mdz_stream_parser_url.js +0 -307
  91. package/dist/mdz_stream_state.svelte.d.ts +0 -44
  92. package/dist/mdz_stream_state.svelte.d.ts.map +0 -1
  93. package/dist/mdz_stream_state.svelte.js +0 -357
  94. package/dist/mdz_to_svelte.d.ts +0 -41
  95. package/dist/mdz_to_svelte.d.ts.map +0 -1
  96. package/dist/mdz_to_svelte.js +0 -100
  97. package/dist/mdz_token_parser.d.ts +0 -14
  98. package/dist/mdz_token_parser.d.ts.map +0 -1
  99. package/dist/mdz_token_parser.js +0 -344
  100. package/dist/svelte_preprocess_mdz.d.ts +0 -65
  101. package/dist/svelte_preprocess_mdz.d.ts.map +0 -1
  102. package/dist/svelte_preprocess_mdz.js +0 -529
  103. package/dist/tsdoc_mdz.d.ts +0 -45
  104. package/dist/tsdoc_mdz.d.ts.map +0 -1
  105. package/dist/tsdoc_mdz.js +0 -88
  106. package/src/lib/mdz.ts +0 -1532
  107. package/src/lib/mdz_components.ts +0 -64
  108. package/src/lib/mdz_helpers.ts +0 -449
  109. package/src/lib/mdz_lexer.ts +0 -1012
  110. package/src/lib/mdz_opcodes.ts +0 -205
  111. package/src/lib/mdz_opcodes_to_nodes.ts +0 -375
  112. package/src/lib/mdz_stream_parser.ts +0 -415
  113. package/src/lib/mdz_stream_parser_block.ts +0 -532
  114. package/src/lib/mdz_stream_parser_inline.ts +0 -414
  115. package/src/lib/mdz_stream_parser_link.ts +0 -271
  116. package/src/lib/mdz_stream_parser_state.ts +0 -539
  117. package/src/lib/mdz_stream_parser_text.ts +0 -77
  118. package/src/lib/mdz_stream_parser_url.ts +0 -365
  119. package/src/lib/mdz_stream_state.svelte.ts +0 -387
  120. package/src/lib/mdz_to_svelte.ts +0 -141
  121. package/src/lib/mdz_token_parser.ts +0 -434
  122. package/src/lib/svelte_preprocess_mdz.ts +0 -742
  123. package/src/lib/tsdoc_mdz.ts +0 -97
@@ -1,415 +0,0 @@
1
- /**
2
- * Streaming opcode parser for mdz.
3
- *
4
- * Fed chunks of text (e.g., from LLM output), emits opcodes as rendering
5
- * instructions. Makes optimistic assumptions about ambiguous syntax and
6
- * emits `revert` opcodes to correct when wrong. Never re-parses.
7
- *
8
- * The design was independently arrived at but shares goals with
9
- * {@link https://bsky.app/profile/pngwn.at/post/3mi527zntb22n @pngwn.at}'s
10
- * Penguin-Flavoured Markdown (PFM): restrict the syntax so streaming is tractable,
11
- * render optimistically and correct when wrong, emit serializable opcodes
12
- * to avoid re-parsing, and keep the opcodes target-agnostic so any renderer
13
- * can consume them. mdz diverges in one respect: the Svelte consumer
14
- * (`MdzStreamState`) does build a reactive tree from opcodes — the platform
15
- * dictates this — but mutations are fine-grained via `$state`, not diffed.
16
- *
17
- * The parser is split across sibling modules: this file holds the public
18
- * `MdzStreamParser` class and the `process_loop` / `process_inline`
19
- * orchestrators. Per-category handlers (block / inline / link / url / text)
20
- * live in `mdz_stream_parser_*.ts` as free functions taking the shared
21
- * `MdzStreamParserState` as first argument.
22
- *
23
- * Usage:
24
- * ```ts
25
- * const parser = new MdzStreamParser();
26
- * parser.feed('hello **bold');
27
- * const ops1 = parser.take_opcodes(); // open Paragraph, text "hello ", open Bold, text "bold"
28
- * parser.feed('** world');
29
- * const ops2 = parser.take_opcodes(); // close Bold, text " world"
30
- * parser.finish();
31
- * const ops3 = parser.take_opcodes(); // close Paragraph
32
- * ```
33
- *
34
- * @module
35
- */
36
-
37
- import type {MdzOpcode} from './mdz_opcodes.js';
38
- import {
39
- BACKTICK,
40
- ASTERISK,
41
- UNDERSCORE,
42
- TILDE,
43
- NEWLINE,
44
- HYPHEN,
45
- HASH,
46
- LEFT_ANGLE,
47
- SLASH,
48
- LEFT_BRACKET,
49
- RIGHT_BRACKET,
50
- H_LOWER,
51
- H_UPPER,
52
- PERIOD,
53
- is_word_char,
54
- } from './mdz_helpers.js';
55
- import {
56
- type MdzStreamParserState,
57
- accumulate_text,
58
- close_codeblock_at_eof,
59
- close_heading,
60
- close_paragraph,
61
- create_state,
62
- ensure_paragraph,
63
- find_open,
64
- flush_text,
65
- handle_paragraph_break,
66
- offset,
67
- } from './mdz_stream_parser_state.js';
68
- import {
69
- process_codeblock,
70
- process_codeblock_forced,
71
- try_codeblock_open,
72
- try_heading,
73
- try_hr,
74
- } from './mdz_stream_parser_block.js';
75
- import {
76
- check_close_word_boundary,
77
- close_bold,
78
- close_single_delimiter,
79
- process_code_content,
80
- try_bold,
81
- try_code,
82
- try_italic,
83
- try_strikethrough,
84
- } from './mdz_stream_parser_inline.js';
85
- import {
86
- try_close_tag,
87
- try_complete_link,
88
- try_link_open,
89
- try_tag_open,
90
- } from './mdz_stream_parser_link.js';
91
- import {
92
- complete_pending_url,
93
- process_url_content,
94
- start_speculative_url,
95
- try_auto_path_absolute,
96
- try_auto_path_relative,
97
- try_auto_url_forced,
98
- } from './mdz_stream_parser_url.js';
99
- import {consume_text_run} from './mdz_stream_parser_text.js';
100
-
101
- /**
102
- * Streaming opcode parser for mdz content.
103
- * Feed chunks via `feed()`, retrieve opcodes via `take_opcodes()`, call `finish()` at end.
104
- *
105
- * The opcode sequence is not deterministic across chunk boundaries — the same input
106
- * fed in different chunk sizes may produce different `text`/`append_text` splits and
107
- * different optimistic/revert sequences. The final tree (via `mdz_opcodes_to_nodes`)
108
- * matches the one-shot result for all input except one case: italic (`_..._`) where
109
- * the opening and closing delimiters straddle a chunk boundary. Italic is
110
- * non-optimistic — it requires a confirmed closer in the buffer — so when the
111
- * closer arrives only in a later chunk, the opening `_` has already been emitted
112
- * as text and italic cannot apply retroactively. See `try_italic` in
113
- * `mdz_stream_parser_inline.ts`.
114
- */
115
- export class MdzStreamParser {
116
- #state: MdzStreamParserState = create_state();
117
-
118
- /**
119
- * Feed a chunk of text to the parser.
120
- * Opcodes are accumulated and retrieved via `take_opcodes()`.
121
- */
122
- feed(chunk: string): void {
123
- const state = this.#state;
124
- state.buffer += chunk;
125
- process_loop(state, false);
126
- // flush any accumulated text so the renderer shows it immediately
127
- flush_text(state);
128
- // drain processed bytes from the buffer
129
- if (state.pos > 0) {
130
- state.base_offset += state.pos;
131
- state.buffer = state.buffer.slice(state.pos);
132
- state.pos = 0;
133
- }
134
- }
135
-
136
- /**
137
- * Signal end of input. Resolves all pending state: closes open blocks,
138
- * reverts unclosed optimistic opens, trims trailing newlines.
139
- *
140
- * Trailing-newline trimming is handled in one place: `trim_trailing_newline()`
141
- * called at the top of `close_paragraph()` and `close_codeblock_at_eof()`,
142
- * before either function reverts its inner stack. The trim sees the
143
- * just-flushed text node's `last_text_id` (or a still-accumulated `\n`) and
144
- * emits a `trim_text` opcode. Revert opcodes only fire after.
145
- *
146
- * Optimistic-container revert is handled by `close_paragraph` and
147
- * `close_heading` (each pops its own inner stack), so no separate
148
- * `revert_all_optimistic` is needed — optimistic containers can only exist
149
- * inside an open Paragraph or Heading (parser invariant).
150
- */
151
- finish(): void {
152
- const state = this.#state;
153
- // process any remaining buffer — `forced=true` treats ambiguous trailing
154
- // bytes as concrete content rather than waiting for more input
155
- if (state.buffer.length > 0) {
156
- process_loop(state, true);
157
- }
158
- // finalize pending URL if any (text-first auto-links)
159
- if (state.pending_url !== null) {
160
- flush_text(state);
161
- complete_pending_url(state);
162
- }
163
- flush_text(state);
164
- // close heading if open — internally reverts its inner optimistic stack
165
- if (state.in_heading) {
166
- close_heading(state);
167
- }
168
- // close paragraph if open — internally trims trailing \n, reverts its
169
- // inner optimistic stack, then pops
170
- close_paragraph(state);
171
- // close codeblock if open (unclosed at EOF) — internally trims trailing \n
172
- if (state.codeblock) {
173
- close_codeblock_at_eof(state);
174
- }
175
- }
176
-
177
- /**
178
- * Drain and return all accumulated opcodes.
179
- * Destructive — empties the internal queue. The returned array is owned by the caller.
180
- */
181
- take_opcodes(): Array<MdzOpcode> {
182
- const ops = this.#state.opcodes;
183
- this.#state.opcodes = [];
184
- return ops;
185
- }
186
- }
187
-
188
- // -- Processing loop --
189
-
190
- /**
191
- * Core processing loop. When `forced` is false (normal streaming), returns
192
- * when more input is needed. When `forced` is true (EOF), treats buffer end
193
- * as content end and never waits for more input.
194
- */
195
- const process_loop = (state: MdzStreamParserState, forced: boolean): void => {
196
- // absorb leading newlines at start of input or left over from a block element
197
- // close in a prior chunk. Only clear the flag once a non-newline char arrives —
198
- // otherwise an all-newline chunk would prematurely drop the flag and let the
199
- // next chunk's leading content stick to a `\n` text node.
200
- if (state.skip_leading_newlines) {
201
- while (state.pos < state.buffer.length && state.buffer.charCodeAt(state.pos) === NEWLINE) {
202
- state.pos++;
203
- }
204
- if (state.pos < state.buffer.length) {
205
- state.skip_leading_newlines = false;
206
- }
207
- }
208
- while (state.pos < state.buffer.length) {
209
- // codeblock mode
210
- if (state.codeblock) {
211
- if (forced) {
212
- process_codeblock_forced(state);
213
- return;
214
- }
215
- if (!process_codeblock(state)) return; // need more input
216
- continue;
217
- }
218
-
219
- // optimistic inline code mode — raw scanning for close/revert
220
- if (state.in_code) {
221
- process_code_content(state);
222
- continue;
223
- }
224
-
225
- // text-first URL/path scanning mode
226
- if (state.pending_url !== null) {
227
- process_url_content(state, forced);
228
- continue;
229
- }
230
-
231
- const char_code = state.buffer.charCodeAt(state.pos);
232
-
233
- // newline handling
234
- if (char_code === NEWLINE) {
235
- if (state.in_heading) {
236
- // newline ends heading
237
- flush_text(state);
238
- close_heading(state);
239
- state.pos++;
240
- // absorb consecutive newlines after heading
241
- while (state.pos < state.buffer.length && state.buffer.charCodeAt(state.pos) === NEWLINE) {
242
- state.pos++;
243
- }
244
- if (state.pos >= state.buffer.length) {
245
- state.skip_leading_newlines = true;
246
- }
247
- state.column = 0;
248
- state.prev_char = NEWLINE;
249
- continue;
250
- }
251
- // hold trailing \n when streaming (could be start of \n\n)
252
- if (!forced && state.pos + 1 >= state.buffer.length) return;
253
- // check for paragraph break (\n\n)
254
- if (
255
- state.pos + 1 < state.buffer.length &&
256
- state.buffer.charCodeAt(state.pos + 1) === NEWLINE
257
- ) {
258
- handle_paragraph_break(state);
259
- continue;
260
- }
261
- // single newline: accumulate as text
262
- accumulate_text(state, '\n', offset(state));
263
- state.pos++;
264
- state.column = 0;
265
- state.prev_char = NEWLINE;
266
- continue;
267
- }
268
-
269
- // block elements at column 0
270
- if (state.column === 0 && !state.in_heading) {
271
- if (char_code === HASH) {
272
- const r = try_heading(state);
273
- if (r === 'consumed') continue;
274
- if (!forced && r === 'need_more') return;
275
- } else if (char_code === HYPHEN) {
276
- const r = try_hr(state, forced);
277
- if (r === 'consumed') continue;
278
- if (!forced && r === 'need_more') return;
279
- } else if (char_code === BACKTICK) {
280
- const r = try_codeblock_open(state);
281
- if (r === 'consumed') continue;
282
- if (!forced && r === 'need_more') return;
283
- }
284
- }
285
-
286
- // inline processing
287
- if (!process_inline(state, forced)) return; // need more input
288
- }
289
- };
290
-
291
- // -- Inline processing --
292
-
293
- /**
294
- * Process one inline element. Returns false if more input is needed.
295
- * When `forced` is true (EOF processing), skips optimistic opening constructs
296
- * (no new bold/code/link/tag opens) but still handles italic (non-optimistic,
297
- * safe to try), all closing constructs, and auto-links.
298
- * Never returns false in forced mode.
299
- */
300
- const process_inline = (state: MdzStreamParserState, forced: boolean): boolean => {
301
- const char_code = state.buffer.charCodeAt(state.pos);
302
-
303
- // check for closing delimiters first (if matching open exists)
304
- if (
305
- char_code === ASTERISK &&
306
- state.pos + 1 < state.buffer.length &&
307
- state.buffer.charCodeAt(state.pos + 1) === ASTERISK
308
- ) {
309
- const bold_idx = find_open(state, 'Bold');
310
- if (bold_idx !== -1) {
311
- return close_bold(state, bold_idx);
312
- }
313
- if (!forced) return try_bold(state);
314
- }
315
-
316
- if (char_code === ASTERISK && !forced) {
317
- // could be start of ** — need next char to decide
318
- if (state.pos + 1 >= state.buffer.length) return false; // hold for next chunk
319
- // single asterisk is text (next char is not *)
320
- ensure_paragraph(state);
321
- accumulate_text(state, '*', offset(state));
322
- state.prev_char = ASTERISK;
323
- state.column++;
324
- state.pos++;
325
- return true;
326
- }
327
-
328
- if (char_code === UNDERSCORE) {
329
- const open_idx = find_open(state, 'Italic');
330
- if (open_idx !== -1 && check_close_word_boundary(state)) {
331
- return close_single_delimiter(state, open_idx);
332
- }
333
- // Italic is non-optimistic — only opens when closer is confirmed in
334
- // the buffer, so it's safe (and necessary) to try in forced mode too.
335
- return try_italic(state, forced);
336
- }
337
-
338
- if (char_code === TILDE) {
339
- const open_idx = find_open(state, 'Strikethrough');
340
- if (open_idx !== -1 && check_close_word_boundary(state)) {
341
- return close_single_delimiter(state, open_idx);
342
- }
343
- if (!forced) return try_strikethrough(state);
344
- }
345
-
346
- if (char_code === BACKTICK && !forced) {
347
- return try_code(state);
348
- }
349
-
350
- if (char_code === LEFT_BRACKET && !forced) {
351
- return try_link_open(state);
352
- }
353
-
354
- if (char_code === RIGHT_BRACKET && !forced) {
355
- const link_idx = find_open(state, 'Link');
356
- if (link_idx !== -1) {
357
- return try_complete_link(state, link_idx);
358
- }
359
- // no open link — treat as text
360
- ensure_paragraph(state);
361
- accumulate_text(state, ']', offset(state));
362
- state.prev_char = RIGHT_BRACKET;
363
- state.column++;
364
- state.pos++;
365
- return true;
366
- }
367
-
368
- if (char_code === LEFT_ANGLE) {
369
- // closing tags are handled even in forced mode
370
- if (state.pos + 1 < state.buffer.length && state.buffer.charCodeAt(state.pos + 1) === SLASH) {
371
- const result = try_close_tag(state);
372
- if (result === 'consumed') return true;
373
- // in forced mode, need_more falls through to text
374
- if (!forced && result === 'need_more') return false;
375
- // not_match — fall through
376
- }
377
- if (!forced) {
378
- const tag_result = try_tag_open(state);
379
- if (tag_result === 'consumed') return true;
380
- if (tag_result === 'need_more') return false;
381
- // not_match — fall through
382
- }
383
- // not a valid tag — fall through to text
384
- }
385
-
386
- // auto-detected URLs (text-first speculative prefix matching).
387
- // Fires on `h` and `H` — scheme matching is case-insensitive (RFC 3986).
388
- if (char_code === H_LOWER || char_code === H_UPPER) {
389
- if (forced) {
390
- const result = try_auto_url_forced(state);
391
- if (result === 'consumed') return true;
392
- if (result === 'need_more') return false;
393
- } else if (state.prev_char === -1 || !is_word_char(state.prev_char)) {
394
- // word boundary — start speculative URL prefix matching
395
- start_speculative_url(state);
396
- return true;
397
- }
398
- }
399
-
400
- // auto-detected paths
401
- if (char_code === SLASH) {
402
- const result = try_auto_path_absolute(state, forced);
403
- if (result === 'consumed') return true;
404
- if (result === 'need_more') return false;
405
- }
406
- if (char_code === PERIOD) {
407
- const result = try_auto_path_relative(state, forced);
408
- if (result === 'consumed') return true;
409
- if (result === 'need_more') return false;
410
- }
411
-
412
- // plain text
413
- consume_text_run(state);
414
- return true;
415
- };