march-cli 0.1.29 → 0.1.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "march-cli",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "description": "March CLI — terminal-native coding agent with context reconstruction",
5
5
  "type": "module",
6
6
  "main": "./src/main.mjs",
@@ -1,6 +1,6 @@
1
1
  export function formatHelpLines() {
2
2
  return [
3
- "Commands: /new, /exit, /help, /hotkeys, /templates, /export jsonl, /export html, /export gist <jsonl|html>, /settings, /extensions, /providers, /providers <name>, /model, /models, /session, /status, /shell, /shell spawn [name], /save, /name, /copy, /mouse",
3
+ "Commands: /new, /exit, /help, /hotkeys, /templates, /export jsonl, /export html, /export gist <jsonl|html>, /settings, /extensions, /providers, /providers <name>, /model, /models, /session, /status, /shell, /shell spawn [name], /save, /name, /copy",
4
4
  "Sessions: /session opens previous sessions and restores the selected one.",
5
5
  "Shortcuts: Tab = toggle Do/Discuss, Esc = abort turn, Ctrl+C = abort turn / press twice to exit when idle, Ctrl+O = toggle tool output, Alt+S = shell pane, Alt+N = next shell, Alt+K/J = shell scroll, PageUp/PageDown = output scroll, Ctrl+G = external editor, Shift+Tab = thinking selector, Ctrl+T = thinking selector, Ctrl+L = model selector",
6
6
  ];
@@ -58,7 +58,6 @@ export function createJsonUI() {
58
58
  getInputText: () => "",
59
59
  insertTextAtCursor: () => {},
60
60
  openExternalEditor: () => {},
61
- toggleMouse: () => false,
62
61
  toggleToolOutput: () => false,
63
62
  requestExit: () => {},
64
63
  close: () => {},
@@ -148,7 +147,6 @@ export function createPlainUI() {
148
147
  getInputText: () => "",
149
148
  insertTextAtCursor: () => {},
150
149
  openExternalEditor: () => {},
151
- toggleMouse: () => false,
152
150
  toggleToolOutput: () => false,
153
151
  requestExit: () => {},
154
152
  close: () => {},
@@ -14,7 +14,6 @@ const MARCH_COMMANDS = [
14
14
  { name: "copy", description: "Copy last assistant response to clipboard" },
15
15
  { name: "thinking", description: "Open thinking selector" },
16
16
  { name: "thinking list", description: "List available thinking levels" },
17
- { name: "mouse", description: "Toggle mouse wheel and TUI selection copy" },
18
17
  { name: "hotkeys", description: "Show keyboard shortcuts and input prefixes" },
19
18
  { name: "templates", description: "List project prompt templates" },
20
19
  { name: "export jsonl", description: "Export current session turns as JSONL" },
@@ -105,12 +105,6 @@ export async function handleSlashCommand(trimmed, {
105
105
  return { handled: true };
106
106
  }
107
107
 
108
- if (trimmed === "/mouse") {
109
- const on = ui.toggleMouse();
110
- ui.writeln(on ? "Mouse tracking: ON (wheel scroll and TUI selection copy enabled)" : "Mouse tracking: OFF (native terminal selection enabled)");
111
- return { handled: true };
112
- }
113
-
114
108
  if (trimmed === "/status") {
115
109
  for (const line of statusCommand({
116
110
  runner,
@@ -145,6 +139,11 @@ export async function handleSlashCommand(trimmed, {
145
139
  return { handled: true };
146
140
  }
147
141
 
142
+ if (trimmed === "/mouse") {
143
+ ui.writeln("Mouse selection is always enabled.");
144
+ return { handled: true };
145
+ }
146
+
148
147
  const sessionSourceCommand = await handleSessionSourceCommand(trimmed, {
149
148
  ui,
150
149
  runner,
@@ -44,8 +44,7 @@ export function createMouseSelectionController({
44
44
  }
45
45
 
46
46
  return {
47
- handleMouseInput(data, mouseOn) {
48
- if (!mouseOn) return undefined;
47
+ handleMouseInput(data) {
49
48
  const mouse = parseMouseEvent(data);
50
49
  if (mouse?.type === "scroll") {
51
50
  if (shellDrawer.isVisible?.() && mouse.col > Math.floor((terminal.columns || 80) * 0.64)) {
@@ -76,7 +75,7 @@ export function createMouseSelectionController({
76
75
 
77
76
  handleCopyKey(data) {
78
77
  if (data !== "\x03") return undefined;
79
- const text = selection.text();
78
+ const text = selection.copyText?.() ?? selection.text();
80
79
  if (!text) return undefined;
81
80
  selection.clear();
82
81
  copySelectionText(text);
@@ -18,11 +18,12 @@ export class MainPaneLayout {
18
18
  const fixedHeight = statusTopLines.length + editorLines.length + statusBottomLines.length;
19
19
  const viewportHeight = Math.max(1, (this.terminal?.rows || 30) - fixedHeight);
20
20
  this.output.setViewportHeight(viewportHeight);
21
- const outputLines = this.output.render(safeWidth);
21
+ const outputView = this.output.renderSelectable?.(safeWidth) ?? { lines: this.output.render(safeWidth), copyText: null };
22
+ const outputLines = outputView.lines;
22
23
  const outputTop = Math.max(0, viewportHeight - outputLines.length);
23
24
  const editorTop = viewportHeight + statusTopLines.length;
24
25
  this.selection?.setRegions?.([
25
- { id: "output", topRow: outputTop, leftCol: 0, width: safeWidth, lines: outputLines },
26
+ { id: "output", topRow: outputTop, leftCol: 0, width: safeWidth, lines: outputLines, copyText: outputView.copyText },
26
27
  { id: "editor", topRow: editorTop, leftCol: 0, width: safeWidth, lines: editorLines },
27
28
  ]);
28
29
  const selectedOutputLines = this.selection?.applyRegion?.("output", outputLines) ?? outputLines;
@@ -0,0 +1,107 @@
1
+ import { visibleWidth } from "@earendil-works/pi-tui";
2
+ import { marked } from "marked";
3
+ import { renderMarkdown } from "../markdown-renderer.mjs";
4
+
5
+ export function appendSelectableEntries(entries, block, lines, width) {
6
+ if (block.type !== "markdown") {
7
+ for (const line of lines) entries.push({ line, source: null, codeSource: null, baseRow: entries.length });
8
+ return;
9
+ }
10
+ const source = { kind: "markdown", text: block.text, startRow: entries.length, endRow: entries.length + lines.length - 1 };
11
+ const fragmentRanges = renderedFragmentRanges(block.text, width, entries.length);
12
+ for (const line of lines) {
13
+ const baseRow = entries.length;
14
+ const fragmentSource = fragmentRanges.find((range) => baseRow >= range.startRow && baseRow <= range.endRow) ?? null;
15
+ const codeSource = fragmentSource?.kind === "code" ? fragmentSource : null;
16
+ entries.push({ line, source, codeSource, fragmentSource, baseRow });
17
+ }
18
+ }
19
+
20
+ export function sliceEntriesWithTail(baseEntries, tailLine, range) {
21
+ if (!range) return tailLine == null ? baseEntries : [...baseEntries, { line: tailLine, source: null, codeSource: null, baseRow: baseEntries.length }];
22
+ const { start, end } = range;
23
+ const visible = baseEntries.slice(start, Math.min(end, baseEntries.length));
24
+ if (tailLine != null && end > baseEntries.length) visible.push({ line: tailLine, source: null, codeSource: null, baseRow: baseEntries.length });
25
+ return visible;
26
+ }
27
+
28
+ export function copySourceTextForRange(entries, range) {
29
+ if (!range) return "";
30
+ const selected = trimEmptyBoundaryEntries(entries.slice(range.start.row, range.end.row + 1));
31
+ const codeText = copyCompleteCodeSource(selected, entries, range);
32
+ if (codeText) return codeText;
33
+ const fragmentText = copyCompleteFragmentSource(selected, entries, range);
34
+ if (fragmentText) return fragmentText;
35
+ if (!selected.length || selected.some((entry) => !entry.source)) return "";
36
+ const sources = uniqueSources(selected, "source");
37
+ if (!sources.length || !sources.every((source) => sourceIsFullySelected(source, entries, range, "source"))) return "";
38
+ return sources.map((source) => source.text).join("\n\n");
39
+ }
40
+
41
+ function trimEmptyBoundaryEntries(entries) {
42
+ let start = 0;
43
+ let end = entries.length;
44
+ while (start < end && !entries[start].source && stripAnsi(entries[start].line).trim() === "") start += 1;
45
+ while (end > start && !entries[end - 1].source && stripAnsi(entries[end - 1].line).trim() === "") end -= 1;
46
+ return entries.slice(start, end);
47
+ }
48
+
49
+ function uniqueSources(entries, key) {
50
+ const result = [];
51
+ for (const entry of entries) {
52
+ const source = entry[key];
53
+ if (!source || result.includes(source)) continue;
54
+ result.push(source);
55
+ }
56
+ return result;
57
+ }
58
+
59
+ function copyCompleteCodeSource(selected, entries, range) {
60
+ if (!selected.length || selected.some((entry) => !entry.codeSource)) return "";
61
+ const sources = uniqueSources(selected, "codeSource");
62
+ if (sources.length !== 1 || !sourceIsFullySelected(sources[0], entries, range, "codeSource")) return "";
63
+ return sources[0].text;
64
+ }
65
+
66
+ function copyCompleteFragmentSource(selected, entries, range) {
67
+ if (!selected.length || selected.some((entry) => !entry.fragmentSource)) return "";
68
+ const sources = uniqueSources(selected, "fragmentSource");
69
+ if (sources.length !== 1 || !sourceIsFullySelected(sources[0], entries, range, "fragmentSource")) return "";
70
+ return sources[0].text;
71
+ }
72
+
73
+ function sourceIsFullySelected(source, entries, range, key) {
74
+ const startIndex = entries.findIndex((entry) => entry[key] === source && entry.baseRow === source.startRow);
75
+ const endIndex = entries.findLastIndex((entry) => entry[key] === source && entry.baseRow === source.endRow);
76
+ if (startIndex < 0 || endIndex < 0) return false;
77
+ if (range.start.row > startIndex || range.end.row < endIndex) return false;
78
+ const lastLine = stripAnsi(entries[endIndex]?.line ?? "");
79
+ const coversStart = range.start.row < startIndex || range.start.col <= 0;
80
+ const coversEnd = range.end.row > endIndex || range.end.col >= visibleWidth(lastLine);
81
+ return coversStart && coversEnd;
82
+ }
83
+
84
+ function renderedFragmentRanges(markdown, width, baseRow) {
85
+ let tokens = [];
86
+ try { tokens = marked.lexer(String(markdown ?? "")); } catch { return []; }
87
+ let row = baseRow;
88
+ const ranges = [];
89
+ for (const token of tokens) {
90
+ const raw = token.raw ?? token.text ?? "";
91
+ const lineCount = renderMarkdown(raw, width).length;
92
+ const range = sourceRangeForToken(token, raw, row, lineCount);
93
+ if (range) ranges.push(range);
94
+ row += lineCount;
95
+ }
96
+ return ranges;
97
+ }
98
+
99
+ function sourceRangeForToken(token, raw, row, lineCount) {
100
+ if (token.type === "code") return { kind: "code", text: String(token.text ?? ""), startRow: row, endRow: row + lineCount - 1 };
101
+ if (token.type === "table") return { kind: "table", text: String(raw).trimEnd(), startRow: row, endRow: row + lineCount - 1 };
102
+ return null;
103
+ }
104
+
105
+ function stripAnsi(text) {
106
+ return String(text ?? "").replace(/\x1b(?:\][^\x07]*(?:\x07|\x1b\\)|\[[0-?]*[ -/]*[@-~]|[@-Z\\-_])/g, "");
107
+ }
@@ -4,12 +4,9 @@ import { renderMarkdown, renderStreamingMarkdown } from "./markdown-renderer.mjs
4
4
  import { renderEditDiffBlock } from "./tui-diff-rendering.mjs";
5
5
  import { OutputScrollState } from "./output/scroll-state.mjs";
6
6
  import { appendTextLines, wrapLine } from "./output/text-line-renderer.mjs";
7
- import { sliceLinesWithTail } from "./output/visible-lines.mjs";
7
+ import { appendSelectableEntries, copySourceTextForRange, sliceEntriesWithTail } from "./output/selectable-copy.mjs";
8
8
 
9
9
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
10
-
11
-
12
-
13
10
  function currentTextToBlocks(textLines, sealed, cache = null) {
14
11
  const blocks = [];
15
12
  for (let i = 0; i < textLines.length;) {
@@ -72,13 +69,11 @@ export class OutputBuffer {
72
69
  this._activeThinking = null;
73
70
  this.overlayStatus = null;
74
71
  this.scrollState = new OutputScrollState();
75
- this._segmentLinesCache = new Map();
76
72
  this._baseLinesCache = new Map();
73
+ this._baseEntriesCache = new Map();
77
74
  }
78
75
 
79
- get scrollOffset() {
80
- return this.scrollState.offset;
81
- }
76
+ get scrollOffset() { return this.scrollState.offset; }
82
77
 
83
78
  clear() {
84
79
  this.segments = [];
@@ -90,27 +85,19 @@ export class OutputBuffer {
90
85
  this._activeThinking = null;
91
86
  this.overlayStatus = null;
92
87
  this.scrollState.clear();
93
- this._segmentLinesCache = new Map();
94
88
  this._baseLinesCache = new Map();
89
+ this._baseEntriesCache = new Map();
95
90
  }
96
91
 
97
- write(text) {
98
- this._writeText(text, false);
99
- }
100
-
101
- writeMarkdown(text) {
102
- this._writeText(text, true);
103
- }
92
+ write(text) { this._writeText(text, false); }
93
+ writeMarkdown(text) { this._writeText(text, true); }
104
94
 
105
95
  _writeText(text, markdown) {
106
96
  this.overlayStatus = null;
107
97
  this._invalidateBaseLines();
108
98
  const current = this.currentText.at(-1);
109
- if (current.markdown !== markdown && current.text !== "") {
110
- this.currentText.push({ text: "", markdown });
111
- } else {
112
- current.markdown = markdown;
113
- }
99
+ if (current.markdown !== markdown && current.text !== "") this.currentText.push({ text: "", markdown });
100
+ else current.markdown = markdown;
114
101
  const parts = text.split("\n");
115
102
  this.currentText[this.currentText.length - 1].text += parts[0];
116
103
  for (let i = 1; i < parts.length; i++) this.currentText.push({ text: parts[i], markdown });
@@ -134,10 +121,9 @@ export class OutputBuffer {
134
121
  startThinking() {
135
122
  this.overlayStatus = null;
136
123
  this._flushText();
137
- const seg = { type: "thinking", tokens: 0, content: [] };
138
- this.segments.push(seg);
139
- this._invalidateSegmentLines();
140
- this._activeThinking = seg;
124
+ this._activeThinking = { type: "thinking", tokens: 0, content: [] };
125
+ this.segments.push(this._activeThinking);
126
+ this._invalidateBaseLines();
141
127
  }
142
128
 
143
129
  appendThinking(text) {
@@ -151,25 +137,24 @@ export class OutputBuffer {
151
137
  }
152
138
 
153
139
  endThinking(tokens) {
154
- if (this._activeThinking) {
155
- this._activeThinking.tokens = tokens;
156
- this._activeThinking = null;
157
- this._invalidateSegmentLines();
158
- }
140
+ if (!this._activeThinking) return;
141
+ this._activeThinking.tokens = tokens;
142
+ this._activeThinking = null;
143
+ this._invalidateBaseLines();
159
144
  }
160
145
 
161
146
  addThinkingBlock(tokens, content) {
162
147
  this.overlayStatus = null;
163
148
  this._flushText();
164
149
  this.segments.push({ type: "thinking", tokens, content: content.split("\n") });
165
- this._invalidateSegmentLines();
150
+ this._invalidateBaseLines();
166
151
  }
167
152
 
168
153
  addBlock(block) {
169
154
  this.overlayStatus = null;
170
155
  this._flushText();
171
156
  this.segments.push(block);
172
- this._invalidateSegmentLines();
157
+ this._invalidateBaseLines();
173
158
  }
174
159
 
175
160
  setOverlayStatus(lines) {
@@ -182,14 +167,12 @@ export class OutputBuffer {
182
167
  this._invalidateBaseLines();
183
168
  }
184
169
 
185
- sealCurrentText() {
186
- return this._flushText();
187
- }
170
+ sealCurrentText() { return this._flushText(); }
188
171
 
189
172
  _flushText() {
190
173
  if (this.currentText.length <= 1 && this.currentText[0].text === "") return false;
191
174
  this.segments.push(...currentTextToBlocks(this.currentText, true));
192
- this._invalidateSegmentLines();
175
+ this._invalidateBaseLines();
193
176
  this.currentText = [{ text: "", markdown: false }];
194
177
  this.currentTextCache = new Map();
195
178
  return true;
@@ -200,60 +183,44 @@ export class OutputBuffer {
200
183
  if (text !== undefined) this.spinnerText = text;
201
184
  }
202
185
 
203
- tick() {
204
- this.spinnerIdx = (this.spinnerIdx + 1) % SPINNER_FRAMES.length;
205
- }
206
-
207
- scroll(delta, options) {
208
- return this.scrollState.scroll(delta, options);
209
- }
210
-
211
- getScrollStep() {
212
- return this.scrollState.getStep();
213
- }
214
-
215
- getMaxScrollOffset() {
216
- return this.scrollState.getMaxOffset();
217
- }
218
-
219
- setViewportHeight(height) {
220
- this.scrollState.setViewportHeight(height);
221
- }
222
-
223
- resetScroll() {
224
- this.scrollState.reset();
225
- }
186
+ tick() { this.spinnerIdx = (this.spinnerIdx + 1) % SPINNER_FRAMES.length; }
187
+ scroll(delta, options) { return this.scrollState.scroll(delta, options); }
188
+ getScrollStep() { return this.scrollState.getStep(); }
189
+ getMaxScrollOffset() { return this.scrollState.getMaxOffset(); }
190
+ setViewportHeight(height) { this.scrollState.setViewportHeight(height); }
191
+ resetScroll() { this.scrollState.reset(); }
226
192
 
227
193
  setToolCardsExpanded(expanded) {
228
194
  let changed = false;
229
195
  for (const seg of this.segments) {
230
- if (seg.type !== "tool-card") continue;
231
- if (seg.expanded === expanded) continue;
196
+ if (seg.type !== "tool-card" || seg.expanded === expanded) continue;
232
197
  seg.expanded = expanded;
233
198
  changed = true;
234
199
  }
235
- if (changed) this._invalidateSegmentLines();
200
+ if (changed) this._invalidateBaseLines();
236
201
  return changed;
237
202
  }
238
203
 
239
- invalidate() {
240
- this._invalidateSegmentLines();
241
- }
242
-
243
- _invalidateSegmentLines() {
244
- this._segmentLinesCache.clear();
245
- this._invalidateBaseLines();
246
- }
204
+ invalidate() { this._invalidateBaseLines(); }
247
205
 
248
206
  _invalidateBaseLines() {
249
207
  this._baseLinesCache.clear();
208
+ this._baseEntriesCache.clear();
250
209
  }
251
210
 
252
211
  render(width) {
253
- const baseLines = this._renderBaseLines(width);
212
+ return this.renderSelectable(width).lines;
213
+ }
214
+
215
+ renderSelectable(width) {
216
+ const baseEntries = this._renderBaseEntries(width);
254
217
  const tailLine = this.spinning ? this._spinnerLine() : null;
255
- this.scrollState.setTotalLines(baseLines.length + (tailLine == null ? 0 : 1));
256
- return sliceLinesWithTail(baseLines, tailLine, this.scrollState.sliceRange());
218
+ this.scrollState.setTotalLines(baseEntries.length + (tailLine == null ? 0 : 1));
219
+ const entries = sliceEntriesWithTail(baseEntries, tailLine, this.scrollState.sliceRange());
220
+ return {
221
+ lines: entries.map((entry) => entry.line),
222
+ copyText: (range) => copySourceTextForRange(entries, range),
223
+ };
257
224
  }
258
225
 
259
226
  _spinnerLine() {
@@ -263,31 +230,28 @@ export class OutputBuffer {
263
230
  _renderBaseLines(width) {
264
231
  const cached = this._baseLinesCache.get(width);
265
232
  if (cached) return cached;
266
- const lines = [...this._renderCachedSegmentLines(width)];
267
- const dynamicStart = this._cachedSegmentPrefixCount();
268
- for (const seg of this.segments.slice(dynamicStart)) for (const line of renderBlock(seg, width)) lines.push(line);
269
- for (const block of currentTextToBlocks(this.currentText, false, this.currentTextCache)) for (const line of renderBlock(block, width)) lines.push(line);
270
- if (this.overlayStatus) for (const line of renderBlock(this.overlayStatus, width)) lines.push(line);
233
+ const lines = this._renderBaseEntries(width).map((entry) => entry.line);
271
234
  this._baseLinesCache.set(width, lines);
272
235
  return lines;
273
236
  }
274
237
 
275
- _renderCachedSegmentLines(width) {
276
- const prefixCount = this._cachedSegmentPrefixCount();
277
- const cached = this._segmentLinesCache.get(width);
278
- if (cached?.prefixCount === prefixCount) return cached.lines;
279
-
280
- const lines = [];
281
- for (let i = 0; i < prefixCount; i += 1) {
282
- for (const line of renderBlock(this.segments[i], width)) lines.push(line);
283
- }
284
- this._segmentLinesCache.set(width, { prefixCount, lines });
285
- return lines;
238
+ _renderBaseEntries(width) {
239
+ const cached = this._baseEntriesCache.get(width);
240
+ if (cached) return cached;
241
+ const entries = [];
242
+ for (const block of this._blocksForRender()) appendBlockEntries(entries, block, width);
243
+ this._baseEntriesCache.set(width, entries);
244
+ return entries;
286
245
  }
287
246
 
288
- _cachedSegmentPrefixCount() {
289
- if (!this._activeThinking) return this.segments.length;
290
- const index = this.segments.indexOf(this._activeThinking);
291
- return index < 0 ? this.segments.length : index;
247
+ _blocksForRender() {
248
+ const blocks = [...this.segments];
249
+ blocks.push(...currentTextToBlocks(this.currentText, false, this.currentTextCache));
250
+ if (this.overlayStatus) blocks.push(this.overlayStatus);
251
+ return blocks;
292
252
  }
293
253
  }
254
+
255
+ function appendBlockEntries(entries, block, width) {
256
+ appendSelectableEntries(entries, block, renderBlock(block, width), width);
257
+ }
@@ -73,6 +73,19 @@ export class ScreenSelection {
73
73
  return hadSelection;
74
74
  }
75
75
 
76
+ copyText() {
77
+ const sourceText = this.sourceText();
78
+ return sourceText || this.text();
79
+ }
80
+
81
+ sourceText() {
82
+ const range = this.range();
83
+ if (!range) return "";
84
+ const region = this._singleRegionForRange(range);
85
+ if (!region?.copyText) return "";
86
+ return region.copyText(localRange(range, region)) || "";
87
+ }
88
+
76
89
  text() {
77
90
  const range = this.range();
78
91
  if (!range) return "";
@@ -110,6 +123,15 @@ export class ScreenSelection {
110
123
  return this._plainLines.get(row);
111
124
  }
112
125
 
126
+ _singleRegionForRange(range) {
127
+ const matches = this.regions.filter((region) => {
128
+ const start = region.docStart;
129
+ const end = region.docStart + region.lines.length - 1;
130
+ return range.start.row >= start && range.end.row <= end;
131
+ });
132
+ return matches.length === 1 ? matches[0] : null;
133
+ }
134
+
113
135
  range() {
114
136
  if (!this.anchor || !this.focus) return null;
115
137
  const [start, end] = comparePoints(this.anchor, this.focus) <= 0
@@ -133,6 +155,14 @@ function normalizeRegion(region, index) {
133
155
  topRow: Math.max(0, Math.trunc(region.topRow ?? 0)),
134
156
  leftCol: Math.max(0, Math.trunc(region.leftCol ?? 0)),
135
157
  width,
158
+ copyText: typeof region.copyText === "function" ? region.copyText : null,
159
+ };
160
+ }
161
+
162
+ function localRange(range, region) {
163
+ return {
164
+ start: { row: range.start.row - region.docStart, col: range.start.col },
165
+ end: { row: range.end.row - region.docStart, col: range.end.col },
136
166
  };
137
167
  }
138
168
 
package/src/cli/ui.mjs CHANGED
@@ -58,7 +58,6 @@ export function createTuiUI({
58
58
  tui.setFocus(editor);
59
59
 
60
60
  let started = false;
61
- let mouseOn = true;
62
61
  let toolsExpanded = false;
63
62
  const activeToolBlocks = [];
64
63
  const renderScheduler = createRenderScheduler({ requestRender: () => tui.requestRender() });
@@ -98,7 +97,7 @@ export function createTuiUI({
98
97
  function ensureStarted() {
99
98
  if (!started) {
100
99
  tui.addInputListener((data) => {
101
- const mouseResult = mouseSelectionController.handleMouseInput(data, mouseOn);
100
+ const mouseResult = mouseSelectionController.handleMouseInput(data);
102
101
  if (mouseResult) return mouseResult;
103
102
  const copyKeyResult = mouseSelectionController.handleCopyKey(data);
104
103
  if (copyKeyResult) return copyKeyResult;
@@ -118,7 +117,7 @@ export function createTuiUI({
118
117
  }
119
118
 
120
119
  function openExternalEditor() {
121
- runTuiExternalEditor({ terminal, tui, editor, output, requestRender, mouseOn: () => mouseOn });
120
+ runTuiExternalEditor({ terminal, tui, editor, output, requestRender, mouseOn: () => true });
122
121
  }
123
122
 
124
123
  function toggleToolOutput() {
@@ -240,18 +239,6 @@ export function createTuiUI({
240
239
  requestRender();
241
240
  },
242
241
 
243
- toggleMouse: () => {
244
- if (mouseOn) {
245
- terminal.write("\x1b[?1002l\x1b[?1006l");
246
- mouseOn = false;
247
- return false;
248
- } else {
249
- terminal.write("\x1b[?1002h\x1b[?1006h");
250
- mouseOn = true;
251
- return true;
252
- }
253
- },
254
-
255
242
  requestPermission: async ({ toolName, params, category }) => {
256
243
  ensureStarted();
257
244
  spinnerStatus.stop();
@@ -281,7 +268,7 @@ export function createTuiUI({
281
268
  retryStatus.stop();
282
269
  if (started) {
283
270
  await terminal.drainInput?.();
284
- if (mouseOn) terminal.write("\x1b[?1002l\x1b[?1006l");
271
+ terminal.write("\x1b[?1002l\x1b[?1006l");
285
272
  tui.stop();
286
273
  terminal.write("\x1b[?1049l");
287
274
  }