@urbicon-ui/blocks 6.35.0 → 6.37.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 (124) hide show
  1. package/dist/components/Chat/Chat/Chat.svelte +53 -0
  2. package/dist/components/Chat/Chat/Chat.svelte.d.ts +4 -0
  3. package/dist/components/Chat/Chat/chat.variants.d.ts +20 -0
  4. package/dist/components/Chat/Chat/chat.variants.js +15 -0
  5. package/dist/components/Chat/Chat/index.d.ts +47 -0
  6. package/dist/components/Chat/Chat/index.js +2 -0
  7. package/dist/components/Chat/ChatMessage/ChatMessage.svelte +342 -0
  8. package/dist/components/Chat/ChatMessage/ChatMessage.svelte.d.ts +4 -0
  9. package/dist/components/Chat/ChatMessage/chat-message.variants.d.ts +138 -0
  10. package/dist/components/Chat/ChatMessage/chat-message.variants.js +118 -0
  11. package/dist/components/Chat/ChatMessage/index.d.ts +102 -0
  12. package/dist/components/Chat/ChatMessage/index.js +2 -0
  13. package/dist/components/Chat/ChatMessageList/ChatMessageList.svelte +301 -0
  14. package/dist/components/Chat/ChatMessageList/ChatMessageList.svelte.d.ts +6 -0
  15. package/dist/components/Chat/ChatMessageList/chat-message-list.variants.d.ts +23 -0
  16. package/dist/components/Chat/ChatMessageList/chat-message-list.variants.js +32 -0
  17. package/dist/components/Chat/ChatMessageList/chat-scroll.d.ts +45 -0
  18. package/dist/components/Chat/ChatMessageList/chat-scroll.js +52 -0
  19. package/dist/components/Chat/ChatMessageList/index.d.ts +93 -0
  20. package/dist/components/Chat/ChatMessageList/index.js +3 -0
  21. package/dist/components/Chat/CitationChip/CitationChip.svelte +107 -0
  22. package/dist/components/Chat/CitationChip/CitationChip.svelte.d.ts +4 -0
  23. package/dist/components/Chat/CitationChip/citation-chip.variants.d.ts +40 -0
  24. package/dist/components/Chat/CitationChip/citation-chip.variants.js +42 -0
  25. package/dist/components/Chat/CitationChip/index.d.ts +69 -0
  26. package/dist/components/Chat/CitationChip/index.js +2 -0
  27. package/dist/components/Chat/CodeBlock/CodeBlock.svelte +120 -0
  28. package/dist/components/Chat/CodeBlock/CodeBlock.svelte.d.ts +4 -0
  29. package/dist/components/Chat/CodeBlock/code-block.variants.d.ts +40 -0
  30. package/dist/components/Chat/CodeBlock/code-block.variants.js +51 -0
  31. package/dist/components/Chat/CodeBlock/index.d.ts +55 -0
  32. package/dist/components/Chat/CodeBlock/index.js +2 -0
  33. package/dist/components/Chat/PromptInput/PromptInput.svelte +432 -0
  34. package/dist/components/Chat/PromptInput/PromptInput.svelte.d.ts +4 -0
  35. package/dist/components/Chat/PromptInput/index.d.ts +165 -0
  36. package/dist/components/Chat/PromptInput/index.js +2 -0
  37. package/dist/components/Chat/PromptInput/prompt-input.variants.d.ts +90 -0
  38. package/dist/components/Chat/PromptInput/prompt-input.variants.js +102 -0
  39. package/dist/components/Chat/ReasoningDisclosure/ReasoningDisclosure.svelte +107 -0
  40. package/dist/components/Chat/ReasoningDisclosure/ReasoningDisclosure.svelte.d.ts +4 -0
  41. package/dist/components/Chat/ReasoningDisclosure/index.d.ts +85 -0
  42. package/dist/components/Chat/ReasoningDisclosure/index.js +2 -0
  43. package/dist/components/Chat/ReasoningDisclosure/reasoning-disclosure.variants.d.ts +20 -0
  44. package/dist/components/Chat/ReasoningDisclosure/reasoning-disclosure.variants.js +31 -0
  45. package/dist/components/Chat/StreamingMarkdown/MdBlock.svelte +99 -0
  46. package/dist/components/Chat/StreamingMarkdown/MdBlock.svelte.d.ts +9 -0
  47. package/dist/components/Chat/StreamingMarkdown/MdInline.svelte +82 -0
  48. package/dist/components/Chat/StreamingMarkdown/MdInline.svelte.d.ts +9 -0
  49. package/dist/components/Chat/StreamingMarkdown/StreamingMarkdown.svelte +118 -0
  50. package/dist/components/Chat/StreamingMarkdown/StreamingMarkdown.svelte.d.ts +4 -0
  51. package/dist/components/Chat/StreamingMarkdown/index.d.ts +86 -0
  52. package/dist/components/Chat/StreamingMarkdown/index.js +2 -0
  53. package/dist/components/Chat/StreamingMarkdown/md-context.d.ts +58 -0
  54. package/dist/components/Chat/StreamingMarkdown/md-context.js +1 -0
  55. package/dist/components/Chat/StreamingMarkdown/streaming-markdown.variants.d.ts +145 -0
  56. package/dist/components/Chat/StreamingMarkdown/streaming-markdown.variants.js +73 -0
  57. package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte +174 -0
  58. package/dist/components/Chat/ToolCallCard/ToolCallCard.svelte.d.ts +4 -0
  59. package/dist/components/Chat/ToolCallCard/index.d.ts +84 -0
  60. package/dist/components/Chat/ToolCallCard/index.js +2 -0
  61. package/dist/components/Chat/ToolCallCard/tool-call-card.variants.d.ts +38 -0
  62. package/dist/components/Chat/ToolCallCard/tool-call-card.variants.js +38 -0
  63. package/dist/components/Chat/chat.types.d.ts +82 -0
  64. package/dist/components/Chat/chat.types.js +7 -0
  65. package/dist/components/Chat/index.d.ts +22 -0
  66. package/dist/components/Chat/index.js +14 -0
  67. package/dist/components/Chat/markdown/blocks.d.ts +54 -0
  68. package/dist/components/Chat/markdown/blocks.js +462 -0
  69. package/dist/components/Chat/markdown/inline.d.ts +31 -0
  70. package/dist/components/Chat/markdown/inline.js +446 -0
  71. package/dist/components/Chat/markdown/repair.d.ts +23 -0
  72. package/dist/components/Chat/markdown/repair.js +337 -0
  73. package/dist/components/Chat/markdown/streaming-harness.d.ts +23 -0
  74. package/dist/components/Chat/markdown/streaming-harness.js +62 -0
  75. package/dist/components/Chat/markdown/types.d.ts +139 -0
  76. package/dist/components/Chat/markdown/types.js +13 -0
  77. package/dist/components/Chat/markdown/url-policy.d.ts +33 -0
  78. package/dist/components/Chat/markdown/url-policy.js +109 -0
  79. package/dist/components/FileUpload/FileUpload.svelte +35 -123
  80. package/dist/components/FileUpload/FileUpload.svelte.d.ts +1 -1
  81. package/dist/components/FileUpload/index.d.ts +2 -28
  82. package/dist/components/PinInput/PinInput.svelte +321 -0
  83. package/dist/components/PinInput/PinInput.svelte.d.ts +4 -0
  84. package/dist/components/PinInput/index.d.ts +98 -0
  85. package/dist/components/PinInput/index.js +2 -0
  86. package/dist/components/PinInput/pin-input.variants.d.ts +96 -0
  87. package/dist/components/PinInput/pin-input.variants.js +111 -0
  88. package/dist/components/QRCode/QRCode.svelte +107 -0
  89. package/dist/components/QRCode/QRCode.svelte.d.ts +4 -0
  90. package/dist/components/QRCode/index.d.ts +74 -0
  91. package/dist/components/QRCode/index.js +3 -0
  92. package/dist/components/QRCode/qr-code.variants.d.ts +25 -0
  93. package/dist/components/QRCode/qr-code.variants.js +24 -0
  94. package/dist/components/QRCode/qr-encode.d.ts +22 -0
  95. package/dist/components/QRCode/qr-encode.js +528 -0
  96. package/dist/components/TimeInput/TimeInput.svelte +521 -0
  97. package/dist/components/TimeInput/TimeInput.svelte.d.ts +4 -0
  98. package/dist/components/TimeInput/index.d.ts +93 -0
  99. package/dist/components/TimeInput/index.js +2 -0
  100. package/dist/components/TimeInput/time-input.variants.d.ts +131 -0
  101. package/dist/components/TimeInput/time-input.variants.js +120 -0
  102. package/dist/components/index.d.ts +7 -0
  103. package/dist/components/index.js +4 -0
  104. package/dist/i18n/index.d.ts +18 -0
  105. package/dist/primitives/Collapsible/Collapsible.svelte +5 -0
  106. package/dist/primitives/SplitPane/SplitPane.svelte +293 -0
  107. package/dist/primitives/SplitPane/SplitPane.svelte.d.ts +4 -0
  108. package/dist/primitives/SplitPane/index.d.ts +88 -0
  109. package/dist/primitives/SplitPane/index.js +2 -0
  110. package/dist/primitives/SplitPane/split-pane.utils.d.ts +73 -0
  111. package/dist/primitives/SplitPane/split-pane.utils.js +94 -0
  112. package/dist/primitives/SplitPane/split-pane.variants.d.ts +40 -0
  113. package/dist/primitives/SplitPane/split-pane.variants.js +56 -0
  114. package/dist/primitives/index.d.ts +2 -0
  115. package/dist/primitives/index.js +1 -0
  116. package/dist/translations/de.d.ts +6 -0
  117. package/dist/translations/de.js +6 -0
  118. package/dist/translations/en.d.ts +6 -0
  119. package/dist/translations/en.js +6 -0
  120. package/dist/utils/file-intake.d.ts +78 -0
  121. package/dist/utils/file-intake.js +144 -0
  122. package/dist/utils/index.d.ts +1 -0
  123. package/dist/utils/index.js +1 -0
  124. package/package.json +3 -3
@@ -0,0 +1,446 @@
1
+ import { checkImageUrl, checkLinkUrl } from './url-policy.js';
2
+ const PUNCT_BANG = '!';
3
+ function isWordChar(ch) {
4
+ if (ch === undefined)
5
+ return false;
6
+ return /[\p{L}\p{N}_]/u.test(ch);
7
+ }
8
+ function isSpaceLike(ch) {
9
+ return ch === undefined || /\s/.test(ch);
10
+ }
11
+ export function parseInlines(input, ctx) {
12
+ return matchDelimiters(tokenize(input, ctx, false));
13
+ }
14
+ function tokenize(input, ctx, insideLink) {
15
+ const tokens = [];
16
+ let text = '';
17
+ const flush = () => {
18
+ if (text.length > 0) {
19
+ tokens.push({ t: 'text', text });
20
+ text = '';
21
+ }
22
+ };
23
+ let i = 0;
24
+ while (i < input.length) {
25
+ const ch = input[i];
26
+ // Backslash escapes — the escaped char becomes literal text.
27
+ if (ch === '\\' && i + 1 < input.length) {
28
+ const next = input[i + 1];
29
+ if (next === '\n') {
30
+ flush();
31
+ tokens.push({ t: 'node', node: { kind: 'break' } });
32
+ }
33
+ else {
34
+ text += next;
35
+ }
36
+ i += 2;
37
+ continue;
38
+ }
39
+ // Hard break: two+ spaces before newline. Soft break renders as a space.
40
+ if (ch === '\n') {
41
+ const hard = /[ ]{2,}$/.test(text);
42
+ text = text.replace(/[ \t]+$/, '');
43
+ flush();
44
+ if (hard)
45
+ tokens.push({ t: 'node', node: { kind: 'break' } });
46
+ else
47
+ text += ' ';
48
+ i += 1;
49
+ continue;
50
+ }
51
+ // Code spans: a backtick run closed by an equally long run.
52
+ if (ch === '`') {
53
+ let runLen = 1;
54
+ while (input[i + runLen] === '`')
55
+ runLen += 1;
56
+ const close = findCodeSpanClose(input, i + runLen, runLen);
57
+ if (close !== -1) {
58
+ flush();
59
+ let code = input.slice(i + runLen, close).replace(/\n/g, ' ');
60
+ if (code.length >= 2 && code.startsWith(' ') && code.endsWith(' ') && code.trim() !== '') {
61
+ code = code.slice(1, -1);
62
+ }
63
+ tokens.push({ t: 'node', node: { kind: 'code', text: code } });
64
+ i = close + runLen;
65
+ }
66
+ else {
67
+ text += input.slice(i, i + runLen);
68
+ i += runLen;
69
+ }
70
+ continue;
71
+ }
72
+ // Images / links / citations / reference links.
73
+ const isImage = ch === PUNCT_BANG && input[i + 1] === '[';
74
+ if ((ch === '[' && !insideLink) || isImage) {
75
+ const parsed = parseBracketConstruct(input, i, isImage, ctx);
76
+ if (parsed) {
77
+ flush();
78
+ tokens.push({ t: 'node', node: parsed.node });
79
+ i = parsed.end;
80
+ continue;
81
+ }
82
+ }
83
+ // Fullwidth citation markers 【id】 (some models emit these).
84
+ if (ch === '【') {
85
+ const parsed = tryFullwidthCitation(input, i, ctx);
86
+ if (parsed) {
87
+ flush();
88
+ tokens.push({ t: 'node', node: parsed.node });
89
+ i = parsed.end;
90
+ continue;
91
+ }
92
+ }
93
+ // Emphasis / strike delimiter runs.
94
+ if (ch === '*' || ch === '_' || ch === '~') {
95
+ let runLen = 1;
96
+ while (input[i + runLen] === ch)
97
+ runLen += 1;
98
+ const before = i > 0 ? input[i - 1] : undefined;
99
+ const after = input[i + runLen];
100
+ let canOpen = !isSpaceLike(after);
101
+ let canClose = !isSpaceLike(before);
102
+ if (ch === '_') {
103
+ // No intraword emphasis for underscores (snake_case stays intact).
104
+ if (isWordChar(before) && isWordChar(after)) {
105
+ canOpen = false;
106
+ canClose = false;
107
+ }
108
+ }
109
+ if (ch === '~' && runLen < 2) {
110
+ // Single tilde is plain text (20~25°C).
111
+ text += input.slice(i, i + runLen);
112
+ i += runLen;
113
+ continue;
114
+ }
115
+ if (canOpen || canClose) {
116
+ flush();
117
+ tokens.push({ t: 'delim', marker: ch, length: runLen, canOpen, canClose });
118
+ }
119
+ else {
120
+ text += input.slice(i, i + runLen);
121
+ }
122
+ i += runLen;
123
+ continue;
124
+ }
125
+ // GFM-style autolinks (opt-in).
126
+ if (ctx.options.autolink && (ch === 'h' || ch === 'H')) {
127
+ const rest = input.slice(i);
128
+ const match = /^https?:\/\/[^\s<>]+/i.exec(rest);
129
+ if (match && !isWordChar(input[i - 1])) {
130
+ const url = match[0].replace(/[.,;:!?)\]]+$/, '');
131
+ flush();
132
+ tokens.push({
133
+ t: 'node',
134
+ node: makeLink(url, undefined, [{ kind: 'text', text: url }], ctx)
135
+ });
136
+ i += url.length;
137
+ continue;
138
+ }
139
+ }
140
+ text += ch;
141
+ i += 1;
142
+ }
143
+ flush();
144
+ return tokens;
145
+ }
146
+ function findCodeSpanClose(input, from, runLen) {
147
+ let i = from;
148
+ while (i < input.length) {
149
+ if (input[i] === '`') {
150
+ let len = 1;
151
+ while (input[i + len] === '`')
152
+ len += 1;
153
+ if (len === runLen)
154
+ return i;
155
+ i += len;
156
+ }
157
+ else {
158
+ i += 1;
159
+ }
160
+ }
161
+ return -1;
162
+ }
163
+ // ── Bracket constructs: links, images, references, citations ────────────────
164
+ const CITATION_FULLWIDTH = /^【([^】\n]{1,64})】/;
165
+ function parseBracketConstruct(input, start, isImage, ctx) {
166
+ const openBracket = isImage ? start + 1 : start;
167
+ const closeBracket = findBracketClose(input, openBracket);
168
+ if (closeBracket === -1)
169
+ return null;
170
+ const label = input.slice(openBracket + 1, closeBracket);
171
+ const afterLabel = closeBracket + 1;
172
+ // Inline destination: [label](dest "title")
173
+ if (input[afterLabel] === '(') {
174
+ const dest = parseDestination(input, afterLabel + 1);
175
+ if (dest) {
176
+ const node = isImage
177
+ ? makeImage(dest.href, label, dest.title, ctx)
178
+ : makeLink(dest.href, dest.title, parseLinkText(label, ctx), ctx);
179
+ return { node, end: dest.end };
180
+ }
181
+ return null;
182
+ }
183
+ // Reference: [label][ref] — empty ref falls back to the label.
184
+ if (input[afterLabel] === '[') {
185
+ const refClose = input.indexOf(']', afterLabel + 1);
186
+ if (refClose !== -1) {
187
+ const refLabel = (input.slice(afterLabel + 1, refClose) || label).trim().toLowerCase();
188
+ const def = ctx.linkRefs.get(refLabel);
189
+ if (def) {
190
+ const node = isImage
191
+ ? makeImage(def.href, label, def.title, ctx)
192
+ : makeLink(def.href, def.title, parseLinkText(label, ctx), ctx);
193
+ return { node, end: refClose + 1 };
194
+ }
195
+ ctx.onUnresolvedRef?.(refLabel);
196
+ return null;
197
+ }
198
+ }
199
+ if (!isImage) {
200
+ // Citation marker [id] — only for known ids, so prose like [1] survives.
201
+ if (ctx.options.citationIds?.has(label)) {
202
+ return { node: { kind: 'citation', id: label }, end: afterLabel };
203
+ }
204
+ // Shortcut reference [label].
205
+ const shortcutLabel = label.trim().toLowerCase();
206
+ const def = ctx.linkRefs.get(shortcutLabel);
207
+ if (def) {
208
+ return {
209
+ node: makeLink(def.href, def.title, parseLinkText(label, ctx), ctx),
210
+ end: afterLabel
211
+ };
212
+ }
213
+ if (shortcutLabel.length > 0)
214
+ ctx.onUnresolvedRef?.(shortcutLabel);
215
+ }
216
+ return null;
217
+ }
218
+ /** Parse citation markers of the form 【id】 (some models emit fullwidth brackets). */
219
+ export function tryFullwidthCitation(input, at, ctx) {
220
+ const match = CITATION_FULLWIDTH.exec(input.slice(at));
221
+ if (match && ctx.options.citationIds?.has(match[1])) {
222
+ return { node: { kind: 'citation', id: match[1] }, end: at + match[0].length };
223
+ }
224
+ return null;
225
+ }
226
+ function findBracketClose(input, open) {
227
+ let depth = 0;
228
+ for (let i = open; i < input.length; i += 1) {
229
+ const ch = input[i];
230
+ if (ch === '\\') {
231
+ i += 1;
232
+ }
233
+ else if (ch === '[') {
234
+ depth += 1;
235
+ }
236
+ else if (ch === ']') {
237
+ depth -= 1;
238
+ if (depth === 0)
239
+ return i;
240
+ }
241
+ else if (ch === '\n' && input[i + 1] === '\n') {
242
+ return -1;
243
+ }
244
+ }
245
+ return -1;
246
+ }
247
+ function parseDestination(input, from) {
248
+ let i = from;
249
+ while (i < input.length && /[ \t\n]/.test(input[i]))
250
+ i += 1;
251
+ let href = '';
252
+ let depth = 0;
253
+ if (input[i] === '<') {
254
+ const close = input.indexOf('>', i + 1);
255
+ if (close === -1)
256
+ return null;
257
+ href = input.slice(i + 1, close);
258
+ i = close + 1;
259
+ }
260
+ else {
261
+ while (i < input.length) {
262
+ const ch = input[i];
263
+ if (ch === '\\' && i + 1 < input.length) {
264
+ href += input[i + 1];
265
+ i += 2;
266
+ continue;
267
+ }
268
+ if (/[\s]/.test(ch))
269
+ break;
270
+ if (ch === '(')
271
+ depth += 1;
272
+ if (ch === ')') {
273
+ if (depth === 0)
274
+ break;
275
+ depth -= 1;
276
+ }
277
+ href += ch;
278
+ i += 1;
279
+ }
280
+ }
281
+ while (i < input.length && /[ \t\n]/.test(input[i]))
282
+ i += 1;
283
+ let title;
284
+ const quote = input[i];
285
+ if (quote === '"' || quote === "'") {
286
+ const close = input.indexOf(quote, i + 1);
287
+ if (close === -1)
288
+ return null;
289
+ title = input.slice(i + 1, close);
290
+ i = close + 1;
291
+ while (i < input.length && /[ \t\n]/.test(input[i]))
292
+ i += 1;
293
+ }
294
+ if (input[i] !== ')')
295
+ return null;
296
+ return { href, title, end: i + 1 };
297
+ }
298
+ /** Link text is parsed without nested links (CommonMark rule). */
299
+ function parseLinkText(label, ctx) {
300
+ return matchDelimiters(tokenize(label, ctx, true));
301
+ }
302
+ function makeLink(href, title, children, ctx) {
303
+ const check = checkLinkUrl(href, ctx.options.urlPolicy);
304
+ if (check.ok)
305
+ return { kind: 'link', href: check.href, title, children };
306
+ return { kind: 'link', href: '', title, children, blocked: true };
307
+ }
308
+ function makeImage(src, alt, title, ctx) {
309
+ const check = checkImageUrl(src, ctx.options.urlPolicy);
310
+ if (check.ok)
311
+ return { kind: 'image', src: check.href, alt, title };
312
+ return { kind: 'image', src: '', alt, title, blocked: true };
313
+ }
314
+ function matchDelimiters(tokens) {
315
+ // Resolve delimiter pairs into explicit open/close instructions, innermost
316
+ // first, then build the tree in a single walk.
317
+ const opens = [];
318
+ const spans = [];
319
+ const consumedOpen = new Map();
320
+ const consumedClose = new Map();
321
+ tokens.forEach((token, index) => {
322
+ if (token.t !== 'delim')
323
+ return;
324
+ let remainingClose = token.length;
325
+ if (token.canClose) {
326
+ while (remainingClose > 0) {
327
+ const openerIdx = findOpener(opens, token.marker);
328
+ if (openerIdx === -1)
329
+ break;
330
+ const opener = opens[openerIdx];
331
+ const isStrike = token.marker === '~';
332
+ const take = isStrike ? 2 : Math.min(2, opener.remaining, remainingClose);
333
+ if (isStrike && (opener.remaining < 2 || remainingClose < 2))
334
+ break;
335
+ spans.push({
336
+ openToken: opener.tokenIndex,
337
+ closeToken: index,
338
+ openLen: take,
339
+ kind: isStrike ? 'strike' : take === 2 ? 'strong' : 'em'
340
+ });
341
+ opener.remaining -= take;
342
+ remainingClose -= take;
343
+ consumedOpen.set(opener.tokenIndex, (consumedOpen.get(opener.tokenIndex) ?? 0) + take);
344
+ consumedClose.set(index, (consumedClose.get(index) ?? 0) + take);
345
+ // Discard younger openers of other markers (CommonMark rule) — this
346
+ // keeps resolved spans strictly nested, which the tree walk relies on.
347
+ opens.splice(openerIdx + 1);
348
+ if (opener.remaining === 0)
349
+ opens.splice(openerIdx, 1);
350
+ }
351
+ }
352
+ if (token.canOpen && remainingClose > 0) {
353
+ opens.push({ tokenIndex: index, marker: token.marker, remaining: remainingClose });
354
+ }
355
+ else if (remainingClose > 0) {
356
+ // Unmatched closer with no opening ability → renders literally later.
357
+ }
358
+ });
359
+ // Build tree: walk tokens, entering/leaving spans at their token indices.
360
+ // Spans are properly nested by construction (stack discipline above).
361
+ const byOpen = new Map();
362
+ const byClose = new Map();
363
+ for (const span of spans) {
364
+ push(byOpen, span.openToken, span);
365
+ push(byClose, span.closeToken, span);
366
+ }
367
+ const root = [];
368
+ const stack = [];
369
+ const sink = () => (stack.length > 0 ? stack[stack.length - 1].children : root);
370
+ tokens.forEach((token, index) => {
371
+ if (token.t === 'delim') {
372
+ const closing = (byClose.get(index) ?? []).sort((a, b) => b.openToken - a.openToken);
373
+ for (const span of closing) {
374
+ while (stack.length > 0) {
375
+ const top = stack.pop();
376
+ if (!top)
377
+ break;
378
+ const node = top.span.kind === 'strong'
379
+ ? { kind: 'strong', children: top.children }
380
+ : top.span.kind === 'em'
381
+ ? { kind: 'em', children: top.children }
382
+ : { kind: 'strike', children: top.children };
383
+ sink().push(node);
384
+ if (top.span === span)
385
+ break;
386
+ }
387
+ }
388
+ const literalLen = token.length - (consumedOpen.get(index) ?? 0) - (consumedClose.get(index) ?? 0);
389
+ if (literalLen > 0) {
390
+ appendText(sink(), token.marker.repeat(literalLen));
391
+ }
392
+ const opening = (byOpen.get(index) ?? []).sort((a, b) => b.closeToken - a.closeToken);
393
+ for (const span of opening) {
394
+ stack.push({ span, children: [] });
395
+ }
396
+ return;
397
+ }
398
+ if (token.t === 'text') {
399
+ appendText(sink(), token.text);
400
+ return;
401
+ }
402
+ sink().push(token.node);
403
+ });
404
+ // Unclosed spans render literally: unwind, re-inserting the literal marker.
405
+ while (stack.length > 0) {
406
+ const top = stack.pop();
407
+ if (!top)
408
+ break;
409
+ const marker = top.span.kind === 'strike' ? '~~' : top.span.kind === 'strong' ? '**' : '*';
410
+ const target = sink();
411
+ appendText(target, marker);
412
+ for (const child of top.children) {
413
+ if (child.kind === 'text')
414
+ appendText(target, child.text);
415
+ else
416
+ target.push(child);
417
+ }
418
+ }
419
+ return mergeText(root);
420
+ }
421
+ function findOpener(opens, marker) {
422
+ for (let i = opens.length - 1; i >= 0; i -= 1) {
423
+ if (opens[i].marker === marker)
424
+ return i;
425
+ }
426
+ return -1;
427
+ }
428
+ function push(map, key, value) {
429
+ const list = map.get(key);
430
+ if (list)
431
+ list.push(value);
432
+ else
433
+ map.set(key, [value]);
434
+ }
435
+ function appendText(target, text) {
436
+ if (text.length === 0)
437
+ return;
438
+ const last = target[target.length - 1];
439
+ if (last && last.kind === 'text')
440
+ last.text += text;
441
+ else
442
+ target.push({ kind: 'text', text });
443
+ }
444
+ function mergeText(nodes) {
445
+ return nodes;
446
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Streaming tail repair — the "remend" step for the incremental markdown engine.
3
+ *
4
+ * The incremental parser calls {@link repairMarkdownTail} on the *unsettled tail*
5
+ * of the stream before parsing it, so half-arrived syntax renders as its settled
6
+ * form instead of flickering (a dangling `**bold` shows as bold immediately, not
7
+ * as two literal asterisks that vanish on the next chunk).
8
+ *
9
+ * Hard invariant: on text that already ends in complete syntax the function is an
10
+ * exact no-op (returns the input unchanged). That is what keeps the streamed end
11
+ * state identical to a one-shot parse of the full source. The function is also
12
+ * idempotent — `repairMarkdownTail(repairMarkdownTail(x)) === repairMarkdownTail(x)`.
13
+ *
14
+ * Scope: this operates on the CommonMark/GFM subset the engine renders. Raw HTML
15
+ * is plain text here (no tag balancing), and `$$` math is ordinary text — neither
16
+ * is repaired. Open fenced code blocks are left entirely untouched so the block
17
+ * parser can keep rendering them as an open code block.
18
+ */
19
+ /**
20
+ * Repair the unsettled tail of a streamed markdown source: close unpaired inline
21
+ * markers and drop half-arrived links/images. A no-op on complete syntax.
22
+ */
23
+ export declare function repairMarkdownTail(text: string): string;