@fusion.dev/helix-headless 0.215.3 → 0.217.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 (113) hide show
  1. package/api.json +1 -1
  2. package/custom-elements.json +1 -1
  3. package/dist/area-chart/root/area-chart-root.d.ts +1 -0
  4. package/dist/area-chart/root/area-chart-root.js +1 -1
  5. package/dist/bar-chart/root/bar-chart-root.d.ts +1 -0
  6. package/dist/bar-chart/root/bar-chart-root.js +1 -1
  7. package/dist/button/root/button-root.d.ts +3 -1
  8. package/dist/button/root/button-root.js +2 -1
  9. package/dist/chat/composer/chat-composer.d.ts +18 -1
  10. package/dist/chat/composer/chat-composer.js +8 -3
  11. package/dist/checkbox/root/checkbox-root.d.ts +3 -1
  12. package/dist/checkbox/root/checkbox-root.js +2 -1
  13. package/dist/code-block/highlight.d.ts +9 -0
  14. package/dist/code-block/highlight.js +39 -0
  15. package/dist/code-block/highlighter.d.ts +44 -0
  16. package/dist/code-block/highlighter.js +44 -0
  17. package/dist/code-block/index.d.ts +1 -0
  18. package/dist/code-block/index.js +1 -0
  19. package/dist/code-block/keyed-lines.d.ts +23 -0
  20. package/dist/code-block/keyed-lines.js +75 -0
  21. package/dist/code-block/root/code-block-root.d.ts +6 -23
  22. package/dist/code-block/root/code-block-root.js +8 -25
  23. package/dist/code-editor/index.d.ts +1 -0
  24. package/dist/code-editor/index.js +1 -0
  25. package/dist/code-editor/root/code-editor-root.d.ts +367 -0
  26. package/dist/code-editor/root/code-editor-root.js +746 -0
  27. package/dist/code-input/index.d.ts +1 -0
  28. package/dist/code-input/index.js +1 -0
  29. package/dist/code-input/root/code-input-root.d.ts +119 -0
  30. package/dist/code-input/root/code-input-root.js +142 -0
  31. package/dist/combobox/root/combobox-root.d.ts +6 -1
  32. package/dist/combobox/root/combobox-root.js +4 -2
  33. package/dist/command/root/command-root.d.ts +61 -11
  34. package/dist/command/root/command-root.js +88 -3
  35. package/dist/donut-chart/root/donut-chart-root.d.ts +1 -0
  36. package/dist/donut-chart/root/donut-chart-root.js +1 -1
  37. package/dist/element-events.generated.js +36 -3
  38. package/dist/filter-input/root/filter-input-root.d.ts +9 -1
  39. package/dist/filter-input/root/filter-input-root.js +7 -3
  40. package/dist/heatmap/root/heatmap-root.d.ts +1 -0
  41. package/dist/heatmap/root/heatmap-root.js +1 -1
  42. package/dist/index.d.ts +4 -0
  43. package/dist/index.js +4 -0
  44. package/dist/input/root/input-root.d.ts +14 -2
  45. package/dist/input/root/input-root.js +11 -3
  46. package/dist/kbd/root/kbd-root.js +9 -2
  47. package/dist/line-chart/root/line-chart-root.d.ts +1 -0
  48. package/dist/line-chart/root/line-chart-root.js +1 -1
  49. package/dist/listbox/root/listbox-root.d.ts +8 -1
  50. package/dist/listbox/root/listbox-root.js +7 -4
  51. package/dist/markdown/blocks.d.ts +66 -0
  52. package/dist/markdown/blocks.js +326 -0
  53. package/dist/markdown/index.d.ts +6 -0
  54. package/dist/markdown/index.js +5 -0
  55. package/dist/markdown/inline.d.ts +57 -0
  56. package/dist/markdown/inline.js +483 -0
  57. package/dist/markdown/root/markdown-root.d.ts +99 -0
  58. package/dist/markdown/root/markdown-root.js +93 -0
  59. package/dist/markdown/stream.d.ts +47 -0
  60. package/dist/markdown/stream.js +62 -0
  61. package/dist/markdown/tree.d.ts +57 -0
  62. package/dist/markdown/tree.js +144 -0
  63. package/dist/markdown/writer.d.ts +33 -0
  64. package/dist/markdown/writer.js +139 -0
  65. package/dist/menu/root/menu-root.d.ts +53 -0
  66. package/dist/menu/root/menu-root.js +92 -15
  67. package/dist/pagination/size/pagination-size.d.ts +3 -0
  68. package/dist/pagination/size/pagination-size.js +2 -1
  69. package/dist/scatter-chart/root/scatter-chart-root.d.ts +1 -0
  70. package/dist/scatter-chart/root/scatter-chart-root.js +1 -1
  71. package/dist/select/root/select-root.d.ts +6 -1
  72. package/dist/select/root/select-root.js +4 -2
  73. package/dist/shortcut-overlay/index.d.ts +1 -0
  74. package/dist/shortcut-overlay/index.js +1 -0
  75. package/dist/shortcut-overlay/root/shortcut-overlay-root.d.ts +128 -0
  76. package/dist/shortcut-overlay/root/shortcut-overlay-root.js +275 -0
  77. package/dist/slider/root/slider-root.d.ts +3 -1
  78. package/dist/slider/root/slider-root.js +2 -1
  79. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +1 -0
  80. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +1 -1
  81. package/dist/spatial-heatmap/root/spatial-heatmap-root.d.ts +1 -0
  82. package/dist/spatial-heatmap/root/spatial-heatmap-root.js +1 -1
  83. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.d.ts +1 -0
  84. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.js +1 -1
  85. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.d.ts +1 -0
  86. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.js +1 -1
  87. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.d.ts +1 -0
  88. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.js +1 -1
  89. package/dist/spatial-treemap/root/spatial-treemap-root.d.ts +1 -0
  90. package/dist/spatial-treemap/root/spatial-treemap-root.js +1 -1
  91. package/dist/switch/root/switch-root.d.ts +3 -1
  92. package/dist/switch/root/switch-root.js +2 -1
  93. package/dist/tabs/tab/tab.d.ts +3 -1
  94. package/dist/tabs/tab/tab.js +2 -1
  95. package/dist/textarea/root/textarea-root.d.ts +20 -4
  96. package/dist/textarea/root/textarea-root.js +31 -23
  97. package/dist/time-input/root/time-input-root.d.ts +6 -0
  98. package/dist/time-input/root/time-input-root.js +4 -2
  99. package/dist/time-picker/root/time-picker-root.d.ts +6 -0
  100. package/dist/time-picker/root/time-picker-root.js +4 -2
  101. package/dist/toast/root/toast-root.d.ts +64 -0
  102. package/dist/toast/root/toast-root.js +128 -3
  103. package/dist/toggle/root/toggle-root.d.ts +3 -1
  104. package/dist/toggle/root/toggle-root.js +2 -1
  105. package/dist/virtual-list/root/virtual-list-root.d.ts +18 -2
  106. package/dist/virtual-list/root/virtual-list-root.js +103 -5
  107. package/examples.json +1 -1
  108. package/lint.json +1 -1
  109. package/package.json +6 -6
  110. package/types/jsx.d.ts +5315 -596
  111. package/types/svelte.d.ts +4 -0
  112. package/types/vue.d.ts +1085 -4
  113. package/types/words.d.ts +491 -0
@@ -0,0 +1,483 @@
1
+ const ENTITIES = {
2
+ amp: '&',
3
+ apos: "'",
4
+ copy: '©',
5
+ gt: '>',
6
+ hellip: '…',
7
+ larr: '←',
8
+ ldquo: '“',
9
+ lsquo: '‘',
10
+ lt: '<',
11
+ mdash: '\u2014',
12
+ nbsp: ' ',
13
+ ndash: '–',
14
+ quot: '"',
15
+ rarr: '→',
16
+ rdquo: '”',
17
+ reg: '®',
18
+ rsquo: '’',
19
+ times: '×',
20
+ trade: '™',
21
+ };
22
+ const PUNCTUATION = /[\p{P}\p{S}]/u;
23
+ const WHITESPACE = /\s/u;
24
+ const ESCAPABLE = /[!-/:-@[-`{-~]/;
25
+ const AUTOLINK = /^<([A-Za-z][A-Za-z0-9+.-]{1,31}:[^\s<>]*|[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*)>/;
26
+ const ENTITY = /^&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([A-Za-z][A-Za-z0-9]{1,31}));/;
27
+ const PENDING_ENTITY = /^&#?[A-Za-z0-9]{0,31}$/;
28
+ const PENDING_AUTOLINK = /^<[A-Za-z][A-Za-z0-9+.-]{0,31}(?::[^\s<>]*)?$/;
29
+ const readDestination = (text, at) => {
30
+ let index = at;
31
+ const skip = () => {
32
+ while (index < text.length && /[ \t\n]/.test(text[index] ?? '')) {
33
+ index += 1;
34
+ }
35
+ };
36
+ skip();
37
+ let href = '';
38
+ if (text[index] === '<') {
39
+ const close = text.indexOf('>', index + 1);
40
+ if (close < 0 || /[\n<]/.test(text.slice(index + 1, close))) {
41
+ return undefined;
42
+ }
43
+ href = text.slice(index + 1, close);
44
+ index = close + 1;
45
+ }
46
+ else {
47
+ let depth = 0;
48
+ const begin = index;
49
+ while (index < text.length) {
50
+ const character = text[index] ?? '';
51
+ if (character === '\\' && ESCAPABLE.test(text[index + 1] ?? '')) {
52
+ index += 2;
53
+ continue;
54
+ }
55
+ if (/[\s]/.test(character)) {
56
+ break;
57
+ }
58
+ if (character === '(') {
59
+ depth += 1;
60
+ }
61
+ else if (character === ')') {
62
+ if (depth === 0) {
63
+ break;
64
+ }
65
+ depth -= 1;
66
+ }
67
+ index += 1;
68
+ }
69
+ href = text.slice(begin, index).replace(/\\([!-/:-@[-`{-~])/g, '$1');
70
+ }
71
+ skip();
72
+ let title;
73
+ const quote = text[index];
74
+ if (quote === '"' || quote === "'" || quote === '(') {
75
+ const closer = quote === '(' ? ')' : quote;
76
+ let end = index + 1;
77
+ while (end < text.length && text[end] !== closer) {
78
+ end += text[end] === '\\' ? 2 : 1;
79
+ }
80
+ if (end >= text.length) {
81
+ return undefined;
82
+ }
83
+ title = text.slice(index + 1, end).replace(/\\([!-/:-@[-`{-~])/g, '$1');
84
+ index = end + 1;
85
+ skip();
86
+ }
87
+ if (text[index] !== ')') {
88
+ return undefined;
89
+ }
90
+ return title === undefined ? { end: index + 1, href } : { end: index + 1, href, title };
91
+ };
92
+ const merge = (nodes) => {
93
+ const merged = [];
94
+ for (const node of nodes) {
95
+ const last = merged.at(-1);
96
+ if (node.kind === 'text' && last?.kind === 'text') {
97
+ merged[merged.length - 1] = { kind: 'text', text: last.text + node.text };
98
+ }
99
+ else if (node.kind !== 'text' || node.text !== '') {
100
+ merged.push(node);
101
+ }
102
+ }
103
+ return merged;
104
+ };
105
+ const processEmphasis = (tokens, from) => {
106
+ let closerAt = from;
107
+ while (closerAt < tokens.length) {
108
+ const closer = tokens[closerAt]?.delimiter;
109
+ if (!closer?.canClose || closer.count === 0) {
110
+ closerAt += 1;
111
+ continue;
112
+ }
113
+ let openerAt = closerAt - 1;
114
+ let found = false;
115
+ while (openerAt >= from) {
116
+ const opener = tokens[openerAt]?.delimiter;
117
+ if (opener?.canOpen &&
118
+ opener.count > 0 &&
119
+ opener.character === closer.character &&
120
+ (closer.character === '~'
121
+ ? opener.count === closer.count
122
+ : !((opener.canClose || closer.canOpen) &&
123
+ (opener.original + closer.original) % 3 === 0 &&
124
+ !(opener.original % 3 === 0 && closer.original % 3 === 0)))) {
125
+ found = true;
126
+ break;
127
+ }
128
+ openerAt -= 1;
129
+ }
130
+ if (!found) {
131
+ closerAt += 1;
132
+ continue;
133
+ }
134
+ const opener = tokens[openerAt]?.delimiter;
135
+ const used = closer.character === '~' ? closer.count : opener.count >= 2 && closer.count >= 2 ? 2 : 1;
136
+ const inside = tokens.splice(openerAt + 1, closerAt - openerAt - 1);
137
+ const kind = closer.character === '~' ? 'del' : used === 2 ? 'strong' : 'em';
138
+ const wrapped = { node: { children: merge(inside.map((token) => token.node)), kind } };
139
+ tokens.splice(openerAt + 1, 0, wrapped);
140
+ closerAt = openerAt + 2;
141
+ opener.count -= used;
142
+ closer.count -= used;
143
+ const openerToken = tokens[openerAt];
144
+ openerToken.node = { kind: 'text', text: opener.character.repeat(opener.count) };
145
+ const closerToken = tokens[closerAt];
146
+ closerToken.node = { kind: 'text', text: closer.character.repeat(closer.count) };
147
+ if (opener.count === 0) {
148
+ delete openerToken.delimiter;
149
+ }
150
+ if (closer.count === 0) {
151
+ delete closerToken.delimiter;
152
+ closerAt += 1;
153
+ }
154
+ }
155
+ };
156
+ const flanking = (before, after, character) => {
157
+ const beforeSpace = before === '' || WHITESPACE.test(before);
158
+ const afterSpace = after === '' || WHITESPACE.test(after);
159
+ const beforePunctuation = PUNCTUATION.test(before);
160
+ const afterPunctuation = PUNCTUATION.test(after);
161
+ const left = !afterSpace && (!afterPunctuation || beforeSpace || beforePunctuation);
162
+ const right = !beforeSpace && (!beforePunctuation || afterSpace || afterPunctuation);
163
+ if (character === '_') {
164
+ return {
165
+ canClose: right && (!left || afterPunctuation),
166
+ canOpen: left && (!right || beforePunctuation),
167
+ };
168
+ }
169
+ return { canClose: right, canOpen: left };
170
+ };
171
+ const trimUrl = (url) => {
172
+ let end = url.length;
173
+ while (end > 0 && /[?!.,:*_~'"]/.test(url[end - 1] ?? '')) {
174
+ end -= 1;
175
+ }
176
+ let trimmed = url.slice(0, end);
177
+ while (trimmed.endsWith(')')) {
178
+ const opens = trimmed.split('(').length - 1;
179
+ const closes = trimmed.split(')').length - 1;
180
+ if (closes <= opens) {
181
+ break;
182
+ }
183
+ trimmed = trimmed.slice(0, -1);
184
+ }
185
+ return trimmed;
186
+ };
187
+ const BARE_URL = /(?:https?:\/\/|www\.)[^\s<]+/g;
188
+ const bareLinks = (nodes) => nodes.flatMap((node) => {
189
+ if (node.kind === 'link') {
190
+ return [node];
191
+ }
192
+ if (node.kind === 'em' || node.kind === 'strong' || node.kind === 'del') {
193
+ return [{ ...node, children: bareLinks(node.children) }];
194
+ }
195
+ if (node.kind !== 'text' || !/https?:\/\/|www\./.test(node.text)) {
196
+ return [node];
197
+ }
198
+ const out = [];
199
+ let last = 0;
200
+ for (const match of node.text.matchAll(BARE_URL)) {
201
+ const before = node.text[match.index - 1];
202
+ if (match.index > 0 && before !== undefined && !/[\s*_~(]/.test(before)) {
203
+ continue;
204
+ }
205
+ const url = trimUrl(match[0]);
206
+ if (url === '' || (url.startsWith('www.') && !/^www\.[^.]+\./.test(`${url}.`))) {
207
+ continue;
208
+ }
209
+ out.push({ kind: 'text', text: node.text.slice(last, match.index) });
210
+ out.push({
211
+ children: [{ kind: 'text', text: url }],
212
+ href: url.startsWith('www.') ? `http://${url}` : url,
213
+ kind: 'link',
214
+ });
215
+ last = match.index + url.length;
216
+ }
217
+ out.push({ kind: 'text', text: node.text.slice(last) });
218
+ return merge(out);
219
+ });
220
+ export const parseInline = (text, options = {}) => {
221
+ const open = options.open === true;
222
+ const tokens = [];
223
+ let plain = '';
224
+ const flush = () => {
225
+ if (plain !== '') {
226
+ tokens.push({ node: { kind: 'text', text: plain } });
227
+ plain = '';
228
+ }
229
+ };
230
+ let at = 0;
231
+ while (at < text.length) {
232
+ const character = text[at] ?? '';
233
+ const rest = text.slice(at);
234
+ if (character === '\\') {
235
+ const next = text[at + 1];
236
+ if (next === '\n') {
237
+ flush();
238
+ tokens.push({ node: { kind: 'break' } });
239
+ at += 2;
240
+ continue;
241
+ }
242
+ if (next !== undefined && ESCAPABLE.test(next)) {
243
+ plain += next;
244
+ at += 2;
245
+ continue;
246
+ }
247
+ if (next === undefined && open) {
248
+ break;
249
+ }
250
+ plain += character;
251
+ at += 1;
252
+ continue;
253
+ }
254
+ if (character === '`') {
255
+ const run = /^`+/.exec(rest)?.[0] ?? '`';
256
+ const closer = new RegExp(`(?<!\`)${run}(?!\`)`, 'g');
257
+ closer.lastIndex = at + run.length;
258
+ const match = closer.exec(text);
259
+ if (match) {
260
+ flush();
261
+ let code = text.slice(at + run.length, match.index).replace(/\n/g, ' ');
262
+ if (/^ .*[^ ].* $/.test(code) || /^ [^ ]+ $/.test(code)) {
263
+ code = code.slice(1, -1);
264
+ }
265
+ tokens.push({ node: { kind: 'code', text: code } });
266
+ at = match.index + run.length;
267
+ continue;
268
+ }
269
+ if (open && at + run.length >= text.length) {
270
+ break;
271
+ }
272
+ if (open) {
273
+ flush();
274
+ tokens.push({
275
+ node: { kind: 'code', text: text.slice(at + run.length).replace(/\n/g, ' ') },
276
+ });
277
+ at = text.length;
278
+ continue;
279
+ }
280
+ plain += run;
281
+ at += run.length;
282
+ continue;
283
+ }
284
+ if (character === '&') {
285
+ const match = ENTITY.exec(rest);
286
+ if (match) {
287
+ const [whole, hex, decimal, name] = match;
288
+ const point = hex ? Number.parseInt(hex, 16) : decimal ? Number(decimal) : undefined;
289
+ const decoded = point !== undefined
290
+ ? String.fromCodePoint(point > 0x10ffff || point === 0 ? 0xfffd : point)
291
+ : ENTITIES[name ?? ''];
292
+ if (decoded !== undefined) {
293
+ plain += decoded;
294
+ at += whole.length;
295
+ continue;
296
+ }
297
+ }
298
+ if (open && PENDING_ENTITY.test(rest)) {
299
+ break;
300
+ }
301
+ plain += character;
302
+ at += 1;
303
+ continue;
304
+ }
305
+ if (character === '<') {
306
+ const match = AUTOLINK.exec(rest);
307
+ if (match) {
308
+ flush();
309
+ const target = match[1] ?? '';
310
+ const href = target.includes(':') ? target : `mailto:${target}`;
311
+ tokens.push({ node: { children: [{ kind: 'text', text: target }], href, kind: 'link' } });
312
+ at += match[0].length;
313
+ continue;
314
+ }
315
+ if (open && PENDING_AUTOLINK.test(rest)) {
316
+ break;
317
+ }
318
+ plain += character;
319
+ at += 1;
320
+ continue;
321
+ }
322
+ if (character === '*' || character === '_' || character === '~') {
323
+ const run = new RegExp(`^\\${character}+`).exec(rest)?.[0] ?? character;
324
+ const after = text[at + run.length] ?? '';
325
+ if (open && after === '') {
326
+ break;
327
+ }
328
+ if (character === '~' && run.length > 2) {
329
+ plain += run;
330
+ at += run.length;
331
+ continue;
332
+ }
333
+ flush();
334
+ const sides = flanking(text[at - 1] ?? '', after, character);
335
+ tokens.push({
336
+ delimiter: { ...sides, character, count: run.length, original: run.length },
337
+ node: { kind: 'text', text: run },
338
+ });
339
+ at += run.length;
340
+ continue;
341
+ }
342
+ if (character === '!' && text[at + 1] === '[') {
343
+ flush();
344
+ tokens.push({ bracket: { active: true, image: true }, node: { kind: 'text', text: '![' } });
345
+ at += 2;
346
+ continue;
347
+ }
348
+ if (character === '!' && open && at + 1 >= text.length) {
349
+ break;
350
+ }
351
+ if (character === '[') {
352
+ flush();
353
+ tokens.push({ bracket: { active: true, image: false }, node: { kind: 'text', text: '[' } });
354
+ at += 1;
355
+ continue;
356
+ }
357
+ if (character === ']') {
358
+ flush();
359
+ let openerAt = tokens.length - 1;
360
+ while (openerAt >= 0 && !tokens[openerAt]?.bracket) {
361
+ openerAt -= 1;
362
+ }
363
+ const opener = tokens[openerAt];
364
+ if (!opener?.bracket) {
365
+ plain += ']';
366
+ at += 1;
367
+ continue;
368
+ }
369
+ if (!opener.bracket.active) {
370
+ tokens.splice(openerAt, 1, { node: opener.node });
371
+ plain += ']';
372
+ at += 1;
373
+ continue;
374
+ }
375
+ const destination = text[at + 1] === '(' ? readDestination(text, at + 2) : undefined;
376
+ const pending = open && destination === undefined && (at + 1 >= text.length || text[at + 1] === '(');
377
+ if (!destination && !pending) {
378
+ tokens.splice(openerAt, 1, { node: opener.node });
379
+ plain += ']';
380
+ at += 1;
381
+ continue;
382
+ }
383
+ processEmphasis(tokens, openerAt + 1);
384
+ const inside = tokens.splice(openerAt + 1).map((token) => token.node);
385
+ tokens.pop();
386
+ if (opener.bracket.image) {
387
+ const alt = textOf(inside);
388
+ tokens.push({
389
+ node: destination
390
+ ? {
391
+ alt,
392
+ kind: 'image',
393
+ src: destination.href,
394
+ ...(destination.title === undefined ? {} : { title: destination.title }),
395
+ }
396
+ : { children: [{ kind: 'text', text: alt }], kind: 'link' },
397
+ });
398
+ }
399
+ else {
400
+ tokens.push({
401
+ node: {
402
+ children: merge(inside),
403
+ kind: 'link',
404
+ ...(destination ? { href: destination.href } : {}),
405
+ ...(destination?.title === undefined ? {} : { title: destination.title }),
406
+ },
407
+ });
408
+ for (const token of tokens) {
409
+ if (token.bracket && !token.bracket.image) {
410
+ token.bracket.active = false;
411
+ }
412
+ }
413
+ }
414
+ if (!destination) {
415
+ at = text.length;
416
+ continue;
417
+ }
418
+ at = destination.end;
419
+ continue;
420
+ }
421
+ if (character === '\n') {
422
+ const hard = / {2,}$/.test(plain);
423
+ plain = plain.replace(/ +$/, '');
424
+ flush();
425
+ tokens.push({ node: hard ? { kind: 'break' } : { kind: 'text', text: '\n' } });
426
+ at += 1;
427
+ while (text[at] === ' ' || text[at] === '\t') {
428
+ at += 1;
429
+ }
430
+ continue;
431
+ }
432
+ plain += character;
433
+ at += 1;
434
+ }
435
+ flush();
436
+ processEmphasis(tokens, 0);
437
+ if (open) {
438
+ closeOpen(tokens);
439
+ }
440
+ return bareLinks(merge(tokens.map((token) => token.node)));
441
+ };
442
+ const closeOpen = (tokens) => {
443
+ for (let at = tokens.length - 1; at >= 0; at -= 1) {
444
+ const token = tokens[at];
445
+ if (token.bracket) {
446
+ token.node = { kind: 'text', text: '' };
447
+ delete token.bracket;
448
+ continue;
449
+ }
450
+ const delimiter = token.delimiter;
451
+ if (!delimiter?.canOpen || delimiter.count === 0 || at === tokens.length - 1) {
452
+ continue;
453
+ }
454
+ const inside = merge(tokens.splice(at + 1).map((one) => one.node));
455
+ let node;
456
+ if (delimiter.character === '~') {
457
+ node = { children: inside, kind: 'del' };
458
+ }
459
+ else if (delimiter.count >= 3) {
460
+ node = { children: [{ children: inside, kind: 'em' }], kind: 'strong' };
461
+ }
462
+ else {
463
+ node = { children: inside, kind: delimiter.count === 2 ? 'strong' : 'em' };
464
+ }
465
+ token.node = node;
466
+ delete token.delimiter;
467
+ }
468
+ };
469
+ export const textOf = (nodes) => nodes
470
+ .map((node) => {
471
+ switch (node.kind) {
472
+ case 'break':
473
+ return '\n';
474
+ case 'code':
475
+ case 'text':
476
+ return node.text;
477
+ case 'image':
478
+ return node.alt;
479
+ default:
480
+ return textOf(node.children);
481
+ }
482
+ })
483
+ .join('');
@@ -0,0 +1,99 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { type CSSResultGroup, type PropertyValues, type TemplateResult } from 'lit';
3
+ import '../../callout/index.js';
4
+ import '../../code-block/index.js';
5
+ /**
6
+ * @summary Markdown drawn as a document, which streams in place as a reply arrives.
7
+ *
8
+ * @description
9
+ * Set `text` to the Markdown, or to everything a reply has said so far, with
10
+ * `streaming` while more is coming. Each time `text` grows, only what changed
11
+ * is drawn: a block that has ended is never drawn again, a word arriving is
12
+ * added to the line it ends, and a code block, a list, or a table fills in
13
+ * where it stands. Text you can already read does not move.
14
+ *
15
+ * - A fence becomes a `Code Block`, with its language as its `label` and its
16
+ * `language`, so a highlighter given to `registerHighlighter` colors it as it
17
+ * grows.
18
+ * - An alert, `> [!NOTE]` through `> [!CAUTION]`, becomes a `Callout` in the
19
+ * tone `rehypeHelix` gives it.
20
+ * - A table's columns are sized once from its header row, so the rows that
21
+ * follow never widen or narrow them.
22
+ * - A list item's first paragraph is drawn the same whether the list is tight
23
+ * or loose, so a blank line arriving between items moves nothing.
24
+ *
25
+ * While `streaming`, the end of the text is read as still arriving. A `**`
26
+ * whose closing pair has not come yet draws bold at once, a code span draws as
27
+ * code, and a link draws as a link before its address arrives. A marker that
28
+ * the next character decides, such as a `#` at the start of a line, waits for
29
+ * that character.
30
+ *
31
+ * **The document is drawn in the element's own children**, so the page's
32
+ * `data-prose` styles reach it, a reader can select and find its text, and a
33
+ * server can send it. Put `data-prose` on the element or on a container. On a
34
+ * server, render `markdownToHtml(text)` inside the element: where what it was
35
+ * sent is exactly what it would draw, it keeps it and draws nothing again.
36
+ *
37
+ * What it reads is CommonMark with GitHub's tables, task lists,
38
+ * strikethrough, autolinks, and alerts. A line of `===` or `---` under a
39
+ * paragraph is not a heading, four spaces of indent are not a code block, and
40
+ * reference links are not read, because each of those changes text above it
41
+ * after it was drawn. HTML is drawn as text. An image is drawn as a link to the
42
+ * picture, which loads nothing until a reader follows it. A link's address is
43
+ * kept only where it is relative or on `http`, `https`, `mailto`, or `tel`.
44
+ *
45
+ * @since 0.216.0
46
+ * @status experimental
47
+ * @category typography
48
+ * @aliases Markdown renderer, Streaming text, Rich text
49
+ * @documentation https://fusion.dev/helix/components/markdown
50
+ *
51
+ * @slot - The drawn document. Written by the element; leave it empty, or fill it with `markdownToHtml(text)` on a server.
52
+ *
53
+ * @cssstate streaming - While `streaming` is set and more text is expected.
54
+ */
55
+ export declare class HelixHeadlessMarkdown extends HelixElement {
56
+ static styles: CSSResultGroup;
57
+ /**
58
+ * The key this family's words are registered under, its styled tag:
59
+ * `registerWords('de', { 'helix-markdown': { noteHeading: 'Hinweis' } })`.
60
+ */
61
+ static words: string;
62
+ /** The heading of a `> [!CAUTION]` alert. */
63
+ cautionHeading: string;
64
+ /** The heading of a `> [!IMPORTANT]` alert. */
65
+ importantHeading: string;
66
+ /** The heading of a `> [!NOTE]` alert. */
67
+ noteHeading: string;
68
+ /**
69
+ * Whether more text is coming. While it is set, the end of `text` is read
70
+ * as a block still arriving, and the element is busy to assistive
71
+ * technology. Clear it when the reply ends, and the last block is read as
72
+ * finished.
73
+ */
74
+ streaming: boolean;
75
+ /**
76
+ * The Markdown. Set it to the whole reply so far each time more arrives: a
77
+ * text that starts with the one before is drawn by its new end alone.
78
+ */
79
+ text: string;
80
+ /** The heading of a `> [!TIP]` alert. */
81
+ tipHeading: string;
82
+ /** The heading of a `> [!WARNING]` alert. */
83
+ warningHeading: string;
84
+ private writer;
85
+ /**
86
+ * Adds text to the end of the document, as a chunk of a stream arrives.
87
+ * The same as setting `text` to the text so far.
88
+ */
89
+ appendText(chunk: string): void;
90
+ protected willUpdate(changed: PropertyValues): void;
91
+ protected firstUpdated(changed: PropertyValues): void;
92
+ protected updated(changed: PropertyValues): void;
93
+ protected render(): TemplateResult;
94
+ }
95
+ declare global {
96
+ interface HTMLElementTagNameMap {
97
+ 'helix-headless-markdown': HelixHeadlessMarkdown;
98
+ }
99
+ }
@@ -0,0 +1,93 @@
1
+ import { __decorate } from "tslib";
2
+ import { customElement, HelixElement, words } from '@fusion.dev/helix-core';
3
+ import { css, html } from 'lit';
4
+ import { property } from 'lit/decorators.js';
5
+ import '../../callout/index.js';
6
+ import '../../code-block/index.js';
7
+ import { MarkdownWriter } from '../writer.js';
8
+ let HelixHeadlessMarkdown = class HelixHeadlessMarkdown extends HelixElement {
9
+ constructor() {
10
+ super(...arguments);
11
+ this.cautionHeading = 'Caution';
12
+ this.importantHeading = 'Important';
13
+ this.noteHeading = 'Note';
14
+ this.streaming = false;
15
+ this.text = '';
16
+ this.tipHeading = 'Tip';
17
+ this.warningHeading = 'Warning';
18
+ }
19
+ static { this.styles = css `
20
+ :host {
21
+ display: block;
22
+ }
23
+ `; }
24
+ static { this.words = 'helix-markdown'; }
25
+ appendText(chunk) {
26
+ this.text += chunk;
27
+ }
28
+ willUpdate(changed) {
29
+ super.willUpdate(changed);
30
+ this.internals.ariaBusy = this.streaming ? 'true' : null;
31
+ this.toggleState('streaming', this.streaming);
32
+ }
33
+ firstUpdated(changed) {
34
+ super.firstUpdated(changed);
35
+ this.finishHydration();
36
+ }
37
+ updated(changed) {
38
+ super.updated(changed);
39
+ const tier = this.constructor.tier;
40
+ const options = {
41
+ headings: {
42
+ caution: this.cautionHeading,
43
+ important: this.importantHeading,
44
+ note: this.noteHeading,
45
+ tip: this.tipHeading,
46
+ warning: this.warningHeading,
47
+ },
48
+ tags: { callout: `${tier}callout`, codeBlock: `${tier}code-block` },
49
+ };
50
+ if (!this.writer) {
51
+ this.writer = new MarkdownWriter(this, options);
52
+ }
53
+ else if (['cautionHeading', 'importantHeading', 'noteHeading', 'tipHeading', 'warningHeading'].some((name) => changed.has(name))) {
54
+ this.writer.configure(options);
55
+ }
56
+ if (changed.has('text') || changed.has('streaming')) {
57
+ this.writer.write(this.text, !this.streaming);
58
+ }
59
+ }
60
+ render() {
61
+ return html `<slot></slot>`;
62
+ }
63
+ };
64
+ __decorate([
65
+ words,
66
+ property({ attribute: 'caution-heading' })
67
+ ], HelixHeadlessMarkdown.prototype, "cautionHeading", void 0);
68
+ __decorate([
69
+ words,
70
+ property({ attribute: 'important-heading' })
71
+ ], HelixHeadlessMarkdown.prototype, "importantHeading", void 0);
72
+ __decorate([
73
+ words,
74
+ property({ attribute: 'note-heading' })
75
+ ], HelixHeadlessMarkdown.prototype, "noteHeading", void 0);
76
+ __decorate([
77
+ property({ type: Boolean })
78
+ ], HelixHeadlessMarkdown.prototype, "streaming", void 0);
79
+ __decorate([
80
+ property()
81
+ ], HelixHeadlessMarkdown.prototype, "text", void 0);
82
+ __decorate([
83
+ words,
84
+ property({ attribute: 'tip-heading' })
85
+ ], HelixHeadlessMarkdown.prototype, "tipHeading", void 0);
86
+ __decorate([
87
+ words,
88
+ property({ attribute: 'warning-heading' })
89
+ ], HelixHeadlessMarkdown.prototype, "warningHeading", void 0);
90
+ HelixHeadlessMarkdown = __decorate([
91
+ customElement('helix-headless-markdown')
92
+ ], HelixHeadlessMarkdown);
93
+ export { HelixHeadlessMarkdown };