@simon_he/vue-tui 0.0.8 → 1.0.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 (249) hide show
  1. package/README.md +142 -30
  2. package/dist/TSelect-BwJJbuTt.js +6089 -0
  3. package/dist/TTree-DK_DQBYm.js +6480 -0
  4. package/dist/agent.cjs +39416 -0
  5. package/dist/agent.d.cts +44 -0
  6. package/dist/agent.d.ts +44 -0
  7. package/dist/agent.js +828 -0
  8. package/dist/ansi-palette-CNXqFciC.js +57 -0
  9. package/dist/ansi-styles.d.ts +200 -0
  10. package/dist/block-source-CKUOnkgT.js +1663 -0
  11. package/dist/cli/headless-renderer.d.ts +29 -0
  12. package/dist/cli/input.d.ts +49 -0
  13. package/dist/cli/node-file-writers.d.ts +13 -0
  14. package/dist/cli/node-module.d.ts +2 -0
  15. package/dist/cli/parse-kitty.d.ts +2 -0
  16. package/dist/cli/parse-mouse.d.ts +2 -0
  17. package/dist/cli/parse-utils.d.ts +14 -0
  18. package/dist/cli/parse-xterm.d.ts +2 -0
  19. package/dist/cli/path-provider-types.d.ts +1 -0
  20. package/dist/cli/path-provider.d.ts +4 -0
  21. package/dist/cli/path-suggest-core.d.ts +1 -0
  22. package/dist/cli/path-suggest.d.ts +15 -0
  23. package/dist/cli/recording.d.ts +4 -0
  24. package/dist/cli/stdin-buffer.d.ts +33 -0
  25. package/dist/cli.cjs +11061 -0
  26. package/dist/cli.d.cts +23 -0
  27. package/dist/cli.d.ts +23 -0
  28. package/dist/cli.js +9875 -0
  29. package/dist/core/ansi/capability.d.ts +20 -0
  30. package/dist/core/ansi/colors.d.ts +41 -0
  31. package/dist/core/ansi/sgr.d.ts +7 -0
  32. package/dist/core/ansi/theme.d.ts +6 -0
  33. package/dist/core/ansi-palette.d.ts +14 -0
  34. package/dist/core/buffer/buffer.d.ts +54 -0
  35. package/dist/core/buffer/width.d.ts +4 -0
  36. package/dist/core/debug-logger.d.ts +18 -0
  37. package/dist/core/hyperlink.d.ts +9 -0
  38. package/dist/core/index.d.ts +4 -0
  39. package/dist/core/path-provider-types.d.ts +24 -0
  40. package/dist/core/path-suggest.d.ts +35 -0
  41. package/dist/core/render-plane.d.ts +3 -0
  42. package/dist/core/terminal/create-terminal.d.ts +8 -0
  43. package/dist/core/terminal/emitter.d.ts +7 -0
  44. package/dist/core/types.d.ts +107 -0
  45. package/dist/core.cjs +3153 -0
  46. package/dist/core.d.cts +14 -0
  47. package/dist/core.d.ts +14 -0
  48. package/dist/core.js +424 -0
  49. package/dist/create-terminal-C2oiWFJU.js +1434 -0
  50. package/dist/create-terminal-app.d.ts +36 -0
  51. package/dist/dom-renderer-BqGijQID.js +1173 -0
  52. package/dist/env-Cl8qmNFN.js +19 -0
  53. package/dist/events/index.d.ts +6 -0
  54. package/dist/events/manager/cli-event-manager.d.ts +22 -0
  55. package/dist/events/manager/event-manager.d.ts +30 -0
  56. package/dist/events/manager/selection-suppression.d.ts +3 -0
  57. package/dist/events/manager/types.d.ts +108 -0
  58. package/dist/events/recording.d.ts +44 -0
  59. package/dist/experimental.cjs +4412 -1641
  60. package/dist/experimental.d.cts +42 -0
  61. package/dist/experimental.d.ts +42 -825
  62. package/dist/experimental.js +31 -7392
  63. package/dist/frame-perf-C6Xfg85O.js +24 -0
  64. package/dist/hyperlink-Bn02UXJb.js +419 -0
  65. package/dist/index.cjs +14765 -17582
  66. package/dist/index.d.cts +35 -0
  67. package/dist/index.d.ts +35 -1528
  68. package/dist/index.js +17 -20026
  69. package/dist/linkify-D_gzn9Xq.js +562 -0
  70. package/dist/markdown.cjs +9321 -7623
  71. package/dist/markdown.d.cts +11 -0
  72. package/dist/markdown.d.ts +11 -112
  73. package/dist/markdown.js +36 -1427
  74. package/dist/observability/cli-latency-node.d.ts +3 -0
  75. package/dist/observability/cli-latency-types.d.ts +41 -0
  76. package/dist/observability/cli-latency.d.ts +3 -0
  77. package/dist/observability/frame-perf-store.d.ts +18 -0
  78. package/dist/observability/frame-perf.d.ts +40 -0
  79. package/dist/observability/perf-sink.d.ts +56 -0
  80. package/dist/observability/trace.d.ts +40 -0
  81. package/dist/observability/tui-profiler.d.ts +33 -0
  82. package/dist/observability.cjs +221 -0
  83. package/dist/observability.d.cts +8 -0
  84. package/dist/observability.d.ts +8 -0
  85. package/dist/observability.js +4 -0
  86. package/dist/path-mnSgEpCm.js +81 -0
  87. package/dist/path-suggest-BNqcuE3-.js +274 -0
  88. package/dist/path-suggest-Dk9TRLh5.js +3 -0
  89. package/dist/{path-suggest-DmrmeueH.js → path-suggest-bvR9SY6Q.js} +86 -18
  90. package/dist/renderer/capabilities.d.ts +13 -0
  91. package/dist/renderer/cli/index.d.ts +2 -0
  92. package/dist/renderer/cli/stdout-metrics.d.ts +18 -0
  93. package/dist/renderer/cli/stdout-renderer.d.ts +48 -0
  94. package/dist/renderer/dom/dom-renderer.d.ts +157 -0
  95. package/dist/renderer/dom/index.d.ts +2 -0
  96. package/dist/renderer/index.d.ts +2 -0
  97. package/dist/renderer-dom.cjs +1505 -0
  98. package/dist/renderer-dom.d.cts +4 -0
  99. package/dist/renderer-dom.d.ts +4 -0
  100. package/dist/renderer-dom.js +7 -0
  101. package/dist/runtime/index.d.ts +36 -0
  102. package/dist/runtime/osc52.d.ts +9 -0
  103. package/dist/runtime-rfcZCyJq.js +1197 -0
  104. package/dist/runtime.cjs +1698 -0
  105. package/dist/runtime.d.cts +9 -0
  106. package/dist/runtime.d.ts +9 -0
  107. package/dist/runtime.js +4 -0
  108. package/dist/selection/terminal-selection.d.ts +123 -0
  109. package/dist/sgr-BmzxEjAO.js +304 -0
  110. package/dist/terminal-selection-DBinO6p5.js +330 -0
  111. package/dist/tlog-theme-qPBt5rge.js +9254 -0
  112. package/dist/trace-CjzYeIxN.js +182 -0
  113. package/dist/utils/env.d.ts +3 -0
  114. package/dist/utils/grapheme.d.ts +5 -0
  115. package/dist/utils/newlines.d.ts +1 -0
  116. package/dist/utils/path.d.ts +7 -0
  117. package/dist/utils/url-safety.d.ts +2 -0
  118. package/dist/vue/agent/view-models.d.ts +98 -0
  119. package/dist/vue/components/TAnchor.d.ts +108 -0
  120. package/dist/vue/components/TBox.d.ts +144 -0
  121. package/dist/vue/components/TCommandPalette.d.ts +587 -0
  122. package/dist/vue/components/TDataTable.d.ts +367 -0
  123. package/dist/vue/components/TDebugOverlay.d.ts +43 -0
  124. package/dist/vue/components/TDialog.d.ts +255 -0
  125. package/dist/vue/components/TFeedback.d.ts +635 -0
  126. package/dist/vue/components/TFlow.d.ts +85 -0
  127. package/dist/vue/components/TForm.d.ts +1230 -0
  128. package/dist/vue/components/TInput.d.ts +545 -0
  129. package/dist/vue/components/TInputBox.d.ts +158 -0
  130. package/dist/vue/components/TJsonEditor.d.ts +165 -0
  131. package/dist/vue/components/TLink.d.ts +239 -0
  132. package/dist/vue/components/TLinkifyText.d.ts +141 -0
  133. package/dist/vue/components/TList.d.ts +119 -0
  134. package/dist/vue/components/TLogLinksPanel.d.ts +238 -0
  135. package/dist/vue/components/TLogMinimap.d.ts +263 -0
  136. package/dist/vue/components/TLogScrollbar.d.ts +236 -0
  137. package/dist/vue/components/TLogSearchBar.d.ts +267 -0
  138. package/dist/vue/components/TLogSearchPager.d.ts +162 -0
  139. package/dist/vue/components/TLogSearchResults.d.ts +200 -0
  140. package/dist/vue/components/TLogView.d.ts +536 -0
  141. package/dist/vue/components/TLogVirtualLinksPanel.d.ts +141 -0
  142. package/dist/vue/components/TLogVirtualSearchResults.d.ts +146 -0
  143. package/dist/vue/components/TMarkdownText.d.ts +134 -0
  144. package/dist/vue/components/TMultilineModal.d.ts +62 -0
  145. package/dist/vue/components/TNavigation.d.ts +324 -0
  146. package/dist/vue/components/TOverlay.d.ts +366 -0
  147. package/dist/vue/components/TPanels.d.ts +244 -0
  148. package/dist/vue/components/TPathPicker.d.ts +213 -0
  149. package/dist/vue/components/TRenderLayer.d.ts +16 -0
  150. package/dist/vue/components/TRenderPlane.d.ts +17 -0
  151. package/dist/vue/components/TSelect.d.ts +335 -0
  152. package/dist/vue/components/TTable.d.ts +317 -0
  153. package/dist/vue/components/TText.d.ts +116 -0
  154. package/dist/vue/components/TThinkingView.d.ts +233 -0
  155. package/dist/vue/components/TToolCallView.d.ts +384 -0
  156. package/dist/vue/components/TTranscriptView.d.ts +221 -0
  157. package/dist/vue/components/TTransition.d.ts +83 -0
  158. package/dist/vue/components/TTree.d.ts +200 -0
  159. package/dist/vue/components/TUserMessageView.d.ts +344 -0
  160. package/dist/vue/components/TView.d.ts +127 -0
  161. package/dist/vue/components/TVirtualList.d.ts +182 -0
  162. package/dist/vue/components/TVirtualMarkdown.d.ts +168 -0
  163. package/dist/vue/components/TVirtualRows.d.ts +247 -0
  164. package/dist/vue/components/TerminalProvider.d.ts +218 -0
  165. package/dist/vue/components/input/host.d.ts +17 -0
  166. package/dist/vue/components/input/plugins/hostPlugin.d.ts +7 -0
  167. package/dist/vue/components/input/plugins/hostPlugin.node.d.ts +15 -0
  168. package/dist/vue/components/input/plugins/mentionUtils.d.ts +17 -0
  169. package/dist/vue/components/input/plugins/nodeMentionPathProvider.d.ts +2 -0
  170. package/dist/vue/components/input/plugins/promptMentionPlugin.d.ts +8 -0
  171. package/dist/vue/components/input/plugins/promptMentionState.d.ts +98 -0
  172. package/dist/vue/components/input/plugins/restrictText.d.ts +27 -0
  173. package/dist/vue/components/input/plugins/types.d.ts +90 -0
  174. package/dist/vue/components/input/utils/inlineText.d.ts +57 -0
  175. package/dist/vue/components/input/utils/inlineTextTokens.d.ts +34 -0
  176. package/dist/vue/components/input/utils/primitives.d.ts +3 -0
  177. package/dist/vue/components/input/utils/wordNavigation.d.ts +6 -0
  178. package/dist/vue/components/link/host.d.ts +15 -0
  179. package/dist/vue/components/list/list-geometry.d.ts +16 -0
  180. package/dist/vue/components/list/list-paint.d.ts +1 -0
  181. package/dist/vue/components/list/scroll-state.d.ts +25 -0
  182. package/dist/vue/components/list/selection-state.d.ts +6 -0
  183. package/dist/vue/components/simple-utils.d.ts +5 -0
  184. package/dist/vue/components/terminal-provider/copy-toast.d.ts +6 -0
  185. package/dist/vue/components/terminal-provider/portals.d.ts +17 -0
  186. package/dist/vue/components/terminal-provider/scheduler.d.ts +29 -0
  187. package/dist/vue/components/terminal-provider/selection-config.d.ts +15 -0
  188. package/dist/vue/components/terminal-provider/utils.d.ts +5 -0
  189. package/dist/vue/composables/use-layout.d.ts +2 -0
  190. package/dist/vue/composables/use-render-node.d.ts +19 -0
  191. package/dist/vue/composables/use-render-stack.d.ts +3 -0
  192. package/dist/vue/composables/use-runtime.d.ts +5 -0
  193. package/dist/vue/composables/use-terminal-node.d.ts +14 -0
  194. package/dist/vue/composables/use-terminal.d.ts +2 -0
  195. package/dist/vue/composables/use-visibility.d.ts +12 -0
  196. package/dist/vue/context.d.ts +153 -0
  197. package/dist/vue/index.d.ts +69 -0
  198. package/dist/vue/injection-key.d.ts +2 -0
  199. package/dist/vue/linkify.d.ts +11 -0
  200. package/dist/vue/log/append-only-log-store.d.ts +2 -0
  201. package/dist/vue/log/tlog-keymap.d.ts +28 -0
  202. package/dist/vue/log/tlog-plugins.d.ts +144 -0
  203. package/dist/vue/log/tlog-session.d.ts +81 -0
  204. package/dist/vue/log/tlog-theme.d.ts +68 -0
  205. package/dist/vue/log/types.d.ts +43 -0
  206. package/dist/vue/log/use-tlog-link-controller.d.ts +31 -0
  207. package/dist/vue/log/use-tlog-retained-index.d.ts +26 -0
  208. package/dist/vue/log/use-tlog-search-controller.d.ts +51 -0
  209. package/dist/vue/log/use-tlog-search-results-page.d.ts +31 -0
  210. package/dist/vue/log/use-tlog-virtual-search-results.d.ts +22 -0
  211. package/dist/vue/markdown/ast.d.ts +3 -0
  212. package/dist/vue/markdown/block-source.d.ts +22 -0
  213. package/dist/vue/markdown/document.d.ts +20 -0
  214. package/dist/vue/markdown/layout.d.ts +21 -0
  215. package/dist/vue/markdown/parser.d.ts +11 -0
  216. package/dist/vue/markdown/render.d.ts +10 -0
  217. package/dist/vue/markdown/theme.d.ts +186 -0
  218. package/dist/vue/markdown/types.d.ts +59 -0
  219. package/dist/vue/overlay.d.ts +27 -0
  220. package/dist/vue/render/context.d.ts +3 -0
  221. package/dist/vue/render/render-manager.d.ts +94 -0
  222. package/dist/vue/router/RouterView.d.ts +25 -0
  223. package/dist/vue/router/composables.d.ts +4 -0
  224. package/dist/vue/router/context.d.ts +4 -0
  225. package/dist/vue/router/router.d.ts +7 -0
  226. package/dist/vue/router/types.d.ts +29 -0
  227. package/dist/vue/scheduler/frame-mailbox.d.ts +55 -0
  228. package/dist/vue/scheduler/frame-scheduler.d.ts +51 -0
  229. package/dist/vue/theme.d.ts +48 -0
  230. package/dist/vue/transcript/hit-regions.d.ts +2 -0
  231. package/dist/vue/transcript/layout.d.ts +20 -0
  232. package/dist/vue/transcript/plain-text.d.ts +2 -0
  233. package/dist/vue/transcript/types.d.ts +122 -0
  234. package/dist/vue/utils/frame-task.d.ts +6 -0
  235. package/dist/vue/utils/rect.d.ts +4 -0
  236. package/dist/vue/utils/row-scroll.d.ts +25 -0
  237. package/dist/vue/utils/style-cache.d.ts +3 -0
  238. package/dist/vue/utils/text.d.ts +27 -0
  239. package/dist/vue/utils/wheel-scroll.d.ts +18 -0
  240. package/dist/vue.cjs +23668 -0
  241. package/dist/vue.d.cts +1 -0
  242. package/dist/vue.d.ts +1 -0
  243. package/dist/vue.js +3957 -0
  244. package/dist/{wheel-scroll-CiNsasLf.js → wheel-scroll-vBRaQ7p-.js} +404 -405
  245. package/package.json +185 -35
  246. package/dist/path-suggest-BeV6n7m-.js +0 -3
  247. package/dist/sgr-Tko2EQ0U.js +0 -484
  248. package/dist/theme-Bsm65g8k.d.ts +0 -32
  249. package/dist/types-ClIV2h1o.d.ts +0 -103
package/dist/markdown.js CHANGED
@@ -1,889 +1,9 @@
1
- import { EventZIndexContextKey, applyWheelScroll, createWheelScrollState, forEachTextCellSegment, intersectRect, normalizeCellRect, repeatChar, resetWheelScrollState, sanitizeInlineText, sanitizeTextBlock, sliceByCellsRange, spaces, textCellWidth, translateRect, useLayout, useRenderNode, useTerminal, useTerminalNode, useVisibility } from "./wheel-scroll-CiNsasLf.js";
2
- import { computed, defineComponent, getCurrentInstance, h, inject, markRaw, onBeforeUnmount, ref, shallowRef, watch, watchEffect } from "vue";
3
- import { getMarkdown, parseMarkdownToStructure } from "stream-markdown-parser";
1
+ import "./hyperlink-Bn02UXJb.js";
2
+ import { intersectRect, translateRect, useLayout, useRenderNode, useTerminal, useVisibility, withTextWidthProvider } from "./wheel-scroll-vBRaQ7p-.js";
3
+ import "./terminal-selection-DBinO6p5.js";
4
+ import { TVirtualMarkdown, buildMarkdownBlocks, buildMarkdownVisualRows, createMarkdownBlockSource, createTuiMarkdownParser, isSafeMarkdownLink, layoutMarkdownBlocks, markdownThemeSignature, paintMarkdownVisualRow } from "./block-source-CKUOnkgT.js";
5
+ import { computed, defineComponent, getCurrentInstance, h, markRaw, onBeforeUnmount, shallowRef, watch } from "vue";
4
6
 
5
- //#region src/vue/markdown/parser.ts
6
- const RELATIVE_LINK_PREFIXES = [
7
- "#",
8
- "/",
9
- "./",
10
- "../"
11
- ];
12
- const SAFE_PROTOCOLS = new Set([
13
- "http:",
14
- "https:",
15
- "mailto:"
16
- ]);
17
- const markdownInstanceCache = new Map();
18
- const MAX_MARKDOWN_INSTANCE_CACHE = 32;
19
- const SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
20
- function hasControlChars(value) {
21
- for (const ch of value) {
22
- const code = ch.codePointAt(0);
23
- if (code >= 0 && code <= 31 || code >= 127 && code <= 159) return true;
24
- }
25
- return false;
26
- }
27
- function isSafeMarkdownLink(url) {
28
- const raw = String(url ?? "").trim();
29
- if (!raw) return false;
30
- if (hasControlChars(raw)) return false;
31
- if (raw.startsWith("//")) return false;
32
- const value = raw.toLowerCase();
33
- if (value.startsWith("javascript:")) return false;
34
- if (value.startsWith("data:")) return false;
35
- if (RELATIVE_LINK_PREFIXES.some((prefix) => raw.startsWith(prefix))) return true;
36
- if (!SCHEME_RE.test(raw)) return true;
37
- try {
38
- const parsed = new URL(raw);
39
- return SAFE_PROTOCOLS.has(parsed.protocol);
40
- } catch {
41
- return false;
42
- }
43
- }
44
- function markdownInstanceCacheKey(config) {
45
- var _config$customHtmlTag;
46
- return JSON.stringify({ customHtmlTags: [...(config === null || config === void 0 || (_config$customHtmlTag = config.customHtmlTags) === null || _config$customHtmlTag === void 0 ? void 0 : _config$customHtmlTag.filter(Boolean)) ?? []].sort() });
47
- }
48
- function getCachedMarkdownInstance(config) {
49
- var _config$customHtmlTag2;
50
- const cacheKey = markdownInstanceCacheKey(config);
51
- const cached = markdownInstanceCache.get(cacheKey);
52
- if (cached) return cached;
53
- const customHtmlTags = config === null || config === void 0 || (_config$customHtmlTag2 = config.customHtmlTags) === null || _config$customHtmlTag2 === void 0 ? void 0 : _config$customHtmlTag2.filter(Boolean);
54
- const created = getMarkdown(`vue-tui-markdown:${cacheKey}`, { customHtmlTags });
55
- if (markdownInstanceCache.size >= MAX_MARKDOWN_INSTANCE_CACHE) markdownInstanceCache.clear();
56
- markdownInstanceCache.set(cacheKey, created);
57
- return created;
58
- }
59
- function createTuiMarkdownParser(config) {
60
- var _config$customHtmlTag3;
61
- const customHtmlTags = config === null || config === void 0 || (_config$customHtmlTag3 = config.customHtmlTags) === null || _config$customHtmlTag3 === void 0 ? void 0 : _config$customHtmlTag3.filter(Boolean);
62
- const md = getCachedMarkdownInstance(config);
63
- function parse(content, final) {
64
- return parseMarkdownToStructure(content, md, {
65
- final,
66
- customHtmlTags,
67
- requireClosingStrong: final || !(config === null || config === void 0 ? void 0 : config.streaming),
68
- validateLink: isSafeMarkdownLink
69
- });
70
- }
71
- return { parse };
72
- }
73
-
74
- //#endregion
75
- //#region src/vue/markdown/ast.ts
76
- const EMPTY_PREFIX = Object.freeze([]);
77
- const HARD_BREAK_SEGMENT = Object.freeze({
78
- text: "",
79
- hardBreak: true
80
- });
81
- function mergeStyle$1(base, overlay) {
82
- if (!base) return overlay;
83
- if (!overlay) return base;
84
- return {
85
- ...base,
86
- ...overlay
87
- };
88
- }
89
- function trimBlockArray(blocks) {
90
- var _blocks$start, _blocks;
91
- let start = 0;
92
- let end = blocks.length;
93
- while (start < end && ((_blocks$start = blocks[start]) === null || _blocks$start === void 0 ? void 0 : _blocks$start.type) === "blank") start++;
94
- while (end > start && ((_blocks = blocks[end - 1]) === null || _blocks === void 0 ? void 0 : _blocks.type) === "blank") end--;
95
- return blocks.slice(start, end);
96
- }
97
- function nodeChildren(node) {
98
- return "children" in node && Array.isArray(node.children) ? node.children : [];
99
- }
100
- function stringProp(node, key) {
101
- const value = node[key];
102
- return typeof value === "string" ? value : "";
103
- }
104
- function booleanProp(node, key) {
105
- return node[key] === true;
106
- }
107
- function pushTextSegments(out, text, style) {
108
- if (!text) return;
109
- const normalized = sanitizeTextBlock(text);
110
- if (!normalized) return;
111
- const parts = normalized.split("\n");
112
- for (let i = 0; i < parts.length; i++) {
113
- const part = sanitizeInlineText(parts[i] ?? "");
114
- if (part) out.push(style ? {
115
- text: part,
116
- style
117
- } : { text: part });
118
- if (i < parts.length - 1) out.push(HARD_BREAK_SEGMENT);
119
- }
120
- }
121
- function sanitizeCodeBlockText(text, tabSize = 4) {
122
- const normalized = String(text ?? "").replace(/\r\n?/g, "\n").replace(/\t/g, spaces(tabSize)).replace(/\u00a0/g, " ");
123
- let out = "";
124
- for (const ch of normalized) {
125
- const code = ch.charCodeAt(0);
126
- if (code >= 0 && code <= 8 || code === 11 || code === 12) continue;
127
- if (code >= 14 && code <= 31 || code === 127) continue;
128
- out += ch;
129
- }
130
- return out;
131
- }
132
- function inlineNodeSegments(nodes, theme, inheritedStyle) {
133
- const out = [];
134
- for (const node of nodes) switch (node.type) {
135
- case "text":
136
- pushTextSegments(out, stringProp(node, "content"), inheritedStyle);
137
- break;
138
- case "inline":
139
- case "paragraph":
140
- case "heading":
141
- case "list_item":
142
- case "blockquote":
143
- out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle));
144
- break;
145
- case "strong":
146
- out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.strong)));
147
- break;
148
- case "emphasis":
149
- out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.emphasis)));
150
- break;
151
- case "strikethrough":
152
- out.push(...inlineNodeSegments(nodeChildren(node), theme, mergeStyle$1(inheritedStyle, theme.strikethrough)));
153
- break;
154
- case "highlight":
155
- case "insert":
156
- case "subscript":
157
- case "superscript":
158
- out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle));
159
- break;
160
- case "inline_code":
161
- pushTextSegments(out, stringProp(node, "code"), mergeStyle$1(inheritedStyle, theme.inlineCode));
162
- break;
163
- case "hardbreak":
164
- out.push(HARD_BREAK_SEGMENT);
165
- break;
166
- case "link": {
167
- const href = stringProp(node, "href");
168
- const safeHref = href && isSafeMarkdownLink(href) ? href : "";
169
- const linkStyle = safeHref ? mergeStyle$1(inheritedStyle, {
170
- ...theme.link,
171
- href: safeHref
172
- }) : inheritedStyle;
173
- const children = nodeChildren(node);
174
- if (children.length) out.push(...inlineNodeSegments(children, theme, linkStyle));
175
- else pushTextSegments(out, stringProp(node, "text"), linkStyle);
176
- break;
177
- }
178
- case "image":
179
- pushTextSegments(out, stringProp(node, "alt") || stringProp(node, "src"), mergeStyle$1(inheritedStyle, theme.link));
180
- break;
181
- case "checkbox":
182
- case "checkbox_input":
183
- pushTextSegments(out, booleanProp(node, "checked") ? "[x]" : "[ ]", inheritedStyle);
184
- break;
185
- case "emoji":
186
- pushTextSegments(out, stringProp(node, "markup") || stringProp(node, "name"), inheritedStyle);
187
- break;
188
- case "math_inline":
189
- pushTextSegments(out, stringProp(node, "content"), mergeStyle$1(inheritedStyle, theme.inlineCode));
190
- break;
191
- case "reference":
192
- case "footnote_reference":
193
- case "footnote_anchor":
194
- pushTextSegments(out, node.raw, inheritedStyle);
195
- break;
196
- case "html_inline":
197
- pushTextSegments(out, stringProp(node, "content"), mergeStyle$1(inheritedStyle, theme.html));
198
- break;
199
- case "html_block":
200
- pushTextSegments(out, stringProp(node, "content"), mergeStyle$1(inheritedStyle, theme.html));
201
- break;
202
- default:
203
- if (nodeChildren(node).length) {
204
- out.push(...inlineNodeSegments(nodeChildren(node), theme, inheritedStyle));
205
- break;
206
- }
207
- pushTextSegments(out, node.raw, inheritedStyle);
208
- break;
209
- }
210
- return out;
211
- }
212
- function inlineBlock(key, segments, context) {
213
- return {
214
- type: "inline",
215
- key,
216
- segments,
217
- prefixSegments: context.prefixSegments,
218
- continuationPrefixSegments: context.continuationPrefixSegments
219
- };
220
- }
221
- function blockFromParagraph(node, key, context, theme) {
222
- return inlineBlock(key, inlineNodeSegments(node.children, theme), context);
223
- }
224
- function blockFromHeading(node, key, context, theme) {
225
- const level = Math.min(6, Math.max(1, Math.floor(node.level || 1)));
226
- return inlineBlock(key, inlineNodeSegments(node.children, theme, theme.heading[level - 1]), context);
227
- }
228
- function blockFromCodeBlock(node, key, context, theme) {
229
- return {
230
- type: "code_block",
231
- key,
232
- lines: sanitizeCodeBlockText(node.code ?? "").split("\n"),
233
- style: theme.codeBlock,
234
- prefixSegments: context.prefixSegments,
235
- continuationPrefixSegments: context.continuationPrefixSegments
236
- };
237
- }
238
- function blockFromHtmlBlock(node, key, context, theme) {
239
- return inlineBlock(key, inlineNodeSegments([{
240
- type: "html_block",
241
- raw: node.raw,
242
- content: node.content
243
- }], theme), context);
244
- }
245
- function childSequenceToBlocks(nodes, context, theme, keyPrefix) {
246
- const out = [];
247
- let appended = false;
248
- for (let i = 0; i < nodes.length; i++) {
249
- var _out, _childBlocks$;
250
- const childBlocks = trimBlockArray(nodeToBlocks(nodes[i], context, theme, `${keyPrefix}-${i}`));
251
- if (!childBlocks.length) continue;
252
- if (appended && ((_out = out[out.length - 1]) === null || _out === void 0 ? void 0 : _out.type) !== "blank" && ((_childBlocks$ = childBlocks[0]) === null || _childBlocks$ === void 0 ? void 0 : _childBlocks$.type) !== "blank") out.push({
253
- type: "blank",
254
- key: `${keyPrefix}-${i}-gap`
255
- });
256
- out.push(...childBlocks);
257
- appended = true;
258
- }
259
- return out;
260
- }
261
- function listItemBlocks(item, index, ordered, start, context, theme, keyPrefix) {
262
- const markerText = ordered ? `${start + index}. ` : "- ";
263
- const markerCells = textCellWidth(markerText);
264
- const markerSegment = {
265
- text: markerText,
266
- style: theme.listMarker
267
- };
268
- const indentSegment = { text: spaces(markerCells) };
269
- const firstContext = {
270
- prefixSegments: [...context.prefixSegments, markerSegment],
271
- continuationPrefixSegments: [...context.continuationPrefixSegments, indentSegment]
272
- };
273
- const nestedContext = {
274
- prefixSegments: [...context.continuationPrefixSegments, indentSegment],
275
- continuationPrefixSegments: [...context.continuationPrefixSegments, indentSegment]
276
- };
277
- if (!item.children.length) return [inlineBlock(`${keyPrefix}-empty`, [], firstContext)];
278
- const out = [];
279
- let usedLead = false;
280
- for (let i = 0; i < item.children.length; i++) {
281
- var _out2, _childBlocks$2, _out3, _childBlocks$3;
282
- const child = item.children[i];
283
- const useLeadContext = !usedLead && (child.type === "paragraph" || child.type === "heading" || child.type === "code_block");
284
- if (!usedLead && !useLeadContext) {
285
- out.push(inlineBlock(`${keyPrefix}-marker`, [], firstContext));
286
- usedLead = true;
287
- }
288
- const childBlocks = trimBlockArray(nodeToBlocks(child, useLeadContext && !usedLead ? firstContext : nestedContext, theme, `${keyPrefix}-${i}`));
289
- if (!childBlocks.length) continue;
290
- const startsNestedList = child.type === "list" && out.length > 0 && ((_out2 = out[out.length - 1]) === null || _out2 === void 0 ? void 0 : _out2.type) === "inline" && ((_childBlocks$2 = childBlocks[0]) === null || _childBlocks$2 === void 0 ? void 0 : _childBlocks$2.type) === "inline";
291
- if (out.length && ((_out3 = out[out.length - 1]) === null || _out3 === void 0 ? void 0 : _out3.type) !== "blank" && ((_childBlocks$3 = childBlocks[0]) === null || _childBlocks$3 === void 0 ? void 0 : _childBlocks$3.type) !== "blank" && !startsNestedList) out.push({
292
- type: "blank",
293
- key: `${keyPrefix}-${i}-gap`
294
- });
295
- out.push(...childBlocks);
296
- usedLead = true;
297
- }
298
- if (!out.length) out.push(inlineBlock(`${keyPrefix}-fallback`, [], firstContext));
299
- return out;
300
- }
301
- function listBlocks(node, context, theme, keyPrefix) {
302
- const out = [];
303
- for (let i = 0; i < node.items.length; i++) out.push(...listItemBlocks(node.items[i], i, node.ordered, node.start ?? 1, context, theme, `${keyPrefix}-${i}`));
304
- return out;
305
- }
306
- function nodeToBlocks(node, context, theme, keyPrefix) {
307
- switch (node.type) {
308
- case "paragraph":
309
- if ("children" in node && Array.isArray(node.children)) return [blockFromParagraph(node, keyPrefix, context, theme)];
310
- break;
311
- case "heading":
312
- if ("children" in node && Array.isArray(node.children) && "level" in node) return [blockFromHeading(node, keyPrefix, context, theme)];
313
- break;
314
- case "code_block":
315
- if ("code" in node) return [blockFromCodeBlock(node, keyPrefix, context, theme)];
316
- break;
317
- case "thematic_break": return [{
318
- type: "thematic_break",
319
- key: keyPrefix,
320
- char: "─",
321
- style: theme.thematicBreak,
322
- prefixSegments: context.prefixSegments
323
- }];
324
- case "blockquote": {
325
- const quoteSegment = {
326
- text: "│ ",
327
- style: theme.blockquote
328
- };
329
- const quoteContext = {
330
- prefixSegments: [...context.prefixSegments, quoteSegment],
331
- continuationPrefixSegments: [...context.continuationPrefixSegments, quoteSegment]
332
- };
333
- return childSequenceToBlocks(nodeChildren(node), quoteContext, theme, keyPrefix);
334
- }
335
- case "list":
336
- if ("items" in node && Array.isArray(node.items)) return listBlocks(node, context, theme, keyPrefix);
337
- break;
338
- case "inline": return [inlineBlock(keyPrefix, inlineNodeSegments(nodeChildren(node), theme), context)];
339
- case "html_block":
340
- if ("content" in node) return [blockFromHtmlBlock(node, keyPrefix, context, theme)];
341
- break;
342
- case "text": return [inlineBlock(keyPrefix, inlineNodeSegments([node], theme), context)];
343
- }
344
- if (nodeChildren(node).length) return childSequenceToBlocks(nodeChildren(node), context, theme, keyPrefix);
345
- return [inlineBlock(keyPrefix, inlineNodeSegments([node], theme), context)];
346
- }
347
- function markdownAstToBlocks(nodes, theme) {
348
- const blocks = trimBlockArray(childSequenceToBlocks(nodes, {
349
- prefixSegments: EMPTY_PREFIX,
350
- continuationPrefixSegments: EMPTY_PREFIX
351
- }, theme, "md"));
352
- return blocks.length ? blocks : [{
353
- type: "blank",
354
- key: "md-empty"
355
- }];
356
- }
357
-
358
- //#endregion
359
- //#region src/vue/markdown/layout.ts
360
- function toVisualSegment(segment) {
361
- if (!segment.text) return null;
362
- const cells = textCellWidth(segment.text);
363
- if (cells <= 0) return null;
364
- return {
365
- text: segment.text,
366
- style: segment.style,
367
- cells
368
- };
369
- }
370
- function segmentsPlainText(segments) {
371
- return segments.map((segment) => segment.text).join("");
372
- }
373
- function segmentsCellWidth(segments) {
374
- let cells = 0;
375
- for (const segment of segments) {
376
- if (segment.hardBreak || !segment.text) continue;
377
- cells += textCellWidth(segment.text);
378
- }
379
- return cells;
380
- }
381
- function normalizeInlineSegments(segments) {
382
- const lines = [[]];
383
- for (const segment of segments) {
384
- if (segment.hardBreak) {
385
- lines.push([]);
386
- continue;
387
- }
388
- lines[lines.length - 1].push(segment);
389
- }
390
- return lines;
391
- }
392
- function clipInlineSegmentsToWidth(segments, width) {
393
- let remaining = Math.max(0, Math.floor(width));
394
- if (remaining <= 0) return [];
395
- const out = [];
396
- for (const segment of segments) {
397
- if (segment.hardBreak || !segment.text || remaining <= 0) continue;
398
- const piece = sliceByCellsRange(segment.text, 0, remaining);
399
- const cells = textCellWidth(piece);
400
- if (!piece || cells <= 0) continue;
401
- out.push(segment.style ? {
402
- text: piece,
403
- style: segment.style
404
- } : { text: piece });
405
- remaining -= cells;
406
- }
407
- return out;
408
- }
409
- function wrapLineSegments(source, prefixSegments, continuationPrefixSegments, width) {
410
- const rows = [];
411
- const prefixCells = segmentsCellWidth(prefixSegments);
412
- const continuationPrefixCells = segmentsCellWidth(continuationPrefixSegments);
413
- const firstPrefix = prefixCells >= width ? clipInlineSegmentsToWidth(prefixSegments, width) : prefixSegments;
414
- const firstPrefixCells = segmentsCellWidth(firstPrefix);
415
- const continuationPrefix = continuationPrefixCells >= width ? clipInlineSegmentsToWidth(continuationPrefixSegments, Math.max(0, width - 1)) : continuationPrefixSegments;
416
- const trimmedContinuationPrefixCells = segmentsCellWidth(continuationPrefix);
417
- const maxFirst = Math.max(0, width - firstPrefixCells);
418
- const maxNext = Math.max(0, width - trimmedContinuationPrefixCells);
419
- const openRow = (useContinuation) => {
420
- const prefix = useContinuation ? continuationPrefix : firstPrefix;
421
- const renderedPrefix = prefix.map(toVisualSegment).filter(Boolean);
422
- return {
423
- useContinuation,
424
- segments: [...renderedPrefix],
425
- remaining: useContinuation ? maxNext : maxFirst
426
- };
427
- };
428
- const openDegradedRow = () => ({
429
- useContinuation: true,
430
- segments: [],
431
- remaining: width
432
- });
433
- let row = openRow(false);
434
- if (!source.length) {
435
- rows.push(row.segments);
436
- return rows;
437
- }
438
- const pushRow = () => {
439
- rows.push(row.segments);
440
- row = openRow(true);
441
- };
442
- const prefixLengthForRow = () => row.useContinuation ? continuationPrefix.length : firstPrefix.length;
443
- const rowHasBody = () => row.segments.length > prefixLengthForRow();
444
- for (const segment of source) {
445
- let aborted = false;
446
- forEachTextCellSegment(segment.text, (piece) => {
447
- while (true) {
448
- if (row.remaining <= 0) {
449
- pushRow();
450
- continue;
451
- }
452
- if (piece.cells > row.remaining) {
453
- if (rowHasBody()) {
454
- pushRow();
455
- continue;
456
- }
457
- if (!row.useContinuation) {
458
- rows.push(row.segments);
459
- row = openDegradedRow();
460
- continue;
461
- }
462
- if (piece.cells <= width) {
463
- row = openDegradedRow();
464
- continue;
465
- }
466
- aborted = true;
467
- return false;
468
- }
469
- row.segments.push({
470
- text: piece.text,
471
- style: segment.style,
472
- cells: piece.cells
473
- });
474
- row.remaining -= piece.cells;
475
- return void 0;
476
- }
477
- });
478
- if (aborted) continue;
479
- }
480
- rows.push(row.segments);
481
- return rows;
482
- }
483
- function inlineBlockRows(block, width) {
484
- const rows = [];
485
- const lines = normalizeInlineSegments(block.segments);
486
- const prefix = block.prefixSegments ?? [];
487
- const continuationPrefix = block.continuationPrefixSegments ?? prefix;
488
- let rowInBlock = 0;
489
- for (const line of lines) {
490
- const wrapped = wrapLineSegments(line, prefix, continuationPrefix, width);
491
- for (const visualSegments of wrapped) {
492
- rows.push({
493
- key: `${block.key}:${rowInBlock}`,
494
- blockKey: block.key,
495
- rowInBlock,
496
- plainText: segmentsPlainText(visualSegments),
497
- segments: visualSegments
498
- });
499
- rowInBlock++;
500
- }
501
- }
502
- return rows.length ? rows : [{
503
- key: `${block.key}:0`,
504
- blockKey: block.key,
505
- rowInBlock: 0,
506
- plainText: "",
507
- segments: prefix.map(toVisualSegment).filter(Boolean)
508
- }];
509
- }
510
- function codeBlockRows(block, width) {
511
- const rows = [];
512
- const prefix = block.prefixSegments ?? [];
513
- const continuationPrefix = block.continuationPrefixSegments ?? prefix;
514
- const lineSegments = block.lines.length ? block.lines : [""];
515
- let rowInBlock = 0;
516
- for (const line of lineSegments) {
517
- const sourceSegments = [{
518
- text: line,
519
- style: block.style
520
- }];
521
- const wrapped = wrapLineSegments(sourceSegments, prefix, continuationPrefix, width);
522
- for (const visualSegments of wrapped) {
523
- rows.push({
524
- key: `${block.key}:${rowInBlock}`,
525
- blockKey: block.key,
526
- rowInBlock,
527
- plainText: segmentsPlainText(visualSegments),
528
- segments: visualSegments
529
- });
530
- rowInBlock++;
531
- }
532
- }
533
- return rows;
534
- }
535
- function thematicBreakRows(block, width) {
536
- const prefix = (block.prefixSegments ?? []).map(toVisualSegment).filter(Boolean);
537
- const prefixCells = prefix.reduce((sum, segment) => sum + segment.cells, 0);
538
- const fill = Math.max(0, width - prefixCells);
539
- const bar = fill > 0 ? [{
540
- text: repeatChar(block.char ?? "─", fill),
541
- style: block.style,
542
- cells: fill
543
- }] : [];
544
- return [{
545
- key: `${block.key}:0`,
546
- blockKey: block.key,
547
- rowInBlock: 0,
548
- plainText: segmentsPlainText([...prefix, ...bar]),
549
- segments: [...prefix, ...bar]
550
- }];
551
- }
552
- function styleSignature(style) {
553
- if (!style) return "";
554
- return [
555
- style.fg ?? "",
556
- style.bg ?? "",
557
- style.bold ? "1" : "0",
558
- style.dim ? "1" : "0",
559
- style.italic ? "1" : "0",
560
- style.underline ? "1" : "0",
561
- style.inverse ? "1" : "0",
562
- style.href ?? ""
563
- ].join("");
564
- }
565
- function inlineSegmentSignature(segment) {
566
- return [
567
- segment.text,
568
- segment.hardBreak ? "1" : "0",
569
- styleSignature(segment.style)
570
- ].join("");
571
- }
572
- function inlineSegmentsSignature(segments) {
573
- return (segments ?? []).map(inlineSegmentSignature).join("");
574
- }
575
- function blockLayoutSignature(block) {
576
- switch (block.type) {
577
- case "inline": return [
578
- block.type,
579
- inlineSegmentsSignature(block.segments),
580
- inlineSegmentsSignature(block.prefixSegments),
581
- inlineSegmentsSignature(block.continuationPrefixSegments)
582
- ].join("");
583
- case "code_block": return [
584
- block.type,
585
- block.lines.join(""),
586
- styleSignature(block.style),
587
- inlineSegmentsSignature(block.prefixSegments),
588
- inlineSegmentsSignature(block.continuationPrefixSegments)
589
- ].join("");
590
- case "thematic_break": return [
591
- block.type,
592
- block.char ?? "",
593
- styleSignature(block.style),
594
- inlineSegmentsSignature(block.prefixSegments)
595
- ].join("");
596
- case "blank": return block.type;
597
- }
598
- }
599
- function layoutMarkdownBlock(block, width) {
600
- switch (block.type) {
601
- case "inline": return inlineBlockRows(block, width);
602
- case "code_block": return codeBlockRows(block, width);
603
- case "thematic_break": return thematicBreakRows(block, width);
604
- case "blank": return [{
605
- key: `${block.key}:0`,
606
- blockKey: block.key,
607
- rowInBlock: 0,
608
- plainText: "",
609
- segments: []
610
- }];
611
- }
612
- }
613
- function layoutMarkdownBlocksCached(blocks, width, previous) {
614
- const normalizedWidth = Math.max(0, Math.floor(width));
615
- if (normalizedWidth <= 0) return {
616
- rows: [],
617
- cache: {
618
- width: normalizedWidth,
619
- entries: new Map()
620
- }
621
- };
622
- const rows = [];
623
- const entries = new Map();
624
- const previousEntries = (previous === null || previous === void 0 ? void 0 : previous.width) === normalizedWidth ? previous.entries : void 0;
625
- for (let index = 0; index < blocks.length; index++) {
626
- const block = blocks[index];
627
- const cacheKey = `${index}\u0000${block.key}`;
628
- const signature = blockLayoutSignature(block);
629
- const cached = previousEntries === null || previousEntries === void 0 ? void 0 : previousEntries.get(cacheKey);
630
- const blockRows = (cached === null || cached === void 0 ? void 0 : cached.signature) === signature ? cached.rows : layoutMarkdownBlock(block, normalizedWidth);
631
- if (blockRows.length) {
632
- rows.push(...blockRows);
633
- entries.set(cacheKey, {
634
- signature,
635
- rows: blockRows
636
- });
637
- }
638
- }
639
- if (!rows.length) {
640
- const emptyRows = [{
641
- key: "md-empty:0",
642
- blockKey: "md-empty",
643
- rowInBlock: 0,
644
- plainText: "",
645
- segments: []
646
- }];
647
- entries.set("0\0md-empty", {
648
- signature: "empty",
649
- rows: emptyRows
650
- });
651
- rows.push(...emptyRows);
652
- }
653
- return {
654
- rows,
655
- cache: {
656
- width: normalizedWidth,
657
- entries
658
- }
659
- };
660
- }
661
- function layoutMarkdownBlocks(blocks, width) {
662
- return layoutMarkdownBlocksCached(blocks, width).rows;
663
- }
664
-
665
- //#endregion
666
- //#region src/vue/markdown/theme.ts
667
- function freezeStyle(style) {
668
- return Object.freeze(style);
669
- }
670
- function mergeFrozenStyle(base, overlay) {
671
- if (!overlay) return base;
672
- return freezeStyle({
673
- ...base,
674
- ...overlay
675
- });
676
- }
677
- const DEFAULT_HEADING = Object.freeze([
678
- freezeStyle({
679
- bold: true,
680
- fg: "cyanBright"
681
- }),
682
- freezeStyle({
683
- bold: true,
684
- fg: "cyanBright"
685
- }),
686
- freezeStyle({
687
- bold: true,
688
- fg: "blueBright"
689
- }),
690
- freezeStyle({
691
- bold: true,
692
- fg: "blueBright"
693
- }),
694
- freezeStyle({ bold: true }),
695
- freezeStyle({ bold: true })
696
- ]);
697
- const DEFAULT_TUI_MARKDOWN_THEME = Object.freeze({
698
- heading: DEFAULT_HEADING,
699
- strong: freezeStyle({ bold: true }),
700
- emphasis: freezeStyle({ italic: true }),
701
- strikethrough: freezeStyle({ dim: true }),
702
- inlineCode: freezeStyle({ fg: "yellowBright" }),
703
- link: freezeStyle({
704
- fg: "blueBright",
705
- underline: true
706
- }),
707
- blockquote: freezeStyle({ dim: true }),
708
- listMarker: freezeStyle({
709
- fg: "cyanBright",
710
- bold: true
711
- }),
712
- codeBlock: freezeStyle({ fg: "yellowBright" }),
713
- thematicBreak: freezeStyle({ dim: true }),
714
- html: freezeStyle({ dim: true })
715
- });
716
- function stableSerialize(value) {
717
- if (value == null) return "null";
718
- if (typeof value !== "object") return JSON.stringify(value);
719
- if (Array.isArray(value)) return `[${value.map((item) => stableSerialize(item)).join(",")}]`;
720
- const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
721
- return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableSerialize(item)}`).join(",")}}`;
722
- }
723
- function markdownThemeSignature(overrides) {
724
- return stableSerialize(overrides ?? null);
725
- }
726
- function resolveTuiMarkdownTheme(overrides) {
727
- if (!overrides) return DEFAULT_TUI_MARKDOWN_THEME;
728
- const heading = DEFAULT_TUI_MARKDOWN_THEME.heading.map((style, index) => {
729
- var _overrides$heading;
730
- return mergeFrozenStyle(style, (_overrides$heading = overrides.heading) === null || _overrides$heading === void 0 ? void 0 : _overrides$heading[index]);
731
- });
732
- return Object.freeze({
733
- heading: Object.freeze(heading),
734
- strong: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.strong, overrides.strong),
735
- emphasis: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.emphasis, overrides.emphasis),
736
- strikethrough: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.strikethrough, overrides.strikethrough),
737
- inlineCode: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.inlineCode, overrides.inlineCode),
738
- link: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.link, overrides.link),
739
- blockquote: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.blockquote, overrides.blockquote),
740
- listMarker: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.listMarker, overrides.listMarker),
741
- codeBlock: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.codeBlock, overrides.codeBlock),
742
- thematicBreak: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.thematicBreak, overrides.thematicBreak),
743
- html: mergeFrozenStyle(DEFAULT_TUI_MARKDOWN_THEME.html, overrides.html)
744
- });
745
- }
746
-
747
- //#endregion
748
- //#region src/vue/markdown/document.ts
749
- /**
750
- * This helper always builds visual rows from the full source string. Components
751
- * that need streaming reuse can call buildMarkdownBlocks and cache block layout.
752
- */
753
- function buildMarkdownBlocks(content, parser, options) {
754
- const theme = resolveTuiMarkdownTheme(options === null || options === void 0 ? void 0 : options.theme);
755
- try {
756
- const nodes = parser.parse(content, (options === null || options === void 0 ? void 0 : options.final) ?? true);
757
- const blocks = markdownAstToBlocks(nodes, theme);
758
- return {
759
- nodes,
760
- blocks
761
- };
762
- } catch (error) {
763
- console.warn("[vue-tui] Markdown parse failed; falling back to plain text rendering.", error);
764
- return {
765
- nodes: [],
766
- blocks: plainTextFallbackBlocks(content)
767
- };
768
- }
769
- }
770
- function buildMarkdownVisualRows(content, width, parser, options) {
771
- const { blocks } = buildMarkdownBlocks(content, parser, options);
772
- return layoutMarkdownBlocks(blocks, width);
773
- }
774
- function plainTextFallbackBlocks(content) {
775
- const normalized = sanitizeTextBlock(content);
776
- const lines = normalized.split("\n");
777
- const blocks = [];
778
- for (let index = 0; index < lines.length; index++) {
779
- const line = sanitizeInlineText(lines[index] ?? "");
780
- if (!line) {
781
- blocks.push({
782
- type: "blank",
783
- key: `md-fallback:${index}`
784
- });
785
- continue;
786
- }
787
- blocks.push({
788
- type: "inline",
789
- key: `md-fallback:${index}`,
790
- segments: [{ text: line }]
791
- });
792
- }
793
- return blocks.length ? blocks : [{
794
- type: "blank",
795
- key: "md-fallback:0"
796
- }];
797
- }
798
-
799
- //#endregion
800
- //#region src/vue/markdown/render.ts
801
- const mergedStyleCache = new WeakMap();
802
- function mergeStyle(base, overlay) {
803
- if (!overlay) return base;
804
- let bucket = mergedStyleCache.get(base);
805
- if (!bucket) {
806
- bucket = new WeakMap();
807
- mergedStyleCache.set(base, bucket);
808
- }
809
- const cached = bucket.get(overlay);
810
- if (cached) return cached;
811
- const merged = Object.freeze({
812
- ...base,
813
- ...overlay
814
- });
815
- bucket.set(overlay, merged);
816
- return merged;
817
- }
818
- function paintMarkdownVisualRow(terminal, row, options) {
819
- const clipStart = Math.max(0, Math.floor(options.clipStart ?? 0));
820
- const clipEnd = clipStart + options.w;
821
- if (!row) {
822
- if (options.clear !== false) terminal.write(spaces(options.w), {
823
- x: options.x,
824
- y: options.y,
825
- style: options.baseStyle
826
- });
827
- return;
828
- }
829
- let logicalX = 0;
830
- let used = 0;
831
- for (const segment of row.segments) {
832
- if (used >= options.w || !segment.text) {
833
- logicalX += segment.cells;
834
- continue;
835
- }
836
- const segmentStart = logicalX;
837
- const segmentEnd = logicalX + segment.cells;
838
- logicalX += segment.cells;
839
- if (segmentEnd <= clipStart || segmentStart >= clipEnd) continue;
840
- const segmentStyle = mergeStyle(options.baseStyle, segment.style);
841
- let pieceStart = segmentStart;
842
- forEachTextCellSegment(segment.text, (piece) => {
843
- if (used >= options.w) return false;
844
- const pieceEnd = pieceStart + piece.cells;
845
- const visibleStart = Math.max(pieceStart, clipStart);
846
- const visibleEnd = Math.min(pieceEnd, clipEnd);
847
- pieceStart = pieceEnd;
848
- if (visibleEnd <= visibleStart) return;
849
- const expectedUsed = visibleStart - clipStart;
850
- if (expectedUsed > used) {
851
- const pad$1 = Math.min(expectedUsed - used, options.w - used);
852
- terminal.write(spaces(pad$1), {
853
- x: options.x + used,
854
- y: options.y,
855
- style: options.baseStyle
856
- });
857
- used += pad$1;
858
- }
859
- if (visibleStart === pieceStart - piece.cells && visibleEnd === pieceEnd) {
860
- terminal.write(piece.text, {
861
- x: options.x + used,
862
- y: options.y,
863
- style: segmentStyle
864
- });
865
- used += piece.cells;
866
- return;
867
- }
868
- const pad = Math.min(visibleEnd - visibleStart, options.w - used);
869
- if (pad > 0) {
870
- terminal.write(spaces(pad), {
871
- x: options.x + used,
872
- y: options.y,
873
- style: segmentStyle
874
- });
875
- used += pad;
876
- }
877
- });
878
- }
879
- if (used < options.w && options.clear !== false) terminal.write(spaces(options.w - used), {
880
- x: options.x + used,
881
- y: options.y,
882
- style: options.baseStyle
883
- });
884
- }
885
-
886
- //#endregion
887
7
  //#region src/vue/components/TMarkdownText.ts
888
8
  const TMarkdownText = defineComponent({
889
9
  name: "TMarkdownText",
@@ -939,7 +59,7 @@ const TMarkdownText = defineComponent({
939
59
  },
940
60
  setup(props) {
941
61
  const instance = getCurrentInstance();
942
- const { terminal, defaultStyle, scheduler } = useTerminal();
62
+ const { terminal, defaultStyle, scheduler, widthProvider } = useTerminal();
943
63
  const layout = useLayout();
944
64
  const { visible, rootProps } = useVisibility();
945
65
  const rows = shallowRef(markRaw([]));
@@ -958,10 +78,10 @@ const TMarkdownText = defineComponent({
958
78
  }));
959
79
  });
960
80
  function rebuildRows() {
961
- rows.value = markRaw(buildMarkdownVisualRows(props.content, props.w, parser.value, {
81
+ rows.value = markRaw(withTextWidthProvider(widthProvider, () => buildMarkdownVisualRows(props.content, props.w, parser.value, {
962
82
  final: props.final,
963
83
  theme: props.theme
964
- }));
84
+ })));
965
85
  documentVersion.value++;
966
86
  }
967
87
  function scheduleRebuild() {
@@ -972,7 +92,7 @@ const TMarkdownText = defineComponent({
972
92
  return;
973
93
  }
974
94
  scheduler.queueFrameTask({
975
- id: `TMarkdownText:${(instance === null || instance === void 0 ? void 0 : instance.uid) ?? "unknown"}:markdown`,
95
+ id: `TMarkdownText:${instance?.uid ?? "unknown"}:markdown`,
976
96
  reason: props.streaming ? "stream" : "data",
977
97
  priority: props.streaming ? "low" : "normal",
978
98
  sync: false,
@@ -1033,549 +153,38 @@ const TMarkdownText = defineComponent({
1033
153
  defaultStyle.value
1034
154
  ],
1035
155
  paint: (dirtyRows) => {
1036
- if (!visible.value) return;
1037
- const r = absRect.value;
1038
- const full = fullRect.value;
1039
- if (r.w <= 0 || r.h <= 0) return;
1040
- const baseStyle = props.style ?? defaultStyle.value;
1041
- const clipStart = Math.max(0, Math.floor(r.x - full.x));
1042
- const paintRow = (y) => {
1043
- if (y < r.y || y >= r.y + r.h) return;
1044
- const rowIndex = Math.floor(y - full.y);
1045
- const row = rows.value[rowIndex];
1046
- if (!row && !props.clear) return;
1047
- paintMarkdownVisualRow(terminal, row, {
1048
- x: r.x,
1049
- y,
1050
- w: r.w,
1051
- clipStart,
1052
- baseStyle,
1053
- clear: props.clear
1054
- });
1055
- };
1056
- if (dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) {
1057
- for (const y of dirtyRows) paintRow(y);
1058
- return;
1059
- }
1060
- for (let y = r.y; y < r.y + r.h; y++) paintRow(y);
1061
- }
1062
- }));
1063
- return () => h("span", rootProps);
1064
- }
1065
- });
1066
-
1067
- //#endregion
1068
- //#region src/vue/components/TVirtualMarkdown.ts
1069
- function clamp(n, min, max) {
1070
- return Math.max(min, Math.min(max, n));
1071
- }
1072
- function markdownStyleSignature(style) {
1073
- if (!style) return "";
1074
- return [
1075
- style.fg ?? "",
1076
- style.bg ?? "",
1077
- style.bold ? "1" : "0",
1078
- style.dim ? "1" : "0",
1079
- style.italic ? "1" : "0",
1080
- style.underline ? "1" : "0",
1081
- style.inverse ? "1" : "0",
1082
- style.href ?? ""
1083
- ].join("");
1084
- }
1085
- function markdownRowSignature(row) {
1086
- if (!row) return "";
1087
- return [
1088
- row.key,
1089
- row.plainText,
1090
- row.segments.map((segment) => `${segment.text}\u0001${segment.cells}\u0001${markdownStyleSignature(segment.style)}`).join("")
1091
- ].join("");
1092
- }
1093
- function getWheelScrollInput(e) {
1094
- const deltaY = Number(e.deltaY ?? 0);
1095
- const deltaMode = typeof e.deltaMode === "number" ? e.deltaMode : void 0;
1096
- if (Number.isInteger(deltaY) && deltaY !== 0 && Math.abs(deltaY) >= 100 && Math.abs(deltaY) % 100 === 0 && deltaMode == null) return {
1097
- deltaY: deltaY / 100,
1098
- mode: "line"
1099
- };
1100
- if (deltaMode === 1) return {
1101
- deltaY,
1102
- mode: "line"
1103
- };
1104
- if (deltaMode === 0) return {
1105
- deltaY,
1106
- mode: "pixel"
1107
- };
1108
- return {
1109
- deltaY,
1110
- mode: "auto"
1111
- };
1112
- }
1113
- const TVirtualMarkdown = defineComponent({
1114
- name: "TVirtualMarkdown",
1115
- props: {
1116
- x: {
1117
- type: Number,
1118
- required: true
1119
- },
1120
- y: {
1121
- type: Number,
1122
- required: true
1123
- },
1124
- w: {
1125
- type: Number,
1126
- required: true
1127
- },
1128
- h: {
1129
- type: Number,
1130
- required: true
1131
- },
1132
- zIndex: {
1133
- type: Number,
1134
- default: 0
1135
- },
1136
- content: {
1137
- type: String,
1138
- default: ""
1139
- },
1140
- blocks: {
1141
- type: Array,
1142
- default: void 0
1143
- },
1144
- scrollTop: {
1145
- type: Number,
1146
- default: 0
1147
- },
1148
- style: {
1149
- type: Object,
1150
- default: void 0
1151
- },
1152
- final: {
1153
- type: Boolean,
1154
- default: true
1155
- },
1156
- streaming: {
1157
- type: Boolean,
1158
- default: false
1159
- },
1160
- autoFocus: {
1161
- type: Boolean,
1162
- default: false
1163
- },
1164
- selectable: {
1165
- type: Boolean,
1166
- default: true
1167
- },
1168
- customHtmlTags: {
1169
- type: Array,
1170
- default: void 0
1171
- },
1172
- theme: {
1173
- type: Object,
1174
- default: void 0
1175
- }
1176
- },
1177
- emits: [
1178
- "update:scrollTop",
1179
- "scroll",
1180
- "focus",
1181
- "blur",
1182
- "keydown"
1183
- ],
1184
- setup(props, { emit }) {
1185
- const instance = getCurrentInstance();
1186
- const { terminal, defaultStyle, events, scheduler } = useTerminal();
1187
- const layout = useLayout();
1188
- const { visible, rootProps } = useVisibility();
1189
- const parentEventZ = inject(EventZIndexContextKey, computed(() => 0));
1190
- const eventZ = computed(() => (parentEventZ.value ?? 0) + (props.zIndex ?? 0));
1191
- const internalScrollTop = ref(0);
1192
- const documentVersion = ref(0);
1193
- const rows = shallowRef(markRaw([]));
1194
- const wheelState = createWheelScrollState();
1195
- let builtOnce = false;
1196
- let rebuildVersion = 0;
1197
- let layoutCache;
1198
- let alive = true;
1199
- const parser = shallowRef(markRaw(createTuiMarkdownParser({
1200
- streaming: props.streaming,
1201
- customHtmlTags: props.customHtmlTags
1202
- })));
1203
- watch(() => `${props.streaming ? 1 : 0}:${(props.customHtmlTags ?? []).join("\0")}`, () => {
1204
- parser.value = markRaw(createTuiMarkdownParser({
1205
- streaming: props.streaming,
1206
- customHtmlTags: props.customHtmlTags
1207
- }));
1208
- });
1209
- function rebuildRows() {
1210
- const prevScrollTop = internalScrollTop.value;
1211
- const prevVisibleRows = visibleRowSignatures(rows.value, prevScrollTop);
1212
- const blocks = props.blocks ?? buildMarkdownBlocks(props.content, parser.value, {
1213
- final: props.final,
1214
- theme: props.theme
1215
- }).blocks;
1216
- const nextLayout = layoutMarkdownBlocksCached(blocks, props.w, layoutCache);
1217
- layoutCache = markRaw(nextLayout.cache);
1218
- const nextRows = nextLayout.rows;
1219
- rows.value = markRaw(nextRows);
1220
- reconcileScrollTop();
1221
- const nextScrollTop = internalScrollTop.value;
1222
- const nextVisibleRows = visibleRowSignatures(nextRows, nextScrollTop);
1223
- const visibleChanged = prevVisibleRows.length !== nextVisibleRows.length || prevVisibleRows.some((row, index) => row !== nextVisibleRows[index]);
1224
- if (!builtOnce || prevScrollTop !== nextScrollTop || visibleChanged) documentVersion.value++;
1225
- }
1226
- function scheduleRebuild() {
1227
- const currentVersion = ++rebuildVersion;
1228
- if (!builtOnce || !props.streaming) {
1229
- builtOnce = true;
1230
- rebuildRows();
1231
- return;
1232
- }
1233
- scheduler.queueFrameTask({
1234
- id: `TVirtualMarkdown:${(instance === null || instance === void 0 ? void 0 : instance.uid) ?? "unknown"}:markdown`,
1235
- reason: props.streaming ? "stream" : "data",
1236
- priority: props.streaming ? "low" : "normal",
1237
- sync: false,
1238
- run: () => {
1239
- if (!alive) return;
1240
- if (currentVersion !== rebuildVersion) return;
1241
- rebuildRows();
1242
- }
1243
- });
1244
- }
1245
- const fullRect = computed(() => translateRect({
1246
- x: props.x,
1247
- y: props.y,
1248
- w: props.w,
1249
- h: props.h
1250
- }, layout.originX, layout.originY));
1251
- const absRect = computed(() => {
1252
- const translated = fullRect.value;
1253
- if (!layout.clipRect) return translated;
1254
- return intersectRect(translated, layout.clipRect) ?? {
1255
- x: 0,
1256
- y: 0,
1257
- w: 0,
1258
- h: 0
1259
- };
1260
- });
1261
- function normalizedRect() {
1262
- return normalizeCellRect(absRect.value);
1263
- }
1264
- function normalizedFullRect() {
1265
- return normalizeCellRect(fullRect.value);
1266
- }
1267
- function clipOffsets() {
1268
- const full = normalizedFullRect();
1269
- const clip = normalizedRect();
1270
- return {
1271
- x: Math.max(0, clip.x - full.x),
1272
- y: Math.max(0, clip.y - full.y)
1273
- };
1274
- }
1275
- function maxScrollTop() {
1276
- const clip = normalizedRect();
1277
- const { y: clipY } = clipOffsets();
1278
- return Math.max(0, rows.value.length - (clipY + clip.h));
1279
- }
1280
- function visibleRowSignatures(sourceRows, scrollTop) {
1281
- const clip = normalizedRect();
1282
- const { y: clipY } = clipOffsets();
1283
- const start = Math.max(0, scrollTop + clipY);
1284
- const end = Math.max(start, start + clip.h);
1285
- const out = [];
1286
- for (let index = start; index < end; index++) out.push(markdownRowSignature(sourceRows[index]));
1287
- return out;
1288
- }
1289
- function hasControlledScrollTop() {
1290
- return Object.prototype.hasOwnProperty.call((instance === null || instance === void 0 ? void 0 : instance.vnode.props) ?? {}, "scrollTop");
1291
- }
1292
- function setScrollTop(next, emitChange = true) {
1293
- const clamped = clamp(Math.floor(Number(next) || 0), 0, maxScrollTop());
1294
- if (internalScrollTop.value === clamped) return;
1295
- internalScrollTop.value = clamped;
1296
- if (emitChange) {
1297
- emit("update:scrollTop", clamped);
1298
- emit("scroll", clamped);
1299
- }
1300
- }
1301
- function reconcileScrollTop() {
1302
- const desired = hasControlledScrollTop() ? Math.floor(Number(props.scrollTop) || 0) : internalScrollTop.value;
1303
- const clamped = clamp(desired, 0, maxScrollTop());
1304
- if (internalScrollTop.value === clamped) return;
1305
- internalScrollTop.value = clamped;
1306
- if (desired !== clamped) {
1307
- emit("update:scrollTop", clamped);
1308
- emit("scroll", clamped);
1309
- }
1310
- }
1311
- watch(() => props.scrollTop, () => {
1312
- if (!hasControlledScrollTop()) return;
1313
- const desired = Math.floor(Number(props.scrollTop) || 0);
1314
- const clamped = clamp(desired, 0, maxScrollTop());
1315
- internalScrollTop.value = clamped;
1316
- if (desired !== clamped) {
1317
- emit("update:scrollTop", clamped);
1318
- emit("scroll", clamped);
1319
- }
1320
- });
1321
- watch([
1322
- () => props.content,
1323
- () => props.blocks,
1324
- () => props.w,
1325
- () => parser.value,
1326
- () => props.final,
1327
- () => markdownThemeSignature(props.theme)
1328
- ], () => {
1329
- scheduleRebuild();
1330
- }, { immediate: true });
1331
- watch([
1332
- () => props.content,
1333
- () => props.w,
1334
- () => props.h,
1335
- () => absRect.value.h
1336
- ], () => {
1337
- resetWheelScrollState(wheelState);
1338
- });
1339
- watch([
1340
- () => props.h,
1341
- () => absRect.value.y,
1342
- () => absRect.value.h,
1343
- () => fullRect.value.y,
1344
- () => fullRect.value.h
1345
- ], () => {
1346
- reconcileScrollTop();
1347
- }, { immediate: true });
1348
- function onKeydown(event) {
1349
- emit("keydown", event);
1350
- const page = Math.max(1, normalizedRect().h);
1351
- if (event.key === "ArrowUp") {
1352
- event.preventDefault();
1353
- setScrollTop(internalScrollTop.value - 1);
1354
- return;
1355
- }
1356
- if (event.key === "ArrowDown") {
1357
- event.preventDefault();
1358
- setScrollTop(internalScrollTop.value + 1);
1359
- return;
1360
- }
1361
- if (event.key === "PageUp") {
1362
- event.preventDefault();
1363
- setScrollTop(internalScrollTop.value - page);
1364
- return;
1365
- }
1366
- if (event.key === "PageDown") {
1367
- event.preventDefault();
1368
- setScrollTop(internalScrollTop.value + page);
1369
- return;
1370
- }
1371
- if (event.key === "Home") {
1372
- event.preventDefault();
1373
- setScrollTop(0);
1374
- return;
1375
- }
1376
- if (event.key === "End") {
1377
- event.preventDefault();
1378
- setScrollTop(maxScrollTop());
1379
- }
1380
- }
1381
- const eventNode = useTerminalNode(() => ({
1382
- rect: normalizedRect(),
1383
- zIndex: eventZ.value,
1384
- visible: visible.value,
1385
- focusable: true,
1386
- selectable: props.selectable,
1387
- handlers: {
1388
- click: (_event) => {},
1389
- wheel: (event) => {
1390
- var _event$preventDefault;
1391
- const { deltaY, mode } = getWheelScrollInput(event);
1392
- if (!deltaY) return;
1393
- const now = typeof event.time === "number" ? event.time : typeof event.timeStamp === "number" ? event.timeStamp : Date.now();
1394
- const { nextTop, dir } = applyWheelScroll(wheelState, deltaY, internalScrollTop.value, maxScrollTop(), now, mode, { disableAcceleration: mode === "pixel" });
1395
- if (!dir || nextTop === internalScrollTop.value) return;
1396
- (_event$preventDefault = event.preventDefault) === null || _event$preventDefault === void 0 || _event$preventDefault.call(event);
1397
- setScrollTop(nextTop);
1398
- },
1399
- focus: () => {
1400
- emit("focus");
1401
- },
1402
- blur: () => {
1403
- emit("blur");
1404
- },
1405
- keydown: onKeydown
1406
- }
1407
- }));
1408
- watchEffect(() => {
1409
- if (!props.autoFocus || !visible.value) return;
1410
- const manager = events.value;
1411
- const nodeId = eventNode.id.value;
1412
- if (!nodeId) return;
1413
- if (!manager) return;
1414
- if (manager.getFocused() === nodeId) return;
1415
- manager.focus(nodeId);
1416
- });
1417
- onBeforeUnmount(() => {
1418
- alive = false;
1419
- rebuildVersion++;
1420
- });
1421
- useRenderNode(() => ({
1422
- zIndex: props.zIndex,
1423
- rect: visible.value ? normalizedRect() : {
1424
- x: 0,
1425
- y: 0,
1426
- w: 0,
1427
- h: 0
1428
- },
1429
- deps: [
1430
- visible.value,
1431
- absRect.value,
1432
- fullRect.value,
1433
- internalScrollTop.value,
1434
- props.style,
1435
- defaultStyle.value,
1436
- documentVersion.value
1437
- ],
1438
- paint: (dirtyRows) => {
1439
- if (!visible.value) return;
1440
- const r = normalizedRect();
1441
- if (r.w <= 0 || r.h <= 0) return;
1442
- const baseStyle = props.style ?? defaultStyle.value;
1443
- const { x: clipX, y: clipY } = clipOffsets();
1444
- const paintRow = (y) => {
1445
- if (y < r.y || y >= r.y + r.h) return;
1446
- const visualIndex = internalScrollTop.value + clipY + (y - r.y);
1447
- paintMarkdownVisualRow(terminal, rows.value[visualIndex], {
1448
- x: r.x,
1449
- y,
1450
- w: r.w,
1451
- clipStart: clipX,
1452
- baseStyle,
1453
- clear: true
1454
- });
1455
- };
1456
- if (dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) {
1457
- for (const y of dirtyRows) paintRow(y);
1458
- return;
1459
- }
1460
- for (let y = r.y; y < r.y + r.h; y++) paintRow(y);
156
+ withTextWidthProvider(widthProvider, () => {
157
+ if (!visible.value) return;
158
+ const r = absRect.value;
159
+ const full = fullRect.value;
160
+ if (r.w <= 0 || r.h <= 0) return;
161
+ const baseStyle = props.style ?? defaultStyle.value;
162
+ const clipStart = Math.max(0, Math.floor(r.x - full.x));
163
+ const paintRow = (y) => {
164
+ if (y < r.y || y >= r.y + r.h) return;
165
+ const rowIndex = Math.floor(y - full.y);
166
+ const row = rows.value[rowIndex];
167
+ if (!row && !props.clear) return;
168
+ paintMarkdownVisualRow(terminal, row, {
169
+ x: r.x,
170
+ y,
171
+ w: r.w,
172
+ clipStart,
173
+ baseStyle,
174
+ clear: props.clear
175
+ });
176
+ };
177
+ if (dirtyRows?.length) {
178
+ for (const y of dirtyRows) paintRow(y);
179
+ return;
180
+ }
181
+ for (let y = r.y; y < r.y + r.h; y++) paintRow(y);
182
+ });
1461
183
  }
1462
184
  }));
1463
185
  return () => h("span", rootProps);
1464
186
  }
1465
187
  });
1466
188
 
1467
- //#endregion
1468
- //#region src/vue/markdown/block-source.ts
1469
- function withBlockKeyPrefix(block, prefix, index) {
1470
- const key = `${prefix}:${index}:${block.key}`;
1471
- switch (block.type) {
1472
- case "inline": return {
1473
- ...block,
1474
- key
1475
- };
1476
- case "code_block": return {
1477
- ...block,
1478
- key
1479
- };
1480
- case "thematic_break": return {
1481
- ...block,
1482
- key
1483
- };
1484
- case "blank": return {
1485
- ...block,
1486
- key
1487
- };
1488
- }
1489
- }
1490
- function parseBlockGroup(text, key, parser, theme, final) {
1491
- const { blocks } = buildMarkdownBlocks(text, parser, {
1492
- final,
1493
- theme
1494
- });
1495
- return {
1496
- key,
1497
- blocks: blocks.map((block, index) => withBlockKeyPrefix(block, key, index))
1498
- };
1499
- }
1500
- function shouldInsertGap(previous, next) {
1501
- return previous != null && next != null && previous.type !== "blank" && next.type !== "blank";
1502
- }
1503
- function mergeGroups(groups) {
1504
- const out = [];
1505
- for (const group of groups) {
1506
- if (!group.blocks.length) continue;
1507
- if (shouldInsertGap(out[out.length - 1], group.blocks[0])) out.push({
1508
- type: "blank",
1509
- key: `${group.key}:gap`
1510
- });
1511
- out.push(...group.blocks);
1512
- }
1513
- return out;
1514
- }
1515
- function createMarkdownBlockSource(options) {
1516
- const parser = (options === null || options === void 0 ? void 0 : options.parser) ?? createTuiMarkdownParser({
1517
- streaming: true,
1518
- customHtmlTags: options === null || options === void 0 ? void 0 : options.customHtmlTags
1519
- });
1520
- const theme = options === null || options === void 0 ? void 0 : options.theme;
1521
- const finalizedGroups = [];
1522
- let tailText = "";
1523
- let nextGroupIndex = 0;
1524
- let version = 0;
1525
- let cachedVersion = -1;
1526
- let cachedBlocks = [];
1527
- const invalidate = () => {
1528
- version++;
1529
- cachedVersion = -1;
1530
- };
1531
- const currentGroups = () => {
1532
- if (!tailText) return finalizedGroups;
1533
- return [...finalizedGroups, parseBlockGroup(tailText, `md-tail-${nextGroupIndex}`, parser, theme, false)];
1534
- };
1535
- const blocks = () => {
1536
- if (cachedVersion === version) return cachedBlocks;
1537
- cachedBlocks = mergeGroups(currentGroups());
1538
- cachedVersion = version;
1539
- return cachedBlocks;
1540
- };
1541
- const snapshot = () => ({
1542
- version,
1543
- blocks: blocks()
1544
- });
1545
- return {
1546
- get version() {
1547
- return version;
1548
- },
1549
- get blocks() {
1550
- return blocks();
1551
- },
1552
- appendDelta(text) {
1553
- if (!text) return;
1554
- tailText += text;
1555
- invalidate();
1556
- },
1557
- replaceTailBlock(text) {
1558
- if (tailText === text) return;
1559
- tailText = text;
1560
- invalidate();
1561
- },
1562
- finalizeBlock() {
1563
- if (!tailText) return;
1564
- const key = `md-block-${nextGroupIndex++}`;
1565
- finalizedGroups.push(parseBlockGroup(tailText, key, parser, theme, true));
1566
- tailText = "";
1567
- invalidate();
1568
- },
1569
- clear() {
1570
- if (!tailText && finalizedGroups.length === 0) return;
1571
- finalizedGroups.length = 0;
1572
- tailText = "";
1573
- nextGroupIndex = 0;
1574
- invalidate();
1575
- },
1576
- snapshot
1577
- };
1578
- }
1579
-
1580
189
  //#endregion
1581
190
  export { TMarkdownText, TVirtualMarkdown, buildMarkdownBlocks, buildMarkdownVisualRows, createMarkdownBlockSource, createTuiMarkdownParser, isSafeMarkdownLink, layoutMarkdownBlocks };