mixdog 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -23
- package/package.json +33 -27
- package/scripts/_test-folder-dialog.mjs +30 -0
- package/scripts/agent-parallel-smoke.mjs +388 -0
- package/scripts/agent-tag-reuse-smoke.mjs +183 -0
- package/scripts/background-task-meta-smoke.mjs +38 -0
- package/scripts/boot-smoke.mjs +52 -9
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/compact-active-turn-test.mjs +68 -0
- package/scripts/compact-smoke.mjs +859 -129
- package/scripts/compact-trigger-migration-smoke.mjs +187 -0
- package/scripts/fix-brief-fn.mjs +35 -0
- package/scripts/fix-format-tool-surface.mjs +24 -0
- package/scripts/fix-tool-exec-visible.mjs +42 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/hook-bus-test.mjs +330 -0
- package/scripts/lead-workflow-smoke.mjs +33 -39
- package/scripts/live-worker-smoke.mjs +43 -37
- package/scripts/llm-trace-summary.mjs +315 -0
- package/scripts/memory-meta-concurrency-test.mjs +20 -0
- package/scripts/output-style-smoke.mjs +56 -15
- package/scripts/parent-abort-link-test.mjs +44 -0
- package/scripts/patch-agent-brief.mjs +48 -0
- package/scripts/patch-app.mjs +21 -0
- package/scripts/patch-app2.mjs +18 -0
- package/scripts/patch-dist-brief.mjs +96 -0
- package/scripts/patch-tool-exec.mjs +70 -0
- package/scripts/pretool-ask-runtime-test.mjs +54 -0
- package/scripts/provider-toolcall-test.mjs +376 -0
- package/scripts/reactive-compact-persist-smoke.mjs +124 -0
- package/scripts/sanitize-tool-pairs-test.mjs +260 -0
- package/scripts/session-context-bench.mjs +344 -0
- package/scripts/session-ingest-smoke.mjs +177 -0
- package/scripts/set-effort-config-test.mjs +41 -0
- package/scripts/smoke-runtime-negative.ps1 +106 -0
- package/scripts/smoke-runtime-negative.sh +97 -0
- package/scripts/smoke.mjs +25 -0
- package/scripts/tool-result-hook-test.mjs +48 -0
- package/scripts/tool-smoke.mjs +1223 -95
- package/scripts/toolcall-args-test.mjs +150 -0
- package/scripts/tui-background-failure-smoke.mjs +73 -0
- package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
- package/src/agents/debugger/AGENT.md +8 -0
- package/src/agents/explore/AGENT.md +4 -0
- package/src/agents/heavy-worker/AGENT.md +9 -3
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +8 -0
- package/src/agents/scheduler-task/AGENT.md +12 -0
- package/src/agents/scheduler-task/agent.json +6 -0
- package/src/agents/webhook-handler/AGENT.md +12 -0
- package/src/agents/webhook-handler/agent.json +6 -0
- package/src/agents/worker/AGENT.md +9 -3
- package/src/app.mjs +77 -3
- package/src/defaults/hidden-roles.json +17 -12
- package/src/headless-role.mjs +117 -0
- package/src/help.mjs +30 -0
- package/src/hooks/lib/permission-evaluator.cjs +11 -475
- package/src/lib/keychain-cjs.cjs +9 -1
- package/src/lib/mixdog-debug.cjs +0 -7
- package/src/lib/rules-builder.cjs +240 -96
- package/src/lib/text-utils.cjs +1 -1
- package/src/mixdog-session-runtime.mjs +2325 -450
- package/src/output-styles/default.md +12 -28
- package/src/output-styles/extreme-simple.md +9 -6
- package/src/output-styles/simple.md +22 -9
- package/src/repl.mjs +118 -59
- package/src/rules/agent/00-common.md +15 -0
- package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
- package/src/rules/agent/30-explorer.md +22 -0
- package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
- package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
- package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
- package/src/rules/lead/01-general.md +9 -5
- package/src/rules/lead/04-workflow.md +51 -12
- package/src/rules/lead/lead-tool.md +6 -0
- package/src/rules/shared/01-tool.md +12 -1
- package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
- package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
- package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
- package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
- package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
- package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
- package/src/runtime/agent/orchestrator/config.mjs +174 -55
- package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
- package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
- package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
- package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
- package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
- package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
- package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
- package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
- package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
- package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
- package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
- package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
- package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
- package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
- package/src/runtime/channels/index.mjs +152 -24
- package/src/runtime/channels/lib/scheduler.mjs +18 -14
- package/src/runtime/channels/lib/session-discovery.mjs +3 -2
- package/src/runtime/channels/lib/tool-format.mjs +0 -2
- package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
- package/src/runtime/channels/lib/webhook.mjs +1 -1
- package/src/runtime/channels/tool-defs.mjs +29 -29
- package/src/runtime/memory/index.mjs +635 -107
- package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
- package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
- package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
- package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
- package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
- package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
- package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
- package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
- package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
- package/src/runtime/memory/lib/memory.mjs +88 -9
- package/src/runtime/memory/lib/model-profile.mjs +1 -1
- package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
- package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
- package/src/runtime/memory/lib/session-ingest.mjs +194 -0
- package/src/runtime/memory/lib/trace-store.mjs +96 -51
- package/src/runtime/memory/tool-defs.mjs +46 -37
- package/src/runtime/search/index.mjs +102 -466
- package/src/runtime/search/lib/web-tools.mjs +45 -25
- package/src/runtime/search/tool-defs.mjs +16 -23
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +4 -3
- package/src/runtime/shared/background-tasks.mjs +122 -11
- package/src/runtime/shared/child-spawn-gate.mjs +145 -0
- package/src/runtime/shared/config.mjs +7 -4
- package/src/runtime/shared/err-text.mjs +131 -4
- package/src/runtime/shared/llm/cost.mjs +2 -2
- package/src/runtime/shared/llm/http-agent.mjs +23 -7
- package/src/runtime/shared/llm/index.mjs +34 -11
- package/src/runtime/shared/llm/usage-log.mjs +4 -4
- package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
- package/src/runtime/shared/singleton-owner.mjs +104 -0
- package/src/runtime/shared/tool-execution-contract.mjs +199 -20
- package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
- package/src/runtime/shared/tool-surface.mjs +624 -98
- package/src/runtime/shared/user-data-guard.mjs +0 -2
- package/src/standalone/agent-task-status.mjs +203 -0
- package/src/standalone/agent-task-status.test.mjs +76 -0
- package/src/standalone/agent-tool.mjs +1913 -0
- package/src/standalone/channel-worker.mjs +370 -14
- package/src/standalone/explore-tool.mjs +165 -70
- package/src/standalone/folder-dialog.mjs +314 -0
- package/src/standalone/hook-bus.mjs +898 -22
- package/src/standalone/memory-runtime-proxy.mjs +320 -0
- package/src/standalone/projects.mjs +226 -0
- package/src/standalone/provider-admin.mjs +41 -24
- package/src/standalone/seeds.mjs +2 -69
- package/src/standalone/usage-dashboard.mjs +96 -8
- package/src/tui/App.jsx +4798 -2153
- package/src/tui/components/AnsiText.jsx +39 -28
- package/src/tui/components/ContextPanel.jsx +87 -29
- package/src/tui/components/Markdown.jsx +43 -77
- package/src/tui/components/MarkdownTable.jsx +9 -184
- package/src/tui/components/Message.jsx +28 -11
- package/src/tui/components/Picker.jsx +95 -56
- package/src/tui/components/PromptInput.jsx +367 -239
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +27 -21
- package/src/tui/components/Spinner.jsx +67 -38
- package/src/tui/components/StatusLine.jsx +606 -38
- package/src/tui/components/TextEntryPanel.jsx +128 -9
- package/src/tui/components/ToolExecution.jsx +617 -368
- package/src/tui/components/TurnDone.jsx +3 -3
- package/src/tui/components/UsagePanel.jsx +3 -5
- package/src/tui/components/tool-output-format.mjs +365 -0
- package/src/tui/components/tool-output-format.test.mjs +220 -0
- package/src/tui/dist/index.mjs +8915 -2418
- package/src/tui/engine-runtime-notification.test.mjs +115 -0
- package/src/tui/engine-tool-result-text.test.mjs +75 -0
- package/src/tui/engine.mjs +1455 -279
- package/src/tui/figures.mjs +21 -40
- package/src/tui/index.jsx +75 -31
- package/src/tui/input-editing.mjs +25 -0
- package/src/tui/markdown/format-token.mjs +511 -68
- package/src/tui/markdown/format-token.test.mjs +216 -0
- package/src/tui/markdown/render-ansi.mjs +94 -0
- package/src/tui/markdown/render-ansi.test.mjs +108 -0
- package/src/tui/markdown/stream-fence.mjs +34 -0
- package/src/tui/markdown/stream-fence.test.mjs +26 -0
- package/src/tui/markdown/table-layout.mjs +250 -0
- package/src/tui/paste-attachments.mjs +0 -7
- package/src/tui/spinner-verbs.mjs +1 -2
- package/src/tui/statusline-ansi-bridge.mjs +172 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
- package/src/tui/theme.mjs +746 -24
- package/src/tui/time-format.mjs +1 -1
- package/src/tui/transcript-tool-failures.mjs +67 -0
- package/src/tui/transcript-tool-failures.test.mjs +111 -0
- package/src/ui/ansi.mjs +1 -2
- package/src/ui/markdown.mjs +85 -26
- package/src/ui/markdown.test.mjs +70 -0
- package/src/ui/model-display.mjs +121 -0
- package/src/ui/session-stats.mjs +44 -0
- package/src/ui/statusline-context-label.test.mjs +15 -0
- package/src/ui/statusline.mjs +386 -178
- package/src/ui/tool-card.mjs +3 -16
- package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
- package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
- package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
- package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
- package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
- package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
- package/src/workflows/default/WORKFLOW.md +12 -5
- package/src/workflows/default/workflow.json +0 -1
- package/src/workflows/solo/WORKFLOW.md +15 -0
- package/src/workflows/solo/workflow.json +7 -0
- package/vendor/ink/build/output.js +6 -1
- package/src/agents/scheduler-task.md +0 -3
- package/src/agents/web-researcher/AGENT.md +0 -3
- package/src/agents/web-researcher/agent.json +0 -6
- package/src/agents/webhook-handler.md +0 -3
- package/src/rules/bridge/00-common.md +0 -5
- package/src/rules/bridge/30-explorer.md +0 -4
- package/src/rules/lead/00-tool-lead.md +0 -5
- package/src/rules/shared/00-language.md +0 -3
- package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
- package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
- package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
- package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
- package/src/standalone/bridge-tool.mjs +0 -1414
- package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
|
@@ -6,7 +6,6 @@ import { Box, Text, useInput, usePaste, useStdin } from 'ink';
|
|
|
6
6
|
import stringWidth from 'string-width';
|
|
7
7
|
import { theme } from '../theme.mjs';
|
|
8
8
|
import {
|
|
9
|
-
caretPosition,
|
|
10
9
|
clearSelection,
|
|
11
10
|
deleteBackwardWord,
|
|
12
11
|
deleteForwardWord,
|
|
@@ -38,7 +37,7 @@ function renderSelectedText(displayValue, range, trailingSpace = false) {
|
|
|
38
37
|
<>
|
|
39
38
|
{start > 0 ? displayValue.slice(0, start) : null}
|
|
40
39
|
{end > start ? (
|
|
41
|
-
<Text color={theme.
|
|
40
|
+
<Text color={theme.selectionText} backgroundColor={theme.selectionBackground}>{displayValue.slice(start, end)}</Text>
|
|
42
41
|
) : null}
|
|
43
42
|
{displayValue.slice(end)}
|
|
44
43
|
{trailingSpace ? ' ' : ''}
|
|
@@ -50,6 +49,76 @@ function normalizeInput(text) {
|
|
|
50
49
|
return String(text ?? '').replace(/\r\n?/g, '\n');
|
|
51
50
|
}
|
|
52
51
|
|
|
52
|
+
// Collapse newlines to a single visible glyph so multiline pasted input stays a
|
|
53
|
+
// single visual row (the draft itself is unchanged for editing/submit).
|
|
54
|
+
const NEWLINE_GLYPH = '⏎';
|
|
55
|
+
function flattenForSingleLine(text) {
|
|
56
|
+
return String(text ?? '').replace(/\n/g, NEWLINE_GLYPH);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Horizontal viewport over a single (flattened) line. Keeps the caret visible
|
|
60
|
+
// inside `width` cells and returns the visible slice plus the caret column so
|
|
61
|
+
// the content box can be hard-bounded to ONE row: long/multiline pasted input
|
|
62
|
+
// scrolls horizontally instead of wrapping and growing the panel. Offsets are
|
|
63
|
+
// code-unit offsets, which map 1:1 to the flattened string (newline → 1 glyph,
|
|
64
|
+
// mask → 1 char), so selection/cursor offsets carry over unchanged.
|
|
65
|
+
function windowSingleLine(flat, cursor, width) {
|
|
66
|
+
const w = Math.max(1, Math.floor(Number(width) || 1));
|
|
67
|
+
const chars = Array.from(flat);
|
|
68
|
+
const cells = chars.map((ch) => stringWidth(ch));
|
|
69
|
+
// Map the code-unit cursor to a char index.
|
|
70
|
+
let cuIndex = 0;
|
|
71
|
+
let cursorCharIdx = chars.length;
|
|
72
|
+
for (let i = 0; i < chars.length; i += 1) {
|
|
73
|
+
if (cuIndex >= cursor) { cursorCharIdx = i; break; }
|
|
74
|
+
cuIndex += chars[i].length;
|
|
75
|
+
}
|
|
76
|
+
let cursorCell = 0;
|
|
77
|
+
for (let i = 0; i < cursorCharIdx; i += 1) cursorCell += cells[i];
|
|
78
|
+
const totalCell = cells.reduce((a, b) => a + b, 0);
|
|
79
|
+
let startCell = cursorCell > w - 1 ? cursorCell - (w - 1) : 0;
|
|
80
|
+
startCell = Math.min(startCell, Math.max(0, totalCell - w));
|
|
81
|
+
startCell = Math.max(0, startCell);
|
|
82
|
+
let acc = 0;
|
|
83
|
+
let a = 0;
|
|
84
|
+
while (a < chars.length && acc + cells[a] <= startCell) { acc += cells[a]; a += 1; }
|
|
85
|
+
const alignedStart = acc;
|
|
86
|
+
let b = a;
|
|
87
|
+
let bAcc = 0;
|
|
88
|
+
while (b < chars.length && bAcc + cells[b] <= w) { bAcc += cells[b]; b += 1; }
|
|
89
|
+
let cuStart = 0;
|
|
90
|
+
for (let i = 0; i < a; i += 1) cuStart += chars[i].length;
|
|
91
|
+
let cuEnd = cuStart;
|
|
92
|
+
for (let i = a; i < b; i += 1) cuEnd += chars[i].length;
|
|
93
|
+
return {
|
|
94
|
+
text: chars.slice(a, b).join(''),
|
|
95
|
+
cuStart,
|
|
96
|
+
cuEnd,
|
|
97
|
+
startCell: alignedStart,
|
|
98
|
+
caretCol: Math.max(0, cursorCell - alignedStart),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Collapse any whitespace/newlines so a hint is always a single visual line.
|
|
103
|
+
function singleLine(text) {
|
|
104
|
+
return String(text ?? '').replace(/\s+/g, ' ').trim();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Width-aware single-line truncation with an ellipsis so a long hint (e.g. an
|
|
108
|
+
// OAuth manual URL) can never wrap into extra rows and overflow the panel.
|
|
109
|
+
function truncateText(value, width) {
|
|
110
|
+
const text = String(value || '');
|
|
111
|
+
if (!(width > 0)) return '';
|
|
112
|
+
if (stringWidth(text) <= width) return text;
|
|
113
|
+
if (width <= 1) return '…'.repeat(Math.max(0, width));
|
|
114
|
+
let out = '';
|
|
115
|
+
for (const ch of text) {
|
|
116
|
+
if (stringWidth(`${out}${ch}…`) > width) break;
|
|
117
|
+
out += ch;
|
|
118
|
+
}
|
|
119
|
+
return `${out}…`;
|
|
120
|
+
}
|
|
121
|
+
|
|
53
122
|
function singleTrailingLineBreakPrefix(text) {
|
|
54
123
|
const normalized = normalizeInput(text);
|
|
55
124
|
if (!normalized.endsWith('\n')) return null;
|
|
@@ -57,6 +126,16 @@ function singleTrailingLineBreakPrefix(text) {
|
|
|
57
126
|
return prefix.includes('\n') ? null : prefix;
|
|
58
127
|
}
|
|
59
128
|
|
|
129
|
+
function isCtrlEnterSequence(input) {
|
|
130
|
+
const text = String(input ?? '');
|
|
131
|
+
const body = text.startsWith('\x1b[') ? text.slice(2) : text.startsWith('[') ? text.slice(1) : '';
|
|
132
|
+
if (!body) return false;
|
|
133
|
+
const kitty = /^13;(\d+)(?::\d+)?(?:;[\d:]+)?u$/.exec(body);
|
|
134
|
+
if (kitty) return ((Number(kitty[1]) - 1) & 4) !== 0;
|
|
135
|
+
const modifyOtherKeys = /^27;(\d+);13~$/.exec(body);
|
|
136
|
+
return Boolean(modifyOtherKeys && (((Number(modifyOtherKeys[1]) - 1) & 4) !== 0));
|
|
137
|
+
}
|
|
138
|
+
|
|
60
139
|
export function TextEntryPanel({
|
|
61
140
|
title,
|
|
62
141
|
hint = '',
|
|
@@ -145,8 +224,9 @@ export function TextEntryPanel({
|
|
|
145
224
|
}, { isActive: isRawModeSupported });
|
|
146
225
|
|
|
147
226
|
useInput((input, key) => {
|
|
227
|
+
const rawSource = String(input ?? '');
|
|
148
228
|
const rawInput = normalizeInput(input);
|
|
149
|
-
if (/(?:\x1b)?\[<\d+;\d+;\d+[Mm]/.test(
|
|
229
|
+
if (/(?:\x1b)?\[<\d+;\d+;\d+[Mm]/.test(rawSource)) return;
|
|
150
230
|
|
|
151
231
|
if (key.escape) {
|
|
152
232
|
if (selectionRange(draftRef.current)) {
|
|
@@ -157,20 +237,30 @@ export function TextEntryPanel({
|
|
|
157
237
|
return;
|
|
158
238
|
}
|
|
159
239
|
const trailingEnterPrefix = singleTrailingLineBreakPrefix(rawInput);
|
|
240
|
+
const rawCtrlEnter = isCtrlEnterSequence(rawSource) || isCtrlEnterSequence(rawInput);
|
|
241
|
+
const modifiedLineBreak = key.shift || key.meta || key.ctrl || rawCtrlEnter;
|
|
160
242
|
const pasteFallback = rawInput.includes('\n') && trailingEnterPrefix === null && (rawInput.length > 1 || !key.return);
|
|
161
243
|
if (pasteFallback) {
|
|
162
244
|
updateDraft((d) => insertText(d, rawInput));
|
|
163
245
|
return;
|
|
164
246
|
}
|
|
165
247
|
if (trailingEnterPrefix !== null) {
|
|
166
|
-
if (
|
|
248
|
+
if (modifiedLineBreak) {
|
|
167
249
|
updateDraft((d) => insertText(d, `${trailingEnterPrefix}\n`));
|
|
168
250
|
return;
|
|
169
251
|
}
|
|
170
252
|
submitEnterChunk(trailingEnterPrefix);
|
|
171
253
|
return;
|
|
172
254
|
}
|
|
255
|
+
if (rawCtrlEnter) {
|
|
256
|
+
updateDraft((d) => replaceSelection(d, '\n'));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
173
259
|
if (key.return || rawInput === '\n') {
|
|
260
|
+
if (modifiedLineBreak) {
|
|
261
|
+
updateDraft((d) => replaceSelection(d, '\n'));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
174
264
|
submit();
|
|
175
265
|
return;
|
|
176
266
|
}
|
|
@@ -280,8 +370,13 @@ export function TextEntryPanel({
|
|
|
280
370
|
const labelWidth = stringWidth(String(promptLabel || ''));
|
|
281
371
|
const w = Math.max(1, (yogaNode?.getComputedWidth?.() ?? columns) - labelWidth);
|
|
282
372
|
contentWidthRef.current = w;
|
|
283
|
-
|
|
284
|
-
|
|
373
|
+
// The content area is hard-bounded to ONE row (see render): newlines are
|
|
374
|
+
// flattened to a glyph and the line scrolls horizontally. So the caret is
|
|
375
|
+
// always on row 0, at the windowed caret column past the prompt label.
|
|
376
|
+
const visible = mask ? d.value.replace(/[^\n]/g, '*') : d.value;
|
|
377
|
+
const flat = flattenForSingleLine(visible);
|
|
378
|
+
const win = windowSingleLine(flat, d.cursor, w);
|
|
379
|
+
return { row: 0, col: labelWidth + win.caretCol };
|
|
285
380
|
};
|
|
286
381
|
return true;
|
|
287
382
|
};
|
|
@@ -301,9 +396,31 @@ export function TextEntryPanel({
|
|
|
301
396
|
}, [isRawModeSupported, title]);
|
|
302
397
|
|
|
303
398
|
const visibleValue = mask ? draft.value.replace(/[^\n]/g, '*') : draft.value;
|
|
304
|
-
|
|
399
|
+
// Hard-bound the content to the ONE reserved row: flatten newlines to a glyph
|
|
400
|
+
// and take a horizontal window around the caret so long/multiline pasted
|
|
401
|
+
// input scrolls sideways instead of wrapping and growing the panel (which
|
|
402
|
+
// would top-clip the title). Offsets map 1:1 to the flattened string, so the
|
|
403
|
+
// selection highlight stays aligned. The full draft value is preserved for
|
|
404
|
+
// editing and submit; only the rendered slice is clipped.
|
|
405
|
+
const labelCells = stringWidth(String(promptLabel || ''));
|
|
406
|
+
const contentCells = Math.max(1, columns - 2 - labelCells);
|
|
407
|
+
const flatValue = flattenForSingleLine(visibleValue);
|
|
408
|
+
const win = windowSingleLine(flatValue, draft.cursor, contentCells);
|
|
409
|
+
const windowSelection = (() => {
|
|
410
|
+
const range = selectionRange(draft);
|
|
411
|
+
if (!range) return null;
|
|
412
|
+
const start = Math.max(win.cuStart, Math.min(win.cuEnd, range.start)) - win.cuStart;
|
|
413
|
+
const end = Math.max(win.cuStart, Math.min(win.cuEnd, range.end)) - win.cuStart;
|
|
414
|
+
return end > start ? { start, end } : null;
|
|
415
|
+
})();
|
|
416
|
+
const trailingCaret = draft.cursor === draft.value.length && win.cuEnd >= flatValue.length;
|
|
417
|
+
const renderedValue = renderSelectedText(win.text, windowSelection, trailingCaret);
|
|
305
418
|
const action = String(actionLabel || 'save').trim() || 'save';
|
|
306
419
|
const helpText = `Enter to ${action} · Esc to cancel`;
|
|
420
|
+
// Standard panel rhythm: title row, blank, single-line hint, blank, content.
|
|
421
|
+
// The hint is collapsed to one line and width-truncated so a long manual
|
|
422
|
+
// OAuth URL can never wrap and push the bordered title off the top.
|
|
423
|
+
const hintText = truncateText(singleLine(hint), Math.max(0, columns - 4));
|
|
307
424
|
|
|
308
425
|
return (
|
|
309
426
|
<Box flexDirection="column" flexShrink={0} width="100%">
|
|
@@ -312,10 +429,12 @@ export function TextEntryPanel({
|
|
|
312
429
|
<Text color={theme.panelTitle}>{title}</Text>
|
|
313
430
|
<Text color={theme.subtle}>{helpText}</Text>
|
|
314
431
|
</Box>
|
|
315
|
-
|
|
432
|
+
<Text> </Text>
|
|
433
|
+
<Text color={theme.subtle}>{hintText || ' '}</Text>
|
|
434
|
+
<Text> </Text>
|
|
316
435
|
<Box ref={boxRef} flexDirection="row" width="100%" backgroundColor={theme.background}>
|
|
317
436
|
<Text color={theme.inactive}>{promptLabel}</Text>
|
|
318
|
-
<Text color={theme.text} wrap="
|
|
437
|
+
<Text color={theme.text} wrap="truncate">{renderedValue}</Text>
|
|
319
438
|
</Box>
|
|
320
439
|
</Box>
|
|
321
440
|
</Box>
|