@simon_he/vue-tui 0.0.7 → 1.0.0-rc.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 (207) hide show
  1. package/README.md +222 -444
  2. package/dist/TSelect-BmSkar0D.js +8697 -0
  3. package/dist/ansi-palette-Bv5Vqr4k.js +1499 -0
  4. package/dist/ansi-styles.d.ts +200 -0
  5. package/dist/cli/headless-renderer.d.ts +29 -0
  6. package/dist/cli/input.d.ts +49 -0
  7. package/dist/cli/node-file-writers.d.ts +12 -0
  8. package/dist/cli/node-module.d.ts +2 -0
  9. package/dist/cli/parse-kitty.d.ts +2 -0
  10. package/dist/cli/parse-mouse.d.ts +2 -0
  11. package/dist/cli/parse-utils.d.ts +14 -0
  12. package/dist/cli/parse-xterm.d.ts +2 -0
  13. package/dist/cli/path-provider-types.d.ts +1 -0
  14. package/dist/cli/path-provider.d.ts +4 -0
  15. package/dist/cli/path-suggest-core.d.ts +1 -0
  16. package/dist/cli/path-suggest.d.ts +15 -0
  17. package/dist/cli/recording.d.ts +4 -0
  18. package/dist/cli/stdin-buffer.d.ts +33 -0
  19. package/dist/cli.cjs +10664 -0
  20. package/dist/cli.d.cts +22 -0
  21. package/dist/cli.d.ts +22 -0
  22. package/dist/cli.js +9521 -0
  23. package/dist/core/ansi/capability.d.ts +20 -0
  24. package/dist/core/ansi/colors.d.ts +41 -0
  25. package/dist/core/ansi/sgr.d.ts +7 -0
  26. package/dist/core/ansi/theme.d.ts +6 -0
  27. package/dist/core/ansi-palette.d.ts +14 -0
  28. package/dist/core/buffer/buffer.d.ts +54 -0
  29. package/dist/core/buffer/width.d.ts +4 -0
  30. package/dist/core/debug-logger.d.ts +18 -0
  31. package/dist/core/hyperlink.d.ts +9 -0
  32. package/dist/core/index.d.ts +4 -0
  33. package/dist/core/path-provider-types.d.ts +24 -0
  34. package/dist/core/path-suggest.d.ts +35 -0
  35. package/dist/core/render-plane.d.ts +3 -0
  36. package/dist/core/terminal/create-terminal.d.ts +7 -0
  37. package/dist/core/terminal/emitter.d.ts +7 -0
  38. package/dist/core/types.d.ts +107 -0
  39. package/dist/core.cjs +3072 -0
  40. package/dist/core.d.cts +14 -0
  41. package/dist/core.d.ts +14 -0
  42. package/dist/core.js +422 -0
  43. package/dist/create-terminal-app.d.ts +34 -0
  44. package/dist/dom-renderer-CDVQIcIL.js +1172 -0
  45. package/dist/events/index.d.ts +6 -0
  46. package/dist/events/manager/cli-event-manager.d.ts +22 -0
  47. package/dist/events/manager/event-manager.d.ts +30 -0
  48. package/dist/events/manager/selection-suppression.d.ts +3 -0
  49. package/dist/events/manager/types.d.ts +108 -0
  50. package/dist/events/recording.d.ts +44 -0
  51. package/dist/experimental.cjs +9380 -0
  52. package/dist/experimental.d.cts +39 -0
  53. package/dist/experimental.d.ts +39 -0
  54. package/dist/experimental.js +7624 -0
  55. package/dist/frame-perf-C-w8mjJL.js +98 -0
  56. package/dist/hyperlink-B5PD7a3Q.js +331 -0
  57. package/dist/index.cjs +12037 -16507
  58. package/dist/index.d.cts +13 -0
  59. package/dist/index.d.ts +13 -1353
  60. package/dist/index.js +13 -18738
  61. package/dist/markdown.cjs +21929 -0
  62. package/dist/markdown.d.cts +10 -0
  63. package/dist/markdown.d.ts +10 -0
  64. package/dist/markdown.js +1649 -0
  65. package/dist/observability/cli-latency-node.d.ts +3 -0
  66. package/dist/observability/cli-latency-types.d.ts +41 -0
  67. package/dist/observability/cli-latency.d.ts +3 -0
  68. package/dist/observability/frame-perf-store.d.ts +13 -0
  69. package/dist/observability/frame-perf.d.ts +40 -0
  70. package/dist/observability/trace.d.ts +40 -0
  71. package/dist/observability/tui-profiler.d.ts +33 -0
  72. package/dist/observability.cjs +99 -0
  73. package/dist/observability.d.cts +6 -0
  74. package/dist/observability.d.ts +6 -0
  75. package/dist/observability.js +3 -0
  76. package/dist/path-DDiSCXrQ.js +99 -0
  77. package/dist/path-suggest-0Q2BDsuj.js +274 -0
  78. package/dist/path-suggest-Dk9TRLh5.js +3 -0
  79. package/dist/{path-suggest-CZQYJIIg.js → path-suggest-bvR9SY6Q.js} +86 -18
  80. package/dist/renderer/capabilities.d.ts +13 -0
  81. package/dist/renderer/cli/index.d.ts +2 -0
  82. package/dist/renderer/cli/stdout-metrics.d.ts +18 -0
  83. package/dist/renderer/cli/stdout-renderer.d.ts +48 -0
  84. package/dist/renderer/dom/dom-renderer.d.ts +157 -0
  85. package/dist/renderer/dom/index.d.ts +2 -0
  86. package/dist/renderer/index.d.ts +2 -0
  87. package/dist/renderer-dom.cjs +1511 -0
  88. package/dist/renderer-dom.d.cts +4 -0
  89. package/dist/renderer-dom.d.ts +4 -0
  90. package/dist/renderer-dom.js +6 -0
  91. package/dist/runtime/index.d.ts +36 -0
  92. package/dist/runtime/osc52.d.ts +9 -0
  93. package/dist/runtime-DtrcQidD.js +1197 -0
  94. package/dist/runtime.cjs +1697 -0
  95. package/dist/runtime.d.cts +9 -0
  96. package/dist/runtime.d.ts +9 -0
  97. package/dist/runtime.js +4 -0
  98. package/dist/selection/terminal-selection.d.ts +122 -0
  99. package/dist/sgr-DMK2AvuT.js +304 -0
  100. package/dist/style-cache-i0ZABZRq.js +178 -0
  101. package/dist/terminal-selection-BCOh9jsL.js +329 -0
  102. package/dist/utils/env.d.ts +3 -0
  103. package/dist/utils/newlines.d.ts +1 -0
  104. package/dist/utils/path.d.ts +7 -0
  105. package/dist/utils/url-safety.d.ts +2 -0
  106. package/dist/vue/components/TAnchor.d.ts +108 -0
  107. package/dist/vue/components/TBox.d.ts +144 -0
  108. package/dist/vue/components/TDebugOverlay.d.ts +43 -0
  109. package/dist/vue/components/TDialog.d.ts +256 -0
  110. package/dist/vue/components/TFlow.d.ts +85 -0
  111. package/dist/vue/components/TInput.d.ts +554 -0
  112. package/dist/vue/components/TInputBox.d.ts +158 -0
  113. package/dist/vue/components/TJsonEditor.d.ts +165 -0
  114. package/dist/vue/components/TList.d.ts +119 -0
  115. package/dist/vue/components/TLogLinksPanel.d.ts +238 -0
  116. package/dist/vue/components/TLogMinimap.d.ts +263 -0
  117. package/dist/vue/components/TLogScrollbar.d.ts +236 -0
  118. package/dist/vue/components/TLogSearchBar.d.ts +267 -0
  119. package/dist/vue/components/TLogSearchPager.d.ts +162 -0
  120. package/dist/vue/components/TLogSearchResults.d.ts +200 -0
  121. package/dist/vue/components/TLogView.d.ts +502 -0
  122. package/dist/vue/components/TLogVirtualLinksPanel.d.ts +141 -0
  123. package/dist/vue/components/TLogVirtualSearchResults.d.ts +146 -0
  124. package/dist/vue/components/TMarkdownText.d.ts +134 -0
  125. package/dist/vue/components/TMultilineModal.d.ts +62 -0
  126. package/dist/vue/components/TPathPicker.d.ts +213 -0
  127. package/dist/vue/components/TRenderLayer.d.ts +16 -0
  128. package/dist/vue/components/TRenderPlane.d.ts +17 -0
  129. package/dist/vue/components/TSelect.d.ts +215 -0
  130. package/dist/vue/components/TText.d.ts +116 -0
  131. package/dist/vue/components/TTransition.d.ts +83 -0
  132. package/dist/vue/components/TView.d.ts +127 -0
  133. package/dist/vue/components/TVirtualList.d.ts +182 -0
  134. package/dist/vue/components/TVirtualMarkdown.d.ts +168 -0
  135. package/dist/vue/components/TerminalProvider.d.ts +195 -0
  136. package/dist/vue/components/input/host.d.ts +17 -0
  137. package/dist/vue/components/input/plugins/hostPlugin.d.ts +7 -0
  138. package/dist/vue/components/input/plugins/hostPlugin.node.d.ts +15 -0
  139. package/dist/vue/components/input/plugins/mentionUtils.d.ts +17 -0
  140. package/dist/vue/components/input/plugins/nodeMentionPathProvider.d.ts +2 -0
  141. package/dist/vue/components/input/plugins/promptMentionPlugin.d.ts +8 -0
  142. package/dist/vue/components/input/plugins/promptMentionState.d.ts +98 -0
  143. package/dist/vue/components/input/plugins/restrictText.d.ts +27 -0
  144. package/dist/vue/components/input/plugins/types.d.ts +90 -0
  145. package/dist/vue/components/input/utils/inlineText.d.ts +57 -0
  146. package/dist/vue/components/input/utils/inlineTextTokens.d.ts +34 -0
  147. package/dist/vue/components/input/utils/primitives.d.ts +3 -0
  148. package/dist/vue/components/input/utils/wordNavigation.d.ts +6 -0
  149. package/dist/vue/components/list/list-geometry.d.ts +16 -0
  150. package/dist/vue/components/list/list-paint.d.ts +1 -0
  151. package/dist/vue/components/list/scroll-state.d.ts +25 -0
  152. package/dist/vue/components/list/selection-state.d.ts +6 -0
  153. package/dist/vue/components/terminal-provider/copy-toast.d.ts +6 -0
  154. package/dist/vue/components/terminal-provider/portals.d.ts +17 -0
  155. package/dist/vue/components/terminal-provider/scheduler.d.ts +29 -0
  156. package/dist/vue/components/terminal-provider/selection-config.d.ts +15 -0
  157. package/dist/vue/components/terminal-provider/utils.d.ts +5 -0
  158. package/dist/vue/composables/use-layout.d.ts +2 -0
  159. package/dist/vue/composables/use-render-node.d.ts +19 -0
  160. package/dist/vue/composables/use-render-stack.d.ts +3 -0
  161. package/dist/vue/composables/use-runtime.d.ts +5 -0
  162. package/dist/vue/composables/use-terminal-node.d.ts +14 -0
  163. package/dist/vue/composables/use-terminal.d.ts +2 -0
  164. package/dist/vue/composables/use-visibility.d.ts +12 -0
  165. package/dist/vue/context.d.ts +153 -0
  166. package/dist/vue/index.d.ts +39 -0
  167. package/dist/vue/injection-key.d.ts +2 -0
  168. package/dist/vue/log/append-only-log-store.d.ts +2 -0
  169. package/dist/vue/log/tlog-keymap.d.ts +28 -0
  170. package/dist/vue/log/tlog-plugins.d.ts +144 -0
  171. package/dist/vue/log/tlog-session.d.ts +81 -0
  172. package/dist/vue/log/tlog-theme.d.ts +68 -0
  173. package/dist/vue/log/types.d.ts +43 -0
  174. package/dist/vue/log/use-tlog-link-controller.d.ts +31 -0
  175. package/dist/vue/log/use-tlog-retained-index.d.ts +26 -0
  176. package/dist/vue/log/use-tlog-search-controller.d.ts +51 -0
  177. package/dist/vue/log/use-tlog-search-results-page.d.ts +31 -0
  178. package/dist/vue/log/use-tlog-virtual-search-results.d.ts +22 -0
  179. package/dist/vue/markdown/ast.d.ts +3 -0
  180. package/dist/vue/markdown/block-source.d.ts +22 -0
  181. package/dist/vue/markdown/document.d.ts +18 -0
  182. package/dist/vue/markdown/layout.d.ts +16 -0
  183. package/dist/vue/markdown/parser.d.ts +11 -0
  184. package/dist/vue/markdown/render.d.ts +10 -0
  185. package/dist/vue/markdown/theme.d.ts +186 -0
  186. package/dist/vue/markdown/types.d.ts +47 -0
  187. package/dist/vue/render/context.d.ts +3 -0
  188. package/dist/vue/render/render-manager.d.ts +94 -0
  189. package/dist/vue/router/RouterView.d.ts +25 -0
  190. package/dist/vue/router/composables.d.ts +4 -0
  191. package/dist/vue/router/context.d.ts +4 -0
  192. package/dist/vue/router/router.d.ts +7 -0
  193. package/dist/vue/router/types.d.ts +29 -0
  194. package/dist/vue/scheduler/frame-mailbox.d.ts +55 -0
  195. package/dist/vue/scheduler/frame-scheduler.d.ts +51 -0
  196. package/dist/vue/utils/frame-task.d.ts +6 -0
  197. package/dist/vue/utils/rect.d.ts +4 -0
  198. package/dist/vue/utils/style-cache.d.ts +3 -0
  199. package/dist/vue/utils/text.d.ts +26 -0
  200. package/dist/vue/utils/wheel-scroll.d.ts +18 -0
  201. package/dist/vue.cjs +18882 -0
  202. package/dist/vue.d.cts +3 -0
  203. package/dist/vue.d.ts +3 -0
  204. package/dist/vue.js +3066 -0
  205. package/dist/wheel-scroll-B-WlFuNB.js +819 -0
  206. package/package.json +226 -58
  207. package/dist/path-suggest-DIMby3r0.js +0 -3
package/dist/vue.js ADDED
@@ -0,0 +1,3066 @@
1
+ import "./ansi-palette-Bv5Vqr4k.js";
2
+ import "./sgr-DMK2AvuT.js";
3
+ import { charCellWidth } from "./hyperlink-B5PD7a3Q.js";
4
+ import "./dom-renderer-CDVQIcIL.js";
5
+ import "./runtime-DtrcQidD.js";
6
+ import "./frame-perf-C-w8mjJL.js";
7
+ import { normalizePath, stripTrailingSlash } from "./path-DDiSCXrQ.js";
8
+ import { TBox, TDialog, TInput, TList, TRenderPlane, TSelect, TText, TView, TerminalProvider, clamp, createTInputHostPlugin, isCommitMention, isWhitespace, mentionChipStyle, padEndByCells$1 as padEndByCells, sliceByCellsWindow, textCellWidth$1 as textCellWidth } from "./TSelect-BmSkar0D.js";
9
+ import "./terminal-selection-BCOh9jsL.js";
10
+ import { EventZIndexContextKey, LayoutContextKey, RenderStackKey, TInputPluginsContextKey, TPathPickerProviderContextKey, VisibilityContextKey, applyWheelScroll, clearTextCaches, createWheelScrollState, formatInlineCellLine, injectionKey, intersectRect, padEndByCells as padEndByCells$1, repeatChar, sanitizeInlineText, sanitizeTextBlock, sliceByCells, sliceByCellsRange, spaces, textCellWidth as textCellWidth$1, translateRect, useLayout, useRenderNode, useRenderStack, useTerminal, useTerminalNode, useVisibility, wrapByCells } from "./wheel-scroll-B-WlFuNB.js";
11
+ import "./style-cache-i0ZABZRq.js";
12
+ import { parsePathQuery, resolveUserPath, suggestPaths } from "./path-suggest-0Q2BDsuj.js";
13
+ import { computed, defineComponent, h, inject, onBeforeUnmount, onMounted, onUnmounted, provide, reactive, ref, shallowReactive, shallowRef, watch, watchEffect } from "vue";
14
+
15
+ //#region src/observability/cli-latency.ts
16
+ const GLOBAL_PROFILER_KEY = "__vueTuiCliLatencyProfiler";
17
+ function getCliLatencyProfiler() {
18
+ const getProfiler = globalThis[GLOBAL_PROFILER_KEY];
19
+ return typeof getProfiler === "function" ? getProfiler() : null;
20
+ }
21
+
22
+ //#endregion
23
+ //#region src/vue/components/input/plugins/promptMentionState.ts
24
+ const MENTION_SUGGEST_DEBOUNCE_MS = 80;
25
+ function fuzzyScore(query, candidate) {
26
+ const q = query.trim().toLowerCase();
27
+ const c = candidate.toLowerCase();
28
+ if (!q) return 0;
29
+ if (c === q) return 1e4;
30
+ let score = 0;
31
+ if (c.startsWith(q)) score += 1e3;
32
+ let qi = 0;
33
+ let streak = 0;
34
+ for (let ci = 0; ci < c.length && qi < q.length; ci++) if (c[ci] === q[qi]) {
35
+ qi++;
36
+ streak++;
37
+ score += 10 + streak * 5;
38
+ } else streak = 0;
39
+ if (qi < q.length) return null;
40
+ score += Math.max(0, 40 - c.length);
41
+ return score;
42
+ }
43
+ function isMentionBoundary(ch, multilineToken, mentionToken) {
44
+ if (ch === multilineToken || ch === mentionToken) return true;
45
+ if (isWhitespace(ch)) return true;
46
+ return /[,;:!?,。!?、()[\]{}<>]/.test(ch);
47
+ }
48
+ function isMentionStart(text, index, trigger, multilineToken, mentionToken) {
49
+ if (!trigger || text[index] !== trigger) return false;
50
+ if (index <= 0) return true;
51
+ const prev = text[index - 1];
52
+ return isMentionBoundary(prev, multilineToken, mentionToken) || !/\w/.test(prev);
53
+ }
54
+ function isMentionChar(ch) {
55
+ return /^[\p{L}\p{M}\p{N}_./~\\:-]$/u.test(ch);
56
+ }
57
+ function computePromptContext(value, cursorIndex, triggers) {
58
+ if (!triggers.length) return null;
59
+ const idx = clamp(cursorIndex, 0, value.length);
60
+ const lineStart = value.lastIndexOf("\n", Math.max(0, idx - 1)) + 1;
61
+ let matchedTrigger = null;
62
+ for (const t of triggers) if (t && value.slice(lineStart, lineStart + t.length) === t) {
63
+ matchedTrigger = t;
64
+ break;
65
+ }
66
+ if (!matchedTrigger) return null;
67
+ let tokenEnd = value.length;
68
+ for (let i = lineStart; i < value.length; i++) if (isWhitespace(value[i])) {
69
+ tokenEnd = i;
70
+ break;
71
+ }
72
+ if (idx < lineStart || idx > tokenEnd) return null;
73
+ const tokenText = value.slice(lineStart, tokenEnd);
74
+ const query = tokenText.slice(matchedTrigger.length);
75
+ const key = `${lineStart}:${tokenText}`;
76
+ return {
77
+ tokenStart: lineStart,
78
+ tokenEnd,
79
+ tokenText,
80
+ query,
81
+ key,
82
+ trigger: matchedTrigger
83
+ };
84
+ }
85
+ function computeMentionContext(value, cursorIndex, trigger, multilineToken, mentionToken) {
86
+ if (!trigger) return null;
87
+ const idx = clamp(cursorIndex, 0, value.length);
88
+ let start = -1;
89
+ for (let i = idx - 1; i >= 0; i--) {
90
+ const ch = value[i];
91
+ if (isMentionStart(value, i, trigger, multilineToken, mentionToken)) {
92
+ start = i;
93
+ break;
94
+ }
95
+ if (isMentionBoundary(ch, multilineToken, mentionToken)) break;
96
+ }
97
+ if (start < 0) return null;
98
+ let end = start + trigger.length;
99
+ const next = value[end];
100
+ if (next === "\"" || next === "'") {
101
+ const quote = next;
102
+ end += 1;
103
+ while (end < value.length && value[end] !== quote) end++;
104
+ if (end < value.length && value[end] === quote) end++;
105
+ } else while (end < value.length && isMentionChar(value[end])) end++;
106
+ if (idx < start || idx > end) return null;
107
+ const tokenText = value.slice(start, end);
108
+ const queryEnd = clamp(idx, start + trigger.length, end);
109
+ const query = value.slice(start + trigger.length, queryEnd);
110
+ const key = `${start}:${tokenText}`;
111
+ return {
112
+ tokenStart: start,
113
+ tokenEnd: end,
114
+ tokenText,
115
+ query,
116
+ key,
117
+ trigger
118
+ };
119
+ }
120
+ function usePromptMentionState(options) {
121
+ const { props, mentionSuggestionProviders, mentionPathProvider, focused, cursor, getValue, rawAbsRect, terminal, scheduler, multilineToken, mentionToken } = options;
122
+ const promptActive = ref(0);
123
+ const promptSuppressedKey = ref(null);
124
+ const providerList = mentionSuggestionProviders ?? [];
125
+ const mentionKindVersion = ref(0);
126
+ const mentionKindByPath = new Map();
127
+ let mentionKindSeq = 0;
128
+ function clearMentionKinds() {
129
+ if (mentionKindByPath.size === 0) return;
130
+ mentionKindByPath.clear();
131
+ mentionKindVersion.value++;
132
+ scheduler.invalidate();
133
+ }
134
+ watchEffect(() => {
135
+ const list = (props.mentions ?? []).map((p) => String(p ?? "")).filter(Boolean).filter((p) => !isCommitMention(p));
136
+ const statPath = mentionPathProvider?.stat;
137
+ if (!statPath || list.length === 0) {
138
+ clearMentionKinds();
139
+ return;
140
+ }
141
+ const seq = ++mentionKindSeq;
142
+ Promise.allSettled(list.map(async (absPath) => {
143
+ const kind = await Promise.resolve(statPath(absPath));
144
+ return {
145
+ absPath,
146
+ kind
147
+ };
148
+ })).then((results) => {
149
+ if (seq !== mentionKindSeq) return;
150
+ mentionKindByPath.clear();
151
+ for (const r of results) {
152
+ if (r.status !== "fulfilled") continue;
153
+ if (!r.value.kind) continue;
154
+ mentionKindByPath.set(r.value.absPath, r.value.kind);
155
+ }
156
+ mentionKindVersion.value++;
157
+ scheduler.invalidate();
158
+ }).catch(() => {
159
+ if (seq !== mentionKindSeq) return;
160
+ clearMentionKinds();
161
+ });
162
+ });
163
+ const promptContext = computed(() => {
164
+ if (!focused.value) return null;
165
+ if (!props.promptSuggestions?.length) return null;
166
+ const triggers = props.promptTriggers?.length ? props.promptTriggers : [props.promptTrigger || "/"];
167
+ return computePromptContext(getValue(), cursor.value, triggers);
168
+ });
169
+ const mentionContext = computed(() => {
170
+ if (!focused.value) return null;
171
+ const hasStatic = props.mentionSuggestions?.length > 0;
172
+ const hasDynamic = providerList.length > 0 || Boolean(mentionPathProvider?.suggest);
173
+ if (!hasStatic && !hasDynamic) return null;
174
+ return computeMentionContext(getValue(), cursor.value, props.mentionTrigger || "@", multilineToken, mentionToken);
175
+ });
176
+ const skillContext = computed(() => {
177
+ if (!focused.value) return null;
178
+ const trigger = props.skillTrigger;
179
+ if (!trigger || !props.skillSuggestions?.length) return null;
180
+ return computeMentionContext(getValue(), cursor.value, trigger, multilineToken, mentionToken);
181
+ });
182
+ const activeContext = computed(() => mentionContext.value ?? skillContext.value ?? promptContext.value);
183
+ watch(() => activeContext.value?.key ?? null, (next, prev) => {
184
+ if (prev && next !== prev) promptSuppressedKey.value = null;
185
+ });
186
+ const mentionPathItems = ref([]);
187
+ let mentionSeq = 0;
188
+ watchEffect((onCleanup) => {
189
+ const ctx = mentionContext.value;
190
+ const suggestPathsForMention = mentionPathProvider?.suggest;
191
+ if (!ctx || !suggestPathsForMention) {
192
+ mentionPathItems.value = [];
193
+ return;
194
+ }
195
+ const seq = ++mentionSeq;
196
+ const max = Math.max(0, Math.floor(props.mentionMaxItems));
197
+ const q = ctx.query.trim();
198
+ const maxDepth = q.length <= 1 ? 2 : q.length === 2 ? 4 : 8;
199
+ let done = false;
200
+ const timer = setTimeout(() => {
201
+ Promise.resolve(suggestPathsForMention({
202
+ workspaceAbs: props.mentionWorkspace,
203
+ input: ctx.query,
204
+ mode: props.mentionMode,
205
+ max,
206
+ showHidden: props.mentionShowHidden,
207
+ maxDepth
208
+ })).then((res) => {
209
+ if (done || seq !== mentionSeq) return;
210
+ mentionPathItems.value = (res ?? []).map((s) => ({
211
+ value: `${props.mentionTrigger}${s.completion}`,
212
+ insert: `${props.mentionTrigger}${s.completion} `,
213
+ detail: s.kind,
214
+ mentionValue: s.absPath
215
+ }));
216
+ scheduler.invalidate();
217
+ }).catch(() => {
218
+ if (done || seq !== mentionSeq) return;
219
+ mentionPathItems.value = [];
220
+ scheduler.invalidate();
221
+ });
222
+ }, MENTION_SUGGEST_DEBOUNCE_MS);
223
+ onCleanup(() => {
224
+ done = true;
225
+ clearTimeout(timer);
226
+ });
227
+ });
228
+ const mentionProviderItems = ref([]);
229
+ let mentionProviderSeq = 0;
230
+ watchEffect((onCleanup) => {
231
+ const ctx = mentionContext.value;
232
+ if (!ctx || providerList.length === 0) {
233
+ mentionProviderItems.value = [];
234
+ return;
235
+ }
236
+ const seq = ++mentionProviderSeq;
237
+ const max = Math.max(0, Math.floor(props.mentionMaxItems));
238
+ const trigger = props.mentionTrigger || "@";
239
+ let done = false;
240
+ const timer = setTimeout(() => {
241
+ Promise.all(providerList.map((provider) => Promise.resolve(provider({
242
+ query: ctx.query,
243
+ tokenText: ctx.tokenText,
244
+ trigger,
245
+ workspace: props.mentionWorkspace,
246
+ maxItems: max
247
+ })).catch(() => []))).then((results) => {
248
+ if (done || seq !== mentionProviderSeq) return;
249
+ const merged = [];
250
+ for (const list of results) {
251
+ if (!list?.length) continue;
252
+ merged.push(...list);
253
+ }
254
+ mentionProviderItems.value = merged;
255
+ scheduler.invalidate();
256
+ }).catch(() => {
257
+ if (done || seq !== mentionProviderSeq) return;
258
+ mentionProviderItems.value = [];
259
+ scheduler.invalidate();
260
+ });
261
+ }, MENTION_SUGGEST_DEBOUNCE_MS);
262
+ onCleanup(() => {
263
+ done = true;
264
+ clearTimeout(timer);
265
+ });
266
+ });
267
+ const promptMatches = computed(() => {
268
+ const ctx = activeContext.value;
269
+ if (!ctx) return [];
270
+ const mentionTrigger = props.mentionTrigger || "@";
271
+ const skillTrigger = props.skillTrigger || "";
272
+ const isMention = ctx.tokenText.startsWith(mentionTrigger);
273
+ const isSkill = !isMention && Boolean(skillTrigger && ctx.tokenText.startsWith(skillTrigger));
274
+ const trigger = isMention ? mentionTrigger : isSkill ? skillTrigger : ctx.trigger;
275
+ const pathItems = isMention ? mentionPathItems.value : [];
276
+ const candidates = isMention ? [...props.mentionSuggestions ?? [], ...mentionProviderItems.value] : isSkill ? props.skillSuggestions ?? [] : props.promptSuggestions ?? [];
277
+ const q = ctx.query.trim();
278
+ const pathMatches = [];
279
+ for (let i = 0; i < pathItems.length; i++) {
280
+ const s = pathItems[i];
281
+ const value = s.value || "";
282
+ if (!value.startsWith(trigger)) continue;
283
+ pathMatches.push({
284
+ item: s,
285
+ score: 0,
286
+ order: i
287
+ });
288
+ }
289
+ const otherMatches = [];
290
+ for (let i = 0; i < candidates.length; i++) {
291
+ const s = candidates[i];
292
+ const value = s.value || "";
293
+ if (!value.startsWith(trigger)) continue;
294
+ if (!q) {
295
+ otherMatches.push({
296
+ item: s,
297
+ score: 0,
298
+ order: i
299
+ });
300
+ continue;
301
+ }
302
+ const commandText = value.slice(trigger.length);
303
+ const valueScore = fuzzyScore(q, commandText);
304
+ if (valueScore != null) {
305
+ otherMatches.push({
306
+ item: s,
307
+ score: valueScore + 1e3,
308
+ order: i
309
+ });
310
+ continue;
311
+ }
312
+ if (!/[^\u0000-\u007F]/.test(q)) continue;
313
+ const metaText = [
314
+ s.label ?? "",
315
+ s.detail ?? "",
316
+ ...s.keywords ?? []
317
+ ].join(" ");
318
+ const metaScore = fuzzyScore(q, metaText);
319
+ if (metaScore != null) otherMatches.push({
320
+ item: s,
321
+ score: metaScore,
322
+ order: i
323
+ });
324
+ }
325
+ if (!q) return [...pathMatches, ...otherMatches];
326
+ otherMatches.sort((a, b) => b.score - a.score || a.order - b.order);
327
+ return [...pathMatches, ...otherMatches];
328
+ });
329
+ watch(() => promptMatches.value.length, (len) => {
330
+ promptActive.value = clamp(promptActive.value, 0, Math.max(0, len - 1));
331
+ });
332
+ const promptMaxVisible = computed(() => {
333
+ const ctx = activeContext.value;
334
+ if (!ctx) return 0;
335
+ const mentionTrigger = props.mentionTrigger || "@";
336
+ const isMention = ctx.tokenText.startsWith(mentionTrigger);
337
+ return Math.max(0, Math.floor(isMention ? props.mentionMaxItems : props.promptMaxItems));
338
+ });
339
+ const promptWindowStart = computed(() => {
340
+ const total = promptMatches.value.length;
341
+ const maxVisible = promptMaxVisible.value;
342
+ if (total <= 0 || maxVisible <= 0) return 0;
343
+ const visible = Math.min(maxVisible, total);
344
+ const maxStart = Math.max(0, total - visible);
345
+ return clamp(promptActive.value - (visible - 1), 0, maxStart);
346
+ });
347
+ const promptMatchesVisible = computed(() => {
348
+ const total = promptMatches.value.length;
349
+ const maxVisible = promptMaxVisible.value;
350
+ if (total <= 0 || maxVisible <= 0) return [];
351
+ const visible = Math.min(maxVisible, total);
352
+ const start = promptWindowStart.value;
353
+ return promptMatches.value.slice(start, start + visible);
354
+ });
355
+ const promptActiveVisible = computed(() => {
356
+ const start = promptWindowStart.value;
357
+ const rel = promptActive.value - start;
358
+ return clamp(rel, 0, Math.max(0, promptMatchesVisible.value.length - 1));
359
+ });
360
+ const promptVisible = computed(() => {
361
+ const ctx = activeContext.value;
362
+ if (!ctx) return false;
363
+ if (promptSuppressedKey.value && promptSuppressedKey.value === ctx.key) return false;
364
+ return true;
365
+ });
366
+ const promptRect = computed(() => {
367
+ const base = rawAbsRect.value;
368
+ const s = terminal.size();
369
+ const clip = {
370
+ x: 0,
371
+ y: 0,
372
+ w: s.cols,
373
+ h: s.rows
374
+ };
375
+ const listH = Math.max(1, promptMatchesVisible.value.length || 1);
376
+ const h$1 = clamp(2 + listH, 3, Math.max(3, Math.floor(clip.h)));
377
+ const w = clamp(Math.floor(base.w), 0, Math.max(0, Math.floor(clip.w)));
378
+ const preferAboveY = Math.floor(base.y) - h$1;
379
+ const preferBelowY = Math.floor(base.y) + Math.floor(base.h);
380
+ const aboveFits = preferAboveY >= Math.floor(clip.y);
381
+ const belowFits = preferBelowY + h$1 <= Math.floor(clip.y) + Math.floor(clip.h);
382
+ const yPref = aboveFits ? preferAboveY : belowFits ? preferBelowY : preferAboveY;
383
+ const y = clamp(yPref, Math.floor(clip.y), Math.floor(clip.y) + Math.floor(clip.h) - h$1);
384
+ const align = props.promptAlign || "input";
385
+ const baseX = Math.floor(base.x);
386
+ const centeredX = Math.floor(Math.floor(clip.x) + Math.floor((Math.floor(clip.w) - w) / 2));
387
+ const preferX = align === "center" ? centeredX : baseX;
388
+ const x = clamp(preferX, Math.floor(clip.x), Math.floor(clip.x) + Math.floor(clip.w) - w);
389
+ return {
390
+ x,
391
+ y,
392
+ w,
393
+ h: h$1
394
+ };
395
+ });
396
+ return {
397
+ promptActive,
398
+ promptSuppressedKey,
399
+ mentionKindByPath,
400
+ mentionKindVersion,
401
+ promptContext,
402
+ mentionContext,
403
+ activeContext,
404
+ promptMatches,
405
+ promptMatchesVisible,
406
+ promptWindowStart,
407
+ promptActiveVisible,
408
+ promptVisible,
409
+ promptRect
410
+ };
411
+ }
412
+
413
+ //#endregion
414
+ //#region src/vue/components/input/plugins/promptMentionPlugin.ts
415
+ function omitInverse(style) {
416
+ if (!style) return {};
417
+ const { inverse: _inverse,...rest } = style;
418
+ return rest;
419
+ }
420
+ function resolveSelectedPromptStyle(popupBase, selectedOverride) {
421
+ if (!selectedOverride) return {
422
+ ...popupBase,
423
+ inverse: true,
424
+ bold: true,
425
+ dim: false
426
+ };
427
+ if (selectedOverride.bg == null) return {
428
+ ...omitInverse(popupBase),
429
+ ...omitInverse(selectedOverride),
430
+ inverse: true,
431
+ bold: true,
432
+ dim: false
433
+ };
434
+ return {
435
+ ...popupBase,
436
+ ...selectedOverride,
437
+ dim: false
438
+ };
439
+ }
440
+ function computeHighlightRanges$1(text, query) {
441
+ const q = query.trim().toLowerCase();
442
+ if (!q) return [];
443
+ const t = text.toLowerCase();
444
+ const idx = t.indexOf(q);
445
+ if (idx >= 0) return [{
446
+ start: idx,
447
+ end: idx + q.length
448
+ }];
449
+ const positions = [];
450
+ let qi = 0;
451
+ for (let i = 0; i < t.length && qi < q.length; i++) if (t[i] === q[qi]) {
452
+ positions.push(i);
453
+ qi++;
454
+ }
455
+ if (qi < q.length) return [];
456
+ const ranges = [];
457
+ let start = positions[0];
458
+ let prev = positions[0];
459
+ for (let i = 1; i < positions.length; i++) {
460
+ const pos = positions[i];
461
+ if (pos === prev + 1) {
462
+ prev = pos;
463
+ continue;
464
+ }
465
+ ranges.push({
466
+ start,
467
+ end: prev + 1
468
+ });
469
+ start = pos;
470
+ prev = pos;
471
+ }
472
+ ranges.push({
473
+ start,
474
+ end: prev + 1
475
+ });
476
+ return ranges;
477
+ }
478
+ function writeHighlightedText$1(opts) {
479
+ const { text, ranges, x, y, maxCells, baseStyle, highlightStyle, terminal } = opts;
480
+ const safeMax = Math.max(0, Math.floor(maxCells));
481
+ if (!text || safeMax <= 0) return 0;
482
+ let rangeIndex = 0;
483
+ let activeRange = ranges[rangeIndex];
484
+ let cellPos = 0;
485
+ let cursorX = x;
486
+ let buffer = "";
487
+ let currentStyle = baseStyle;
488
+ const flush = () => {
489
+ if (!buffer) return;
490
+ terminal.write(buffer, {
491
+ x: cursorX,
492
+ y,
493
+ style: currentStyle
494
+ });
495
+ cursorX += textCellWidth(buffer);
496
+ buffer = "";
497
+ };
498
+ for (let i = 0; i < text.length && cellPos < safeMax;) {
499
+ const code = text.charCodeAt(i);
500
+ const seg = code <= 127 ? text[i] : String.fromCodePoint(text.codePointAt(i) ?? 0);
501
+ const segLen = seg.length;
502
+ const segWidth = charCellWidth(seg);
503
+ if (cellPos + segWidth > safeMax) break;
504
+ while (activeRange && activeRange.end <= i) {
505
+ rangeIndex++;
506
+ activeRange = ranges[rangeIndex];
507
+ }
508
+ const isHighlighted = Boolean(activeRange && i < activeRange.end && i + segLen > activeRange.start);
509
+ const nextStyle = isHighlighted ? highlightStyle : baseStyle;
510
+ if (nextStyle !== currentStyle) {
511
+ flush();
512
+ currentStyle = nextStyle;
513
+ }
514
+ buffer += seg;
515
+ cellPos += segWidth;
516
+ i += segLen;
517
+ }
518
+ flush();
519
+ return cellPos;
520
+ }
521
+ function buildPromptMatchHighlightStyle(baseStyle, overrideStyle) {
522
+ return {
523
+ ...baseStyle,
524
+ ...overrideStyle ?? {
525
+ fg: "yellow",
526
+ bold: true,
527
+ dim: false
528
+ }
529
+ };
530
+ }
531
+ function countMentionTokens(value, mentionToken, endIndex = value.length) {
532
+ let count = 0;
533
+ const limit = clamp(endIndex, 0, value.length);
534
+ for (let i = 0; i < limit; i++) if (value[i] === mentionToken) count++;
535
+ return count;
536
+ }
537
+ function mentionIndexAt(value, mentionToken, index) {
538
+ return countMentionTokens(value, mentionToken, index);
539
+ }
540
+ function createPromptMentionPlugin(options = {}) {
541
+ const latency = getCliLatencyProfiler();
542
+ return {
543
+ name: "promptMention",
544
+ install: (ctx) => {
545
+ const getProps = () => ctx.getProps();
546
+ const promptOverlayStack = ctx.render.createStack(ctx.render.rootStack, 1e4);
547
+ let lastTopClampedRect = null;
548
+ let lastPromptRect = null;
549
+ const derivedStyleCache = new WeakMap();
550
+ const reactiveProps = reactive({
551
+ promptSuggestions: getProps().promptSuggestions,
552
+ promptTrigger: getProps().promptTrigger,
553
+ promptTriggers: getProps().promptTriggers,
554
+ promptMaxItems: getProps().promptMaxItems,
555
+ promptAlign: getProps().promptAlign,
556
+ mentionTrigger: getProps().mentionTrigger,
557
+ mentionWorkspace: getProps().mentionWorkspace,
558
+ mentionMode: getProps().mentionMode,
559
+ mentionShowHidden: getProps().mentionShowHidden,
560
+ mentionSuggestions: getProps().mentionSuggestions,
561
+ mentionMaxItems: getProps().mentionMaxItems,
562
+ mentions: getProps().mentions,
563
+ skillTrigger: getProps().skillTrigger,
564
+ skillSuggestions: getProps().skillSuggestions
565
+ });
566
+ watchEffect(() => {
567
+ const p = getProps();
568
+ reactiveProps.promptSuggestions = p.promptSuggestions;
569
+ reactiveProps.promptTrigger = p.promptTrigger;
570
+ reactiveProps.promptTriggers = p.promptTriggers;
571
+ reactiveProps.promptMaxItems = p.promptMaxItems;
572
+ reactiveProps.promptAlign = p.promptAlign;
573
+ reactiveProps.mentionTrigger = p.mentionTrigger;
574
+ reactiveProps.mentionWorkspace = p.mentionWorkspace;
575
+ reactiveProps.mentionMode = p.mentionMode;
576
+ reactiveProps.mentionShowHidden = p.mentionShowHidden;
577
+ reactiveProps.mentionSuggestions = p.mentionSuggestions;
578
+ reactiveProps.mentionMaxItems = p.mentionMaxItems;
579
+ reactiveProps.mentions = p.mentions;
580
+ reactiveProps.skillTrigger = p.skillTrigger;
581
+ reactiveProps.skillSuggestions = p.skillSuggestions;
582
+ });
583
+ const promptMention = usePromptMentionState({
584
+ props: reactiveProps,
585
+ mentionSuggestionProviders: options.mentionSuggestionProviders,
586
+ mentionPathProvider: options.mentionPathProvider,
587
+ focused: ctx.focused,
588
+ cursor: ctx.cursor,
589
+ getValue: ctx.getValue,
590
+ rawAbsRect: ctx.rawAbsRect,
591
+ terminal: ctx.terminal,
592
+ scheduler: ctx.scheduler,
593
+ multilineToken: "",
594
+ mentionToken: ctx.mentionToken
595
+ });
596
+ watch(() => {
597
+ if (!ctx.visible.value || !promptMention.promptVisible.value) return null;
598
+ const rect = promptMention.promptRect.value;
599
+ return [
600
+ Math.floor(rect.x),
601
+ Math.floor(rect.y),
602
+ Math.floor(rect.w),
603
+ Math.floor(rect.h)
604
+ ].join(":");
605
+ }, (nextRectKey, prevRectKey) => {
606
+ if (!prevRectKey || nextRectKey === prevRectKey) return;
607
+ ctx.render.invalidatePlane("default");
608
+ ctx.scheduler.invalidate({ priority: "high" });
609
+ });
610
+ ctx.registerChipStyleProvider({
611
+ getStyle: (baseStyle, chip) => {
612
+ if (chip.kind !== "mention") return null;
613
+ const absPath = String(chip.absPath ?? "");
614
+ if (!absPath) return null;
615
+ const fsKind = promptMention.mentionKindByPath.get(absPath);
616
+ return {
617
+ ...mentionChipStyle(baseStyle, absPath, fsKind),
618
+ ...getProps().mentionChipStyle ?? {}
619
+ };
620
+ },
621
+ version: promptMention.mentionKindVersion
622
+ });
623
+ function replaceMentionInContext(absPath, tokenStart, tokenEnd) {
624
+ const cleaned = String(absPath || "").trim();
625
+ if (!cleaned) return;
626
+ const value = ctx.getValue();
627
+ const start = clamp(tokenStart, 0, value.length);
628
+ const end = clamp(tokenEnd, start, value.length);
629
+ const currentMentions = getProps().mentions ?? [];
630
+ const nextMentions = [...currentMentions];
631
+ let before = value.slice(0, start);
632
+ let after = value.slice(end);
633
+ const closeMatch = after.match(/^[\s\u200B]*\]+/u);
634
+ if (closeMatch) {
635
+ after = after.slice(closeMatch[0].length);
636
+ let i = before.length - 1;
637
+ while (i >= 0 && /[\s\u200B]/u.test(before[i])) i--;
638
+ if (i >= 0 && before[i] === "[") before = before.slice(0, i) + before.slice(i + 1);
639
+ }
640
+ const insertIndex = mentionIndexAt(value, ctx.mentionToken, start);
641
+ nextMentions.splice(insertIndex, 0, cleaned);
642
+ ctx.emit("update:mentions", nextMentions);
643
+ let nextAfter = after;
644
+ if (nextAfter.startsWith(" ")) nextAfter = nextAfter.slice(1);
645
+ const nextValue = `${before}${ctx.mentionToken} ${nextAfter}`;
646
+ const nextCursor = before.length + 2;
647
+ ctx.pushUndoSnapshot(nextValue);
648
+ ctx.applyEdit(nextValue, nextCursor);
649
+ }
650
+ function acceptPrompt(index) {
651
+ const ctx0 = promptMention.activeContext.value;
652
+ if (!ctx0) return;
653
+ const list = promptMention.promptMatches.value;
654
+ const match = list[clamp(index, 0, Math.max(0, list.length - 1))];
655
+ if (!match) return;
656
+ const suggestion = match.item;
657
+ const mentionTrigger = getProps().mentionTrigger || "@";
658
+ const isMention = ctx0.tokenText.startsWith(mentionTrigger);
659
+ if (isMention) {
660
+ if (!getProps().collectMentions || suggestion.mentionBehavior === "inline") {
661
+ const insert$1 = suggestion.insert ?? (suggestion.value.endsWith(" ") ? suggestion.value : `${suggestion.value} `);
662
+ const before$1 = ctx.getValue().slice(0, ctx0.tokenStart);
663
+ let after$1 = ctx.getValue().slice(ctx0.tokenEnd);
664
+ if (insert$1.endsWith(" ") && after$1.startsWith(" ")) after$1 = after$1.slice(1);
665
+ const nextValue$1 = `${before$1}${insert$1}${after$1}`;
666
+ const nextCursor$1 = ctx0.tokenStart + insert$1.length;
667
+ promptMention.promptSuppressedKey.value = null;
668
+ ctx.pushUndoSnapshot(nextValue$1);
669
+ ctx.applyEdit(nextValue$1, nextCursor$1);
670
+ return;
671
+ }
672
+ const mentionValue = typeof suggestion.mentionValue === "string" ? String(suggestion.mentionValue) : "";
673
+ const raw = mentionValue || (String(suggestion.value || "").startsWith(mentionTrigger) ? String(suggestion.value).slice(mentionTrigger.length) : String(suggestion.value));
674
+ const absPath = mentionValue ? raw : getProps().mentionWorkspace ? ctx.resolvePath(raw) : raw;
675
+ if (!absPath) {
676
+ promptMention.promptSuppressedKey.value = null;
677
+ return;
678
+ }
679
+ replaceMentionInContext(absPath, ctx0.tokenStart, ctx0.tokenEnd);
680
+ promptMention.promptSuppressedKey.value = null;
681
+ ctx.scheduler.invalidate();
682
+ return;
683
+ }
684
+ if (typeof suggestion.onSelect === "function") {
685
+ const handled = suggestion.onSelect({
686
+ value: suggestion.value,
687
+ query: ctx0.query
688
+ });
689
+ if (handled !== false) {
690
+ const value = ctx.getValue();
691
+ const before$1 = value.slice(0, ctx0.tokenStart);
692
+ const after$1 = value.slice(ctx0.tokenEnd);
693
+ const nextValue$1 = `${before$1}${after$1}`;
694
+ ctx.pushUndoSnapshot(nextValue$1);
695
+ ctx.applyEdit(nextValue$1, ctx0.tokenStart);
696
+ promptMention.promptSuppressedKey.value = ctx0.key;
697
+ ctx.scheduler.invalidate();
698
+ return;
699
+ }
700
+ }
701
+ const insert = suggestion.insert ?? (suggestion.value.endsWith(" ") ? suggestion.value : `${suggestion.value} `);
702
+ const before = ctx.getValue().slice(0, ctx0.tokenStart);
703
+ let after = ctx.getValue().slice(ctx0.tokenEnd);
704
+ if (insert.endsWith(" ") && after.startsWith(" ")) after = after.slice(1);
705
+ const nextValue = `${before}${insert}${after}`;
706
+ const nextCursor = ctx0.tokenStart + insert.length;
707
+ promptMention.promptSuppressedKey.value = null;
708
+ ctx.pushUndoSnapshot(nextValue);
709
+ ctx.applyEdit(nextValue, nextCursor);
710
+ }
711
+ function completeMentionDirectory(suggestion, ctx0) {
712
+ const mentionTrigger = getProps().mentionTrigger || "@";
713
+ if (!ctx0.tokenText.startsWith(mentionTrigger)) return false;
714
+ if (String(suggestion.detail ?? "") !== "directory") return false;
715
+ let insert = String(suggestion.value ?? "");
716
+ if (!insert.startsWith(mentionTrigger)) return false;
717
+ if (!insert.endsWith("/") && !insert.endsWith("\\")) insert = `${insert}/`;
718
+ const value = ctx.getValue();
719
+ const before = value.slice(0, clamp(ctx0.tokenStart, 0, value.length));
720
+ const after = value.slice(clamp(ctx0.tokenEnd, 0, value.length));
721
+ const nextValue = `${before}${insert}${after}`;
722
+ const nextCursor = before.length + insert.length;
723
+ promptMention.promptSuppressedKey.value = null;
724
+ promptMention.promptActive.value = 0;
725
+ ctx.pushUndoSnapshot(nextValue);
726
+ ctx.applyEdit(nextValue, nextCursor);
727
+ ctx.scheduler.invalidate();
728
+ return true;
729
+ }
730
+ function handlePromptKeydown(e) {
731
+ if (!promptMention.promptVisible.value) return false;
732
+ const markPromptOperation = (suffix) => {
733
+ const ctx0 = promptMention.activeContext.value;
734
+ const mentionTrigger = getProps().mentionTrigger || "@";
735
+ const skillTrigger = getProps().skillTrigger || "";
736
+ const tokenText = String(ctx0?.tokenText ?? "");
737
+ const scope = tokenText.startsWith(mentionTrigger) ? "prompt.mention" : skillTrigger && tokenText.startsWith(skillTrigger) ? "prompt.skill" : "prompt.command";
738
+ latency?.markOperation(`${scope}.${suffix}`);
739
+ };
740
+ const len = promptMention.promptMatches.value.length;
741
+ if (e.key === "ArrowDown") {
742
+ markPromptOperation("navigate.next");
743
+ e.preventDefault();
744
+ if (len <= 0) promptMention.promptActive.value = 0;
745
+ else if (promptMention.promptActive.value >= len - 1) promptMention.promptActive.value = 0;
746
+ else promptMention.promptActive.value = clamp(promptMention.promptActive.value + 1, 0, len - 1);
747
+ ctx.scheduler.invalidate();
748
+ return true;
749
+ }
750
+ if (e.key === "ArrowUp") {
751
+ markPromptOperation("navigate.prev");
752
+ e.preventDefault();
753
+ if (len <= 0) promptMention.promptActive.value = 0;
754
+ else if (promptMention.promptActive.value <= 0) promptMention.promptActive.value = len - 1;
755
+ else promptMention.promptActive.value = clamp(promptMention.promptActive.value - 1, 0, len - 1);
756
+ ctx.scheduler.invalidate();
757
+ return true;
758
+ }
759
+ if (e.key === "Tab" || e.key === "Enter" && !e.shiftKey) {
760
+ const ctx0 = promptMention.activeContext.value;
761
+ const mentionTrigger = getProps().mentionTrigger || "@";
762
+ const skillTrigger = getProps().skillTrigger || "";
763
+ const isMention = Boolean(ctx0?.tokenText?.startsWith(mentionTrigger));
764
+ const isSkill = Boolean(skillTrigger && ctx0?.tokenText?.startsWith(skillTrigger) && !isMention);
765
+ if (e.key === "Enter" && !e.shiftKey && !isMention && !isSkill) {
766
+ if (len === 0) return false;
767
+ const match = promptMention.promptMatches.value[clamp(promptMention.promptActive.value, 0, Math.max(0, promptMention.promptMatches.value.length - 1))];
768
+ const typed = String(ctx0?.tokenText ?? "");
769
+ const candidate = String(match?.item?.value ?? "");
770
+ if (!typed || !candidate.startsWith(typed)) return false;
771
+ markPromptOperation("accept");
772
+ e.preventDefault();
773
+ acceptPrompt(promptMention.promptActive.value);
774
+ return true;
775
+ }
776
+ if (e.key === "Tab" && (isMention || isSkill)) {
777
+ markPromptOperation("accept");
778
+ e.preventDefault();
779
+ if (len === 0) return true;
780
+ const list = promptMention.promptMatches.value;
781
+ const match = list[clamp(promptMention.promptActive.value, 0, Math.max(0, list.length - 1))];
782
+ if (isMention && match && ctx0 && completeMentionDirectory(match.item, ctx0)) return true;
783
+ acceptPrompt(promptMention.promptActive.value);
784
+ return true;
785
+ }
786
+ if (len === 0) return false;
787
+ markPromptOperation("accept");
788
+ e.preventDefault();
789
+ acceptPrompt(promptMention.promptActive.value);
790
+ return true;
791
+ }
792
+ if (e.key === "Escape") {
793
+ markPromptOperation("dismiss");
794
+ e.preventDefault();
795
+ promptMention.promptSuppressedKey.value = promptMention.activeContext.value?.key ?? null;
796
+ ctx.scheduler.invalidate();
797
+ return true;
798
+ }
799
+ return false;
800
+ }
801
+ ctx.registerKeydownInterceptor(handlePromptKeydown);
802
+ useRenderNode(() => ({
803
+ zIndex: getProps().zIndex + 5,
804
+ stack: promptOverlayStack,
805
+ dirtyRowsHint: (() => {
806
+ const currentRect = ctx.visible.value && promptMention.promptVisible.value ? {
807
+ y: Math.floor(promptMention.promptRect.value.y),
808
+ h: Math.max(0, Math.floor(promptMention.promptRect.value.h))
809
+ } : null;
810
+ const rects = [lastPromptRect, currentRect].filter(Boolean);
811
+ if (!rects.length) return void 0;
812
+ const maxRows = ctx.terminal.size().rows;
813
+ const start = Math.max(0, Math.min(...rects.map((rect) => rect.y)) - 1);
814
+ const end = Math.min(maxRows, Math.max(...rects.map((rect) => rect.y + rect.h)) + 1);
815
+ if (end <= start) return void 0;
816
+ return Array.from({ length: end - start }, (_, index) => start + index);
817
+ })(),
818
+ priority: "high",
819
+ rect: ctx.visible.value && promptMention.promptVisible.value ? promptMention.promptRect.value : {
820
+ x: 0,
821
+ y: 0,
822
+ w: 0,
823
+ h: 0
824
+ },
825
+ deps: [
826
+ ctx.visible.value,
827
+ promptMention.promptVisible.value,
828
+ promptMention.promptRect.value,
829
+ promptMention.promptMatches.value,
830
+ promptMention.promptMatchesVisible.value,
831
+ promptMention.promptWindowStart.value,
832
+ promptMention.promptActive.value,
833
+ promptMention.promptActiveVisible.value,
834
+ getProps().style,
835
+ getProps().promptPopupStyle,
836
+ getProps().promptPopupBorderStyle,
837
+ getProps().promptPopupMatchStyle,
838
+ getProps().promptSelectedStyle,
839
+ getProps().zIndex,
840
+ ctx.defaultStyle.value,
841
+ promptMention.mentionKindVersion.value
842
+ ],
843
+ paint: () => {
844
+ if (!ctx.visible.value || !promptMention.promptVisible.value) return;
845
+ const r = promptMention.promptRect.value;
846
+ if (r.w < 3 || r.h < 3) return;
847
+ lastPromptRect = {
848
+ y: Math.floor(r.y),
849
+ h: Math.max(0, Math.floor(r.h))
850
+ };
851
+ const popupBase = getProps().promptPopupStyle ?? getProps().style ?? ctx.defaultStyle.value;
852
+ const popupBorderStyle = getProps().promptPopupBorderStyle;
853
+ const popupMatchStyle = getProps().promptPopupMatchStyle;
854
+ const selectedOverride = getProps().promptSelectedStyle;
855
+ const styleKey = popupBase ?? null;
856
+ const cacheKey = selectedOverride ? null : styleKey;
857
+ let derived = cacheKey ? derivedStyleCache.get(cacheKey) : null;
858
+ if (!derived) {
859
+ const borderStyle$1 = popupBorderStyle ? {
860
+ ...popupBase,
861
+ ...popupBorderStyle
862
+ } : {
863
+ ...popupBase,
864
+ dim: true
865
+ };
866
+ const itemStyle = {
867
+ ...popupBase,
868
+ dim: false
869
+ };
870
+ const selectedStyle = resolveSelectedPromptStyle(popupBase, selectedOverride);
871
+ const detailStyle = {
872
+ ...popupBase,
873
+ dim: true
874
+ };
875
+ const selectedDetailStyle = {
876
+ ...selectedStyle,
877
+ dim: true
878
+ };
879
+ const emptyStyle = {
880
+ ...popupBase,
881
+ dim: true
882
+ };
883
+ derived = {
884
+ borderStyle: borderStyle$1,
885
+ itemStyle,
886
+ selectedStyle,
887
+ detailStyle,
888
+ selectedDetailStyle,
889
+ emptyStyle
890
+ };
891
+ if (cacheKey) derivedStyleCache.set(cacheKey, derived);
892
+ }
893
+ const x0 = Math.floor(r.x);
894
+ const y0 = Math.floor(r.y);
895
+ const w = Math.max(0, Math.floor(r.w));
896
+ const h$1 = Math.max(0, Math.floor(r.h));
897
+ const innerW = Math.max(0, w - 2);
898
+ const sidePad = innerW >= 4 ? 1 : 0;
899
+ const contentW = Math.max(0, innerW - sidePad * 2);
900
+ const contentX = x0 + 1 + sidePad;
901
+ const borderStyle = derived.borderStyle;
902
+ if (lastTopClampedRect && y0 > 0) {
903
+ ctx.terminal.write(spaces(lastTopClampedRect.w), {
904
+ x: lastTopClampedRect.x,
905
+ y: 0,
906
+ style: popupBase
907
+ });
908
+ lastTopClampedRect = null;
909
+ }
910
+ if (y0 === 0) lastTopClampedRect = {
911
+ x: x0,
912
+ w
913
+ };
914
+ else lastTopClampedRect = null;
915
+ ctx.terminal.write(`┌${repeatChar("─", innerW)}┐`, {
916
+ x: x0,
917
+ y: y0,
918
+ style: borderStyle
919
+ });
920
+ ctx.terminal.write(`└${repeatChar("─", innerW)}┘`, {
921
+ x: x0,
922
+ y: y0 + h$1 - 1,
923
+ style: borderStyle
924
+ });
925
+ const totalList = promptMention.promptMatches.value;
926
+ const start = promptMention.promptWindowStart.value;
927
+ const list = promptMention.promptMatchesVisible.value;
928
+ const query = promptMention.activeContext.value?.query ?? "";
929
+ const total = totalList.length;
930
+ const visible = list.length;
931
+ const hasAbove = start > 0;
932
+ const hasBelow = start + visible < total;
933
+ const contentH = Math.max(0, h$1 - 2);
934
+ for (let row = 0; row < contentH; row++) {
935
+ const y = y0 + 1 + row;
936
+ ctx.terminal.put(x0, y, "│", borderStyle);
937
+ const rightBorderChar = row === 0 && hasAbove ? "▲" : row === contentH - 1 && hasBelow ? "▼" : "│";
938
+ ctx.terminal.put(x0 + w - 1, y, rightBorderChar, borderStyle);
939
+ if (row >= list.length) {
940
+ ctx.terminal.write(spaces(innerW), {
941
+ x: x0 + 1,
942
+ y,
943
+ style: borderStyle
944
+ });
945
+ continue;
946
+ }
947
+ const match = list[row];
948
+ const isSelected = row === promptMention.promptActiveVisible.value;
949
+ const rawValue = sanitizeInlineText(match.item.value);
950
+ const rawDetail = match.item.detail ? sanitizeInlineText(match.item.detail) : "";
951
+ const highlightRanges = query ? computeHighlightRanges$1(rawValue, query) : [];
952
+ const valueCells = textCellWidth(rawValue);
953
+ const detailCells = rawDetail ? textCellWidth(rawDetail) : 0;
954
+ const minGap = 2;
955
+ const availableForDetail = contentW - valueCells - minGap;
956
+ if (rawDetail && availableForDetail >= 4) {
957
+ const valueStyle = isSelected ? derived.selectedStyle : derived.itemStyle;
958
+ const highlightStyle = buildPromptMatchHighlightStyle(valueStyle, popupMatchStyle);
959
+ const rowStyle = valueStyle;
960
+ if (sidePad > 0) ctx.terminal.write(spaces(sidePad), {
961
+ x: x0 + 1,
962
+ y,
963
+ style: rowStyle
964
+ });
965
+ writeHighlightedText$1({
966
+ text: rawValue,
967
+ ranges: highlightRanges,
968
+ x: contentX,
969
+ y,
970
+ maxCells: valueCells,
971
+ baseStyle: valueStyle,
972
+ highlightStyle,
973
+ terminal: ctx.terminal
974
+ });
975
+ const gapWidth = contentW - valueCells - Math.min(detailCells, availableForDetail);
976
+ const gapStyle = isSelected ? derived.selectedStyle : derived.itemStyle;
977
+ ctx.terminal.write(spaces(gapWidth), {
978
+ x: contentX + valueCells,
979
+ y,
980
+ style: gapStyle
981
+ });
982
+ const detailText = sliceByCellsWindow(rawDetail, 0, availableForDetail);
983
+ const dStyle = isSelected ? derived.selectedDetailStyle : derived.detailStyle;
984
+ ctx.terminal.write(detailText, {
985
+ x: contentX + valueCells + gapWidth,
986
+ y,
987
+ style: dStyle
988
+ });
989
+ if (sidePad > 0) ctx.terminal.write(spaces(sidePad), {
990
+ x: contentX + contentW,
991
+ y,
992
+ style: rowStyle
993
+ });
994
+ } else {
995
+ const style = isSelected ? derived.selectedStyle : derived.itemStyle;
996
+ const highlightStyle = buildPromptMatchHighlightStyle(style, popupMatchStyle);
997
+ if (sidePad > 0) ctx.terminal.write(spaces(sidePad), {
998
+ x: x0 + 1,
999
+ y,
1000
+ style
1001
+ });
1002
+ const clippedValue = sliceByCellsWindow(rawValue, 0, contentW);
1003
+ const usedCells = writeHighlightedText$1({
1004
+ text: clippedValue,
1005
+ ranges: highlightRanges,
1006
+ x: contentX,
1007
+ y,
1008
+ maxCells: contentW,
1009
+ baseStyle: style,
1010
+ highlightStyle,
1011
+ terminal: ctx.terminal
1012
+ });
1013
+ if (usedCells < contentW) ctx.terminal.write(spaces(contentW - usedCells), {
1014
+ x: contentX + usedCells,
1015
+ y,
1016
+ style
1017
+ });
1018
+ if (sidePad > 0) ctx.terminal.write(spaces(sidePad), {
1019
+ x: contentX + contentW,
1020
+ y,
1021
+ style
1022
+ });
1023
+ }
1024
+ }
1025
+ if (list.length === 0 && contentH > 0) {
1026
+ const y = y0 + 1;
1027
+ ctx.terminal.put(x0, y, "│", borderStyle);
1028
+ ctx.terminal.put(x0 + w - 1, y, "│", borderStyle);
1029
+ const msg = padEndByCells(sliceByCellsWindow("(no matches)", 0, contentW), contentW);
1030
+ if (sidePad > 0) ctx.terminal.write(spaces(sidePad), {
1031
+ x: x0 + 1,
1032
+ y,
1033
+ style: derived.emptyStyle
1034
+ });
1035
+ ctx.terminal.write(msg, {
1036
+ x: contentX,
1037
+ y,
1038
+ style: derived.emptyStyle
1039
+ });
1040
+ if (sidePad > 0) ctx.terminal.write(spaces(sidePad), {
1041
+ x: contentX + contentW,
1042
+ y,
1043
+ style: derived.emptyStyle
1044
+ });
1045
+ }
1046
+ }
1047
+ }));
1048
+ useTerminalNode(() => ({
1049
+ rect: promptMention.promptVisible.value ? promptMention.promptRect.value : {
1050
+ x: 0,
1051
+ y: 0,
1052
+ w: 0,
1053
+ h: 0
1054
+ },
1055
+ zIndex: ctx.eventZ.value + 1e4,
1056
+ visible: ctx.visible.value && promptMention.promptVisible.value,
1057
+ focusable: false,
1058
+ handlers: { click: (e) => {
1059
+ const r = promptMention.promptRect.value;
1060
+ const y = e.cellY - r.y;
1061
+ if (y <= 0 || y >= r.h - 1) return;
1062
+ const idx = y - 1;
1063
+ const start = promptMention.promptWindowStart.value;
1064
+ const globalIdx = start + idx;
1065
+ promptMention.promptActive.value = clamp(globalIdx, 0, Math.max(0, promptMention.promptMatches.value.length - 1));
1066
+ acceptPrompt(promptMention.promptActive.value);
1067
+ } }
1068
+ }));
1069
+ watchEffect(() => {
1070
+ getProps().mentions;
1071
+ promptMention.mentionKindVersion.value;
1072
+ });
1073
+ }
1074
+ };
1075
+ }
1076
+
1077
+ //#endregion
1078
+ //#region src/vue/components/input/plugins/restrictText.ts
1079
+ function computeProposedValue(info, text) {
1080
+ const start = info.selection ? info.selection.start : info.cursor;
1081
+ const end = info.selection ? info.selection.end : info.cursor;
1082
+ return info.value.slice(0, start) + text + info.value.slice(end);
1083
+ }
1084
+ function applyRule(rule, info) {
1085
+ if ("filter" in rule) return rule.filter(info);
1086
+ if ("replace" in rule) return info.text.replace(rule.replace.from, rule.replace.to);
1087
+ if ("allowChars" in rule) {
1088
+ let out = "";
1089
+ for (const ch of info.text) if (rule.allowChars.test(ch)) out += ch;
1090
+ return out;
1091
+ }
1092
+ if ("denyChars" in rule) return info.text.replace(rule.denyChars, "");
1093
+ if ("allow" in rule) {
1094
+ const next = computeProposedValue(info, info.text);
1095
+ return rule.allow.test(next) ? info.text : "";
1096
+ }
1097
+ return info.text;
1098
+ }
1099
+ function createTextRestrictionPlugin(options) {
1100
+ const name = options.name ?? "restrictText";
1101
+ const rules = options.rules ?? [];
1102
+ return {
1103
+ name,
1104
+ install(ctx) {
1105
+ ctx.registerTextFilter((info) => {
1106
+ const originalText = info.text;
1107
+ let text = info.text;
1108
+ for (const rule of rules) {
1109
+ text = applyRule(rule, {
1110
+ ...info,
1111
+ text
1112
+ });
1113
+ if (!text) {
1114
+ if (originalText) ctx.emit("validationError", {
1115
+ plugin: name,
1116
+ kind: "reject",
1117
+ originalText,
1118
+ acceptedText: "",
1119
+ value: info.value,
1120
+ cursor: info.cursor,
1121
+ selection: info.selection
1122
+ });
1123
+ return "";
1124
+ }
1125
+ }
1126
+ if (originalText && text !== originalText) ctx.emit("validationError", {
1127
+ plugin: name,
1128
+ kind: "filter",
1129
+ originalText,
1130
+ acceptedText: text,
1131
+ value: info.value,
1132
+ cursor: info.cursor,
1133
+ selection: info.selection
1134
+ });
1135
+ return text;
1136
+ });
1137
+ }
1138
+ };
1139
+ }
1140
+
1141
+ //#endregion
1142
+ //#region src/vue/components/TAnchor.ts
1143
+ const EMPTY_RECT = Object.freeze({
1144
+ x: 0,
1145
+ y: 0,
1146
+ w: 0,
1147
+ h: 0
1148
+ });
1149
+ const TAnchor = defineComponent({
1150
+ name: "TAnchor",
1151
+ props: {
1152
+ left: {
1153
+ type: Number,
1154
+ default: void 0
1155
+ },
1156
+ top: {
1157
+ type: Number,
1158
+ default: void 0
1159
+ },
1160
+ right: {
1161
+ type: Number,
1162
+ default: void 0
1163
+ },
1164
+ bottom: {
1165
+ type: Number,
1166
+ default: void 0
1167
+ },
1168
+ w: {
1169
+ type: Number,
1170
+ default: void 0
1171
+ },
1172
+ h: {
1173
+ type: Number,
1174
+ default: void 0
1175
+ },
1176
+ zIndex: {
1177
+ type: Number,
1178
+ default: 0
1179
+ },
1180
+ focusable: {
1181
+ type: Boolean,
1182
+ default: false
1183
+ },
1184
+ selectable: {
1185
+ type: Boolean,
1186
+ default: void 0
1187
+ }
1188
+ },
1189
+ emits: [
1190
+ "clickCapture",
1191
+ "click",
1192
+ "dblclickCapture",
1193
+ "dblclick",
1194
+ "pointerdownCapture",
1195
+ "pointerdown",
1196
+ "pointerupCapture",
1197
+ "pointerup",
1198
+ "pointermoveCapture",
1199
+ "pointermove",
1200
+ "wheelCapture",
1201
+ "wheel",
1202
+ "keydownCapture",
1203
+ "keydown",
1204
+ "keyupCapture",
1205
+ "keyup",
1206
+ "focusCapture",
1207
+ "focus",
1208
+ "blurCapture",
1209
+ "blur"
1210
+ ],
1211
+ setup(props, { emit, slots }) {
1212
+ const parent = useLayout();
1213
+ const { render } = useTerminal();
1214
+ const parentStack = useRenderStack();
1215
+ const { visible, rootProps } = useVisibility({ provide: true });
1216
+ const parentEventZ = inject(EventZIndexContextKey, computed(() => 0));
1217
+ const eventZ = computed(() => (parentEventZ.value ?? 0) + (props.zIndex ?? 0));
1218
+ const rawRect = computed(() => {
1219
+ const base = parent.clipRect ?? {
1220
+ x: parent.originX,
1221
+ y: parent.originY,
1222
+ w: 0,
1223
+ h: 0
1224
+ };
1225
+ const left = props.left;
1226
+ const right = props.right;
1227
+ const top = props.top;
1228
+ const bottom = props.bottom;
1229
+ const width = props.w;
1230
+ const height = props.h;
1231
+ let w = width;
1232
+ let h$1 = height;
1233
+ if (w == null && left != null && right != null) w = base.w - left - right;
1234
+ if (h$1 == null && top != null && bottom != null) h$1 = base.h - top - bottom;
1235
+ w = Math.max(0, Math.floor(w ?? 0));
1236
+ h$1 = Math.max(0, Math.floor(h$1 ?? 0));
1237
+ let x = base.x;
1238
+ let y = base.y;
1239
+ if (left != null) x = base.x + Math.floor(left);
1240
+ else if (right != null) x = base.x + base.w - Math.floor(right) - w;
1241
+ if (top != null) y = base.y + Math.floor(top);
1242
+ else if (bottom != null) y = base.y + base.h - Math.floor(bottom) - h$1;
1243
+ return translateRect({
1244
+ x,
1245
+ y,
1246
+ w,
1247
+ h: h$1
1248
+ }, 0, 0);
1249
+ });
1250
+ const rect = computed(() => {
1251
+ const translated = rawRect.value;
1252
+ return parent.clipRect ? intersectRect(translated, parent.clipRect) ?? EMPTY_RECT : translated;
1253
+ });
1254
+ useTerminalNode(() => ({
1255
+ rect: rect.value,
1256
+ zIndex: eventZ.value,
1257
+ visible: visible.value,
1258
+ focusable: props.focusable,
1259
+ selectable: props.selectable,
1260
+ handlers: {
1261
+ clickCapture: (e) => emit("clickCapture", e),
1262
+ click: (e) => emit("click", e),
1263
+ dblclickCapture: (e) => emit("dblclickCapture", e),
1264
+ dblclick: (e) => emit("dblclick", e),
1265
+ pointerdownCapture: (e) => emit("pointerdownCapture", e),
1266
+ pointerdown: (e) => emit("pointerdown", e),
1267
+ pointerupCapture: (e) => emit("pointerupCapture", e),
1268
+ pointerup: (e) => emit("pointerup", e),
1269
+ pointermoveCapture: (e) => emit("pointermoveCapture", e),
1270
+ pointermove: (e) => emit("pointermove", e),
1271
+ wheelCapture: (e) => emit("wheelCapture", e),
1272
+ wheel: (e) => emit("wheel", e),
1273
+ keydownCapture: (e) => emit("keydownCapture", e),
1274
+ keydown: (e) => emit("keydown", e),
1275
+ keyupCapture: (e) => emit("keyupCapture", e),
1276
+ keyup: (e) => emit("keyup", e),
1277
+ focusCapture: (e) => emit("focusCapture", e),
1278
+ focus: (e) => emit("focus", e),
1279
+ blurCapture: (e) => emit("blurCapture", e),
1280
+ blur: (e) => emit("blur", e)
1281
+ }
1282
+ }));
1283
+ const childLayout = shallowReactive({
1284
+ originX: 0,
1285
+ originY: 0,
1286
+ clipRect: null
1287
+ });
1288
+ const childStack = computed(() => render.createStack(parentStack.value, props.zIndex));
1289
+ watchEffect(() => {
1290
+ const translated = rawRect.value;
1291
+ childLayout.originX = translated.x;
1292
+ childLayout.originY = translated.y;
1293
+ childLayout.clipRect = rect.value;
1294
+ });
1295
+ provide(LayoutContextKey, childLayout);
1296
+ provide(RenderStackKey, childStack);
1297
+ provide(EventZIndexContextKey, eventZ);
1298
+ return () => h("div", rootProps, slots.default?.());
1299
+ }
1300
+ });
1301
+
1302
+ //#endregion
1303
+ //#region src/vue/components/TDebugOverlay.ts
1304
+ const TDebugOverlay = defineComponent({
1305
+ name: "TDebugOverlay",
1306
+ props: {
1307
+ mode: {
1308
+ type: String,
1309
+ default: "focus"
1310
+ },
1311
+ panel: {
1312
+ type: Boolean,
1313
+ default: true
1314
+ },
1315
+ maxRects: {
1316
+ type: Number,
1317
+ default: 40
1318
+ },
1319
+ zIndex: {
1320
+ type: Number,
1321
+ default: 90
1322
+ }
1323
+ },
1324
+ setup(props) {
1325
+ const { terminal, events, observability, scheduler } = useTerminal();
1326
+ const trace = observability.trace;
1327
+ const framePerf = observability.framePerf;
1328
+ const releasePerf = framePerf.acquire("debug-overlay");
1329
+ const latestPerf = shallowRef(framePerf.latest());
1330
+ let timer = null;
1331
+ function refreshLatestPerf() {
1332
+ const next = framePerf.latest();
1333
+ if (!next) return;
1334
+ if (next.reason === "manual") return;
1335
+ if (latestPerf.value?.frameId === next.frameId) return;
1336
+ latestPerf.value = next;
1337
+ scheduler.invalidate({
1338
+ priority: "low",
1339
+ reason: "manual"
1340
+ });
1341
+ }
1342
+ onMounted(() => {
1343
+ refreshLatestPerf();
1344
+ timer = setInterval(refreshLatestPerf, 250);
1345
+ });
1346
+ onUnmounted(() => {
1347
+ releasePerf();
1348
+ if (timer) clearInterval(timer);
1349
+ timer = null;
1350
+ });
1351
+ function boxStyle(kind) {
1352
+ if (kind === "panel") return {
1353
+ fg: "whiteBright",
1354
+ bg: "black"
1355
+ };
1356
+ if (kind === "focus") return { fg: "cyanBright" };
1357
+ return { fg: "magentaBright" };
1358
+ }
1359
+ return () => {
1360
+ const { cols, rows } = terminal.size();
1361
+ if (cols <= 0 || rows <= 0) return null;
1362
+ const mgr = events.value;
1363
+ const debugNodes = (() => {
1364
+ const nodes = typeof mgr?.debugNodes === "function" ? mgr.debugNodes() : [];
1365
+ return nodes.filter((n) => n && n.rect && n.visible);
1366
+ })();
1367
+ const focused = typeof mgr?.getFocused === "function" ? mgr.getFocused() : null;
1368
+ const focusedRect = debugNodes.find((n) => n.id === focused)?.rect ?? null;
1369
+ const panelText = (() => {
1370
+ if (!props.panel) return "";
1371
+ const records = trace.records;
1372
+ const last = [...records].reverse().find((r) => r.type === "commit");
1373
+ const perf = latestPerf.value ?? framePerf.latest();
1374
+ const dirty = last ? last.dirtyRows === null ? "all" : String(last.dirtyRows?.length ?? 0) : "0";
1375
+ const lines = [
1376
+ `trace: ${trace.enabled.value ? "ON" : "OFF"}`,
1377
+ `focus: ${focused ?? "null"}`,
1378
+ last ? `last commit: dirtyRows=${dirty}` : "last commit: -"
1379
+ ];
1380
+ if (perf) lines.push(`frame: ${perf.durationMs.toFixed(1)}ms`, `reason: ${perf.reason}`, `dirtyRows: ${perf.dirtyRows === null ? "all" : perf.dirtyRows}`, `scannedNodes: ${perf.scannedNodes}`, `paintedNodes: ${perf.paintedNodes}`, `commit: ${perf.commitMs.toFixed(1)}ms`, `domFlush: ${perf.domFlushMs == null ? "-" : `${perf.domFlushMs.toFixed(1)}ms`}`, `coalescedInvalidates: ${perf.coalescedInvalidates}`, `frameTasks: ${perf.frameTaskCount} queue:${perf.frameTaskQueueDepthBeforeRun}->${perf.frameTaskQueueDepthAfterRun}`, `coalescedTasks: ${perf.coalescedFrameTasks}`, `droppedUpdates: ${perf.droppedUpdates}`, `queueDepth: ${perf.queueDepth}`);
1381
+ else lines.push("frame: -");
1382
+ return lines.join("\n");
1383
+ })();
1384
+ const children = [];
1385
+ if (props.mode === "all") for (const n of debugNodes.slice(0, Math.max(0, Math.floor(props.maxRects)))) {
1386
+ const r = n.rect;
1387
+ if (!r || r.w < 2 || r.h < 2) continue;
1388
+ children.push(h(TBox, {
1389
+ x: r.x,
1390
+ y: r.y,
1391
+ w: r.w,
1392
+ h: r.h,
1393
+ zIndex: props.zIndex,
1394
+ clear: false,
1395
+ padding: 0,
1396
+ title: n.id,
1397
+ style: boxStyle("rect")
1398
+ }));
1399
+ }
1400
+ const fr = focusedRect;
1401
+ if (fr && fr.w >= 2 && fr.h >= 2) children.push(h(TBox, {
1402
+ x: fr.x,
1403
+ y: fr.y,
1404
+ w: fr.w,
1405
+ h: fr.h,
1406
+ zIndex: props.zIndex,
1407
+ clear: false,
1408
+ padding: 0,
1409
+ title: `focus:${focused ?? ""}`,
1410
+ style: boxStyle("focus")
1411
+ }));
1412
+ if (props.panel) {
1413
+ const panelW = Math.min(cols, 42);
1414
+ const panelH = rows >= 16 ? Math.min(rows, 17) : Math.min(rows, 6);
1415
+ children.push(h(TBox, {
1416
+ x: 0,
1417
+ y: 0,
1418
+ w: panelW,
1419
+ h: panelH,
1420
+ zIndex: props.zIndex,
1421
+ clear: true,
1422
+ padding: 0,
1423
+ title: "debug",
1424
+ style: boxStyle("panel")
1425
+ }, () => h(TText, {
1426
+ x: 0,
1427
+ y: 0,
1428
+ w: panelW - 2,
1429
+ h: panelH - 2,
1430
+ wrap: true,
1431
+ value: panelText
1432
+ })));
1433
+ }
1434
+ return h(TView, {
1435
+ x: 0,
1436
+ y: 0,
1437
+ w: cols,
1438
+ h: rows,
1439
+ zIndex: props.zIndex
1440
+ }, () => children);
1441
+ };
1442
+ }
1443
+ });
1444
+
1445
+ //#endregion
1446
+ //#region src/vue/components/TFlow.ts
1447
+ const TFlow = defineComponent({
1448
+ name: "TFlow",
1449
+ props: {
1450
+ x: {
1451
+ type: Number,
1452
+ required: true
1453
+ },
1454
+ y: {
1455
+ type: Number,
1456
+ required: true
1457
+ },
1458
+ w: {
1459
+ type: Number,
1460
+ required: true
1461
+ },
1462
+ h: {
1463
+ type: Number,
1464
+ required: true
1465
+ },
1466
+ items: {
1467
+ type: Array,
1468
+ required: true
1469
+ },
1470
+ direction: {
1471
+ type: String,
1472
+ default: "vertical"
1473
+ },
1474
+ gap: {
1475
+ type: Number,
1476
+ default: 0
1477
+ },
1478
+ itemSize: {
1479
+ type: Number,
1480
+ default: 1
1481
+ },
1482
+ zIndex: {
1483
+ type: Number,
1484
+ default: 0
1485
+ }
1486
+ },
1487
+ setup(props, { slots }) {
1488
+ return () => h(TView, {
1489
+ x: props.x,
1490
+ y: props.y,
1491
+ w: props.w,
1492
+ h: props.h,
1493
+ zIndex: props.zIndex
1494
+ }, () => props.items.map((item, index) => {
1495
+ const step = Math.max(0, Math.floor(props.itemSize)) + Math.max(0, Math.floor(props.gap));
1496
+ const offset = index * step;
1497
+ const childProps = props.direction === "vertical" ? {
1498
+ x: 0,
1499
+ y: offset,
1500
+ w: props.w,
1501
+ h: Math.max(0, Math.floor(props.itemSize))
1502
+ } : {
1503
+ x: offset,
1504
+ y: 0,
1505
+ w: Math.max(0, Math.floor(props.itemSize)),
1506
+ h: props.h
1507
+ };
1508
+ return h(TView, {
1509
+ key: index,
1510
+ ...childProps
1511
+ }, () => slots.item?.({
1512
+ item,
1513
+ index
1514
+ }) ?? null);
1515
+ }));
1516
+ }
1517
+ });
1518
+
1519
+ //#endregion
1520
+ //#region src/vue/components/TInputBox.ts
1521
+ const TInputBox = defineComponent({
1522
+ name: "TInputBox",
1523
+ props: {
1524
+ x: {
1525
+ type: Number,
1526
+ required: true
1527
+ },
1528
+ y: {
1529
+ type: Number,
1530
+ required: true
1531
+ },
1532
+ w: {
1533
+ type: Number,
1534
+ required: true
1535
+ },
1536
+ h: {
1537
+ type: Number,
1538
+ required: true
1539
+ },
1540
+ zIndex: {
1541
+ type: Number,
1542
+ default: 0
1543
+ },
1544
+ title: {
1545
+ type: String,
1546
+ default: ""
1547
+ },
1548
+ padding: {
1549
+ type: Number,
1550
+ default: 0
1551
+ },
1552
+ modelValue: {
1553
+ type: String,
1554
+ required: true
1555
+ },
1556
+ placeholder: {
1557
+ type: String,
1558
+ default: ""
1559
+ },
1560
+ style: {
1561
+ type: Object,
1562
+ default: void 0
1563
+ },
1564
+ autoFocus: {
1565
+ type: Boolean,
1566
+ default: false
1567
+ },
1568
+ plugins: {
1569
+ type: Array,
1570
+ default: () => []
1571
+ },
1572
+ cursorBlink: {
1573
+ type: Boolean,
1574
+ default: true
1575
+ },
1576
+ cursorShape: {
1577
+ type: String,
1578
+ default: "block"
1579
+ },
1580
+ blinkInterval: {
1581
+ type: Number,
1582
+ default: 500
1583
+ }
1584
+ },
1585
+ emits: [
1586
+ "update:modelValue",
1587
+ "input",
1588
+ "change",
1589
+ "keydown",
1590
+ "focus",
1591
+ "blur"
1592
+ ],
1593
+ setup(props, { emit }) {
1594
+ const innerW = computed(() => Math.max(0, Math.floor(props.w) - 2 - Math.max(0, Math.floor(props.padding)) * 2));
1595
+ const innerH = computed(() => Math.max(1, Math.floor(props.h) - 2 - Math.max(0, Math.floor(props.padding)) * 2));
1596
+ return () => h(TBox, {
1597
+ x: props.x,
1598
+ y: props.y,
1599
+ w: props.w,
1600
+ h: props.h,
1601
+ zIndex: props.zIndex,
1602
+ border: true,
1603
+ title: props.title,
1604
+ padding: props.padding,
1605
+ style: props.style
1606
+ }, () => h(TInput, {
1607
+ x: 0,
1608
+ y: 0,
1609
+ w: innerW.value,
1610
+ h: innerH.value,
1611
+ modelValue: props.modelValue,
1612
+ "onUpdate:modelValue": (v) => emit("update:modelValue", v),
1613
+ placeholder: props.placeholder,
1614
+ style: props.style,
1615
+ autoFocus: props.autoFocus,
1616
+ plugins: props.plugins,
1617
+ cursorBlink: props.cursorBlink,
1618
+ cursorShape: props.cursorShape,
1619
+ blinkInterval: props.blinkInterval,
1620
+ onInput: (v) => emit("input", v),
1621
+ onChange: (v) => emit("change", v),
1622
+ onKeydown: (e) => emit("keydown", e),
1623
+ onFocus: () => emit("focus"),
1624
+ onBlur: () => emit("blur")
1625
+ }));
1626
+ }
1627
+ });
1628
+
1629
+ //#endregion
1630
+ //#region src/vue/components/TJsonEditor.ts
1631
+ function indexToLineColumn(text, index) {
1632
+ const capped = Math.max(0, Math.min(text.length, Math.floor(index)));
1633
+ let line = 1;
1634
+ let column = 1;
1635
+ for (let i = 0; i < capped; i += 1) if (text[i] === "\n") {
1636
+ line += 1;
1637
+ column = 1;
1638
+ } else column += 1;
1639
+ return {
1640
+ line,
1641
+ column
1642
+ };
1643
+ }
1644
+ function parseJsonErrorPosition(message) {
1645
+ const m = /position\s+(\d+)/iu.exec(String(message ?? ""));
1646
+ if (!m?.[1]) return null;
1647
+ const n = Number(m[1]);
1648
+ if (!Number.isFinite(n)) return null;
1649
+ return Math.max(0, Math.floor(n));
1650
+ }
1651
+ function lintJsonText(input) {
1652
+ const text = String(input ?? "");
1653
+ if (!text.trim()) return { state: "idle" };
1654
+ try {
1655
+ JSON.parse(text);
1656
+ return {
1657
+ state: "success",
1658
+ message: "JSON syntax OK"
1659
+ };
1660
+ } catch (error) {
1661
+ const raw = error instanceof Error ? error.message : String(error);
1662
+ const short = raw.replace(/\s*at position\s+\d.*$/iu, "").trim() || raw;
1663
+ const pos = parseJsonErrorPosition(raw);
1664
+ if (pos != null) {
1665
+ const { line, column } = indexToLineColumn(text, pos);
1666
+ return {
1667
+ state: "error",
1668
+ message: short,
1669
+ line,
1670
+ column
1671
+ };
1672
+ }
1673
+ return {
1674
+ state: "error",
1675
+ message: short
1676
+ };
1677
+ }
1678
+ }
1679
+ const DEFAULT_GUIDE_COLORS = [
1680
+ "cyanBright",
1681
+ "yellowBright",
1682
+ "greenBright",
1683
+ "magentaBright",
1684
+ "blueBright",
1685
+ "redBright"
1686
+ ];
1687
+ function computeJsonIndentGuideDepths(input) {
1688
+ const lines = String(input ?? "").replace(/\r/g, "").split("\n");
1689
+ const depths = [];
1690
+ let depth = 0;
1691
+ let inString = false;
1692
+ let escaping = false;
1693
+ for (const line of lines) {
1694
+ const trimmed = line.trimStart();
1695
+ const startsWithCloser = trimmed.startsWith("}") || trimmed.startsWith("]");
1696
+ const visualDepth = startsWithCloser ? Math.max(0, depth - 1) : depth;
1697
+ depths.push(visualDepth);
1698
+ for (let i = 0; i < line.length; i += 1) {
1699
+ const ch = line[i];
1700
+ if (inString) {
1701
+ if (escaping) {
1702
+ escaping = false;
1703
+ continue;
1704
+ }
1705
+ if (ch === "\\") {
1706
+ escaping = true;
1707
+ continue;
1708
+ }
1709
+ if (ch === "\"") inString = false;
1710
+ continue;
1711
+ }
1712
+ if (ch === "\"") {
1713
+ inString = true;
1714
+ continue;
1715
+ }
1716
+ if (ch === "{" || ch === "[") {
1717
+ depth += 1;
1718
+ continue;
1719
+ }
1720
+ if (ch === "}" || ch === "]") depth = Math.max(0, depth - 1);
1721
+ }
1722
+ }
1723
+ return depths;
1724
+ }
1725
+ const TJsonEditor = defineComponent({
1726
+ name: "TJsonEditor",
1727
+ props: {
1728
+ x: {
1729
+ type: Number,
1730
+ required: true
1731
+ },
1732
+ y: {
1733
+ type: Number,
1734
+ required: true
1735
+ },
1736
+ w: {
1737
+ type: Number,
1738
+ required: true
1739
+ },
1740
+ h: {
1741
+ type: Number,
1742
+ default: 8
1743
+ },
1744
+ zIndex: {
1745
+ type: Number,
1746
+ default: 0
1747
+ },
1748
+ modelValue: {
1749
+ type: String,
1750
+ required: true
1751
+ },
1752
+ placeholder: {
1753
+ type: String,
1754
+ default: ""
1755
+ },
1756
+ style: {
1757
+ type: Object,
1758
+ default: void 0
1759
+ },
1760
+ showIndentGuides: {
1761
+ type: Boolean,
1762
+ default: true
1763
+ },
1764
+ indentSize: {
1765
+ type: Number,
1766
+ default: 2
1767
+ },
1768
+ guideColors: {
1769
+ type: Array,
1770
+ default: () => [...DEFAULT_GUIDE_COLORS]
1771
+ },
1772
+ autoFocus: {
1773
+ type: Boolean,
1774
+ default: false
1775
+ },
1776
+ cursorToEndOnFirstFocus: {
1777
+ type: Boolean,
1778
+ default: true
1779
+ },
1780
+ cursorToEndOnExternalUpdate: {
1781
+ type: Boolean,
1782
+ default: true
1783
+ },
1784
+ submitOnEnter: {
1785
+ type: Boolean,
1786
+ default: false
1787
+ }
1788
+ },
1789
+ emits: [
1790
+ "update:modelValue",
1791
+ "keydown",
1792
+ "focus",
1793
+ "blur",
1794
+ "undo",
1795
+ "redo",
1796
+ "lintChange",
1797
+ "validationError"
1798
+ ],
1799
+ setup(props, { emit }) {
1800
+ let cachedLintText = null;
1801
+ let cachedLintStatus = { state: "idle" };
1802
+ let lastPublishedLintText = null;
1803
+ function normalizeText(value) {
1804
+ return String(value ?? "").replace(/\r/g, "");
1805
+ }
1806
+ function getLintStatus(value) {
1807
+ const normalized = normalizeText(value);
1808
+ if (cachedLintText === normalized) return cachedLintStatus;
1809
+ cachedLintText = normalized;
1810
+ cachedLintStatus = lintJsonText(normalized);
1811
+ return cachedLintStatus;
1812
+ }
1813
+ function publishLint(value) {
1814
+ const normalized = normalizeText(value);
1815
+ if (lastPublishedLintText === normalized) return;
1816
+ lastPublishedLintText = normalized;
1817
+ emit("lintChange", getLintStatus(normalized));
1818
+ }
1819
+ watch(() => props.modelValue, (value) => {
1820
+ publishLint(value);
1821
+ }, { immediate: true });
1822
+ return () => {
1823
+ const text = normalizeText(props.modelValue);
1824
+ const lines = text.split("\n");
1825
+ const lint = getLintStatus(text);
1826
+ const indentSize = Math.max(1, Math.floor(props.indentSize ?? 2));
1827
+ const contentW = Math.max(0, Math.floor(props.w) - 2);
1828
+ const visibleLines = Math.max(0, Math.floor(props.h));
1829
+ const colors = props.guideColors?.length ? props.guideColors : DEFAULT_GUIDE_COLORS;
1830
+ const guideNodes = [];
1831
+ if (props.showIndentGuides && contentW > 0 && visibleLines > 0) {
1832
+ const depths = computeJsonIndentGuideDepths(text);
1833
+ const lineCount = Math.min(visibleLines, lines.length, depths.length);
1834
+ for (let row = 0; row < lineCount; row += 1) {
1835
+ const line = lines[row] ?? "";
1836
+ const leadingSpaces = line.match(/^ +/u)?.[0]?.length ?? 0;
1837
+ const depth = depths[row] ?? 0;
1838
+ const guides = Math.min(depth, Math.floor(leadingSpaces / indentSize));
1839
+ const maxGuidesByWidth = Math.floor(contentW / indentSize);
1840
+ const guideCount = Math.min(guides, maxGuidesByWidth);
1841
+ for (let i = 0; i < guideCount; i += 1) {
1842
+ const x = props.x + 1 + i * indentSize;
1843
+ const y = props.y + row;
1844
+ const color = colors[i % colors.length] ?? "cyanBright";
1845
+ guideNodes.push(h(TText, {
1846
+ key: `guide-${row}-${i}`,
1847
+ x,
1848
+ y,
1849
+ w: 1,
1850
+ h: 1,
1851
+ zIndex: (props.zIndex ?? 0) + 1,
1852
+ value: "│",
1853
+ clear: false,
1854
+ style: {
1855
+ fg: color,
1856
+ dim: true
1857
+ }
1858
+ }));
1859
+ }
1860
+ }
1861
+ }
1862
+ const markerNodes = [];
1863
+ if (lint.state === "error" && typeof lint.line === "number" && typeof lint.column === "number") {
1864
+ const row = lint.line - 1;
1865
+ const col = lint.column - 1;
1866
+ if (row >= 0 && row < visibleLines && col >= 0 && col < contentW) {
1867
+ const line = lines[row] ?? "";
1868
+ const ch = line[col] ?? " ";
1869
+ markerNodes.push(h(TText, {
1870
+ key: `marker-${row}-${col}`,
1871
+ x: props.x + 1 + col,
1872
+ y: props.y + row,
1873
+ w: 1,
1874
+ h: 1,
1875
+ zIndex: (props.zIndex ?? 0) + 2,
1876
+ value: ch,
1877
+ clear: false,
1878
+ style: {
1879
+ fg: "redBright",
1880
+ bold: true,
1881
+ underline: true
1882
+ }
1883
+ }));
1884
+ }
1885
+ }
1886
+ const inputNode = h(TInput, {
1887
+ x: props.x,
1888
+ y: props.y,
1889
+ w: props.w,
1890
+ h: props.h,
1891
+ zIndex: props.zIndex,
1892
+ modelValue: props.modelValue,
1893
+ "onUpdate:modelValue": (v) => {
1894
+ emit("update:modelValue", v);
1895
+ publishLint(v);
1896
+ },
1897
+ placeholder: props.placeholder,
1898
+ style: props.style,
1899
+ autoFocus: props.autoFocus,
1900
+ cursorToEndOnFirstFocus: props.cursorToEndOnFirstFocus,
1901
+ cursorToEndOnExternalUpdate: props.cursorToEndOnExternalUpdate,
1902
+ submitOnEnter: props.submitOnEnter,
1903
+ onKeydown: (e) => {
1904
+ emit("keydown", e);
1905
+ if (e?.defaultPrevented) return;
1906
+ const isShortcut = Boolean((e.metaKey || e.ctrlKey) && !e.altKey);
1907
+ if (!isShortcut) return;
1908
+ if (e.key === "z" || e.key === "Z") {
1909
+ e.preventDefault?.();
1910
+ e.stopPropagation?.();
1911
+ if (e.shiftKey) emit("redo");
1912
+ else emit("undo");
1913
+ return;
1914
+ }
1915
+ if (e.key === "y" || e.key === "Y") {
1916
+ e.preventDefault?.();
1917
+ e.stopPropagation?.();
1918
+ emit("redo");
1919
+ }
1920
+ },
1921
+ onFocus: () => emit("focus"),
1922
+ onBlur: () => emit("blur"),
1923
+ onValidationError: (info) => emit("validationError", info)
1924
+ });
1925
+ return h("div", null, [
1926
+ inputNode,
1927
+ ...guideNodes,
1928
+ ...markerNodes
1929
+ ]);
1930
+ };
1931
+ }
1932
+ });
1933
+
1934
+ //#endregion
1935
+ //#region src/vue/components/TMultilineModal.ts
1936
+ const BORDER = {
1937
+ tl: "┌",
1938
+ tr: "┐",
1939
+ bl: "└",
1940
+ br: "┘",
1941
+ h: "─",
1942
+ v: "│"
1943
+ };
1944
+ const TMultilineModal = defineComponent({
1945
+ name: "TMultilineModal",
1946
+ props: {
1947
+ visible: {
1948
+ type: Boolean,
1949
+ required: true
1950
+ },
1951
+ content: {
1952
+ type: String,
1953
+ required: true
1954
+ },
1955
+ title: {
1956
+ type: String,
1957
+ default: "Multiline Text"
1958
+ },
1959
+ style: {
1960
+ type: Object,
1961
+ default: void 0
1962
+ },
1963
+ zIndex: {
1964
+ type: Number,
1965
+ default: 1e3
1966
+ }
1967
+ },
1968
+ emits: ["close"],
1969
+ setup(props, { emit }) {
1970
+ const { terminal, scheduler, defaultStyle, events } = useTerminal();
1971
+ const layout = useLayout();
1972
+ const { visible: parentVisible, rootProps } = useVisibility();
1973
+ const parentEventZ = inject(EventZIndexContextKey, computed(() => 0));
1974
+ const eventZ = computed(() => (parentEventZ.value ?? 0) + (props.zIndex ?? 0));
1975
+ const isVisible = computed(() => parentVisible.value && props.visible);
1976
+ const modalRect = computed(() => {
1977
+ const size = terminal.size();
1978
+ const termW = size.cols;
1979
+ const termH = size.rows;
1980
+ const w = Math.floor(termW * .8);
1981
+ const h$1 = Math.floor(termH * .7);
1982
+ const x = Math.floor((termW - w) / 2);
1983
+ const y = Math.floor((termH - h$1) / 2);
1984
+ return translateRect({
1985
+ x,
1986
+ y,
1987
+ w,
1988
+ h: h$1
1989
+ }, layout.originX, layout.originY);
1990
+ });
1991
+ const contentLines = computed(() => {
1992
+ const text = sanitizeTextBlock(String(props.content || ""));
1993
+ return text.split("\n");
1994
+ });
1995
+ const scrollY = computed(() => 0);
1996
+ function onKeydown(e) {
1997
+ e.preventDefault();
1998
+ e.stopPropagation();
1999
+ if (e.key === "Escape") {
2000
+ emit("close");
2001
+ scheduler.invalidate();
2002
+ }
2003
+ }
2004
+ const backdropNode = useTerminalNode(() => ({
2005
+ rect: isVisible.value ? {
2006
+ x: 0,
2007
+ y: 0,
2008
+ w: terminal.size().cols,
2009
+ h: terminal.size().rows
2010
+ } : {
2011
+ x: 0,
2012
+ y: 0,
2013
+ w: 0,
2014
+ h: 0
2015
+ },
2016
+ zIndex: eventZ.value,
2017
+ visible: isVisible.value,
2018
+ focusable: true,
2019
+ handlers: {
2020
+ click: (e) => {
2021
+ e.preventDefault();
2022
+ e.stopPropagation();
2023
+ emit("close");
2024
+ scheduler.invalidate();
2025
+ },
2026
+ keydown: onKeydown
2027
+ }
2028
+ }));
2029
+ const contentNode = useTerminalNode(() => ({
2030
+ rect: isVisible.value ? modalRect.value : {
2031
+ x: 0,
2032
+ y: 0,
2033
+ w: 0,
2034
+ h: 0
2035
+ },
2036
+ zIndex: eventZ.value + 1,
2037
+ visible: isVisible.value,
2038
+ focusable: true,
2039
+ handlers: {
2040
+ click: (e) => {
2041
+ e.preventDefault();
2042
+ e.stopPropagation();
2043
+ const manager = events.value;
2044
+ const nodeId = contentNode.id.value;
2045
+ if (manager && nodeId) manager.focus(nodeId);
2046
+ },
2047
+ keydown: onKeydown
2048
+ }
2049
+ }));
2050
+ watchEffect(() => {
2051
+ if (!isVisible.value) return;
2052
+ const manager = events.value;
2053
+ const nodeId = contentNode.id.value || backdropNode.id.value;
2054
+ if (!manager || !nodeId) return;
2055
+ manager.focus(nodeId);
2056
+ });
2057
+ useRenderNode(() => ({
2058
+ zIndex: props.zIndex,
2059
+ rect: isVisible.value ? {
2060
+ x: 0,
2061
+ y: 0,
2062
+ w: terminal.size().cols,
2063
+ h: terminal.size().rows
2064
+ } : {
2065
+ x: 0,
2066
+ y: 0,
2067
+ w: 0,
2068
+ h: 0
2069
+ },
2070
+ deps: [
2071
+ isVisible.value,
2072
+ modalRect.value,
2073
+ props.content,
2074
+ props.title,
2075
+ props.style,
2076
+ defaultStyle.value
2077
+ ],
2078
+ paint: () => {
2079
+ if (!isVisible.value) return;
2080
+ const snapshot = terminal.size();
2081
+ const style = props.style ?? defaultStyle.value;
2082
+ const backdropStyle = {
2083
+ ...style,
2084
+ bg: defaultStyle.value.bg ?? style.bg,
2085
+ dim: true
2086
+ };
2087
+ const backdropRow = spaces(snapshot.cols);
2088
+ for (let y = 0; y < snapshot.rows; y++) terminal.write(backdropRow, {
2089
+ x: 0,
2090
+ y,
2091
+ style: backdropStyle
2092
+ });
2093
+ const r = modalRect.value;
2094
+ const fallbackFg = style.fg ?? defaultStyle.value.fg;
2095
+ const fallbackBg = style.bg ?? defaultStyle.value.bg;
2096
+ const borderStyle = {
2097
+ ...style,
2098
+ fg: defaultStyle.value.fg ?? style.fg
2099
+ };
2100
+ const contentStyle = {
2101
+ ...style,
2102
+ fg: fallbackFg,
2103
+ bg: fallbackBg
2104
+ };
2105
+ const footerStyle = {
2106
+ ...style,
2107
+ dim: true
2108
+ };
2109
+ terminal.put(r.x, r.y, BORDER.tl, borderStyle);
2110
+ terminal.put(r.x + r.w - 1, r.y, BORDER.tr, borderStyle);
2111
+ terminal.put(r.x, r.y + r.h - 1, BORDER.bl, borderStyle);
2112
+ terminal.put(r.x + r.w - 1, r.y + r.h - 1, BORDER.br, borderStyle);
2113
+ const innerW = Math.max(0, r.w - 2);
2114
+ const titleText = ` ${sanitizeInlineText(props.title)} `;
2115
+ const titleW = Math.min(titleText.length, innerW);
2116
+ const titleX = r.x + 1;
2117
+ terminal.write(titleText.slice(0, titleW), {
2118
+ x: titleX,
2119
+ y: r.y,
2120
+ style: borderStyle
2121
+ });
2122
+ const remainingTop = innerW - titleW;
2123
+ if (remainingTop > 0) terminal.write(BORDER.h.repeat(remainingTop), {
2124
+ x: titleX + titleW,
2125
+ y: r.y,
2126
+ style: borderStyle
2127
+ });
2128
+ const footer = " Press ESC to close ";
2129
+ const footerW = Math.min(footer.length, innerW);
2130
+ const footerX = r.x + Math.floor((r.w - footerW) / 2);
2131
+ const leftBorder = footerX - r.x - 1;
2132
+ const rightBorder = innerW - leftBorder - footerW;
2133
+ if (leftBorder > 0) terminal.write(BORDER.h.repeat(leftBorder), {
2134
+ x: r.x + 1,
2135
+ y: r.y + r.h - 1,
2136
+ style: borderStyle
2137
+ });
2138
+ terminal.write(footer, {
2139
+ x: footerX,
2140
+ y: r.y + r.h - 1,
2141
+ style: footerStyle
2142
+ });
2143
+ if (rightBorder > 0) terminal.write(BORDER.h.repeat(rightBorder), {
2144
+ x: footerX + footerW,
2145
+ y: r.y + r.h - 1,
2146
+ style: borderStyle
2147
+ });
2148
+ for (let y = 1; y < r.h - 1; y++) {
2149
+ terminal.put(r.x, r.y + y, BORDER.v, borderStyle);
2150
+ terminal.put(r.x + r.w - 1, r.y + y, BORDER.v, borderStyle);
2151
+ }
2152
+ const contentH = Math.max(0, r.h - 2);
2153
+ const lines = contentLines.value;
2154
+ const startLine = scrollY.value;
2155
+ for (let row = 0; row < contentH; row++) {
2156
+ const lineIndex = startLine + row;
2157
+ const line = lines[lineIndex] ?? "";
2158
+ const visible = sliceByCells(line, innerW);
2159
+ const padded = padEndByCells$1(visible, innerW);
2160
+ terminal.write(padded, {
2161
+ x: r.x + 1,
2162
+ y: r.y + 1 + row,
2163
+ style: contentStyle
2164
+ });
2165
+ }
2166
+ }
2167
+ }));
2168
+ return () => h("span", rootProps);
2169
+ }
2170
+ });
2171
+
2172
+ //#endregion
2173
+ //#region src/vue/components/TPathPicker.ts
2174
+ function clamp$1(n, min, max) {
2175
+ return Math.max(min, Math.min(max, n));
2176
+ }
2177
+ function getWheelScrollInput(e) {
2178
+ const deltaY = Number(e.deltaY ?? 0);
2179
+ const deltaMode = typeof e.deltaMode === "number" ? e.deltaMode : void 0;
2180
+ if (Number.isInteger(deltaY) && deltaY !== 0 && Math.abs(deltaY) >= 100 && Math.abs(deltaY) % 100 === 0 && deltaMode == null) return {
2181
+ deltaY: deltaY / 100,
2182
+ mode: "line"
2183
+ };
2184
+ if (deltaMode === 1) return {
2185
+ deltaY,
2186
+ mode: "line"
2187
+ };
2188
+ if (deltaMode === 0) return {
2189
+ deltaY,
2190
+ mode: "pixel"
2191
+ };
2192
+ return {
2193
+ deltaY,
2194
+ mode: "auto"
2195
+ };
2196
+ }
2197
+ function isCtrlLike(e) {
2198
+ return Boolean(e.ctrlKey || e.metaKey);
2199
+ }
2200
+ function computeHighlightRanges(text, query) {
2201
+ const q = query.trim().toLowerCase();
2202
+ if (!q) return [];
2203
+ const t = text.toLowerCase();
2204
+ const idx = t.indexOf(q);
2205
+ if (idx >= 0) return [{
2206
+ start: idx,
2207
+ end: idx + q.length
2208
+ }];
2209
+ const positions = [];
2210
+ let qi = 0;
2211
+ for (let i = 0; i < t.length && qi < q.length; i++) if (t[i] === q[qi]) {
2212
+ positions.push(i);
2213
+ qi++;
2214
+ }
2215
+ if (qi < q.length) return [];
2216
+ const ranges = [];
2217
+ let start = positions[0];
2218
+ let prev = positions[0];
2219
+ for (let i = 1; i < positions.length; i++) {
2220
+ const pos = positions[i];
2221
+ if (pos === prev + 1) {
2222
+ prev = pos;
2223
+ continue;
2224
+ }
2225
+ ranges.push({
2226
+ start,
2227
+ end: prev + 1
2228
+ });
2229
+ start = pos;
2230
+ prev = pos;
2231
+ }
2232
+ ranges.push({
2233
+ start,
2234
+ end: prev + 1
2235
+ });
2236
+ return ranges;
2237
+ }
2238
+ function writeHighlightedText(opts) {
2239
+ const { text, ranges, x, y, maxCells, baseStyle, highlightStyle, terminal } = opts;
2240
+ const safeMax = Math.max(0, Math.floor(maxCells));
2241
+ if (!text || safeMax <= 0) return 0;
2242
+ let rangeIndex = 0;
2243
+ let activeRange = ranges[rangeIndex];
2244
+ let cellPos = 0;
2245
+ let cursorX = x;
2246
+ let buffer = "";
2247
+ let currentStyle = baseStyle;
2248
+ const flush = () => {
2249
+ if (!buffer) return;
2250
+ terminal.write(buffer, {
2251
+ x: cursorX,
2252
+ y,
2253
+ style: currentStyle
2254
+ });
2255
+ cursorX += textCellWidth$1(buffer);
2256
+ buffer = "";
2257
+ };
2258
+ for (let i = 0; i < text.length && cellPos < safeMax;) {
2259
+ const code = text.charCodeAt(i);
2260
+ const seg = code <= 127 ? text[i] : String.fromCodePoint(text.codePointAt(i) ?? 0);
2261
+ const segLen = seg.length;
2262
+ const segWidth = charCellWidth(seg);
2263
+ if (cellPos + segWidth > safeMax) break;
2264
+ while (activeRange && activeRange.end <= i) {
2265
+ rangeIndex++;
2266
+ activeRange = ranges[rangeIndex];
2267
+ }
2268
+ const isHighlighted = Boolean(activeRange && i < activeRange.end && i + segLen > activeRange.start);
2269
+ const nextStyle = isHighlighted ? highlightStyle : baseStyle;
2270
+ if (nextStyle !== currentStyle) {
2271
+ flush();
2272
+ currentStyle = nextStyle;
2273
+ }
2274
+ buffer += seg;
2275
+ cellPos += segWidth;
2276
+ i += segLen;
2277
+ }
2278
+ flush();
2279
+ return cellPos;
2280
+ }
2281
+ const TPathPicker = defineComponent({
2282
+ name: "TPathPicker",
2283
+ props: {
2284
+ x: {
2285
+ type: Number,
2286
+ required: true
2287
+ },
2288
+ y: {
2289
+ type: Number,
2290
+ required: true
2291
+ },
2292
+ w: {
2293
+ type: Number,
2294
+ required: true
2295
+ },
2296
+ h: {
2297
+ type: Number,
2298
+ required: true
2299
+ },
2300
+ zIndex: {
2301
+ type: Number,
2302
+ default: 0
2303
+ },
2304
+ workspace: {
2305
+ type: String,
2306
+ required: true
2307
+ },
2308
+ mode: {
2309
+ type: String,
2310
+ default: "any"
2311
+ },
2312
+ modelValue: {
2313
+ type: String,
2314
+ required: true
2315
+ },
2316
+ placeholder: {
2317
+ type: String,
2318
+ default: ""
2319
+ },
2320
+ style: {
2321
+ type: Object,
2322
+ default: void 0
2323
+ },
2324
+ inputStyle: {
2325
+ type: Object,
2326
+ default: void 0
2327
+ },
2328
+ activeStyle: {
2329
+ type: Object,
2330
+ default: void 0
2331
+ },
2332
+ matchStyle: {
2333
+ type: Object,
2334
+ default: void 0
2335
+ },
2336
+ autoFocus: {
2337
+ type: Boolean,
2338
+ default: false
2339
+ },
2340
+ showHidden: {
2341
+ type: Boolean,
2342
+ default: false
2343
+ },
2344
+ maxSuggestions: {
2345
+ type: Number,
2346
+ default: 50
2347
+ },
2348
+ provider: {
2349
+ type: Object,
2350
+ default: void 0
2351
+ }
2352
+ },
2353
+ emits: [
2354
+ "update:modelValue",
2355
+ "select",
2356
+ "invalid",
2357
+ "keydown",
2358
+ "focus",
2359
+ "blur"
2360
+ ],
2361
+ setup(props, { emit }) {
2362
+ const { terminal, scheduler, defaultStyle } = useTerminal();
2363
+ const layout = useLayout();
2364
+ const { visible, rootProps } = useVisibility();
2365
+ const injectedProvider = inject(TPathPickerProviderContextKey, null);
2366
+ const parentEventZ = inject(EventZIndexContextKey, computed(() => 0));
2367
+ const eventZ = computed(() => (parentEventZ.value ?? 0) + (props.zIndex ?? 0));
2368
+ const provider = computed(() => props.provider ?? injectedProvider?.value ?? null);
2369
+ const suggestions = ref([]);
2370
+ const active = ref(0);
2371
+ const scrollTop = ref(0);
2372
+ const error = ref(null);
2373
+ const inputH = 1;
2374
+ const listH = computed(() => Math.max(0, Math.floor(props.h) - inputH));
2375
+ const absListRect = computed(() => {
2376
+ const raw = {
2377
+ x: props.x,
2378
+ y: props.y + inputH,
2379
+ w: props.w,
2380
+ h: listH.value
2381
+ };
2382
+ const translated = translateRect(raw, layout.originX, layout.originY);
2383
+ if (!layout.clipRect) return translated;
2384
+ return intersectRect(translated, layout.clipRect) ?? {
2385
+ x: 0,
2386
+ y: 0,
2387
+ w: 0,
2388
+ h: 0
2389
+ };
2390
+ });
2391
+ function ensureActiveVisible() {
2392
+ const h$1 = Math.max(0, listH.value);
2393
+ if (h$1 <= 0) {
2394
+ scrollTop.value = 0;
2395
+ return;
2396
+ }
2397
+ const maxTop = Math.max(0, suggestions.value.length - h$1);
2398
+ scrollTop.value = clamp$1(scrollTop.value, 0, maxTop);
2399
+ if (active.value < scrollTop.value) scrollTop.value = active.value;
2400
+ else if (active.value >= scrollTop.value + h$1) scrollTop.value = clamp$1(active.value - (h$1 - 1), 0, maxTop);
2401
+ }
2402
+ watch([
2403
+ () => active.value,
2404
+ () => suggestions.value.length,
2405
+ () => listH.value
2406
+ ], () => {
2407
+ ensureActiveVisible();
2408
+ });
2409
+ let refreshSeq = 0;
2410
+ async function refresh(seq) {
2411
+ const currentProvider = provider.value;
2412
+ if (!currentProvider) {
2413
+ if (seq !== refreshSeq) return;
2414
+ suggestions.value = [];
2415
+ active.value = 0;
2416
+ scheduler.invalidate();
2417
+ return;
2418
+ }
2419
+ const limit = Math.max(0, Math.floor(props.maxSuggestions));
2420
+ const res = currentProvider.suggest ? await currentProvider.suggest({
2421
+ workspaceAbs: props.workspace,
2422
+ input: props.modelValue,
2423
+ mode: props.mode,
2424
+ max: limit,
2425
+ showHidden: props.showHidden,
2426
+ gitignore: "nonBlocking"
2427
+ }) : await suggestPaths({
2428
+ workspaceAbs: props.workspace,
2429
+ input: props.modelValue,
2430
+ mode: props.mode,
2431
+ max: limit,
2432
+ showHidden: props.showHidden,
2433
+ listDir: currentProvider.listDir
2434
+ });
2435
+ if (seq !== refreshSeq) return;
2436
+ suggestions.value = res.suggestions;
2437
+ active.value = clamp$1(active.value, 0, Math.max(0, suggestions.value.length - 1));
2438
+ ensureActiveVisible();
2439
+ scheduler.invalidate();
2440
+ }
2441
+ watchEffect(() => {
2442
+ if (!visible.value) return;
2443
+ props.workspace;
2444
+ props.mode;
2445
+ props.showHidden;
2446
+ props.modelValue;
2447
+ listH.value;
2448
+ error.value = null;
2449
+ const seq = ++refreshSeq;
2450
+ refresh(seq).catch((e) => {
2451
+ if (seq !== refreshSeq) return;
2452
+ suggestions.value = [];
2453
+ error.value = e instanceof Error ? e.message : String(e);
2454
+ scheduler.invalidate();
2455
+ });
2456
+ });
2457
+ function setValue(next) {
2458
+ const cleaned = next.replace(/\r/g, "").replace(/\n/g, " ");
2459
+ emit("update:modelValue", cleaned);
2460
+ }
2461
+ function moveActive(delta) {
2462
+ const len = suggestions.value.length;
2463
+ if (len <= 0) return;
2464
+ const next = clamp$1(active.value + delta, 0, len - 1);
2465
+ if (next !== active.value) {
2466
+ active.value = next;
2467
+ ensureActiveVisible();
2468
+ scheduler.invalidate();
2469
+ }
2470
+ }
2471
+ function applyCompletion() {
2472
+ const s = suggestions.value[active.value];
2473
+ if (!s) return;
2474
+ setValue(s.completion);
2475
+ }
2476
+ async function trySelect(absPath) {
2477
+ const currentProvider = provider.value;
2478
+ if (!currentProvider) {
2479
+ error.value = "Path provider unavailable";
2480
+ emit("invalid", {
2481
+ reason: "provider_missing",
2482
+ absPath
2483
+ });
2484
+ scheduler.invalidate();
2485
+ return;
2486
+ }
2487
+ const stat = await currentProvider.stat(absPath);
2488
+ if (!stat.exists) {
2489
+ error.value = "Not found";
2490
+ emit("invalid", {
2491
+ reason: "not_found",
2492
+ absPath
2493
+ });
2494
+ scheduler.invalidate();
2495
+ return;
2496
+ }
2497
+ if (props.mode === "file" && stat.kind === "directory") {
2498
+ const abs = normalizePath(absPath);
2499
+ const workspaceAbs = normalizePath(props.workspace);
2500
+ const rel = abs === workspaceAbs ? "" : abs.startsWith(`${workspaceAbs}/`) ? abs.slice(workspaceAbs.length + 1) : abs;
2501
+ setValue(rel ? `${rel}/` : "");
2502
+ error.value = null;
2503
+ scheduler.invalidate();
2504
+ return;
2505
+ }
2506
+ if (props.mode === "file" && stat.kind !== "file") {
2507
+ error.value = "Not a file";
2508
+ emit("invalid", {
2509
+ reason: "not_file",
2510
+ absPath
2511
+ });
2512
+ scheduler.invalidate();
2513
+ return;
2514
+ }
2515
+ if (props.mode === "directory" && stat.kind !== "directory") {
2516
+ error.value = "Not a directory";
2517
+ emit("invalid", {
2518
+ reason: "not_directory",
2519
+ absPath
2520
+ });
2521
+ scheduler.invalidate();
2522
+ return;
2523
+ }
2524
+ emit("select", stripTrailingSlash(absPath));
2525
+ }
2526
+ async function onCommit() {
2527
+ const currentProvider = provider.value;
2528
+ const typedAbs = currentProvider?.resolvePath ? await currentProvider.resolvePath(props.workspace, props.modelValue) : resolveUserPath(props.workspace, props.modelValue);
2529
+ if (!currentProvider) {
2530
+ error.value = "Path provider unavailable";
2531
+ emit("invalid", {
2532
+ reason: "provider_missing",
2533
+ absPath: typedAbs
2534
+ });
2535
+ scheduler.invalidate();
2536
+ return;
2537
+ }
2538
+ const typedStat = await currentProvider.stat(typedAbs);
2539
+ if (typedStat.exists) {
2540
+ if (props.mode === "file" && typedStat.kind === "directory") {
2541
+ const base = props.modelValue.replace(/\r/g, "").replace(/\\/g, "/");
2542
+ const next = base.endsWith("/") ? base : `${base}/`;
2543
+ setValue(next);
2544
+ error.value = null;
2545
+ scheduler.invalidate();
2546
+ return;
2547
+ }
2548
+ await trySelect(typedAbs);
2549
+ return;
2550
+ }
2551
+ const s = suggestions.value[active.value];
2552
+ if (s) {
2553
+ if (props.mode === "file" && s.kind === "directory") {
2554
+ setValue(s.completion);
2555
+ error.value = null;
2556
+ scheduler.invalidate();
2557
+ return;
2558
+ }
2559
+ await trySelect(s.absPath);
2560
+ return;
2561
+ }
2562
+ error.value = "Not found";
2563
+ emit("invalid", {
2564
+ reason: "not_found",
2565
+ absPath: typedAbs
2566
+ });
2567
+ scheduler.invalidate();
2568
+ }
2569
+ function onKeydown(e) {
2570
+ emit("keydown", e);
2571
+ if (e.key === "Tab") {
2572
+ e.preventDefault();
2573
+ applyCompletion();
2574
+ return;
2575
+ }
2576
+ if (e.key === "ArrowUp") {
2577
+ e.preventDefault();
2578
+ moveActive(-1);
2579
+ return;
2580
+ }
2581
+ if (e.key === "ArrowDown") {
2582
+ e.preventDefault();
2583
+ moveActive(1);
2584
+ return;
2585
+ }
2586
+ if (isCtrlLike(e) && (e.key === "p" || e.key === "P")) {
2587
+ e.preventDefault();
2588
+ moveActive(-1);
2589
+ return;
2590
+ }
2591
+ if (isCtrlLike(e) && (e.key === "n" || e.key === "N")) {
2592
+ e.preventDefault();
2593
+ moveActive(1);
2594
+ }
2595
+ }
2596
+ const wheelState = createWheelScrollState();
2597
+ useTerminalNode(() => ({
2598
+ rect: absListRect.value,
2599
+ zIndex: eventZ.value,
2600
+ visible: visible.value,
2601
+ focusable: false,
2602
+ handlers: {
2603
+ click: (e) => {
2604
+ const r = absListRect.value;
2605
+ const idx = scrollTop.value + (e.cellY - r.y);
2606
+ if (idx >= 0 && idx < suggestions.value.length) {
2607
+ active.value = idx;
2608
+ ensureActiveVisible();
2609
+ scheduler.invalidate();
2610
+ }
2611
+ },
2612
+ dblclick: async (e) => {
2613
+ const r = absListRect.value;
2614
+ const idx = scrollTop.value + (e.cellY - r.y);
2615
+ const s = suggestions.value[idx];
2616
+ if (!s) return;
2617
+ if (props.mode === "file" && s.kind === "directory") {
2618
+ setValue(s.completion);
2619
+ error.value = null;
2620
+ scheduler.invalidate();
2621
+ return;
2622
+ }
2623
+ await trySelect(s.absPath);
2624
+ },
2625
+ wheel: (e) => {
2626
+ const { deltaY, mode } = getWheelScrollInput(e);
2627
+ const delta = deltaY;
2628
+ if (!delta) return;
2629
+ const h$1 = Math.max(0, listH.value);
2630
+ const maxTop = Math.max(0, suggestions.value.length - h$1);
2631
+ const { nextTop, dir } = applyWheelScroll(wheelState, delta, scrollTop.value, maxTop, Date.now(), mode, { disableAcceleration: mode === "pixel" });
2632
+ if (!dir || nextTop === scrollTop.value) return;
2633
+ scrollTop.value = nextTop;
2634
+ const visibleStart = nextTop;
2635
+ const visibleEnd = nextTop + h$1 - 1;
2636
+ if (active.value < visibleStart || active.value > visibleEnd) {
2637
+ const newActive = dir > 0 ? visibleEnd : visibleStart;
2638
+ active.value = clamp$1(newActive, 0, Math.max(0, suggestions.value.length - 1));
2639
+ }
2640
+ scheduler.invalidate({ reason: "scroll" });
2641
+ }
2642
+ }
2643
+ }));
2644
+ useRenderNode(() => ({
2645
+ zIndex: props.zIndex,
2646
+ rect: visible.value ? absListRect.value : {
2647
+ x: 0,
2648
+ y: 0,
2649
+ w: 0,
2650
+ h: 0
2651
+ },
2652
+ deps: [
2653
+ visible.value,
2654
+ absListRect.value,
2655
+ props.w,
2656
+ listH.value,
2657
+ props.style,
2658
+ props.activeStyle,
2659
+ props.matchStyle,
2660
+ defaultStyle.value,
2661
+ suggestions.value,
2662
+ active.value,
2663
+ scrollTop.value,
2664
+ error.value
2665
+ ],
2666
+ paint: () => {
2667
+ if (!visible.value) return;
2668
+ const r = absListRect.value;
2669
+ if (r.w <= 0 || r.h <= 0) return;
2670
+ const base = props.style ?? defaultStyle.value;
2671
+ const query = parsePathQuery(String(props.modelValue ?? "")).query;
2672
+ const top = clamp$1(scrollTop.value, 0, Math.max(0, suggestions.value.length - r.h));
2673
+ for (let i = 0; i < r.h; i++) {
2674
+ const idx = top + i;
2675
+ const s = suggestions.value[idx];
2676
+ const raw = s ? sanitizeInlineText(s.display) : "";
2677
+ const clipped = sliceByCells(raw, r.w);
2678
+ const rowStyle = idx === active.value ? {
2679
+ ...base,
2680
+ ...props.activeStyle ?? { inverse: true }
2681
+ } : base;
2682
+ const rowMatchStyle = {
2683
+ ...rowStyle,
2684
+ ...props.matchStyle ?? {
2685
+ bold: true,
2686
+ dim: false,
2687
+ underline: true
2688
+ }
2689
+ };
2690
+ const ranges = query ? computeHighlightRanges(raw, query) : [];
2691
+ const used = writeHighlightedText({
2692
+ text: clipped,
2693
+ ranges,
2694
+ x: r.x,
2695
+ y: r.y + i,
2696
+ maxCells: r.w,
2697
+ baseStyle: rowStyle,
2698
+ highlightStyle: rowMatchStyle,
2699
+ terminal
2700
+ });
2701
+ if (used < r.w) terminal.write(spaces(r.w - used), {
2702
+ x: r.x + used,
2703
+ y: r.y + i,
2704
+ style: rowStyle
2705
+ });
2706
+ }
2707
+ if (suggestions.value.length === 0 && r.h > 0) {
2708
+ const msg = error.value ? `(${sanitizeInlineText(error.value)})` : "(no matches)";
2709
+ terminal.write(padEndByCells$1(sliceByCells(msg, r.w), r.w), {
2710
+ x: r.x,
2711
+ y: r.y,
2712
+ style: {
2713
+ ...base,
2714
+ dim: true
2715
+ }
2716
+ });
2717
+ }
2718
+ }
2719
+ }));
2720
+ return () => h("span", rootProps, [h(TInput, {
2721
+ x: props.x,
2722
+ y: props.y,
2723
+ w: props.w,
2724
+ h: inputH,
2725
+ zIndex: props.zIndex,
2726
+ modelValue: props.modelValue,
2727
+ "onUpdate:modelValue": (v) => setValue(v),
2728
+ cursorToEndOnExternalUpdate: true,
2729
+ placeholder: props.placeholder,
2730
+ style: props.inputStyle ?? props.style,
2731
+ autoFocus: props.autoFocus,
2732
+ onKeydown: (e) => onKeydown(e),
2733
+ onChange: () => void onCommit(),
2734
+ onFocus: () => emit("focus"),
2735
+ onBlur: () => emit("blur")
2736
+ })]);
2737
+ }
2738
+ });
2739
+
2740
+ //#endregion
2741
+ //#region src/vue/components/TRenderLayer.ts
2742
+ const TRenderLayer = defineComponent({
2743
+ name: "TRenderLayer",
2744
+ props: { zIndex: {
2745
+ type: Number,
2746
+ default: 0
2747
+ } },
2748
+ setup(props, { slots }) {
2749
+ const { render } = useTerminal();
2750
+ const parentStack = useRenderStack();
2751
+ const stack = computed(() => render.createStack(parentStack.value, props.zIndex));
2752
+ provide(RenderStackKey, stack);
2753
+ return () => slots.default?.() ?? null;
2754
+ }
2755
+ });
2756
+
2757
+ //#endregion
2758
+ //#region src/vue/components/TTransition.ts
2759
+ const TTransition = defineComponent({
2760
+ name: "TTransition",
2761
+ props: {
2762
+ show: {
2763
+ type: Boolean,
2764
+ required: true
2765
+ },
2766
+ duration: {
2767
+ type: Number,
2768
+ default: 200
2769
+ },
2770
+ beforeEnter: {
2771
+ type: Function,
2772
+ default: void 0
2773
+ },
2774
+ enter: {
2775
+ type: Function,
2776
+ default: void 0
2777
+ },
2778
+ afterEnter: {
2779
+ type: Function,
2780
+ default: void 0
2781
+ },
2782
+ beforeLeave: {
2783
+ type: Function,
2784
+ default: void 0
2785
+ },
2786
+ leave: {
2787
+ type: Function,
2788
+ default: void 0
2789
+ },
2790
+ afterLeave: {
2791
+ type: Function,
2792
+ default: void 0
2793
+ }
2794
+ },
2795
+ setup(props, { slots }) {
2796
+ const { scheduler } = useTerminal();
2797
+ let raf = 0;
2798
+ let runToken = 0;
2799
+ const phase = ref("idle");
2800
+ const progress = ref(props.show ? 1 : 0);
2801
+ const rendered = ref(props.show);
2802
+ const visible = ref(props.show);
2803
+ provide(VisibilityContextKey, visible);
2804
+ function cancel() {
2805
+ if (raf > 0) {
2806
+ cancelAnimationFrame(raf);
2807
+ raf = 0;
2808
+ }
2809
+ runToken += 1;
2810
+ }
2811
+ async function runHook(hook, phaseName, p) {
2812
+ if (!hook) return;
2813
+ await hook({
2814
+ phase: phaseName,
2815
+ progress: p
2816
+ });
2817
+ }
2818
+ function animate(from, to) {
2819
+ const duration = Math.max(0, Math.floor(props.duration));
2820
+ if (duration === 0) {
2821
+ progress.value = to;
2822
+ scheduler.invalidate();
2823
+ return Promise.resolve();
2824
+ }
2825
+ const token = runToken;
2826
+ const t0 = typeof performance !== "undefined" ? performance.now() : Date.now();
2827
+ return new Promise((resolve) => {
2828
+ const schedule = (fn) => {
2829
+ raf = -1;
2830
+ const id = requestAnimationFrame(fn);
2831
+ if (raf === -1) raf = id;
2832
+ };
2833
+ const tick = () => {
2834
+ if (token !== runToken) {
2835
+ raf = 0;
2836
+ resolve();
2837
+ return;
2838
+ }
2839
+ const t = typeof performance !== "undefined" ? performance.now() : Date.now();
2840
+ const p = Math.min(1, (t - t0) / duration);
2841
+ progress.value = from + (to - from) * p;
2842
+ scheduler.invalidate();
2843
+ if (p >= 1) {
2844
+ raf = 0;
2845
+ resolve();
2846
+ return;
2847
+ }
2848
+ schedule(tick);
2849
+ };
2850
+ schedule(tick);
2851
+ });
2852
+ }
2853
+ async function enter() {
2854
+ cancel();
2855
+ rendered.value = true;
2856
+ visible.value = true;
2857
+ phase.value = "enter";
2858
+ progress.value = 0;
2859
+ scheduler.invalidate();
2860
+ await runHook(props.beforeEnter, "enter", 0);
2861
+ await runHook(props.enter, "enter", 0);
2862
+ await animate(0, 1);
2863
+ progress.value = 1;
2864
+ phase.value = "idle";
2865
+ scheduler.invalidate();
2866
+ await runHook(props.afterEnter, "enter", 1);
2867
+ }
2868
+ async function leave() {
2869
+ cancel();
2870
+ rendered.value = true;
2871
+ visible.value = true;
2872
+ phase.value = "leave";
2873
+ progress.value = 1;
2874
+ scheduler.invalidate();
2875
+ await runHook(props.beforeLeave, "leave", 1);
2876
+ await runHook(props.leave, "leave", 1);
2877
+ await animate(1, 0);
2878
+ progress.value = 0;
2879
+ phase.value = "idle";
2880
+ visible.value = false;
2881
+ rendered.value = false;
2882
+ scheduler.invalidate();
2883
+ await runHook(props.afterLeave, "leave", 0);
2884
+ }
2885
+ watch(() => props.show, (next, prev) => {
2886
+ if (next === prev) return;
2887
+ next ? enter() : leave();
2888
+ });
2889
+ onBeforeUnmount(() => cancel());
2890
+ return () => {
2891
+ if (!rendered.value) return null;
2892
+ return slots.default?.({
2893
+ phase: phase.value,
2894
+ progress: progress.value
2895
+ }) ?? null;
2896
+ };
2897
+ }
2898
+ });
2899
+
2900
+ //#endregion
2901
+ //#region src/vue/composables/use-runtime.ts
2902
+ function useTerminalRuntime() {
2903
+ return useTerminal().runtime;
2904
+ }
2905
+
2906
+ //#endregion
2907
+ //#region src/vue/router/context.ts
2908
+ const TerminalRouterKey = injectionKey("TerminalRouter");
2909
+ const TerminalRouteKey = injectionKey("TerminalRoute");
2910
+
2911
+ //#endregion
2912
+ //#region src/vue/router/composables.ts
2913
+ function useRouter() {
2914
+ const r = inject(TerminalRouterKey, null);
2915
+ if (!r) throw new Error("TerminalRouter is missing");
2916
+ return r;
2917
+ }
2918
+ function useRoute() {
2919
+ const route = inject(TerminalRouteKey, null);
2920
+ if (!route) throw new Error("TerminalRoute is missing");
2921
+ return computed(() => route.value);
2922
+ }
2923
+
2924
+ //#endregion
2925
+ //#region src/vue/router/router.ts
2926
+ function normalize(to, records) {
2927
+ const raw = typeof to === "string" ? { name: to } : to;
2928
+ const record = records.get(raw.name);
2929
+ return {
2930
+ name: raw.name,
2931
+ params: raw.params,
2932
+ meta: record?.meta
2933
+ };
2934
+ }
2935
+ function isRedirect(v) {
2936
+ return typeof v === "string" || typeof v === "object" && v != null && "name" in v;
2937
+ }
2938
+ function createTerminalRouter(options) {
2939
+ const records = new Map(options.routes.map((r) => [r.name, r]));
2940
+ const currentRoute = ref(normalize(options.initialRoute, records));
2941
+ const history = [currentRoute.value];
2942
+ let index = 0;
2943
+ const befores = [];
2944
+ const resolves = [];
2945
+ const afters = [];
2946
+ async function runGuards(list, to, from) {
2947
+ for (const guard of list) {
2948
+ const r = await guard(to, from);
2949
+ if (r === false) return false;
2950
+ if (isRedirect(r)) return r;
2951
+ }
2952
+ return true;
2953
+ }
2954
+ async function navigate(toRaw, mode) {
2955
+ const from = currentRoute.value;
2956
+ const to = normalize(toRaw, records);
2957
+ const fromRecord = records.get(from.name) ?? null;
2958
+ const toRecord = records.get(to.name) ?? null;
2959
+ const r1 = await runGuards(befores, to, from);
2960
+ if (r1 === false) return;
2961
+ if (isRedirect(r1)) return navigate(r1, mode);
2962
+ const r2 = await runGuards(resolves, to, from);
2963
+ if (r2 === false) return;
2964
+ if (isRedirect(r2)) return navigate(r2, mode);
2965
+ if (fromRecord?.onLeave) await fromRecord.onLeave(to, from);
2966
+ currentRoute.value = to;
2967
+ if (mode === "replace") history[index] = to;
2968
+ else if (mode === "push") {
2969
+ history.splice(index + 1);
2970
+ history.push(to);
2971
+ index = history.length - 1;
2972
+ }
2973
+ if (toRecord?.onEnter) await toRecord.onEnter(to, from);
2974
+ for (const hook of afters) hook(to, from);
2975
+ }
2976
+ const router = {
2977
+ currentRoute,
2978
+ async push(to) {
2979
+ await navigate(to, "push");
2980
+ },
2981
+ async replace(to) {
2982
+ await navigate(to, "replace");
2983
+ },
2984
+ async back() {
2985
+ if (index <= 0) return;
2986
+ const from = currentRoute.value;
2987
+ const to = history[index - 1];
2988
+ const fromRecord = records.get(from.name) ?? null;
2989
+ const toRecord = records.get(to.name) ?? null;
2990
+ const r1 = await runGuards(befores, to, from);
2991
+ if (r1 === false) return;
2992
+ if (isRedirect(r1)) return navigate(r1, "push");
2993
+ const r2 = await runGuards(resolves, to, from);
2994
+ if (r2 === false) return;
2995
+ if (isRedirect(r2)) return navigate(r2, "push");
2996
+ if (fromRecord?.onLeave) await fromRecord.onLeave(to, from);
2997
+ index -= 1;
2998
+ currentRoute.value = to;
2999
+ if (toRecord?.onEnter) await toRecord.onEnter(to, from);
3000
+ for (const hook of afters) hook(to, from);
3001
+ },
3002
+ beforeEach(guard) {
3003
+ befores.push(guard);
3004
+ return () => {
3005
+ const idx = befores.indexOf(guard);
3006
+ if (idx >= 0) befores.splice(idx, 1);
3007
+ };
3008
+ },
3009
+ beforeResolve(guard) {
3010
+ resolves.push(guard);
3011
+ return () => {
3012
+ const idx = resolves.indexOf(guard);
3013
+ if (idx >= 0) resolves.splice(idx, 1);
3014
+ };
3015
+ },
3016
+ afterEach(hook) {
3017
+ afters.push(hook);
3018
+ return () => {
3019
+ const idx = afters.indexOf(hook);
3020
+ if (idx >= 0) afters.splice(idx, 1);
3021
+ };
3022
+ },
3023
+ install(app) {
3024
+ app.provide(TerminalRouterKey, router);
3025
+ app.provide(TerminalRouteKey, currentRoute);
3026
+ }
3027
+ };
3028
+ return router;
3029
+ }
3030
+ function resolveTerminalRouteComponent(router, routes) {
3031
+ const cur = router.currentRoute.value;
3032
+ const rec = routes.find((r) => r.name === cur.name);
3033
+ return rec?.component ?? null;
3034
+ }
3035
+
3036
+ //#endregion
3037
+ //#region src/vue/router/RouterView.ts
3038
+ const TRouterView = defineComponent({
3039
+ name: "TRouterView",
3040
+ props: {
3041
+ routes: {
3042
+ type: Array,
3043
+ required: true
3044
+ },
3045
+ forceRemount: {
3046
+ type: Boolean,
3047
+ default: true
3048
+ }
3049
+ },
3050
+ setup(props) {
3051
+ const router = inject(TerminalRouterKey, null);
3052
+ if (!router) throw new Error("TerminalRouter is missing (did you forget to app.use(router)?)");
3053
+ const key = ref(0);
3054
+ watch(() => router.currentRoute.value, () => {
3055
+ if (props.forceRemount) key.value++;
3056
+ }, { deep: true });
3057
+ const component = computed(() => resolveTerminalRouteComponent(router, props.routes));
3058
+ return () => {
3059
+ const C = component.value;
3060
+ return C ? h(C, { key: props.forceRemount ? key.value : void 0 }) : null;
3061
+ };
3062
+ }
3063
+ });
3064
+
3065
+ //#endregion
3066
+ export { TAnchor, TBox, TDebugOverlay, TDialog, TFlow, TInput, TInputBox, TInputPluginsContextKey, TJsonEditor, TList, TMultilineModal, TPathPicker, TRenderLayer, TRenderPlane, TRouterView, TSelect, TText, TTransition, TView, TerminalProvider, applyWheelScroll, clearTextCaches, createPromptMentionPlugin, createTInputHostPlugin, createTerminalRouter, createTextRestrictionPlugin, createWheelScrollState, formatInlineCellLine, lintJsonText, padEndByCells$1 as padEndByCells, sanitizeInlineText, sanitizeTextBlock, sliceByCells, sliceByCellsRange, spaces, textCellWidth$1 as textCellWidth, useLayout, useRenderNode, useRoute, useRouter, useTerminal, useTerminalNode, useTerminalRuntime, useVisibility, wrapByCells };