@fuzdev/fuz_ui 0.198.1 → 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 (101) hide show
  1. package/dist/ApiIndex.svelte +2 -2
  2. package/dist/ApiModule.svelte +1 -1
  3. package/dist/LibraryDetail.svelte +14 -16
  4. package/dist/LibraryDetail.svelte.d.ts.map +1 -1
  5. package/dist/LibrarySummary.svelte +12 -12
  6. package/dist/Mdz.svelte +2 -2
  7. package/dist/MdzNodeView.svelte +8 -6
  8. package/dist/MdzNodeView.svelte.d.ts.map +1 -1
  9. package/dist/MdzRoot.svelte +30 -0
  10. package/dist/MdzRoot.svelte.d.ts +12 -0
  11. package/dist/MdzRoot.svelte.d.ts.map +1 -0
  12. package/dist/MdzStream.svelte +32 -0
  13. package/dist/MdzStream.svelte.d.ts +12 -0
  14. package/dist/MdzStream.svelte.d.ts.map +1 -0
  15. package/dist/MdzStreamNodeView.svelte +106 -0
  16. package/dist/MdzStreamNodeView.svelte.d.ts +9 -0
  17. package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -0
  18. package/dist/declaration.svelte.js +2 -2
  19. package/dist/library.svelte.d.ts +9 -70
  20. package/dist/library.svelte.d.ts.map +1 -1
  21. package/dist/library.svelte.js +24 -18
  22. package/dist/library_helpers.d.ts +3 -3
  23. package/dist/library_helpers.js +3 -3
  24. package/dist/mdz.d.ts.map +1 -1
  25. package/dist/mdz.js +37 -29
  26. package/dist/mdz_components.d.ts +37 -23
  27. package/dist/mdz_components.d.ts.map +1 -1
  28. package/dist/mdz_components.js +16 -4
  29. package/dist/mdz_helpers.d.ts +46 -14
  30. package/dist/mdz_helpers.d.ts.map +1 -1
  31. package/dist/mdz_helpers.js +189 -56
  32. package/dist/mdz_lexer.d.ts.map +1 -1
  33. package/dist/mdz_lexer.js +18 -22
  34. package/dist/mdz_opcodes.d.ts +174 -0
  35. package/dist/mdz_opcodes.d.ts.map +1 -0
  36. package/dist/mdz_opcodes.js +14 -0
  37. package/dist/mdz_opcodes_to_nodes.d.ts +20 -0
  38. package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -0
  39. package/dist/mdz_opcodes_to_nodes.js +332 -0
  40. package/dist/mdz_stream_parser.d.ts +80 -0
  41. package/dist/mdz_stream_parser.d.ts.map +1 -0
  42. package/dist/mdz_stream_parser.js +354 -0
  43. package/dist/mdz_stream_parser_block.d.ts +76 -0
  44. package/dist/mdz_stream_parser_block.d.ts.map +1 -0
  45. package/dist/mdz_stream_parser_block.js +458 -0
  46. package/dist/mdz_stream_parser_inline.d.ts +52 -0
  47. package/dist/mdz_stream_parser_inline.d.ts.map +1 -0
  48. package/dist/mdz_stream_parser_inline.js +378 -0
  49. package/dist/mdz_stream_parser_link.d.ts +22 -0
  50. package/dist/mdz_stream_parser_link.d.ts.map +1 -0
  51. package/dist/mdz_stream_parser_link.js +215 -0
  52. package/dist/mdz_stream_parser_state.d.ts +187 -0
  53. package/dist/mdz_stream_parser_state.d.ts.map +1 -0
  54. package/dist/mdz_stream_parser_state.js +398 -0
  55. package/dist/mdz_stream_parser_text.d.ts +14 -0
  56. package/dist/mdz_stream_parser_text.d.ts.map +1 -0
  57. package/dist/mdz_stream_parser_text.js +50 -0
  58. package/dist/mdz_stream_parser_url.d.ts +60 -0
  59. package/dist/mdz_stream_parser_url.d.ts.map +1 -0
  60. package/dist/mdz_stream_parser_url.js +307 -0
  61. package/dist/mdz_stream_state.svelte.d.ts +44 -0
  62. package/dist/mdz_stream_state.svelte.d.ts.map +1 -0
  63. package/dist/mdz_stream_state.svelte.js +357 -0
  64. package/dist/mdz_token_parser.d.ts.map +1 -1
  65. package/dist/mdz_token_parser.js +8 -39
  66. package/dist/module.svelte.js +1 -1
  67. package/dist/site.svelte.d.ts +13 -0
  68. package/dist/site.svelte.d.ts.map +1 -1
  69. package/dist/site.svelte.js +8 -2
  70. package/dist/tsdoc_mdz.d.ts +2 -2
  71. package/dist/tsdoc_mdz.js +2 -2
  72. package/dist/vite_plugin_pkg_json.d.ts +63 -0
  73. package/dist/vite_plugin_pkg_json.d.ts.map +1 -0
  74. package/dist/vite_plugin_pkg_json.js +126 -0
  75. package/package.json +6 -5
  76. package/src/lib/declaration.svelte.ts +2 -2
  77. package/src/lib/library.svelte.ts +37 -20
  78. package/src/lib/library_helpers.ts +3 -3
  79. package/src/lib/mdz.ts +38 -29
  80. package/src/lib/mdz_components.ts +40 -19
  81. package/src/lib/mdz_helpers.ts +199 -56
  82. package/src/lib/mdz_lexer.ts +18 -20
  83. package/src/lib/mdz_opcodes.ts +205 -0
  84. package/src/lib/mdz_opcodes_to_nodes.ts +375 -0
  85. package/src/lib/mdz_stream_parser.ts +415 -0
  86. package/src/lib/mdz_stream_parser_block.ts +532 -0
  87. package/src/lib/mdz_stream_parser_inline.ts +414 -0
  88. package/src/lib/mdz_stream_parser_link.ts +271 -0
  89. package/src/lib/mdz_stream_parser_state.ts +539 -0
  90. package/src/lib/mdz_stream_parser_text.ts +77 -0
  91. package/src/lib/mdz_stream_parser_url.ts +365 -0
  92. package/src/lib/mdz_stream_state.svelte.ts +387 -0
  93. package/src/lib/mdz_token_parser.ts +13 -40
  94. package/src/lib/module.svelte.ts +1 -1
  95. package/src/lib/site.svelte.ts +17 -2
  96. package/src/lib/tsdoc_mdz.ts +2 -2
  97. package/src/lib/vite_plugin_pkg_json.ts +142 -0
  98. package/dist/package_helpers.d.ts +0 -150
  99. package/dist/package_helpers.d.ts.map +0 -1
  100. package/dist/package_helpers.js +0 -179
  101. package/src/lib/package_helpers.ts +0 -186
@@ -0,0 +1,458 @@
1
+ /**
2
+ * Block-element handlers for the streaming mdz parser.
3
+ *
4
+ * Each `try_*` returns {@link TryResult} so the orchestrator in
5
+ * {@link ./mdz_stream_parser.ts} can fall through or wait for more input.
6
+ *
7
+ * @module
8
+ */
9
+ import { BACKTICK, HASH, HR_HYPHEN_COUNT, HYPHEN, MAX_HEADING_LEVEL, MIN_CODEBLOCK_BACKTICKS, NEWLINE, SPACE, } from './mdz_helpers.js';
10
+ import { accumulate_text, alloc_id, close_paragraph, emit, flush_text, offset, push_stack_entry, } from './mdz_stream_parser_state.js';
11
+ /**
12
+ * Try to parse a heading at column 0.
13
+ * Returns true if consumed, false if needs more input, null if definitely not a heading.
14
+ */
15
+ export const try_heading = (state) => {
16
+ const start = state.pos;
17
+ let i = start;
18
+ // count hashes
19
+ let hash_count = 0;
20
+ while (i < state.buffer.length &&
21
+ state.buffer.charCodeAt(i) === HASH &&
22
+ hash_count <= MAX_HEADING_LEVEL) {
23
+ hash_count++;
24
+ i++;
25
+ }
26
+ if (hash_count === 0 || hash_count > MAX_HEADING_LEVEL)
27
+ return 'not_match';
28
+ // need at least one more char to check for space
29
+ if (i >= state.buffer.length)
30
+ return 'need_more';
31
+ // must have space after hashes
32
+ if (state.buffer.charCodeAt(i) !== SPACE)
33
+ return 'not_match';
34
+ i++; // consume space
35
+ // must have at least one non-whitespace char (but we might not have it yet)
36
+ // for streaming, we'll open the heading optimistically when we see `# `
37
+ // if the content turns out empty, that's an edge case we can handle on close
38
+ // close any open paragraph first
39
+ flush_text(state);
40
+ close_paragraph(state);
41
+ const id = alloc_id(state);
42
+ const heading_start = offset(state, start);
43
+ emit(state, {
44
+ type: 'open',
45
+ id,
46
+ node_type: 'Heading',
47
+ start: heading_start,
48
+ level: hash_count,
49
+ });
50
+ push_stack_entry(state, id, 'Heading', heading_start);
51
+ state.in_heading = true;
52
+ state.heading_text_parts = [''];
53
+ state.pos = i;
54
+ state.column = i - start;
55
+ state.prev_char = SPACE;
56
+ return 'consumed';
57
+ };
58
+ /**
59
+ * Try to parse a horizontal rule at column 0.
60
+ * Returns true if consumed, false if needs more input, null if definitely not an HR.
61
+ * When `forced` is true, treats buffer end as EOF (no more input coming).
62
+ */
63
+ export const try_hr = (state, forced = false) => {
64
+ const start = state.pos;
65
+ let i = start;
66
+ // must have exactly 3 hyphens
67
+ if (i + HR_HYPHEN_COUNT > state.buffer.length)
68
+ return 'need_more';
69
+ for (let j = 0; j < HR_HYPHEN_COUNT; j++) {
70
+ if (state.buffer.charCodeAt(i + j) !== HYPHEN)
71
+ return 'not_match';
72
+ }
73
+ i += HR_HYPHEN_COUNT;
74
+ // after hyphens, only spaces allowed before newline/EOF
75
+ while (i < state.buffer.length) {
76
+ const c = state.buffer.charCodeAt(i);
77
+ if (c === NEWLINE)
78
+ break;
79
+ if (c !== SPACE)
80
+ return 'not_match';
81
+ i++;
82
+ }
83
+ // if buffer ends without newline/EOF confirmation, need more input (unless forced)
84
+ if (i >= state.buffer.length && !forced) {
85
+ return 'need_more';
86
+ }
87
+ flush_text(state);
88
+ close_paragraph(state);
89
+ const id = alloc_id(state);
90
+ emit(state, {
91
+ type: 'void',
92
+ id,
93
+ node_type: 'Hr',
94
+ start: offset(state, start),
95
+ end: offset(state, i),
96
+ });
97
+ if (forced) {
98
+ state.pos = state.buffer.length;
99
+ }
100
+ else {
101
+ state.pos = i;
102
+ if (i < state.buffer.length && state.buffer.charCodeAt(i) === NEWLINE) {
103
+ state.pos++; // consume the newline
104
+ }
105
+ // skip additional newlines
106
+ while (state.pos < state.buffer.length && state.buffer.charCodeAt(state.pos) === NEWLINE) {
107
+ state.pos++;
108
+ }
109
+ if (state.pos >= state.buffer.length) {
110
+ state.skip_leading_newlines = true;
111
+ }
112
+ }
113
+ state.column = 0;
114
+ state.prev_char = NEWLINE;
115
+ return 'consumed';
116
+ };
117
+ /**
118
+ * Try to open a code block at column 0.
119
+ *
120
+ * Opens the codeblock optimistically when possible. When the current paragraph
121
+ * has no prior content, opens immediately on `\`\`\`lang\n` without lookahead,
122
+ * enabling streaming. When the paragraph has content (text before the fence on
123
+ * a prior line), falls back to lookahead via `find_closing_fence` to avoid
124
+ * splitting the paragraph on revert.
125
+ */
126
+ export const try_codeblock_open = (state) => {
127
+ const start = state.pos;
128
+ let i = start;
129
+ // count backticks
130
+ let backtick_count = 0;
131
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === BACKTICK) {
132
+ backtick_count++;
133
+ i++;
134
+ }
135
+ if (backtick_count < MIN_CODEBLOCK_BACKTICKS) {
136
+ // if buffer ended mid-backtick-sequence, need more input
137
+ if (i >= state.buffer.length)
138
+ return 'need_more';
139
+ // definitely not enough backticks
140
+ return 'not_match';
141
+ }
142
+ // find end of opening fence line (need newline)
143
+ const lang_start = i;
144
+ while (i < state.buffer.length) {
145
+ const c = state.buffer.charCodeAt(i);
146
+ if (c === NEWLINE)
147
+ break;
148
+ if (c === SPACE)
149
+ break;
150
+ i++;
151
+ }
152
+ const lang = i > lang_start ? state.buffer.slice(lang_start, i) : null;
153
+ // skip trailing spaces on opening fence line
154
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
155
+ i++;
156
+ }
157
+ // must find newline
158
+ if (i >= state.buffer.length)
159
+ return 'need_more';
160
+ if (state.buffer.charCodeAt(i) !== NEWLINE)
161
+ return 'not_match';
162
+ i++; // consume newline
163
+ // when the current paragraph has content, closing it would split text on
164
+ // revert — fall back to lookahead to avoid that
165
+ if (state.in_paragraph && (state.accumulated_text.length > 0 || state.active_text_id !== null)) {
166
+ const closing_result = find_closing_fence(state, i, backtick_count);
167
+ if (closing_result === 'not_found')
168
+ return 'need_more';
169
+ if (closing_result === 'invalid')
170
+ return 'not_match';
171
+ }
172
+ // build delimiter for potential revert (e.g. "```ts\n" or "```\n")
173
+ const delimiter = state.buffer.slice(start, i);
174
+ flush_text(state);
175
+ close_paragraph(state);
176
+ const id = alloc_id(state);
177
+ const cb_start = offset(state, start);
178
+ emit(state, { type: 'open', id, node_type: 'Codeblock', start: cb_start, lang });
179
+ state.codeblock = { id, backtick_count, text_id: null, delimiter, start: cb_start };
180
+ state.pos = i;
181
+ state.column = 0;
182
+ state.prev_char = NEWLINE;
183
+ return 'consumed';
184
+ };
185
+ /**
186
+ * Scan buffer from `start` for a valid closing fence with `backtick_count` backticks.
187
+ * Used as a fallback when the current paragraph has prior content (to avoid
188
+ * splitting the paragraph on revert). Returns 'found' if a valid closing fence
189
+ * exists, 'not_found' if the buffer ends before we can determine, 'invalid' if
190
+ * we can see the full remaining content and there's no valid closing fence.
191
+ */
192
+ export const find_closing_fence = (state, start, backtick_count) => {
193
+ let i = start;
194
+ const content_start = start;
195
+ let at_col0 = true;
196
+ while (i < state.buffer.length) {
197
+ if (at_col0 && state.buffer.charCodeAt(i) === BACKTICK) {
198
+ // count backticks
199
+ let count = 0;
200
+ const fence_start = i;
201
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === BACKTICK) {
202
+ count++;
203
+ i++;
204
+ }
205
+ if (count === backtick_count) {
206
+ // skip trailing spaces
207
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
208
+ i++;
209
+ }
210
+ // must be followed by newline or EOF
211
+ if (i >= state.buffer.length || state.buffer.charCodeAt(i) === NEWLINE) {
212
+ // check for empty content
213
+ const content = state.buffer.slice(content_start, fence_start);
214
+ const final_content = content.endsWith('\n') ? content.slice(0, -1) : content;
215
+ if (final_content.length === 0)
216
+ return 'invalid'; // empty codeblock
217
+ return 'found';
218
+ }
219
+ }
220
+ // not a valid fence — continue scanning
221
+ continue;
222
+ }
223
+ if (state.buffer.charCodeAt(i) === NEWLINE) {
224
+ at_col0 = true;
225
+ }
226
+ else {
227
+ at_col0 = false;
228
+ }
229
+ i++;
230
+ }
231
+ // reached end of buffer without finding closing fence
232
+ return 'not_found';
233
+ };
234
+ /**
235
+ * Emit codeblock content as `text` (first chunk) or `append_text` (subsequent
236
+ * chunks), updating `cb.text_id` on first emit. `content_start` is the local
237
+ * buffer position where the content slice began — only used to compute the
238
+ * global byte offset for the first `text` opcode.
239
+ */
240
+ export const emit_codeblock_text = (state, cb, content, content_start) => {
241
+ if (cb.text_id !== null) {
242
+ emit(state, { type: 'append_text', id: cb.text_id, content });
243
+ }
244
+ else {
245
+ const id = alloc_id(state);
246
+ const text_start = offset(state, content_start);
247
+ emit(state, {
248
+ type: 'text',
249
+ id,
250
+ content,
251
+ text_type: 'Text',
252
+ start: text_start,
253
+ end: text_start + content.length,
254
+ });
255
+ cb.text_id = id;
256
+ }
257
+ };
258
+ /**
259
+ * Drain the remaining buffer as codeblock content at EOF, looking for a closing
260
+ * fence whose terminator is EOF rather than `\n`. Matches `mdz_parse`'s
261
+ * `#match_code_block`, which allows a fence at the end of the document.
262
+ *
263
+ * If a fence is found, the codeblock closes normally. Otherwise the remaining
264
+ * buffer becomes content and `close_codeblock_at_eof` (in state.ts) handles
265
+ * the revert-and-wrap dance after process completes.
266
+ */
267
+ export const process_codeblock_forced = (state) => {
268
+ const cb = state.codeblock;
269
+ const start = state.pos;
270
+ while (state.pos < state.buffer.length) {
271
+ if (state.column === 0) {
272
+ const fence_match = match_codeblock_close(state, cb.backtick_count, true);
273
+ if (fence_match !== -1) {
274
+ // emit content up to the closing fence (excluding trailing newline before fence)
275
+ let content_end = state.pos;
276
+ if (content_end > start && state.buffer.charCodeAt(content_end - 1) === NEWLINE) {
277
+ content_end--;
278
+ }
279
+ const content = state.buffer.slice(start, content_end);
280
+ if (content.length > 0) {
281
+ emit_codeblock_text(state, cb, content, start);
282
+ }
283
+ // empty codeblock — revert (matches process_codeblock behavior)
284
+ if (cb.text_id === null && content.length === 0) {
285
+ revert_empty_codeblock(state, cb);
286
+ return;
287
+ }
288
+ emit(state, { type: 'close', id: cb.id, end: offset(state, fence_match) });
289
+ state.pos = fence_match;
290
+ state.codeblock = null;
291
+ return;
292
+ }
293
+ }
294
+ const c = state.buffer.charCodeAt(state.pos);
295
+ if (c === NEWLINE) {
296
+ state.column = 0;
297
+ }
298
+ else {
299
+ state.column++;
300
+ }
301
+ state.pos++;
302
+ }
303
+ // no closing fence — dump the rest as codeblock content; caller's
304
+ // `close_codeblock_at_eof` then reverts the (now-unclosed) codeblock.
305
+ const remaining = state.buffer.slice(start, state.pos);
306
+ if (remaining.length > 0) {
307
+ emit_codeblock_text(state, cb, remaining, start);
308
+ }
309
+ };
310
+ /**
311
+ * Process bytes while in codeblock mode.
312
+ * Returns true if processing should continue, false if more input needed.
313
+ */
314
+ export const process_codeblock = (state) => {
315
+ const cb = state.codeblock;
316
+ const start = state.pos;
317
+ while (state.pos < state.buffer.length) {
318
+ // check for closing fence at column 0
319
+ if (state.column === 0) {
320
+ const fence_match = match_codeblock_close(state, cb.backtick_count);
321
+ if (fence_match === -2) {
322
+ // need more input — hold from this position (don't consume potential fence chars)
323
+ break;
324
+ }
325
+ if (fence_match !== -1) {
326
+ // emit content up to the closing fence (excluding trailing newline before fence)
327
+ let content_end = state.pos;
328
+ if (content_end > start && state.buffer.charCodeAt(content_end - 1) === NEWLINE) {
329
+ content_end--;
330
+ }
331
+ const content = state.buffer.slice(start, content_end);
332
+ if (content.length > 0) {
333
+ emit_codeblock_text(state, cb, content, start);
334
+ }
335
+ // empty codeblock: no content was ever emitted → invalid, revert
336
+ if (cb.text_id === null && content.length === 0) {
337
+ return revert_empty_codeblock(state, cb);
338
+ }
339
+ // codeblock end = position of newline after closing fence (exclusive)
340
+ emit(state, { type: 'close', id: cb.id, end: offset(state, fence_match - 1) });
341
+ state.pos = fence_match;
342
+ state.codeblock = null;
343
+ state.column = 0;
344
+ state.prev_char = NEWLINE;
345
+ // skip newlines after codeblock
346
+ while (state.pos < state.buffer.length && state.buffer.charCodeAt(state.pos) === NEWLINE) {
347
+ state.pos++;
348
+ }
349
+ // if buffer ended before absorbing all trailing newlines, continue in next chunk
350
+ if (state.pos >= state.buffer.length) {
351
+ state.skip_leading_newlines = true;
352
+ }
353
+ return true;
354
+ }
355
+ }
356
+ // not a closing fence — advance
357
+ const c = state.buffer.charCodeAt(state.pos);
358
+ if (c === NEWLINE) {
359
+ state.column = 0;
360
+ }
361
+ else {
362
+ state.column++;
363
+ }
364
+ state.pos++;
365
+ }
366
+ // buffer exhausted or broke for potential fence — emit content so far, hold trailing data.
367
+ // Hold trailing \n (could be before a closing fence in next chunk), and also hold
368
+ // \n + backticks when we broke mid-fence-check (the \n precedes the backticks and
369
+ // would be stripped by fence close, so must not be emitted as content yet).
370
+ let emit_end = state.pos;
371
+ if (emit_end > start && state.buffer.charCodeAt(emit_end - 1) === NEWLINE) {
372
+ emit_end--;
373
+ state.pos = emit_end; // leave \n (+ any held backticks after it) in buffer
374
+ }
375
+ const content = state.buffer.slice(start, emit_end);
376
+ if (content.length > 0) {
377
+ emit_codeblock_text(state, cb, content, start);
378
+ }
379
+ return false; // need more input
380
+ };
381
+ /**
382
+ * Check if the buffer at current position has a codeblock closing fence.
383
+ * Returns the position after the fence (including trailing newline) on success,
384
+ * `-1` when definitely not a fence, or `-2` when more input is needed to decide.
385
+ *
386
+ * When `forced` is true, EOF after the fence counts as a valid terminator
387
+ * (matches `mdz_parse`'s handling of code blocks at end of input).
388
+ */
389
+ export const match_codeblock_close = (state, backtick_count, forced = false) => {
390
+ let i = state.pos;
391
+ // count backticks
392
+ let count = 0;
393
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === BACKTICK) {
394
+ count++;
395
+ i++;
396
+ }
397
+ if (count !== backtick_count) {
398
+ // buffer ended mid-backtick-sequence — could still become a fence
399
+ if (!forced && i >= state.buffer.length && count < backtick_count)
400
+ return -2;
401
+ return -1;
402
+ }
403
+ // trailing spaces allowed
404
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
405
+ i++;
406
+ }
407
+ // must be followed by newline or EOF
408
+ if (i < state.buffer.length && state.buffer.charCodeAt(i) === NEWLINE) {
409
+ return i + 1;
410
+ }
411
+ if (i >= state.buffer.length) {
412
+ // in forced mode, EOF terminates the fence
413
+ if (forced)
414
+ return i;
415
+ // exact backtick count but buffer ended (could be spaces or newline coming)
416
+ return -2;
417
+ }
418
+ return -1;
419
+ };
420
+ /**
421
+ * Revert an empty codeblock (closing fence found but no content was emitted).
422
+ * Wraps the opening fence delimiter in a new paragraph and accumulates the
423
+ * closing fence text for normal paragraph processing.
424
+ */
425
+ export const revert_empty_codeblock = (state, cb) => {
426
+ const wrap_id = alloc_id(state);
427
+ emit(state, {
428
+ type: 'revert',
429
+ id: cb.id,
430
+ replacement_text: cb.delimiter,
431
+ start: cb.start,
432
+ wrap_node_type: 'Paragraph',
433
+ wrap_id,
434
+ });
435
+ // enter paragraph mode with the wrapper
436
+ push_stack_entry(state, wrap_id, 'Paragraph', cb.start);
437
+ state.in_paragraph = true;
438
+ state.active_text_id = null;
439
+ state.codeblock = null;
440
+ // accumulate the closing fence text (backticks + spaces, without trailing newline)
441
+ const fence_start = state.pos;
442
+ let fence_content_end = fence_start;
443
+ while (fence_content_end < state.buffer.length &&
444
+ state.buffer.charCodeAt(fence_content_end) === BACKTICK) {
445
+ fence_content_end++;
446
+ }
447
+ while (fence_content_end < state.buffer.length &&
448
+ state.buffer.charCodeAt(fence_content_end) === SPACE) {
449
+ fence_content_end++;
450
+ }
451
+ accumulate_text(state, state.buffer.slice(fence_start, fence_content_end), offset(state, fence_start));
452
+ // position past the closing fence content, leaving any trailing newline for normal processing
453
+ state.pos = fence_content_end;
454
+ state.column = fence_content_end - fence_start;
455
+ state.prev_char =
456
+ fence_content_end > fence_start ? state.buffer.charCodeAt(fence_content_end - 1) : NEWLINE;
457
+ return true;
458
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Inline-formatting handlers (bold, italic, strikethrough, inline code)
3
+ * for the streaming mdz parser.
4
+ *
5
+ * @module
6
+ */
7
+ import { type MdzStreamParserState } from './mdz_stream_parser_state.js';
8
+ export declare const try_bold: (state: MdzStreamParserState) => boolean;
9
+ /**
10
+ * Close a `Bold` container at the given stack index. The caller in
11
+ * `process_inline` already located the open via `find_open`, so we trust the
12
+ * index rather than re-walking the stack.
13
+ */
14
+ export declare const close_bold: (state: MdzStreamParserState, stack_idx: number) => boolean;
15
+ export declare const try_italic: (state: MdzStreamParserState, forced?: boolean) => boolean;
16
+ export declare const try_strikethrough: (state: MdzStreamParserState) => boolean;
17
+ export declare const check_close_word_boundary: (state: MdzStreamParserState) => boolean;
18
+ export declare const close_single_delimiter: (state: MdzStreamParserState, stack_idx: number) => boolean;
19
+ /**
20
+ * Scan for the first potential closing delimiter and check its word boundary.
21
+ * Returns a tri-state:
22
+ * - `'confirmed'`: closer found in buffer with valid word boundary
23
+ * - `'rejected'`: closer found but with invalid word boundary (greedy first-match fails)
24
+ * - `'pending'`: no closer in buffer yet, or can't determine boundary
25
+ *
26
+ * Optimistic constructs (strikethrough) treat `'pending'` as proceed.
27
+ * Non-optimistic constructs (italic) treat `'pending'` as text.
28
+ *
29
+ * Uses native `indexOf` instead of a manual char loop for faster scanning.
30
+ */
31
+ export declare const scan_closer_boundary: (state: MdzStreamParserState, delimiter: string) => "confirmed" | "rejected" | "pending";
32
+ export declare const try_code: (state: MdzStreamParserState) => boolean;
33
+ /**
34
+ * Compute search limit for inline code scanning.
35
+ * Finds the closest closing delimiter for any open formatting on the stack.
36
+ * This prevents backtick scanning from crossing bold/italic/etc boundaries.
37
+ */
38
+ export declare const code_search_limit: (state: MdzStreamParserState, from: number) => number;
39
+ /**
40
+ * Check if any inline formatting container (Bold, Italic, Strikethrough, Link)
41
+ * is on the stack above the current block boundary.
42
+ * Used to decide whether backtick can go optimistic — when formatting is open,
43
+ * the code span might cross the formatting boundary, so we hold instead.
44
+ */
45
+ export declare const has_inline_formatting_on_stack: (state: MdzStreamParserState) => boolean;
46
+ /**
47
+ * Process content inside an optimistic inline Code container.
48
+ * Scans raw text (no inline formatting) until closing backtick or newline.
49
+ * Backtick closes (or reverts if empty). Newline reverts (code can't span lines).
50
+ */
51
+ export declare const process_code_content: (state: MdzStreamParserState) => void;
52
+ //# sourceMappingURL=mdz_stream_parser_inline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mdz_stream_parser_inline.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_stream_parser_inline.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACN,KAAK,oBAAoB,EASzB,MAAM,8BAA8B,CAAC;AAItC,eAAO,MAAM,QAAQ,GAAI,OAAO,oBAAoB,KAAG,OAmBtD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,oBAAoB,EAAE,WAAW,MAAM,KAAG,OAsB3E,CAAC;AAIF,eAAO,MAAM,UAAU,GAAI,OAAO,oBAAoB,EAAE,gBAAc,KAAG,OA4DxE,CAAC;AAIF,eAAO,MAAM,iBAAiB,GAAI,OAAO,oBAAoB,KAAG,OAmC/D,CAAC;AAIF,eAAO,MAAM,yBAAyB,GAAI,OAAO,oBAAoB,KAAG,OAOvE,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,OAAO,oBAAoB,EAAE,WAAW,MAAM,KAAG,OAqBvF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAChC,OAAO,oBAAoB,EAC3B,WAAW,MAAM,KACf,WAAW,GAAG,UAAU,GAAG,SAe7B,CAAC;AAIF,eAAO,MAAM,QAAQ,GAAI,OAAO,oBAAoB,KAAG,OAoFtD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,oBAAoB,EAAE,MAAM,MAAM,KAAG,MAc7E,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAAI,OAAO,oBAAoB,KAAG,OAO5E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,oBAAoB,KAAG,IA2ClE,CAAC"}