@bubblebrain-ai/bubble 0.0.24 → 0.0.25
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.
- package/README.md +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +22 -6
- package/dist/goal/format.js +34 -4
- package/dist/goal/store.d.ts +3 -0
- package/dist/goal/store.js +14 -1
- package/dist/goal/usage.d.ts +2 -0
- package/dist/goal/usage.js +3 -0
- package/dist/main.js +23 -42
- package/dist/provider.js +20 -5
- package/dist/tui/detect-theme.d.ts +1 -0
- package/dist/tui/detect-theme.js +23 -0
- package/dist/tui/image-display.d.ts +13 -0
- package/dist/tui/image-display.js +49 -0
- package/dist/tui/input-history.d.ts +37 -6
- package/dist/tui/input-history.js +194 -23
- package/dist/tui/model-switch.d.ts +42 -0
- package/dist/tui/model-switch.js +55 -0
- package/dist/tui-ink/app.d.ts +32 -2
- package/dist/tui-ink/app.js +1360 -522
- package/dist/tui-ink/approval/select.js +10 -0
- package/dist/tui-ink/detect-theme.d.ts +1 -2
- package/dist/tui-ink/detect-theme.js +1 -87
- package/dist/tui-ink/display-history.d.ts +1 -0
- package/dist/tui-ink/display-history.js +11 -0
- package/dist/tui-ink/feedback-dialog.js +10 -0
- package/dist/tui-ink/feishu-setup-picker.js +10 -0
- package/dist/tui-ink/footer.d.ts +1 -0
- package/dist/tui-ink/footer.js +8 -2
- package/dist/tui-ink/input-box.d.ts +70 -9
- package/dist/tui-ink/input-box.js +354 -120
- package/dist/tui-ink/input-history.d.ts +1 -16
- package/dist/tui-ink/input-history.js +1 -79
- package/dist/tui-ink/input-queue.d.ts +12 -0
- package/dist/tui-ink/input-queue.js +17 -0
- package/dist/tui-ink/key-events.d.ts +9 -0
- package/dist/tui-ink/key-events.js +8 -0
- package/dist/tui-ink/markdown.js +1 -1
- package/dist/tui-ink/message-list.d.ts +3 -1
- package/dist/tui-ink/message-list.js +42 -24
- package/dist/tui-ink/model-picker.d.ts +24 -2
- package/dist/tui-ink/model-picker.js +224 -20
- package/dist/tui-ink/plan-confirm.js +10 -0
- package/dist/tui-ink/question-dialog.js +10 -0
- package/dist/tui-ink/run.d.ts +10 -1
- package/dist/tui-ink/run.js +21 -28
- package/dist/tui-ink/session-picker.js +3 -0
- package/dist/tui-ink/submit-dedupe.d.ts +5 -0
- package/dist/tui-ink/submit-dedupe.js +25 -0
- package/dist/tui-ink/terminal-mouse.d.ts +13 -1
- package/dist/tui-ink/terminal-mouse.js +63 -21
- package/dist/tui-ink/theme.d.ts +6 -3
- package/dist/tui-ink/theme.js +10 -4
- package/dist/tui-ink/transcript-input.d.ts +8 -0
- package/dist/tui-ink/transcript-input.js +9 -0
- package/dist/tui-ink/transcript-viewport-math.d.ts +1 -2
- package/dist/tui-ink/transcript-viewport-math.js +1 -2
- package/dist/tui-ink/welcome.d.ts +1 -0
- package/dist/tui-ink/welcome.js +25 -28
- package/package.json +1 -5
- package/dist/tui/clipboard.d.ts +0 -1
- package/dist/tui/clipboard.js +0 -53
- package/dist/tui/escape-confirmation.d.ts +0 -15
- package/dist/tui/escape-confirmation.js +0 -30
- package/dist/tui/global-key-router.d.ts +0 -3
- package/dist/tui/global-key-router.js +0 -87
- package/dist/tui/markdown-inline.d.ts +0 -22
- package/dist/tui/markdown-inline.js +0 -68
- package/dist/tui/markdown-theme-rules.d.ts +0 -23
- package/dist/tui/markdown-theme-rules.js +0 -164
- package/dist/tui/markdown-theme.d.ts +0 -5
- package/dist/tui/markdown-theme.js +0 -27
- package/dist/tui/opencode-spinner.d.ts +0 -22
- package/dist/tui/opencode-spinner.js +0 -216
- package/dist/tui/prompt-keybindings.d.ts +0 -42
- package/dist/tui/prompt-keybindings.js +0 -35
- package/dist/tui/render-signature.d.ts +0 -1
- package/dist/tui/render-signature.js +0 -7
- package/dist/tui/run.d.ts +0 -67
- package/dist/tui/run.js +0 -10166
- package/dist/tui/sidebar-mcp.d.ts +0 -31
- package/dist/tui/sidebar-mcp.js +0 -62
- package/dist/tui/sidebar-state.d.ts +0 -12
- package/dist/tui/sidebar-state.js +0 -69
- package/dist/tui/streaming-tool-args.d.ts +0 -15
- package/dist/tui/streaming-tool-args.js +0 -30
- package/dist/tui/tool-renderers/fallback.d.ts +0 -2
- package/dist/tui/tool-renderers/fallback.js +0 -75
- package/dist/tui/tool-renderers/registry.d.ts +0 -3
- package/dist/tui/tool-renderers/registry.js +0 -11
- package/dist/tui/tool-renderers/subagent.d.ts +0 -2
- package/dist/tui/tool-renderers/subagent.js +0 -135
- package/dist/tui/tool-renderers/types.d.ts +0 -36
- package/dist/tui/tool-renderers/types.js +0 -1
- package/dist/tui/tool-renderers/write-preview.d.ts +0 -12
- package/dist/tui/tool-renderers/write-preview.js +0 -32
- package/dist/tui/tool-renderers/write.d.ts +0 -6
- package/dist/tui/tool-renderers/write.js +0 -88
- package/dist/tui-opentui/app.d.ts +0 -54
- package/dist/tui-opentui/app.js +0 -1371
- package/dist/tui-opentui/approval/approval-dialog.d.ts +0 -15
- package/dist/tui-opentui/approval/approval-dialog.js +0 -155
- package/dist/tui-opentui/approval/diff-view.d.ts +0 -9
- package/dist/tui-opentui/approval/diff-view.js +0 -43
- package/dist/tui-opentui/approval/select.d.ts +0 -37
- package/dist/tui-opentui/approval/select.js +0 -91
- package/dist/tui-opentui/detect-theme.d.ts +0 -2
- package/dist/tui-opentui/detect-theme.js +0 -87
- package/dist/tui-opentui/display-history.d.ts +0 -56
- package/dist/tui-opentui/display-history.js +0 -130
- package/dist/tui-opentui/edit-diff.d.ts +0 -11
- package/dist/tui-opentui/edit-diff.js +0 -57
- package/dist/tui-opentui/feedback-dialog.d.ts +0 -21
- package/dist/tui-opentui/feedback-dialog.js +0 -164
- package/dist/tui-opentui/feishu-setup-picker.d.ts +0 -7
- package/dist/tui-opentui/feishu-setup-picker.js +0 -272
- package/dist/tui-opentui/file-mentions.d.ts +0 -29
- package/dist/tui-opentui/file-mentions.js +0 -174
- package/dist/tui-opentui/footer.d.ts +0 -26
- package/dist/tui-opentui/footer.js +0 -40
- package/dist/tui-opentui/image-paste.d.ts +0 -54
- package/dist/tui-opentui/image-paste.js +0 -288
- package/dist/tui-opentui/input-box.d.ts +0 -32
- package/dist/tui-opentui/input-box.js +0 -462
- package/dist/tui-opentui/input-history.d.ts +0 -16
- package/dist/tui-opentui/input-history.js +0 -79
- package/dist/tui-opentui/markdown.d.ts +0 -66
- package/dist/tui-opentui/markdown.js +0 -127
- package/dist/tui-opentui/message-list.d.ts +0 -31
- package/dist/tui-opentui/message-list.js +0 -131
- package/dist/tui-opentui/model-picker.d.ts +0 -63
- package/dist/tui-opentui/model-picker.js +0 -450
- package/dist/tui-opentui/plan-confirm.d.ts +0 -9
- package/dist/tui-opentui/plan-confirm.js +0 -124
- package/dist/tui-opentui/question-dialog.d.ts +0 -10
- package/dist/tui-opentui/question-dialog.js +0 -110
- package/dist/tui-opentui/recent-activity.d.ts +0 -8
- package/dist/tui-opentui/recent-activity.js +0 -71
- package/dist/tui-opentui/run-session-picker.d.ts +0 -10
- package/dist/tui-opentui/run-session-picker.js +0 -28
- package/dist/tui-opentui/run.d.ts +0 -38
- package/dist/tui-opentui/run.js +0 -48
- package/dist/tui-opentui/session-picker.d.ts +0 -12
- package/dist/tui-opentui/session-picker.js +0 -120
- package/dist/tui-opentui/theme.d.ts +0 -89
- package/dist/tui-opentui/theme.js +0 -157
- package/dist/tui-opentui/todos.d.ts +0 -9
- package/dist/tui-opentui/todos.js +0 -45
- package/dist/tui-opentui/trace-groups.d.ts +0 -27
- package/dist/tui-opentui/trace-groups.js +0 -455
- package/dist/tui-opentui/use-terminal-size.d.ts +0 -4
- package/dist/tui-opentui/use-terminal-size.js +0 -5
- package/dist/tui-opentui/welcome.d.ts +0 -25
- package/dist/tui-opentui/welcome.js +0 -77
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect, useMemo } from "react";
|
|
3
3
|
import { Box, Text, useInput, usePaste, useStdout } from "ink";
|
|
4
|
+
import { isKeyReleaseEvent } from "./key-events.js";
|
|
4
5
|
import { useTheme } from "./theme.js";
|
|
5
6
|
import { encodeModel, decodeModel, displayModel, isUserVisibleProvider } from "../provider-registry.js";
|
|
6
7
|
import { listBuiltinModels } from "../model-catalog.js";
|
|
7
8
|
import { padVisual, truncateVisual } from "../text-display.js";
|
|
9
|
+
import { hasTerminalMouseSequence } from "./terminal-mouse.js";
|
|
10
|
+
import { getAvailableThinkingLevels, normalizeThinkingLevel } from "../provider-transform.js";
|
|
8
11
|
export { padVisual, truncateVisual } from "../text-display.js";
|
|
9
12
|
export function resolvePickerKeyAction(input, key) {
|
|
10
13
|
if (key.escape)
|
|
@@ -29,6 +32,8 @@ export function resolvePickerKeyAction(input, key) {
|
|
|
29
32
|
export function isPrintablePickerInput(input) {
|
|
30
33
|
if (!input)
|
|
31
34
|
return false;
|
|
35
|
+
if (hasTerminalMouseSequence(input))
|
|
36
|
+
return false;
|
|
32
37
|
if (input.startsWith("\x1b"))
|
|
33
38
|
return false;
|
|
34
39
|
if (isRawEscapeTail(input))
|
|
@@ -47,20 +52,116 @@ export function formatSkillPickerRow(skill, options) {
|
|
|
47
52
|
: `${marker}${nameCell}`;
|
|
48
53
|
return padVisual(truncateVisual(row, width), width);
|
|
49
54
|
}
|
|
55
|
+
export const MODEL_PICKER_MAX_BODY_ROWS = 10;
|
|
56
|
+
export const MODEL_PICKER_CHROME_ROWS = 13;
|
|
57
|
+
export function modelPickerBodyRows(termHeight) {
|
|
58
|
+
const rows = Number.isFinite(termHeight) ? Math.floor(termHeight) : 24;
|
|
59
|
+
return Math.max(1, Math.min(MODEL_PICKER_MAX_BODY_ROWS, rows - MODEL_PICKER_CHROME_ROWS));
|
|
60
|
+
}
|
|
61
|
+
export function clampPickerIndex(index, length) {
|
|
62
|
+
if (length <= 0)
|
|
63
|
+
return 0;
|
|
64
|
+
return Math.max(0, Math.min(length - 1, index));
|
|
65
|
+
}
|
|
66
|
+
export function pickerWindowStart(selectedIndex, length, visibleRows) {
|
|
67
|
+
const rows = Math.max(1, Math.floor(visibleRows));
|
|
68
|
+
const safeIndex = clampPickerIndex(selectedIndex, length);
|
|
69
|
+
const maxStart = Math.max(0, length - rows);
|
|
70
|
+
return Math.max(0, Math.min(maxStart, safeIndex - Math.floor(rows / 2)));
|
|
71
|
+
}
|
|
72
|
+
export function padPickerRows(rows, bodyRows, width) {
|
|
73
|
+
const rowCount = Math.max(1, Math.floor(bodyRows));
|
|
74
|
+
const rowWidth = Math.max(1, Math.floor(width));
|
|
75
|
+
const padded = rows.slice(0, rowCount).map((row) => padVisual(truncateVisual(row, rowWidth), rowWidth));
|
|
76
|
+
while (padded.length < rowCount) {
|
|
77
|
+
padded.push(padVisual("", rowWidth));
|
|
78
|
+
}
|
|
79
|
+
return padded;
|
|
80
|
+
}
|
|
81
|
+
export function formatReasoningLevelsLabel(levels) {
|
|
82
|
+
const normalized = levels.length > 0 ? levels : ["off"];
|
|
83
|
+
return `effort ${normalized.join("/")}`;
|
|
84
|
+
}
|
|
85
|
+
export function formatModelPickerRow(option, options) {
|
|
86
|
+
const width = Math.max(24, options.width);
|
|
87
|
+
const marker = options.selected ? "> " : " ";
|
|
88
|
+
const label = option.label.replace(/\s+/g, " ").trim();
|
|
89
|
+
const provider = option.providerBadge.replace(/\s+/g, " ").trim();
|
|
90
|
+
const effort = formatReasoningLevelsLabel(option.reasoningLevels);
|
|
91
|
+
const current = options.current ? " ●" : "";
|
|
92
|
+
const providerWidth = Math.max(6, Math.min(16, Math.floor(width * 0.18)));
|
|
93
|
+
const effortWidth = Math.max(12, Math.min(30, Math.floor(width * 0.32)));
|
|
94
|
+
const labelWidth = Math.max(6, width - marker.length - providerWidth - effortWidth - 4 - current.length);
|
|
95
|
+
const row = [
|
|
96
|
+
marker,
|
|
97
|
+
padVisual(truncateVisual(label, labelWidth), labelWidth),
|
|
98
|
+
" ",
|
|
99
|
+
padVisual(truncateVisual(provider, providerWidth), providerWidth),
|
|
100
|
+
" ",
|
|
101
|
+
truncateVisual(effort, effortWidth),
|
|
102
|
+
current,
|
|
103
|
+
].join("");
|
|
104
|
+
return padVisual(truncateVisual(row, width), width);
|
|
105
|
+
}
|
|
106
|
+
export function formatEffortPickerRow(level, options) {
|
|
107
|
+
const width = Math.max(24, options.width);
|
|
108
|
+
const marker = options.selected ? "> " : " ";
|
|
109
|
+
const row = `${marker}${level} ${effortDescription(level)}`;
|
|
110
|
+
return padVisual(truncateVisual(row, width), width);
|
|
111
|
+
}
|
|
112
|
+
export function formatNoModelResultsRow(query, width) {
|
|
113
|
+
const rowWidth = Math.max(24, width);
|
|
114
|
+
const normalizedQuery = query.replace(/\s+/g, " ").trim();
|
|
115
|
+
const row = normalizedQuery
|
|
116
|
+
? ` No models match "${normalizedQuery}"`
|
|
117
|
+
: " No models available";
|
|
118
|
+
return padVisual(truncateVisual(row, rowWidth), rowWidth);
|
|
119
|
+
}
|
|
120
|
+
export function preferredEffortIndex(option, currentThinkingLevel) {
|
|
121
|
+
const preferred = normalizeThinkingLevel(currentThinkingLevel, option.reasoningLevels);
|
|
122
|
+
const index = option.reasoningLevels.indexOf(preferred);
|
|
123
|
+
return index >= 0 ? index : 0;
|
|
124
|
+
}
|
|
125
|
+
export function shouldOpenEffortPicker(option) {
|
|
126
|
+
return option.reasoningLevels.length > 1;
|
|
127
|
+
}
|
|
128
|
+
function effortDescription(level) {
|
|
129
|
+
switch (level) {
|
|
130
|
+
case "off":
|
|
131
|
+
return "no reasoning effort";
|
|
132
|
+
case "minimal":
|
|
133
|
+
return "fastest reasoning";
|
|
134
|
+
case "low":
|
|
135
|
+
return "light reasoning";
|
|
136
|
+
case "medium":
|
|
137
|
+
return "balanced reasoning";
|
|
138
|
+
case "high":
|
|
139
|
+
return "deeper reasoning";
|
|
140
|
+
case "xhigh":
|
|
141
|
+
return "extra high reasoning";
|
|
142
|
+
case "max":
|
|
143
|
+
return "maximum provider effort";
|
|
144
|
+
default:
|
|
145
|
+
return "reasoning effort";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
50
148
|
function normalizeEscapeSequence(input) {
|
|
51
149
|
return input.startsWith("\x1b") ? input.slice(1) : input;
|
|
52
150
|
}
|
|
53
151
|
function isRawEscapeTail(input) {
|
|
54
152
|
return /^(?:O[ABCDHF]|\[[\d;:]*[A-Za-z~])$/.test(input);
|
|
55
153
|
}
|
|
56
|
-
export function ModelPicker({ registry, current, recent, onSelect, onCancel }) {
|
|
154
|
+
export function ModelPicker({ registry, current, currentThinkingLevel, recent, onSelect, onCancel }) {
|
|
57
155
|
const theme = useTheme();
|
|
58
156
|
const { stdout } = useStdout();
|
|
59
157
|
const termHeight = stdout?.rows || 24;
|
|
60
|
-
const
|
|
158
|
+
const terminalColumns = stdout?.columns || 80;
|
|
159
|
+
const bodyRows = modelPickerBodyRows(termHeight);
|
|
160
|
+
const rowWidth = Math.max(36, Math.min(110, terminalColumns - 6));
|
|
61
161
|
const [rawOptions, setRawOptions] = useState(() => buildLocalModelOptions(registry, current, recent));
|
|
62
162
|
const [query, setQuery] = useState("");
|
|
63
163
|
const [selectedIndex, setSelectedIndex] = useState(() => preferredModelIndex(buildLocalModelOptions(registry, current, recent), current));
|
|
164
|
+
const [phase, setPhase] = useState({ kind: "model" });
|
|
64
165
|
useEffect(() => {
|
|
65
166
|
let cancelled = false;
|
|
66
167
|
const localOptions = buildLocalModelOptions(registry, current, recent);
|
|
@@ -103,13 +204,21 @@ export function ModelPicker({ registry, current, recent, onSelect, onCancel }) {
|
|
|
103
204
|
if (current && !seen.has(current)) {
|
|
104
205
|
const { providerId } = decodeModel(current);
|
|
105
206
|
const provider = enabled.find((p) => p.id === providerId);
|
|
106
|
-
opts.unshift({
|
|
207
|
+
opts.unshift({
|
|
208
|
+
id: current,
|
|
209
|
+
label: displayModel(current),
|
|
210
|
+
group: "Current",
|
|
211
|
+
providerBadge: provider?.name || providerId || "",
|
|
212
|
+
reasoningLevels: reasoningLevelsForModel(current),
|
|
213
|
+
});
|
|
107
214
|
}
|
|
108
215
|
if (!cancelled) {
|
|
109
216
|
setRawOptions(opts);
|
|
110
217
|
setSelectedIndex((index) => {
|
|
111
218
|
const currentIndex = preferredModelIndex(opts, current);
|
|
112
|
-
return index === preferredModelIndex(localOptions, current)
|
|
219
|
+
return index === preferredModelIndex(localOptions, current)
|
|
220
|
+
? currentIndex
|
|
221
|
+
: clampPickerIndex(index, opts.length);
|
|
113
222
|
});
|
|
114
223
|
}
|
|
115
224
|
}
|
|
@@ -125,23 +234,59 @@ export function ModelPicker({ registry, current, recent, onSelect, onCancel }) {
|
|
|
125
234
|
return rawOptions.filter((opt) => opt.label.toLowerCase().includes(q) || opt.providerBadge.toLowerCase().includes(q));
|
|
126
235
|
}, [rawOptions, query]);
|
|
127
236
|
useInput((input, key) => {
|
|
237
|
+
if (isKeyReleaseEvent(key))
|
|
238
|
+
return;
|
|
128
239
|
const action = resolvePickerKeyAction(input, key);
|
|
240
|
+
if (phase.kind === "effort") {
|
|
241
|
+
const levels = phase.model.reasoningLevels;
|
|
242
|
+
if (action === "escape" || action === "backspace" || action === "delete") {
|
|
243
|
+
setPhase({ kind: "model" });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (action === "enter") {
|
|
247
|
+
onSelect(phase.model.id, levels[clampPickerIndex(phase.selectedIndex, levels.length)] ?? "off");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (action === "up") {
|
|
251
|
+
setPhase((currentPhase) => currentPhase.kind === "effort"
|
|
252
|
+
? { ...currentPhase, selectedIndex: clampPickerIndex(currentPhase.selectedIndex - 1, levels.length) }
|
|
253
|
+
: currentPhase);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (action === "down") {
|
|
257
|
+
setPhase((currentPhase) => currentPhase.kind === "effort"
|
|
258
|
+
? { ...currentPhase, selectedIndex: clampPickerIndex(currentPhase.selectedIndex + 1, levels.length) }
|
|
259
|
+
: currentPhase);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
129
264
|
if (action === "escape") {
|
|
130
265
|
onCancel();
|
|
131
266
|
return;
|
|
132
267
|
}
|
|
133
268
|
if (action === "enter") {
|
|
134
|
-
const opt = options[selectedIndex];
|
|
135
|
-
if (opt)
|
|
136
|
-
|
|
269
|
+
const opt = options[clampPickerIndex(selectedIndex, options.length)];
|
|
270
|
+
if (opt) {
|
|
271
|
+
if (shouldOpenEffortPicker(opt)) {
|
|
272
|
+
setPhase({
|
|
273
|
+
kind: "effort",
|
|
274
|
+
model: opt,
|
|
275
|
+
selectedIndex: preferredEffortIndex(opt, currentThinkingLevel),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
onSelect(opt.id, opt.reasoningLevels[0] ?? "off");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
137
282
|
return;
|
|
138
283
|
}
|
|
139
284
|
if (action === "up") {
|
|
140
|
-
setSelectedIndex((i) =>
|
|
285
|
+
setSelectedIndex((i) => clampPickerIndex(i - 1, options.length));
|
|
141
286
|
return;
|
|
142
287
|
}
|
|
143
288
|
if (action === "down") {
|
|
144
|
-
setSelectedIndex((i) =>
|
|
289
|
+
setSelectedIndex((i) => clampPickerIndex(i + 1, options.length));
|
|
145
290
|
return;
|
|
146
291
|
}
|
|
147
292
|
if (action === "backspace" || action === "delete") {
|
|
@@ -161,22 +306,67 @@ export function ModelPicker({ registry, current, recent, onSelect, onCancel }) {
|
|
|
161
306
|
return;
|
|
162
307
|
}
|
|
163
308
|
});
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
309
|
+
const safeSelectedIndex = clampPickerIndex(selectedIndex, options.length);
|
|
310
|
+
const start = pickerWindowStart(safeSelectedIndex, options.length, bodyRows);
|
|
311
|
+
const visible = options.slice(start, start + bodyRows);
|
|
312
|
+
const rawModelRows = options.length === 0
|
|
313
|
+
? [{
|
|
314
|
+
key: "no-results",
|
|
315
|
+
row: formatNoModelResultsRow(query, rowWidth),
|
|
316
|
+
selected: false,
|
|
317
|
+
}]
|
|
318
|
+
: visible.map((opt, i) => {
|
|
319
|
+
const actualIndex = start + i;
|
|
320
|
+
const isSelected = actualIndex === safeSelectedIndex;
|
|
321
|
+
return {
|
|
322
|
+
key: opt.id,
|
|
323
|
+
row: formatModelPickerRow(opt, {
|
|
324
|
+
selected: isSelected,
|
|
325
|
+
current: opt.id === current,
|
|
326
|
+
width: rowWidth,
|
|
327
|
+
}),
|
|
328
|
+
selected: isSelected,
|
|
329
|
+
};
|
|
330
|
+
});
|
|
331
|
+
const modelRows = padPickerRows(rawModelRows.map((row) => row.row), bodyRows, rowWidth).map((row, index) => ({
|
|
332
|
+
key: rawModelRows[index]?.key ?? `blank-${index}`,
|
|
333
|
+
row,
|
|
334
|
+
selected: rawModelRows[index]?.selected ?? false,
|
|
335
|
+
}));
|
|
336
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, paddingX: 1, borderStyle: "round", borderColor: theme.borderActive, children: [_jsx(Text, { bold: true, color: theme.accent, children: phase.kind === "effort" ? "Select Reasoning Effort" : "Select Model" }), phase.kind === "effort" ? (_jsx(EffortPickerView, { model: phase.model, selectedIndex: phase.selectedIndex, bodyRows: bodyRows, rowWidth: rowWidth })) : (_jsxs(_Fragment, { children: [_jsx(SearchField, { query: query, placeholder: "Type to search models...", width: rowWidth }), _jsx(Text, { color: theme.muted, children: "\u2191/\u2193 navigate \u00B7 Enter choose effort \u00B7 Esc cancel \u00B7 Backspace clear" })] })), phase.kind === "model" && _jsx(Box, { flexDirection: "column", height: bodyRows, overflow: "hidden", marginTop: 1, children: modelRows.map(({ key, row, selected }) => (_jsx(Box, { height: 1, overflow: "hidden", children: _jsx(Text, { color: selected ? theme.accent : (key === "no-results" ? theme.muted : undefined), bold: selected, children: row }) }, key))) })] }));
|
|
337
|
+
}
|
|
338
|
+
function EffortPickerView({ model, selectedIndex, bodyRows, rowWidth, }) {
|
|
339
|
+
const theme = useTheme();
|
|
340
|
+
const safeSelectedIndex = clampPickerIndex(selectedIndex, model.reasoningLevels.length);
|
|
341
|
+
const rawRows = model.reasoningLevels.map((level, index) => ({
|
|
342
|
+
key: level,
|
|
343
|
+
row: formatEffortPickerRow(level, {
|
|
344
|
+
selected: index === safeSelectedIndex,
|
|
345
|
+
width: rowWidth,
|
|
346
|
+
}),
|
|
347
|
+
selected: index === safeSelectedIndex,
|
|
348
|
+
}));
|
|
349
|
+
const effortRows = padPickerRows(rawRows.map((row) => row.row), bodyRows, rowWidth).map((row, index) => ({
|
|
350
|
+
key: rawRows[index]?.key ?? `blank-${index}`,
|
|
351
|
+
row,
|
|
352
|
+
selected: rawRows[index]?.selected ?? false,
|
|
353
|
+
}));
|
|
354
|
+
const modelDetail = padVisual(truncateVisual(`${model.label} · ${model.providerBadge}`, rowWidth), rowWidth);
|
|
355
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Box, { height: 1, overflow: "hidden", children: _jsx(Text, { color: theme.muted, children: modelDetail }) }), _jsx(Text, { color: theme.muted, children: "\u2191/\u2193 navigate \u00B7 Enter select \u00B7 Esc back" }), _jsx(Box, { flexDirection: "column", height: bodyRows, overflow: "hidden", marginTop: 1, children: effortRows.map(({ key, row, selected }) => (_jsx(Box, { height: 1, overflow: "hidden", children: _jsx(Text, { color: selected ? theme.accent : undefined, bold: selected, children: row }) }, key))) })] }));
|
|
171
356
|
}
|
|
172
|
-
function SearchField({ query, placeholder }) {
|
|
357
|
+
function SearchField({ query, placeholder, width }) {
|
|
173
358
|
const theme = useTheme();
|
|
174
359
|
const [cursorVisible, setCursorVisible] = useState(true);
|
|
175
360
|
useEffect(() => {
|
|
176
361
|
const t = setInterval(() => setCursorVisible((v) => !v), 500);
|
|
177
362
|
return () => clearInterval(t);
|
|
178
363
|
}, []);
|
|
179
|
-
|
|
364
|
+
const contentBudget = width ? Math.max(1, width - 3) : undefined;
|
|
365
|
+
const visibleQuery = contentBudget ? truncateVisual(query, contentBudget) : query;
|
|
366
|
+
const visiblePlaceholder = !query
|
|
367
|
+
? (contentBudget ? truncateVisual(` ${placeholder}`, contentBudget) : ` ${placeholder}`)
|
|
368
|
+
: "";
|
|
369
|
+
return (_jsxs(Box, { height: 1, overflow: "hidden", marginTop: 1, children: [_jsx(Text, { color: theme.accent, children: "❯ " }), _jsx(Text, { children: visibleQuery }), _jsx(Text, { color: theme.accent, inverse: cursorVisible, children: " " }), visiblePlaceholder && _jsx(Text, { color: theme.muted, dimColor: true, children: visiblePlaceholder })] }));
|
|
180
370
|
}
|
|
181
371
|
export function buildLocalModelOptions(registry, current, recent) {
|
|
182
372
|
const enabled = registry.getEnabled();
|
|
@@ -210,6 +400,7 @@ export function buildLocalModelOptions(registry, current, recent) {
|
|
|
210
400
|
label: displayModel(current),
|
|
211
401
|
group: "Current",
|
|
212
402
|
providerBadge: provider?.name || providerId || "",
|
|
403
|
+
reasoningLevels: reasoningLevelsForModel(current),
|
|
213
404
|
});
|
|
214
405
|
}
|
|
215
406
|
return opts;
|
|
@@ -231,12 +422,19 @@ function appendModelOption(options, seen, option) {
|
|
|
231
422
|
if (seen.has(option.id))
|
|
232
423
|
return;
|
|
233
424
|
seen.add(option.id);
|
|
234
|
-
options.push(
|
|
425
|
+
options.push({
|
|
426
|
+
...option,
|
|
427
|
+
reasoningLevels: option.reasoningLevels ?? reasoningLevelsForModel(option.id),
|
|
428
|
+
});
|
|
235
429
|
}
|
|
236
430
|
function preferredModelIndex(options, current) {
|
|
237
431
|
const idx = options.findIndex((option) => option.id === current);
|
|
238
432
|
return idx >= 0 ? idx : 0;
|
|
239
433
|
}
|
|
434
|
+
function reasoningLevelsForModel(model) {
|
|
435
|
+
const { providerId, modelId } = decodeModel(model);
|
|
436
|
+
return getAvailableThinkingLevels(providerId || "openai", modelId);
|
|
437
|
+
}
|
|
240
438
|
export function ProviderPicker({ providers, current, onSelect, onCancel, title }) {
|
|
241
439
|
const theme = useTheme();
|
|
242
440
|
const { stdout } = useStdout();
|
|
@@ -247,6 +445,8 @@ export function ProviderPicker({ providers, current, onSelect, onCancel, title }
|
|
|
247
445
|
return idx >= 0 ? idx : 0;
|
|
248
446
|
});
|
|
249
447
|
useInput((input, key) => {
|
|
448
|
+
if (isKeyReleaseEvent(key))
|
|
449
|
+
return;
|
|
250
450
|
const action = resolvePickerKeyAction(input, key);
|
|
251
451
|
if (action === "escape") {
|
|
252
452
|
onCancel();
|
|
@@ -294,6 +494,8 @@ export function KeyPicker({ providerName, onSubmit, onCancel }) {
|
|
|
294
494
|
const theme = useTheme();
|
|
295
495
|
const [value, setValue] = useState("");
|
|
296
496
|
useInput((input, key) => {
|
|
497
|
+
if (isKeyReleaseEvent(key))
|
|
498
|
+
return;
|
|
297
499
|
const action = resolvePickerKeyAction(input, key);
|
|
298
500
|
if (action === "escape") {
|
|
299
501
|
onCancel();
|
|
@@ -338,6 +540,8 @@ export function SkillPicker({ skills, onSelect, onCancel }) {
|
|
|
338
540
|
return skills.filter((skill) => skill.name.toLowerCase().includes(q) || skill.description.toLowerCase().includes(q));
|
|
339
541
|
}, [query, skills]);
|
|
340
542
|
useInput((input, key) => {
|
|
543
|
+
if (isKeyReleaseEvent(key))
|
|
544
|
+
return;
|
|
341
545
|
const action = resolvePickerKeyAction(input, key);
|
|
342
546
|
if (action === "escape") {
|
|
343
547
|
onCancel();
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
|
+
import { isKeyReleaseEvent } from "./key-events.js";
|
|
4
5
|
import { useTheme } from "./theme.js";
|
|
5
6
|
import { MarkdownContent } from "./markdown.js";
|
|
7
|
+
import { stripTerminalMouseSequences } from "./terminal-mouse.js";
|
|
6
8
|
export function PlanConfirm({ initialPlan, onApprove, onReject }) {
|
|
7
9
|
const theme = useTheme();
|
|
8
10
|
const [stage, setStage] = useState("view");
|
|
9
11
|
const [draft, setDraft] = useState(initialPlan);
|
|
10
12
|
const [cursor, setCursor] = useState(initialPlan.length);
|
|
11
13
|
useInput((input, key) => {
|
|
14
|
+
if (isKeyReleaseEvent(key))
|
|
15
|
+
return;
|
|
16
|
+
const strippedMouseInput = stripTerminalMouseSequences(input);
|
|
17
|
+
if (strippedMouseInput !== input) {
|
|
18
|
+
if (!strippedMouseInput)
|
|
19
|
+
return;
|
|
20
|
+
input = strippedMouseInput;
|
|
21
|
+
}
|
|
12
22
|
if (stage === "view") {
|
|
13
23
|
if (key.escape || input === "n" || input === "N") {
|
|
14
24
|
onReject();
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
|
+
import { isKeyReleaseEvent } from "./key-events.js";
|
|
4
5
|
import { useTheme } from "./theme.js";
|
|
6
|
+
import { stripTerminalMouseSequences } from "./terminal-mouse.js";
|
|
5
7
|
export function QuestionDialog({ request, onSubmit, onCancel }) {
|
|
6
8
|
const theme = useTheme();
|
|
7
9
|
const [index, setIndex] = useState(0);
|
|
@@ -51,6 +53,14 @@ export function QuestionDialog({ request, onSubmit, onCancel }) {
|
|
|
51
53
|
}));
|
|
52
54
|
};
|
|
53
55
|
useInput((input, key) => {
|
|
56
|
+
if (isKeyReleaseEvent(key))
|
|
57
|
+
return;
|
|
58
|
+
const strippedMouseInput = stripTerminalMouseSequences(input);
|
|
59
|
+
if (strippedMouseInput !== input) {
|
|
60
|
+
if (!strippedMouseInput)
|
|
61
|
+
return;
|
|
62
|
+
input = strippedMouseInput;
|
|
63
|
+
}
|
|
54
64
|
if (key.escape) {
|
|
55
65
|
onCancel();
|
|
56
66
|
return;
|
package/dist/tui-ink/run.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import type { Agent } from "../agent.js";
|
|
2
3
|
import type { CliArgs } from "../cli.js";
|
|
3
4
|
import type { SessionManager } from "../session.js";
|
|
@@ -23,9 +24,14 @@ export interface RunTuiOptions {
|
|
|
23
24
|
questionController?: QuestionController;
|
|
24
25
|
bashAllowlist?: BashAllowlist;
|
|
25
26
|
settingsManager?: SettingsManager;
|
|
27
|
+
switchSession?: (sessionFile: string) => {
|
|
28
|
+
manager: SessionManager;
|
|
29
|
+
} | {
|
|
30
|
+
error: string;
|
|
31
|
+
};
|
|
26
32
|
lspService?: LspService;
|
|
27
33
|
mcpManager?: McpManager;
|
|
28
|
-
/**
|
|
34
|
+
/** Shared with the model-facing goal tools and the Ink auto-continuation loop. */
|
|
29
35
|
goalStore?: import("../goal/store.js").GoalStore;
|
|
30
36
|
themeMode?: ThemeMode;
|
|
31
37
|
themeOverrides?: Record<string, string>;
|
|
@@ -38,7 +44,10 @@ export interface RunTuiOptions {
|
|
|
38
44
|
bypassEnabled?: boolean;
|
|
39
45
|
/** One-line "update available" notice rendered under the welcome banner version. */
|
|
40
46
|
updateNotice?: string;
|
|
47
|
+
/** Late update notice refresh surfaced after startup without restarting Ink. */
|
|
48
|
+
updateNoticeRefresh?: Promise<string | null>;
|
|
41
49
|
/** External lifecycle hooks, threaded into slash-command execution. */
|
|
42
50
|
hookController?: ExternalHookController;
|
|
43
51
|
}
|
|
52
|
+
export declare function createInkAppElement(agent: Agent, args: CliArgs, options: RunTuiOptions, onExit: (summary: ExitSummary) => void): React.ReactElement;
|
|
44
53
|
export declare function runTui(agent: Agent, args: CliArgs, options?: RunTuiOptions): Promise<ExitSummary | undefined>;
|
package/dist/tui-ink/run.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { render } from "ink";
|
|
3
3
|
import { App } from "./app.js";
|
|
4
|
-
import { MOUSE_REPORTING_DISABLE } from "./terminal-mouse.js";
|
|
5
|
-
// DECSET 1007: terminals translate the mouse wheel into Up/Down arrow keys
|
|
6
|
-
// while the alternate screen is active. Mouse reporting stays OFF on purpose
|
|
7
|
-
// so plain drag-select and copy keep their native terminal behavior; the
|
|
8
|
-
// composer classifies wheel-synthesized arrows vs real key presses.
|
|
9
|
-
const ALTERNATE_SCROLL_ENABLE = "\x1b[?1007h";
|
|
10
|
-
const ALTERNATE_SCROLL_DISABLE = "\x1b[?1007l";
|
|
4
|
+
import { ALTERNATE_SCROLL_DISABLE, MOUSE_REPORTING_DISABLE, MOUSE_REPORTING_ENABLE, } from "./terminal-mouse.js";
|
|
11
5
|
import { warmHighlighter } from "./code-highlight.js";
|
|
6
|
+
export function createInkAppElement(agent, args, options, onExit) {
|
|
7
|
+
return (_jsx(App, { agent: agent, args: args, sessionManager: options.sessionManager, switchSession: options.switchSession, createProvider: options.createProvider, registry: options.registry, skillRegistry: options.skillRegistry, planHandlerRef: options.planHandlerRef, approvalHandlerRef: options.approvalHandlerRef, questionController: options.questionController, bashAllowlist: options.bashAllowlist, settingsManager: options.settingsManager, lspService: options.lspService, mcpManager: options.mcpManager, goalStore: options.goalStore, themeMode: options.themeMode, themeOverrides: options.themeOverrides, detectedTheme: options.detectedTheme, onThemeModeChange: options.onThemeModeChange, flushMemory: options.flushMemory, runMemoryCompaction: options.runMemoryCompaction, runMemorySummary: options.runMemorySummary, runMemoryRefresh: options.runMemoryRefresh, bypassEnabled: options.bypassEnabled, updateNotice: options.updateNotice, updateNoticeRefresh: options.updateNoticeRefresh, hookController: options.hookController, onExit: onExit }));
|
|
8
|
+
}
|
|
12
9
|
/**
|
|
13
10
|
* Best-effort terminal restore for abnormal exits. DECSET mouse modes are
|
|
14
11
|
* global terminal state — if the process dies without disabling them, the
|
|
@@ -49,15 +46,15 @@ export async function runTui(agent, args, options = {}) {
|
|
|
49
46
|
};
|
|
50
47
|
process.on("uncaughtException", onFatalError);
|
|
51
48
|
process.on("SIGTERM", onSigterm);
|
|
52
|
-
const instance = render(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
const instance = render(createInkAppElement(agent, args, options, (summary) => {
|
|
50
|
+
// The app already called useApp().exit() inside requestExit, which
|
|
51
|
+
// triggers Ink's own unmount + TTY restore. waitUntilExit() below is
|
|
52
|
+
// the canonical signal that we're done — we deliberately do *not*
|
|
53
|
+
// call instance.unmount() again here to avoid double-teardown
|
|
54
|
+
// warnings on React 19. We capture the summary and render it after
|
|
55
|
+
// teardown so it lands in the real shell scrollback (Claude-Code style).
|
|
56
|
+
exitSummary = summary;
|
|
57
|
+
}), {
|
|
61
58
|
// Bubble owns Ctrl+C so it can route both raw ETX and kitty keyboard
|
|
62
59
|
// Ctrl+C through App.requestExit(). Ink's default only exits reliably
|
|
63
60
|
// for raw "\x03"; with kitty keyboard it can swallow the parsed
|
|
@@ -65,10 +62,7 @@ export async function runTui(agent, args, options = {}) {
|
|
|
65
62
|
exitOnCtrlC: false,
|
|
66
63
|
kittyKeyboard: {
|
|
67
64
|
mode: "enabled",
|
|
68
|
-
// reportEventTypes
|
|
69
|
-
// (kitty-enhanced, carry eventType) apart from the bare arrow
|
|
70
|
-
// sequences terminals synthesize for wheel scrolling in alternate
|
|
71
|
-
// screen — see the classifier in input-box.tsx.
|
|
65
|
+
// reportEventTypes keeps release events out of text input.
|
|
72
66
|
flags: ["disambiguateEscapeCodes", "reportEventTypes"],
|
|
73
67
|
},
|
|
74
68
|
// The whole point of the Ink migration: render into the 1049 alternate
|
|
@@ -76,21 +70,20 @@ export async function runTui(agent, args, options = {}) {
|
|
|
76
70
|
// Ink degrades this to false automatically when stdout is not a TTY.
|
|
77
71
|
alternateScreen: true,
|
|
78
72
|
});
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
73
|
+
// Keep alternate-scroll disabled so wheel events do not alias keyboard
|
|
74
|
+
// arrows. Enable SGR mouse reporting after alt-screen entry so wheel events
|
|
75
|
+
// scroll the transcript through a distinct input channel.
|
|
82
76
|
if (process.stdout.isTTY) {
|
|
83
|
-
process.stdout.write(
|
|
77
|
+
process.stdout.write(ALTERNATE_SCROLL_DISABLE + MOUSE_REPORTING_ENABLE);
|
|
84
78
|
}
|
|
85
79
|
try {
|
|
86
80
|
await instance.waitUntilExit();
|
|
87
81
|
}
|
|
88
82
|
finally {
|
|
89
|
-
// Reset
|
|
90
|
-
//
|
|
91
|
-
// waitUntilExit() resolves.
|
|
83
|
+
// Reset mouse reporting before anything is printed to the primary screen;
|
|
84
|
+
// Ink has already left the alt screen by the time waitUntilExit() resolves.
|
|
92
85
|
if (process.stdout.isTTY) {
|
|
93
|
-
process.stdout.write(ALTERNATE_SCROLL_DISABLE);
|
|
86
|
+
process.stdout.write(ALTERNATE_SCROLL_DISABLE + MOUSE_REPORTING_DISABLE);
|
|
94
87
|
}
|
|
95
88
|
process.off("uncaughtException", onFatalError);
|
|
96
89
|
process.off("SIGTERM", onSigterm);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
3
|
import { Box, Text, useInput, useStdout } from "ink";
|
|
4
|
+
import { isKeyReleaseEvent } from "./key-events.js";
|
|
4
5
|
import { useTheme } from "./theme.js";
|
|
5
6
|
import { formatRelativeTime } from "./recent-activity.js";
|
|
6
7
|
import { padVisual, truncateVisual } from "../text-display.js";
|
|
@@ -19,6 +20,8 @@ export function SessionPicker({ currentCwd, currentSessions, allSessions, onSele
|
|
|
19
20
|
: Math.min(selectedSessionIdx, sessionRowIndices.length - 1);
|
|
20
21
|
const selectedRowIndex = sessionRowIndices[clampedIdx] ?? -1;
|
|
21
22
|
useInput((input, key) => {
|
|
23
|
+
if (isKeyReleaseEvent(key))
|
|
24
|
+
return;
|
|
22
25
|
if (key.escape) {
|
|
23
26
|
onCancel();
|
|
24
27
|
return;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SubmitPayload } from "./input-box.js";
|
|
2
|
+
export type StartingSubmitDecision = "accept" | "ignore" | "queue";
|
|
3
|
+
export declare function submitPayloadFingerprint(payload: SubmitPayload): string;
|
|
4
|
+
export declare function decideStartingSubmit(activeFingerprint: string | null, payload: SubmitPayload): StartingSubmitDecision;
|
|
5
|
+
export declare function decideStartingSubmitFingerprint(activeFingerprint: string | null, submitFingerprint: string): StartingSubmitDecision;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
function hashValue(value) {
|
|
3
|
+
return createHash("sha256").update(value).digest("hex");
|
|
4
|
+
}
|
|
5
|
+
export function submitPayloadFingerprint(payload) {
|
|
6
|
+
return JSON.stringify({
|
|
7
|
+
text: payload.text,
|
|
8
|
+
displayText: payload.displayText ?? "",
|
|
9
|
+
images: payload.images.map((image) => ({
|
|
10
|
+
mediaType: image.mediaType,
|
|
11
|
+
bytes: image.bytes,
|
|
12
|
+
filename: image.filename ?? "",
|
|
13
|
+
sourcePath: image.sourcePath ?? "",
|
|
14
|
+
dataUrlHash: hashValue(image.dataUrl),
|
|
15
|
+
})),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export function decideStartingSubmit(activeFingerprint, payload) {
|
|
19
|
+
return decideStartingSubmitFingerprint(activeFingerprint, submitPayloadFingerprint(payload));
|
|
20
|
+
}
|
|
21
|
+
export function decideStartingSubmitFingerprint(activeFingerprint, submitFingerprint) {
|
|
22
|
+
if (!activeFingerprint)
|
|
23
|
+
return "accept";
|
|
24
|
+
return activeFingerprint === submitFingerprint ? "ignore" : "queue";
|
|
25
|
+
}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const ALTERNATE_SCROLL_ENABLE = "\u001B[?1007h";
|
|
2
|
+
export declare const ALTERNATE_SCROLL_DISABLE = "\u001B[?1007l";
|
|
3
|
+
export declare const MOUSE_REPORTING_ENABLE = "\u001B[?1000h\u001B[?1006h";
|
|
4
|
+
export declare const MOUSE_REPORTING_DISABLE = "\u001B[?1003l\u001B[?1002l\u001B[?1000l\u001B[?1005l\u001B[?1006l\u001B[?1015l";
|
|
2
5
|
export type MouseWheelDirection = "up" | "down";
|
|
6
|
+
export interface TerminalMouseInput {
|
|
7
|
+
strippedInput: string;
|
|
8
|
+
wheelDirections: MouseWheelDirection[];
|
|
9
|
+
hasMouse: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function sanitizeTerminalMouseInput(input: string): TerminalMouseInput;
|
|
12
|
+
export declare function transcriptScrollLinesFromMouseInput(mouseInput: TerminalMouseInput, options: {
|
|
13
|
+
overlayActive: boolean;
|
|
14
|
+
}): number[];
|
|
3
15
|
export declare function stripTerminalMouseSequences(input: string): string;
|
|
4
16
|
export declare function hasTerminalMouseSequence(input: string): boolean;
|
|
5
17
|
export declare function parseTerminalMouseWheel(input: string): MouseWheelDirection[];
|