march-cli 0.1.21 → 0.1.22
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/bin/march.mjs +13 -13
- package/package.json +43 -43
- package/src/agent/command-exec-tool.mjs +172 -168
- package/src/agent/context-stats-tool.mjs +57 -57
- package/src/agent/editing/diff-apply.mjs +28 -28
- package/src/agent/editing/diff-format.mjs +57 -57
- package/src/agent/editing/lsp-report.mjs +69 -69
- package/src/agent/file-edit-tool.mjs +262 -262
- package/src/agent/file-tools/read-file-tool.mjs +112 -112
- package/src/agent/file-tools/read-image-tool.mjs +76 -76
- package/src/agent/model-payload-dumper.mjs +208 -208
- package/src/agent/pi-session/pi-session-sidecar-failure.mjs +10 -10
- package/src/agent/provider/payload-messages.mjs +138 -138
- package/src/agent/runner/codex-large-context-guard.mjs +87 -87
- package/src/agent/runner/codex-transport-compression.mjs +180 -180
- package/src/agent/runner/codex-transport-debug.mjs +113 -113
- package/src/agent/runner/codex-websocket-event-debug.mjs +130 -130
- package/src/agent/runner/fast-model.mjs +36 -36
- package/src/agent/runner/runner-cleanup.mjs +12 -12
- package/src/agent/runner/runner-init.mjs +15 -15
- package/src/agent/runner/runner-session-state.mjs +40 -40
- package/src/agent/runner/runner-utils.mjs +24 -24
- package/src/agent/runner.mjs +299 -299
- package/src/agent/runtime/ipc/ipc-peer.mjs +99 -99
- package/src/agent/runtime/ipc/process-ipc-transport.mjs +16 -16
- package/src/agent/runtime/remote-runner-client.mjs +73 -73
- package/src/agent/runtime/remote-ui-client.mjs +20 -20
- package/src/agent/runtime/runner-ipc-target.mjs +125 -125
- package/src/agent/runtime/runner-process-client.mjs +47 -47
- package/src/agent/runtime/runner-process-entry.mjs +11 -11
- package/src/agent/runtime/runner-process-factory.mjs +108 -108
- package/src/agent/runtime/runner-runtime-host.mjs +79 -79
- package/src/agent/runtime/runtime-factory.mjs +42 -42
- package/src/agent/runtime/runtime-host.mjs +34 -34
- package/src/agent/runtime/ui-event-bridge.mjs +95 -95
- package/src/agent/screen-tools/list-windows-tool.mjs +39 -39
- package/src/agent/screen-tools/screen-tool.mjs +49 -49
- package/src/agent/screen-tools/windows-screen.mjs +133 -133
- package/src/agent/session/session-auto-name.mjs +41 -41
- package/src/agent/session/session-binding.mjs +12 -12
- package/src/agent/session/session-options.mjs +47 -47
- package/src/agent/tool-names.mjs +1 -1
- package/src/agent/tool-result.mjs +3 -3
- package/src/agent/tool-summary.mjs +112 -112
- package/src/agent/tools.mjs +58 -58
- package/src/agent/turn/turn-events.mjs +111 -111
- package/src/agent/turn/turn-logging.mjs +30 -30
- package/src/agent/turn/turn-runner.mjs +196 -196
- package/src/agent/vision-capability.mjs +14 -14
- package/src/auth/login-command.mjs +90 -90
- package/src/auth/storage.mjs +34 -34
- package/src/cli/args.mjs +79 -79
- package/src/cli/commands/copy-command.mjs +87 -87
- package/src/cli/commands/export-command.mjs +206 -206
- package/src/cli/commands/extensions-command.mjs +53 -53
- package/src/cli/commands/help-command.mjs +7 -7
- package/src/cli/commands/model-command.mjs +141 -141
- package/src/cli/commands/paste-image-command.mjs +43 -43
- package/src/cli/commands/provider-command.mjs +59 -59
- package/src/cli/commands/status-command.mjs +194 -194
- package/src/cli/commands/thinking-command.mjs +87 -87
- package/src/cli/fallback-ui.mjs +156 -156
- package/src/cli/input/attachment-tokens.mjs +20 -20
- package/src/cli/input/autocomplete.mjs +74 -106
- package/src/cli/input/external-editor.mjs +39 -39
- package/src/cli/input/file-search/index.mjs +160 -0
- package/src/cli/input/history-store.mjs +35 -35
- package/src/cli/input/image-clipboard.mjs +55 -55
- package/src/cli/input/keybinding-dispatch.mjs +76 -76
- package/src/cli/input/keybindings.mjs +96 -96
- package/src/cli/input/mode-state.mjs +43 -43
- package/src/cli/input/prompt-templates.mjs +84 -84
- package/src/cli/input/select-with-keyboard.mjs +86 -86
- package/src/cli/permissions.mjs +103 -103
- package/src/cli/repl-commands.mjs +86 -86
- package/src/cli/repl-loop.mjs +183 -183
- package/src/cli/selector-list.mjs +21 -21
- package/src/cli/session/pi-session-switch-command.mjs +41 -41
- package/src/cli/session/session-command.mjs +23 -23
- package/src/cli/session/session-list-command.mjs +68 -68
- package/src/cli/session/session-name-command.mjs +26 -26
- package/src/cli/session/session-source-command.mjs +89 -89
- package/src/cli/session/session-switch-command.mjs +1 -1
- package/src/cli/shell/shell-command.mjs +55 -55
- package/src/cli/shell/shell-drawer-controls.mjs +33 -33
- package/src/cli/shell/shell-drawer.mjs +192 -192
- package/src/cli/shell/shell-split-layout.mjs +70 -70
- package/src/cli/slash-commands.mjs +192 -192
- package/src/cli/startup/create-runtime-runner.mjs +61 -61
- package/src/cli/startup/runtime-close.mjs +23 -23
- package/src/cli/startup/startup-banner.mjs +71 -71
- package/src/cli/startup/startup-session.mjs +51 -51
- package/src/cli/status-line-updater.mjs +75 -75
- package/src/cli/tool-output.mjs +9 -9
- package/src/cli/tui/editor/external-editor-runner.mjs +24 -24
- package/src/cli/tui/input/mouse-selection-controller.mjs +91 -91
- package/src/cli/tui/input/mouse-tracking.mjs +20 -20
- package/src/cli/tui/layout/main-pane-layout.mjs +47 -47
- package/src/cli/tui/layout/safe-render-boundary.mjs +46 -46
- package/src/cli/tui/markdown-renderer.mjs +285 -285
- package/src/cli/tui/output/scroll-state.mjs +79 -79
- package/src/cli/tui/output/text-line-renderer.mjs +50 -50
- package/src/cli/tui/output/tool-card-renderer.mjs +59 -59
- package/src/cli/tui/output/visible-lines.mjs +8 -8
- package/src/cli/tui/output-buffer.mjs +293 -293
- package/src/cli/tui/permission-request-ui.mjs +18 -18
- package/src/cli/tui/recall-rendering.mjs +25 -25
- package/src/cli/tui/render/render-scheduler.mjs +26 -26
- package/src/cli/tui/render/stream-delta-buffer.mjs +46 -46
- package/src/cli/tui/select/editor-select-list.mjs +111 -111
- package/src/cli/tui/selection-screen.mjs +269 -269
- package/src/cli/tui/status/retry-status.mjs +72 -72
- package/src/cli/tui/status/spinner-status.mjs +42 -42
- package/src/cli/tui/status/status-bar.mjs +225 -225
- package/src/cli/tui/syntax/highlighting.mjs +260 -260
- package/src/cli/tui/syntax/languages.mjs +91 -91
- package/src/cli/tui/syntax/tree-sitter/bash.highlights.scm +261 -261
- package/src/cli/tui/syntax/tree-sitter/c.highlights.scm +341 -341
- package/src/cli/tui/syntax/tree-sitter/cpp.highlights.scm +268 -268
- package/src/cli/tui/syntax/tree-sitter/csharp.highlights.scm +577 -577
- package/src/cli/tui/syntax/tree-sitter/css.highlights.scm +109 -109
- package/src/cli/tui/syntax/tree-sitter/diff.highlights.scm +49 -49
- package/src/cli/tui/syntax/tree-sitter/go.highlights.scm +254 -254
- package/src/cli/tui/syntax/tree-sitter/html.highlights.scm +13 -13
- package/src/cli/tui/syntax/tree-sitter/java.highlights.scm +330 -330
- package/src/cli/tui/syntax/tree-sitter/json.highlights.scm +38 -38
- package/src/cli/tui/syntax/tree-sitter/php.highlights.scm +203 -203
- package/src/cli/tui/syntax/tree-sitter/python.highlights.scm +137 -137
- package/src/cli/tui/syntax/tree-sitter/ruby.highlights.scm +309 -309
- package/src/cli/tui/syntax/tree-sitter/rust.highlights.scm +531 -531
- package/src/cli/tui/syntax/tree-sitter/toml.highlights.scm +39 -39
- package/src/cli/tui/syntax/tree-sitter/tsx.highlights.scm +35 -35
- package/src/cli/tui/syntax/tree-sitter/typescript.highlights.scm +35 -35
- package/src/cli/tui/syntax/tree-sitter/yaml.highlights.scm +99 -99
- package/src/cli/tui/tool-rendering.mjs +87 -87
- package/src/cli/tui/tui-diff-rendering.mjs +157 -157
- package/src/cli/tui/tui-handlers.mjs +111 -111
- package/src/cli/tui/tui-input-controller.mjs +61 -61
- package/src/cli/tui/ui-theme.mjs +157 -157
- package/src/cli/ui.mjs +297 -297
- package/src/config/config-json.mjs +84 -84
- package/src/config/dotenv.mjs +20 -20
- package/src/config/features.mjs +75 -75
- package/src/config/loader.mjs +143 -143
- package/src/config/settings-command.mjs +97 -97
- package/src/context/engine.mjs +198 -198
- package/src/context/injections.mjs +26 -26
- package/src/context/profiles.mjs +39 -39
- package/src/context/project-context.mjs +20 -20
- package/src/context/session-status.mjs +17 -17
- package/src/context/shell-layers.mjs +23 -23
- package/src/context/system-core/base.md +65 -65
- package/src/context/system-core/prompts/deepseek-v4-pro.md +3 -3
- package/src/context/system-core/prompts/default.md +3 -3
- package/src/context/system-core.mjs +35 -35
- package/src/debug/logger.mjs +141 -141
- package/src/debug/model-context-dumper.mjs +52 -52
- package/src/extensions/discovery.mjs +40 -40
- package/src/extensions/lifecycle-adapter.mjs +210 -210
- package/src/extensions/lifecycle-manifest.mjs +69 -69
- package/src/image-gen/index.mjs +7 -7
- package/src/image-gen/provider.mjs +231 -231
- package/src/image-gen/tool.mjs +84 -84
- package/src/lsp/client.mjs +257 -257
- package/src/lsp/diagnostic-store.mjs +42 -42
- package/src/lsp/diagnostics-format.mjs +72 -72
- package/src/lsp/managed-node-server.mjs +99 -99
- package/src/lsp/path-match.mjs +10 -10
- package/src/lsp/server-definitions.mjs +188 -188
- package/src/lsp/servers.mjs +165 -165
- package/src/lsp/service.mjs +110 -110
- package/src/lsp/status-message.mjs +9 -9
- package/src/lsp/typescript-project-resolver.mjs +186 -186
- package/src/main.mjs +299 -299
- package/src/mcp/client.mjs +195 -195
- package/src/mcp/config.mjs +130 -130
- package/src/mcp/index.mjs +48 -48
- package/src/mcp/tools.mjs +98 -98
- package/src/memory/database.mjs +219 -219
- package/src/memory/glossary.mjs +124 -124
- package/src/memory/graph/graph-cascades.mjs +109 -109
- package/src/memory/graph/graph-diagnostics.mjs +73 -73
- package/src/memory/graph/graph-path-removal.mjs +50 -50
- package/src/memory/graph/graph-path-utils.mjs +17 -17
- package/src/memory/graph/graph-primitives.mjs +103 -103
- package/src/memory/graph/graph-read.mjs +159 -159
- package/src/memory/graph.mjs +282 -282
- package/src/memory/markdown/markdown-delete.mjs +23 -23
- package/src/memory/markdown/markdown-format.mjs +128 -128
- package/src/memory/markdown/markdown-recall.mjs +28 -28
- package/src/memory/markdown/ripgrep.mjs +16 -16
- package/src/memory/markdown/sqlite-index.mjs +87 -87
- package/src/memory/markdown-store.mjs +286 -286
- package/src/memory/markdown-tools.mjs +103 -103
- package/src/memory/search.mjs +142 -142
- package/src/memory/snapshot.mjs +86 -86
- package/src/memory/system-views.mjs +120 -120
- package/src/memory/tools.mjs +282 -282
- package/src/network/environment.mjs +131 -131
- package/src/notification/desktop-notifier.mjs +262 -262
- package/src/platform/open-file.mjs +28 -28
- package/src/platform/spawn-command.mjs +27 -27
- package/src/provider/accept-command.mjs +89 -89
- package/src/provider/command.mjs +21 -21
- package/src/provider/config-command.mjs +129 -129
- package/src/provider/custom-provider.mjs +113 -113
- package/src/provider/hosted-tools.mjs +111 -111
- package/src/provider/presets.mjs +72 -72
- package/src/provider/share-command.mjs +79 -79
- package/src/provider/share-payload.mjs +52 -52
- package/src/session/attachment-display.mjs +16 -16
- package/src/session/attachment-references.mjs +65 -65
- package/src/session/attachments.mjs +140 -140
- package/src/session/persist.mjs +1 -1
- package/src/session/pi-manager.mjs +34 -34
- package/src/session/session-utils.mjs +16 -16
- package/src/session/sidecar-sync.mjs +19 -19
- package/src/session/sidecar.mjs +69 -69
- package/src/session/transcript.mjs +83 -83
- package/src/session/tree.mjs +42 -42
- package/src/shell/cli-runtime.mjs +11 -11
- package/src/shell/hints.mjs +12 -12
- package/src/shell/node-pty-adapter.mjs +81 -81
- package/src/shell/runtime-state.mjs +126 -126
- package/src/shell/runtime.mjs +252 -252
- package/src/shell/screen-buffer.mjs +136 -136
- package/src/shell/tool-read.mjs +74 -74
- package/src/shell/tools.mjs +299 -299
- package/src/supergrok/actions/image-generate.mjs +60 -60
- package/src/supergrok/actions/search.mjs +78 -78
- package/src/supergrok/auth.mjs +36 -36
- package/src/supergrok/constants.mjs +18 -18
- package/src/supergrok/oauth-provider.mjs +278 -278
- package/src/supergrok/provider.mjs +35 -35
- package/src/supergrok/response.mjs +76 -76
- package/src/supergrok/tool.mjs +61 -61
- package/src/text/ansi.mjs +3 -3
- package/src/web/config-command.mjs +43 -43
- package/src/web/fetch.mjs +78 -78
- package/src/web/presets.mjs +16 -16
- package/src/web/search.mjs +83 -83
- package/src/web/tools.mjs +107 -107
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
-
import { MODES, formatModeLabel } from "../input/mode-state.mjs";
|
|
4
|
-
import { brightBlack, cyan, modeLabel, white } from "../tui/ui-theme.mjs";
|
|
5
|
-
|
|
6
|
-
const CARD_WIDTH = 76;
|
|
7
|
-
const ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
8
|
-
const { version: packageVersion = "0.1" } = createRequire(import.meta.url)("../../../package.json");
|
|
9
|
-
|
|
10
|
-
export function formatStartupBanner({ cwd, modelId = "model?", thinkingLevel = "thinking?", mode = MODES.DO, dumpContextPath = null } = {}) {
|
|
11
|
-
const nextMode = mode === MODES.DISCUSS ? MODES.DO : MODES.DISCUSS;
|
|
12
|
-
const tip = dumpContextPath
|
|
13
|
-
? `${brightBlack("Tip:")} ${cyan("dumps:")} ${brightBlack(dumpContextPath)}`
|
|
14
|
-
: `${brightBlack("Tip:")} ${brightBlack("Tab to")} ${formatModeTip(nextMode)} ${brightBlack("·")} ${cyan("/help")} ${brightBlack("for commands")}`;
|
|
15
|
-
return [
|
|
16
|
-
"",
|
|
17
|
-
...renderStartupCard([
|
|
18
|
-
`${cyan(" █▙ ▟█")} ${white("March")} ${brightBlack(`v${packageVersion}`)}`,
|
|
19
|
-
`${cyan(" █▜▙▟▛█")} ${brightBlack("Describe a task to get started.")}`,
|
|
20
|
-
`${cyan(" ▀ ▀")}`,
|
|
21
|
-
"",
|
|
22
|
-
tip,
|
|
23
|
-
brightBlack("March uses AI. Check for mistakes."),
|
|
24
|
-
]),
|
|
25
|
-
"",
|
|
26
|
-
];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function formatModeTip(mode) {
|
|
30
|
-
const label = formatModeLabel(mode);
|
|
31
|
-
return (modeLabel[mode] ?? modeLabel.fallback)(label);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function renderStartupCard(contentLines, width = CARD_WIDTH) {
|
|
35
|
-
const safeWidth = Math.max(24, Math.trunc(width));
|
|
36
|
-
const innerWidth = safeWidth - 4;
|
|
37
|
-
return [
|
|
38
|
-
white(`╭${"─".repeat(safeWidth - 2)}╮`),
|
|
39
|
-
...contentLines.map((line) => white("│ ") + padAnsi(line, innerWidth) + white(" │")),
|
|
40
|
-
white(`╰${"─".repeat(safeWidth - 2)}╯`),
|
|
41
|
-
];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function padAnsi(text, width) {
|
|
45
|
-
const clipped = clipAnsi(text, width);
|
|
46
|
-
const padding = Math.max(0, width - visibleWidth(stripAnsi(clipped)));
|
|
47
|
-
return `${clipped}${" ".repeat(padding)}`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function clipAnsi(text, width) {
|
|
51
|
-
let output = "";
|
|
52
|
-
let plainWidth = 0;
|
|
53
|
-
let inAnsi = false;
|
|
54
|
-
for (const ch of Array.from(String(text ?? ""))) {
|
|
55
|
-
if (ch === "\x1b") inAnsi = true;
|
|
56
|
-
if (inAnsi) {
|
|
57
|
-
output += ch;
|
|
58
|
-
if (/[@-~]/.test(ch)) inAnsi = false;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
const charWidth = visibleWidth(ch);
|
|
62
|
-
if (plainWidth + charWidth > width) break;
|
|
63
|
-
output += ch;
|
|
64
|
-
plainWidth += charWidth;
|
|
65
|
-
}
|
|
66
|
-
return output;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function stripAnsi(text) {
|
|
70
|
-
return String(text ?? "").replace(ANSI_RE, "");
|
|
71
|
-
}
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import { MODES, formatModeLabel } from "../input/mode-state.mjs";
|
|
4
|
+
import { brightBlack, cyan, modeLabel, white } from "../tui/ui-theme.mjs";
|
|
5
|
+
|
|
6
|
+
const CARD_WIDTH = 76;
|
|
7
|
+
const ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
8
|
+
const { version: packageVersion = "0.1" } = createRequire(import.meta.url)("../../../package.json");
|
|
9
|
+
|
|
10
|
+
export function formatStartupBanner({ cwd, modelId = "model?", thinkingLevel = "thinking?", mode = MODES.DO, dumpContextPath = null } = {}) {
|
|
11
|
+
const nextMode = mode === MODES.DISCUSS ? MODES.DO : MODES.DISCUSS;
|
|
12
|
+
const tip = dumpContextPath
|
|
13
|
+
? `${brightBlack("Tip:")} ${cyan("dumps:")} ${brightBlack(dumpContextPath)}`
|
|
14
|
+
: `${brightBlack("Tip:")} ${brightBlack("Tab to")} ${formatModeTip(nextMode)} ${brightBlack("·")} ${cyan("/help")} ${brightBlack("for commands")}`;
|
|
15
|
+
return [
|
|
16
|
+
"",
|
|
17
|
+
...renderStartupCard([
|
|
18
|
+
`${cyan(" █▙ ▟█")} ${white("March")} ${brightBlack(`v${packageVersion}`)}`,
|
|
19
|
+
`${cyan(" █▜▙▟▛█")} ${brightBlack("Describe a task to get started.")}`,
|
|
20
|
+
`${cyan(" ▀ ▀")}`,
|
|
21
|
+
"",
|
|
22
|
+
tip,
|
|
23
|
+
brightBlack("March uses AI. Check for mistakes."),
|
|
24
|
+
]),
|
|
25
|
+
"",
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function formatModeTip(mode) {
|
|
30
|
+
const label = formatModeLabel(mode);
|
|
31
|
+
return (modeLabel[mode] ?? modeLabel.fallback)(label);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function renderStartupCard(contentLines, width = CARD_WIDTH) {
|
|
35
|
+
const safeWidth = Math.max(24, Math.trunc(width));
|
|
36
|
+
const innerWidth = safeWidth - 4;
|
|
37
|
+
return [
|
|
38
|
+
white(`╭${"─".repeat(safeWidth - 2)}╮`),
|
|
39
|
+
...contentLines.map((line) => white("│ ") + padAnsi(line, innerWidth) + white(" │")),
|
|
40
|
+
white(`╰${"─".repeat(safeWidth - 2)}╯`),
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function padAnsi(text, width) {
|
|
45
|
+
const clipped = clipAnsi(text, width);
|
|
46
|
+
const padding = Math.max(0, width - visibleWidth(stripAnsi(clipped)));
|
|
47
|
+
return `${clipped}${" ".repeat(padding)}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function clipAnsi(text, width) {
|
|
51
|
+
let output = "";
|
|
52
|
+
let plainWidth = 0;
|
|
53
|
+
let inAnsi = false;
|
|
54
|
+
for (const ch of Array.from(String(text ?? ""))) {
|
|
55
|
+
if (ch === "\x1b") inAnsi = true;
|
|
56
|
+
if (inAnsi) {
|
|
57
|
+
output += ch;
|
|
58
|
+
if (/[@-~]/.test(ch)) inAnsi = false;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const charWidth = visibleWidth(ch);
|
|
62
|
+
if (plainWidth + charWidth > width) break;
|
|
63
|
+
output += ch;
|
|
64
|
+
plainWidth += charWidth;
|
|
65
|
+
}
|
|
66
|
+
return output;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function stripAnsi(text) {
|
|
70
|
+
return String(text ?? "").replace(ANSI_RE, "");
|
|
71
|
+
}
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
import { listPiSessionInfos } from "../../session/pi-manager.mjs";
|
|
5
|
-
import { loadPiSessionTranscriptTurns } from "../../session/transcript.mjs";
|
|
6
|
-
import { resumePiSessionById } from "../session/pi-session-switch-command.mjs";
|
|
7
|
-
|
|
8
|
-
export function loadOrCreateProjectId(projectMarchDir) {
|
|
9
|
-
if (!existsSync(projectMarchDir)) mkdirSync(projectMarchDir, { recursive: true });
|
|
10
|
-
const idFile = resolve(projectMarchDir, "project-id");
|
|
11
|
-
if (existsSync(idFile)) {
|
|
12
|
-
return readFileSync(idFile, "utf8").trim();
|
|
13
|
-
}
|
|
14
|
-
const id = randomUUID();
|
|
15
|
-
writeFileSync(idFile, id, "utf8");
|
|
16
|
-
return id;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function resumeStartupSession({
|
|
20
|
-
resumeId,
|
|
21
|
-
runner,
|
|
22
|
-
sessionState,
|
|
23
|
-
projectMarchDir,
|
|
24
|
-
ui,
|
|
25
|
-
listPiSessions = listPiSessionInfos,
|
|
26
|
-
}) {
|
|
27
|
-
if (!resumeId) return { source: "none", lines: [] };
|
|
28
|
-
|
|
29
|
-
const sessions = await listPiSessions({
|
|
30
|
-
cwd: runner.engine.cwd,
|
|
31
|
-
projectMarchDir,
|
|
32
|
-
});
|
|
33
|
-
const lines = await resumePiSessionById(resumeId, {
|
|
34
|
-
runner,
|
|
35
|
-
sessions,
|
|
36
|
-
projectMarchDir,
|
|
37
|
-
});
|
|
38
|
-
for (const line of lines) ui.status(line);
|
|
39
|
-
return { source: "pi", lines, transcriptTurns: loadResumeTranscriptTurns(resumeId, sessions, lines) };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function loadResumeTranscriptTurns(resumeId, sessions, lines) {
|
|
43
|
-
if (!Array.isArray(lines) || !lines.some((line) => String(line).startsWith("Resumed pi session:"))) return [];
|
|
44
|
-
const matches = sessions.filter((session) => session.id.startsWith(resumeId));
|
|
45
|
-
if (matches.length !== 1) return [];
|
|
46
|
-
try {
|
|
47
|
-
return loadPiSessionTranscriptTurns(matches[0].path);
|
|
48
|
-
} catch {
|
|
49
|
-
return [];
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { listPiSessionInfos } from "../../session/pi-manager.mjs";
|
|
5
|
+
import { loadPiSessionTranscriptTurns } from "../../session/transcript.mjs";
|
|
6
|
+
import { resumePiSessionById } from "../session/pi-session-switch-command.mjs";
|
|
7
|
+
|
|
8
|
+
export function loadOrCreateProjectId(projectMarchDir) {
|
|
9
|
+
if (!existsSync(projectMarchDir)) mkdirSync(projectMarchDir, { recursive: true });
|
|
10
|
+
const idFile = resolve(projectMarchDir, "project-id");
|
|
11
|
+
if (existsSync(idFile)) {
|
|
12
|
+
return readFileSync(idFile, "utf8").trim();
|
|
13
|
+
}
|
|
14
|
+
const id = randomUUID();
|
|
15
|
+
writeFileSync(idFile, id, "utf8");
|
|
16
|
+
return id;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function resumeStartupSession({
|
|
20
|
+
resumeId,
|
|
21
|
+
runner,
|
|
22
|
+
sessionState,
|
|
23
|
+
projectMarchDir,
|
|
24
|
+
ui,
|
|
25
|
+
listPiSessions = listPiSessionInfos,
|
|
26
|
+
}) {
|
|
27
|
+
if (!resumeId) return { source: "none", lines: [] };
|
|
28
|
+
|
|
29
|
+
const sessions = await listPiSessions({
|
|
30
|
+
cwd: runner.engine.cwd,
|
|
31
|
+
projectMarchDir,
|
|
32
|
+
});
|
|
33
|
+
const lines = await resumePiSessionById(resumeId, {
|
|
34
|
+
runner,
|
|
35
|
+
sessions,
|
|
36
|
+
projectMarchDir,
|
|
37
|
+
});
|
|
38
|
+
for (const line of lines) ui.status(line);
|
|
39
|
+
return { source: "pi", lines, transcriptTurns: loadResumeTranscriptTurns(resumeId, sessions, lines) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function loadResumeTranscriptTurns(resumeId, sessions, lines) {
|
|
43
|
+
if (!Array.isArray(lines) || !lines.some((line) => String(line).startsWith("Resumed pi session:"))) return [];
|
|
44
|
+
const matches = sessions.filter((session) => session.id.startsWith(resumeId));
|
|
45
|
+
if (matches.length !== 1) return [];
|
|
46
|
+
try {
|
|
47
|
+
return loadPiSessionTranscriptTurns(matches[0].path);
|
|
48
|
+
} catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
import { statusBarLine } from "./commands/status-command.mjs";
|
|
2
|
-
|
|
3
|
-
const WORKING_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
4
|
-
const WORKING_INTERVAL_MS = 120;
|
|
5
|
-
|
|
6
|
-
export function createStatusLineUpdater({
|
|
7
|
-
ui,
|
|
8
|
-
runner,
|
|
9
|
-
sessionState,
|
|
10
|
-
sessionSource = "pi",
|
|
11
|
-
getMode = () => undefined,
|
|
12
|
-
}) {
|
|
13
|
-
let contextTokens = null;
|
|
14
|
-
let activity = null;
|
|
15
|
-
let frameIndex = 0;
|
|
16
|
-
let timer = null;
|
|
17
|
-
|
|
18
|
-
const update = (options = {}) => {
|
|
19
|
-
if (typeof ui.setStatusBar !== "function") return null;
|
|
20
|
-
if (Object.hasOwn(options, "contextTokens")) contextTokens = options.contextTokens;
|
|
21
|
-
const line = statusBarLine({
|
|
22
|
-
runner,
|
|
23
|
-
sessionState,
|
|
24
|
-
sessionSource,
|
|
25
|
-
extensionDiagnostics: runner.getExtensionDiagnostics?.() ?? [],
|
|
26
|
-
lifecycleState: runner.getExtensionLifecycleState?.() ?? null,
|
|
27
|
-
mode: getMode(),
|
|
28
|
-
contextTokens,
|
|
29
|
-
activity: formatActivity(activity, frameIndex),
|
|
30
|
-
lspStatus: runner.getLspStatus?.() ?? null,
|
|
31
|
-
});
|
|
32
|
-
ui.setStatusBar(line);
|
|
33
|
-
return line;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
update.startWorking = () => {
|
|
37
|
-
activity = "working";
|
|
38
|
-
frameIndex = 0;
|
|
39
|
-
const line = update();
|
|
40
|
-
if (!timer) {
|
|
41
|
-
timer = setInterval(() => {
|
|
42
|
-
frameIndex = (frameIndex + 1) % WORKING_FRAMES.length;
|
|
43
|
-
update();
|
|
44
|
-
}, WORKING_INTERVAL_MS);
|
|
45
|
-
timer.unref?.();
|
|
46
|
-
}
|
|
47
|
-
return line;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
update.stopWorking = () => {
|
|
51
|
-
if (activity === "working") activity = null;
|
|
52
|
-
if (timer) {
|
|
53
|
-
clearInterval(timer);
|
|
54
|
-
timer = null;
|
|
55
|
-
}
|
|
56
|
-
return update();
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
update.markAborted = () => {
|
|
60
|
-
activity = "aborted";
|
|
61
|
-
if (timer) {
|
|
62
|
-
clearInterval(timer);
|
|
63
|
-
timer = null;
|
|
64
|
-
}
|
|
65
|
-
return update();
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
return update;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function formatActivity(activity, frameIndex) {
|
|
72
|
-
if (activity === "working") return { frame: WORKING_FRAMES[frameIndex], label: "Working" };
|
|
73
|
-
if (activity === "aborted") return { frame: "x", label: "Aborted" };
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
1
|
+
import { statusBarLine } from "./commands/status-command.mjs";
|
|
2
|
+
|
|
3
|
+
const WORKING_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
4
|
+
const WORKING_INTERVAL_MS = 120;
|
|
5
|
+
|
|
6
|
+
export function createStatusLineUpdater({
|
|
7
|
+
ui,
|
|
8
|
+
runner,
|
|
9
|
+
sessionState,
|
|
10
|
+
sessionSource = "pi",
|
|
11
|
+
getMode = () => undefined,
|
|
12
|
+
}) {
|
|
13
|
+
let contextTokens = null;
|
|
14
|
+
let activity = null;
|
|
15
|
+
let frameIndex = 0;
|
|
16
|
+
let timer = null;
|
|
17
|
+
|
|
18
|
+
const update = (options = {}) => {
|
|
19
|
+
if (typeof ui.setStatusBar !== "function") return null;
|
|
20
|
+
if (Object.hasOwn(options, "contextTokens")) contextTokens = options.contextTokens;
|
|
21
|
+
const line = statusBarLine({
|
|
22
|
+
runner,
|
|
23
|
+
sessionState,
|
|
24
|
+
sessionSource,
|
|
25
|
+
extensionDiagnostics: runner.getExtensionDiagnostics?.() ?? [],
|
|
26
|
+
lifecycleState: runner.getExtensionLifecycleState?.() ?? null,
|
|
27
|
+
mode: getMode(),
|
|
28
|
+
contextTokens,
|
|
29
|
+
activity: formatActivity(activity, frameIndex),
|
|
30
|
+
lspStatus: runner.getLspStatus?.() ?? null,
|
|
31
|
+
});
|
|
32
|
+
ui.setStatusBar(line);
|
|
33
|
+
return line;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
update.startWorking = () => {
|
|
37
|
+
activity = "working";
|
|
38
|
+
frameIndex = 0;
|
|
39
|
+
const line = update();
|
|
40
|
+
if (!timer) {
|
|
41
|
+
timer = setInterval(() => {
|
|
42
|
+
frameIndex = (frameIndex + 1) % WORKING_FRAMES.length;
|
|
43
|
+
update();
|
|
44
|
+
}, WORKING_INTERVAL_MS);
|
|
45
|
+
timer.unref?.();
|
|
46
|
+
}
|
|
47
|
+
return line;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
update.stopWorking = () => {
|
|
51
|
+
if (activity === "working") activity = null;
|
|
52
|
+
if (timer) {
|
|
53
|
+
clearInterval(timer);
|
|
54
|
+
timer = null;
|
|
55
|
+
}
|
|
56
|
+
return update();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
update.markAborted = () => {
|
|
60
|
+
activity = "aborted";
|
|
61
|
+
if (timer) {
|
|
62
|
+
clearInterval(timer);
|
|
63
|
+
timer = null;
|
|
64
|
+
}
|
|
65
|
+
return update();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return update;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function formatActivity(activity, frameIndex) {
|
|
72
|
+
if (activity === "working") return { frame: WORKING_FRAMES[frameIndex], label: "Working" };
|
|
73
|
+
if (activity === "aborted") return { frame: "x", label: "Aborted" };
|
|
74
|
+
return null;
|
|
75
|
+
}
|
package/src/cli/tool-output.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export function extractToolOutput(result) {
|
|
2
|
-
try {
|
|
3
|
-
const content = result?.content;
|
|
4
|
-
if (Array.isArray(content)) {
|
|
5
|
-
return content.filter(c => c.type === "text").map(c => c.text).join("\n");
|
|
6
|
-
}
|
|
7
|
-
} catch {}
|
|
8
|
-
return "";
|
|
9
|
-
}
|
|
1
|
+
export function extractToolOutput(result) {
|
|
2
|
+
try {
|
|
3
|
+
const content = result?.content;
|
|
4
|
+
if (Array.isArray(content)) {
|
|
5
|
+
return content.filter(c => c.type === "text").map(c => c.text).join("\n");
|
|
6
|
+
}
|
|
7
|
+
} catch {}
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { getExternalEditorCommand, openTextInExternalEditor } from "../../input/external-editor.mjs";
|
|
2
|
-
import { yellow } from "../ui-theme.mjs";
|
|
3
|
-
|
|
4
|
-
export function runTuiExternalEditor({ terminal, tui, editor, output, requestRender, mouseOn }) {
|
|
5
|
-
const editorCommand = getExternalEditorCommand();
|
|
6
|
-
if (!editorCommand) {
|
|
7
|
-
output.writeln(yellow(`● No editor configured. Set $VISUAL or $EDITOR.`));
|
|
8
|
-
requestRender();
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
try {
|
|
12
|
-
terminal.write("\x1b[?1049l");
|
|
13
|
-
tui.stop();
|
|
14
|
-
if (mouseOn()) terminal.write("\x1b[?1002l\x1b[?1006l");
|
|
15
|
-
const result = openTextInExternalEditor({ text: editor.getText(), editorCommand });
|
|
16
|
-
if (result.ok) editor.setText(result.text);
|
|
17
|
-
else output.writeln(yellow(`● ${result.error}`));
|
|
18
|
-
} finally {
|
|
19
|
-
tui.start();
|
|
20
|
-
terminal.write("\x1b[?1049h");
|
|
21
|
-
if (mouseOn()) terminal.write("\x1b[?1002h\x1b[?1006h");
|
|
22
|
-
tui.requestRender(true);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
1
|
+
import { getExternalEditorCommand, openTextInExternalEditor } from "../../input/external-editor.mjs";
|
|
2
|
+
import { yellow } from "../ui-theme.mjs";
|
|
3
|
+
|
|
4
|
+
export function runTuiExternalEditor({ terminal, tui, editor, output, requestRender, mouseOn }) {
|
|
5
|
+
const editorCommand = getExternalEditorCommand();
|
|
6
|
+
if (!editorCommand) {
|
|
7
|
+
output.writeln(yellow(`● No editor configured. Set $VISUAL or $EDITOR.`));
|
|
8
|
+
requestRender();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
terminal.write("\x1b[?1049l");
|
|
13
|
+
tui.stop();
|
|
14
|
+
if (mouseOn()) terminal.write("\x1b[?1002l\x1b[?1006l");
|
|
15
|
+
const result = openTextInExternalEditor({ text: editor.getText(), editorCommand });
|
|
16
|
+
if (result.ok) editor.setText(result.text);
|
|
17
|
+
else output.writeln(yellow(`● ${result.error}`));
|
|
18
|
+
} finally {
|
|
19
|
+
tui.start();
|
|
20
|
+
terminal.write("\x1b[?1049h");
|
|
21
|
+
if (mouseOn()) terminal.write("\x1b[?1002h\x1b[?1006h");
|
|
22
|
+
tui.requestRender(true);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
import { parseMouseEvent } from "./mouse-tracking.mjs";
|
|
2
|
-
import { brightBlack } from "../ui-theme.mjs";
|
|
3
|
-
|
|
4
|
-
const OUTPUT_WHEEL_SCROLL_LINES = 3;
|
|
5
|
-
|
|
6
|
-
export function createMouseSelectionController({
|
|
7
|
-
terminal,
|
|
8
|
-
output,
|
|
9
|
-
shellDrawer,
|
|
10
|
-
shellDrawerControls,
|
|
11
|
-
selection,
|
|
12
|
-
writeClipboard,
|
|
13
|
-
requestRender,
|
|
14
|
-
}) {
|
|
15
|
-
function copySelectionText(text) {
|
|
16
|
-
if (!text) return false;
|
|
17
|
-
let result;
|
|
18
|
-
try {
|
|
19
|
-
result = writeClipboard(text);
|
|
20
|
-
} catch (err) {
|
|
21
|
-
setCopyStatus({ ok: false, message: err.message }, text.length);
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
if (result?.then) {
|
|
25
|
-
output.setOverlayStatus([brightBlack(`● copying selection (${text.length} chars)`)]);
|
|
26
|
-
requestRender();
|
|
27
|
-
result.then(
|
|
28
|
-
(resolved) => setCopyStatus(resolved, text.length),
|
|
29
|
-
(err) => setCopyStatus({ ok: false, message: err.message }, text.length)
|
|
30
|
-
);
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
setCopyStatus(result, text.length);
|
|
34
|
-
return result?.ok !== false;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function setCopyStatus(result, length) {
|
|
38
|
-
output.setOverlayStatus([
|
|
39
|
-
result?.ok === false
|
|
40
|
-
? brightBlack(`● selection copy failed: ${compactStatusMessage(result.message)}`)
|
|
41
|
-
: brightBlack(`● copied selection (${length} chars)`),
|
|
42
|
-
]);
|
|
43
|
-
requestRender();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
handleMouseInput(data, mouseOn) {
|
|
48
|
-
if (!mouseOn) return undefined;
|
|
49
|
-
const mouse = parseMouseEvent(data);
|
|
50
|
-
if (mouse?.type === "scroll") {
|
|
51
|
-
if (shellDrawer.isVisible?.() && mouse.col > Math.floor((terminal.columns || 80) * 0.64)) {
|
|
52
|
-
shellDrawerControls.scroll(mouse.delta);
|
|
53
|
-
} else {
|
|
54
|
-
output.scroll(mouse.delta, { step: OUTPUT_WHEEL_SCROLL_LINES });
|
|
55
|
-
}
|
|
56
|
-
requestRender();
|
|
57
|
-
return { consume: true };
|
|
58
|
-
}
|
|
59
|
-
if (mouse?.type === "down" && mouse.button === 0) {
|
|
60
|
-
selection.start(mouse);
|
|
61
|
-
requestRender();
|
|
62
|
-
return { consume: true };
|
|
63
|
-
}
|
|
64
|
-
if (mouse?.type === "drag" && mouse.button === 0) {
|
|
65
|
-
selection.update(mouse);
|
|
66
|
-
requestRender();
|
|
67
|
-
return { consume: true };
|
|
68
|
-
}
|
|
69
|
-
if (mouse?.type === "up") {
|
|
70
|
-
selection.finish(mouse, { clear: false });
|
|
71
|
-
requestRender();
|
|
72
|
-
return { consume: true };
|
|
73
|
-
}
|
|
74
|
-
return undefined;
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
handleCopyKey(data) {
|
|
78
|
-
if (data !== "\x03") return undefined;
|
|
79
|
-
const text = selection.text();
|
|
80
|
-
if (!text) return undefined;
|
|
81
|
-
selection.clear();
|
|
82
|
-
copySelectionText(text);
|
|
83
|
-
return { consume: true };
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function compactStatusMessage(message) {
|
|
89
|
-
const text = String(message || "unknown error").replace(/\s+/g, " ").trim();
|
|
90
|
-
return text.length > 80 ? `${text.slice(0, 79)}…` : text;
|
|
91
|
-
}
|
|
1
|
+
import { parseMouseEvent } from "./mouse-tracking.mjs";
|
|
2
|
+
import { brightBlack } from "../ui-theme.mjs";
|
|
3
|
+
|
|
4
|
+
const OUTPUT_WHEEL_SCROLL_LINES = 3;
|
|
5
|
+
|
|
6
|
+
export function createMouseSelectionController({
|
|
7
|
+
terminal,
|
|
8
|
+
output,
|
|
9
|
+
shellDrawer,
|
|
10
|
+
shellDrawerControls,
|
|
11
|
+
selection,
|
|
12
|
+
writeClipboard,
|
|
13
|
+
requestRender,
|
|
14
|
+
}) {
|
|
15
|
+
function copySelectionText(text) {
|
|
16
|
+
if (!text) return false;
|
|
17
|
+
let result;
|
|
18
|
+
try {
|
|
19
|
+
result = writeClipboard(text);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
setCopyStatus({ ok: false, message: err.message }, text.length);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
if (result?.then) {
|
|
25
|
+
output.setOverlayStatus([brightBlack(`● copying selection (${text.length} chars)`)]);
|
|
26
|
+
requestRender();
|
|
27
|
+
result.then(
|
|
28
|
+
(resolved) => setCopyStatus(resolved, text.length),
|
|
29
|
+
(err) => setCopyStatus({ ok: false, message: err.message }, text.length)
|
|
30
|
+
);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
setCopyStatus(result, text.length);
|
|
34
|
+
return result?.ok !== false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function setCopyStatus(result, length) {
|
|
38
|
+
output.setOverlayStatus([
|
|
39
|
+
result?.ok === false
|
|
40
|
+
? brightBlack(`● selection copy failed: ${compactStatusMessage(result.message)}`)
|
|
41
|
+
: brightBlack(`● copied selection (${length} chars)`),
|
|
42
|
+
]);
|
|
43
|
+
requestRender();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
handleMouseInput(data, mouseOn) {
|
|
48
|
+
if (!mouseOn) return undefined;
|
|
49
|
+
const mouse = parseMouseEvent(data);
|
|
50
|
+
if (mouse?.type === "scroll") {
|
|
51
|
+
if (shellDrawer.isVisible?.() && mouse.col > Math.floor((terminal.columns || 80) * 0.64)) {
|
|
52
|
+
shellDrawerControls.scroll(mouse.delta);
|
|
53
|
+
} else {
|
|
54
|
+
output.scroll(mouse.delta, { step: OUTPUT_WHEEL_SCROLL_LINES });
|
|
55
|
+
}
|
|
56
|
+
requestRender();
|
|
57
|
+
return { consume: true };
|
|
58
|
+
}
|
|
59
|
+
if (mouse?.type === "down" && mouse.button === 0) {
|
|
60
|
+
selection.start(mouse);
|
|
61
|
+
requestRender();
|
|
62
|
+
return { consume: true };
|
|
63
|
+
}
|
|
64
|
+
if (mouse?.type === "drag" && mouse.button === 0) {
|
|
65
|
+
selection.update(mouse);
|
|
66
|
+
requestRender();
|
|
67
|
+
return { consume: true };
|
|
68
|
+
}
|
|
69
|
+
if (mouse?.type === "up") {
|
|
70
|
+
selection.finish(mouse, { clear: false });
|
|
71
|
+
requestRender();
|
|
72
|
+
return { consume: true };
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
handleCopyKey(data) {
|
|
78
|
+
if (data !== "\x03") return undefined;
|
|
79
|
+
const text = selection.text();
|
|
80
|
+
if (!text) return undefined;
|
|
81
|
+
selection.clear();
|
|
82
|
+
copySelectionText(text);
|
|
83
|
+
return { consume: true };
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function compactStatusMessage(message) {
|
|
89
|
+
const text = String(message || "unknown error").replace(/\s+/g, " ").trim();
|
|
90
|
+
return text.length > 80 ? `${text.slice(0, 79)}…` : text;
|
|
91
|
+
}
|