mixdog 0.9.26 → 0.9.27
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 +2 -1
- package/scripts/arg-guard-test.mjs +68 -0
- package/scripts/compacted-placeholder-scrub-test.mjs +77 -0
- package/src/cli.mjs +40 -4
- package/src/rules/shared/01-tool.md +3 -0
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +5 -0
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +14 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
- package/src/runtime/memory/tool-defs.mjs +2 -2
- package/src/runtime/shared/staged-install-worker.mjs +14 -0
- package/src/runtime/shared/staged-update.mjs +530 -0
- package/src/runtime/shared/update-checker.mjs +1 -1
- package/src/session-runtime/lifecycle-api.mjs +9 -11
- package/src/session-runtime/runtime-core.mjs +35 -36
- package/src/standalone/agent-tool/tool-def.mjs +1 -1
- package/src/tui/app/transcript-window.mjs +26 -6
- package/src/tui/components/Spinner.jsx +5 -2
- package/src/tui/components/StatusLine.jsx +7 -7
- package/src/tui/components/ToolExecution.jsx +24 -58
- package/src/tui/display-width.mjs +8 -4
- package/src/tui/dist/index.mjs +373 -269
- package/src/tui/hooks/useSharedTick.mjs +103 -0
- package/src/tui/markdown/format-token.mjs +46 -8
- package/src/tui/markdown/render-ansi.mjs +24 -1
- package/src/tui/markdown/stream-fence.mjs +60 -0
- package/src/tui/markdown/streaming-markdown.mjs +22 -1
- package/vendor/ink/build/display-width.js +5 -4
package/src/tui/dist/index.mjs
CHANGED
|
@@ -163,7 +163,7 @@ import { performance as performance3 } from "node:perf_hooks";
|
|
|
163
163
|
import { format } from "node:util";
|
|
164
164
|
|
|
165
165
|
// src/tui/App.jsx
|
|
166
|
-
import React20, { useState as useState8, useCallback as useCallback6, useEffect as useEffect10, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as
|
|
166
|
+
import React20, { useState as useState8, useCallback as useCallback6, useEffect as useEffect10, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as useRef10 } from "react";
|
|
167
167
|
import { Box as Box18, Text as Text20, useApp, useInput as useInput5, useStdin as useStdin3, useStdout as useStdout2 } from "../../../vendor/ink/build/index.js";
|
|
168
168
|
|
|
169
169
|
// src/tui/themes/base.mjs
|
|
@@ -3100,8 +3100,67 @@ function formatAggregateDetail(summaries) {
|
|
|
3100
3100
|
}
|
|
3101
3101
|
|
|
3102
3102
|
// src/tui/components/Spinner.jsx
|
|
3103
|
-
import React, { useRef } from "react";
|
|
3104
|
-
import { Box, Text
|
|
3103
|
+
import React, { useRef as useRef2 } from "react";
|
|
3104
|
+
import { Box, Text } from "../../../vendor/ink/build/index.js";
|
|
3105
|
+
|
|
3106
|
+
// src/tui/hooks/useSharedTick.mjs
|
|
3107
|
+
import { useEffect, useRef, useState } from "react";
|
|
3108
|
+
var TICK_SLOP_MS = 16;
|
|
3109
|
+
var subscribers = /* @__PURE__ */ new Set();
|
|
3110
|
+
var timer = null;
|
|
3111
|
+
function nextDelayMs(now) {
|
|
3112
|
+
let soonest = Infinity;
|
|
3113
|
+
for (const sub of subscribers) {
|
|
3114
|
+
const due = sub.lastFire + sub.interval - now;
|
|
3115
|
+
if (due < soonest) soonest = due;
|
|
3116
|
+
}
|
|
3117
|
+
return Number.isFinite(soonest) ? Math.max(1, soonest) : null;
|
|
3118
|
+
}
|
|
3119
|
+
function reconcileTimer() {
|
|
3120
|
+
if (timer) {
|
|
3121
|
+
clearTimeout(timer);
|
|
3122
|
+
timer = null;
|
|
3123
|
+
}
|
|
3124
|
+
const delay = nextDelayMs(Date.now());
|
|
3125
|
+
if (delay == null) return;
|
|
3126
|
+
timer = setTimeout(onTick, delay);
|
|
3127
|
+
timer.unref?.();
|
|
3128
|
+
}
|
|
3129
|
+
function onTick() {
|
|
3130
|
+
timer = null;
|
|
3131
|
+
const now = Date.now();
|
|
3132
|
+
for (const sub of Array.from(subscribers)) {
|
|
3133
|
+
if (!subscribers.has(sub)) continue;
|
|
3134
|
+
if (now - sub.lastFire >= sub.interval - TICK_SLOP_MS) {
|
|
3135
|
+
sub.lastFire = now;
|
|
3136
|
+
sub.notify(now);
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
reconcileTimer();
|
|
3140
|
+
}
|
|
3141
|
+
function useSharedTick(intervalMs, isActive = true, onTick2 = null) {
|
|
3142
|
+
const [, setTick] = useState(0);
|
|
3143
|
+
const cbRef = useRef(onTick2);
|
|
3144
|
+
cbRef.current = onTick2;
|
|
3145
|
+
useEffect(() => {
|
|
3146
|
+
if (!isActive) return void 0;
|
|
3147
|
+
const interval = Math.max(1, Number(intervalMs) || 0);
|
|
3148
|
+
const sub = {
|
|
3149
|
+
interval,
|
|
3150
|
+
lastFire: Date.now(),
|
|
3151
|
+
notify: (now) => {
|
|
3152
|
+
if (cbRef.current) cbRef.current(now);
|
|
3153
|
+
else setTick((t) => (t + 1) % 1e6);
|
|
3154
|
+
}
|
|
3155
|
+
};
|
|
3156
|
+
subscribers.add(sub);
|
|
3157
|
+
reconcileTimer();
|
|
3158
|
+
return () => {
|
|
3159
|
+
subscribers.delete(sub);
|
|
3160
|
+
reconcileTimer();
|
|
3161
|
+
};
|
|
3162
|
+
}, [intervalMs, isActive]);
|
|
3163
|
+
}
|
|
3105
3164
|
|
|
3106
3165
|
// src/tui/spinner-verbs.mjs
|
|
3107
3166
|
var SPINNER_VERBS = [
|
|
@@ -3438,19 +3497,19 @@ function tokenModeGlyph(mode) {
|
|
|
3438
3497
|
}
|
|
3439
3498
|
}
|
|
3440
3499
|
function Spinner({ verb = "Working", startedAt, outputTokens = 0, tokens = 0, thinking = false, thinkingActiveSince = 0, mode = "responding", columns = 80, marginTop = 1 }) {
|
|
3441
|
-
|
|
3500
|
+
useSharedTick(FRAME_MS);
|
|
3442
3501
|
const { TEXT_RGB, SHIMMER_RGB, SPINNER_GLYPH_RGB, THINKING_INACTIVE, THINKING_SHIMMER, STALL_RGB } = spinnerRgb();
|
|
3443
3502
|
const now = Date.now();
|
|
3444
3503
|
const elapsedMs = startedAt ? Math.max(0, now - startedAt) : 0;
|
|
3445
3504
|
const frame = Math.floor(elapsedMs / FRAME_MS);
|
|
3446
|
-
const lastGrowRef =
|
|
3447
|
-
const lastTokensRef =
|
|
3448
|
-
const displayedOutputRef =
|
|
3449
|
-
const displayVerbRef =
|
|
3450
|
-
const displayVerbModeRef =
|
|
3451
|
-
const nextVerbCheckRef =
|
|
3452
|
-
const stallSmoothRef =
|
|
3453
|
-
const lastStallTickRef =
|
|
3505
|
+
const lastGrowRef = useRef2(now);
|
|
3506
|
+
const lastTokensRef = useRef2(0);
|
|
3507
|
+
const displayedOutputRef = useRef2(0);
|
|
3508
|
+
const displayVerbRef = useRef2("");
|
|
3509
|
+
const displayVerbModeRef = useRef2("");
|
|
3510
|
+
const nextVerbCheckRef = useRef2(0);
|
|
3511
|
+
const stallSmoothRef = useRef2(0);
|
|
3512
|
+
const lastStallTickRef = useRef2(0);
|
|
3454
3513
|
const targetOutputTokens = Math.max(0, Number(outputTokens || tokens || 0));
|
|
3455
3514
|
if (targetOutputTokens > lastTokensRef.current) {
|
|
3456
3515
|
lastTokensRef.current = targetOutputTokens;
|
|
@@ -3559,7 +3618,7 @@ function Spinner({ verb = "Working", startedAt, outputTokens = 0, tokens = 0, th
|
|
|
3559
3618
|
}
|
|
3560
3619
|
|
|
3561
3620
|
// src/tui/components/StatusLine.jsx
|
|
3562
|
-
import React2, { useEffect, useRef as
|
|
3621
|
+
import React2, { useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
|
|
3563
3622
|
import { Box as Box2, Text as Text2 } from "../../../vendor/ink/build/index.js";
|
|
3564
3623
|
|
|
3565
3624
|
// src/tui/statusline-ansi-bridge.mjs
|
|
@@ -3693,11 +3752,11 @@ var statuslineModulePrewarmScheduled = false;
|
|
|
3693
3752
|
function scheduleStatuslineModulePrewarm() {
|
|
3694
3753
|
if (statuslineModulePrewarmScheduled) return;
|
|
3695
3754
|
statuslineModulePrewarmScheduled = true;
|
|
3696
|
-
const
|
|
3755
|
+
const timer2 = setTimeout(() => {
|
|
3697
3756
|
loadStatuslineModule().catch(() => {
|
|
3698
3757
|
});
|
|
3699
3758
|
}, STATUSLINE_MODULE_PREWARM_DELAY_MS);
|
|
3700
|
-
|
|
3759
|
+
timer2.unref?.();
|
|
3701
3760
|
}
|
|
3702
3761
|
var STATUSLINE_BOOT_FULL_DELAY_MS = 1e3;
|
|
3703
3762
|
var STATUSLINE_BOOT_FULL_DELAY_ACTIVE_MS = 2200;
|
|
@@ -4006,7 +4065,7 @@ function workflowModeLabel(workflow = {}, remoteEnabled = false) {
|
|
|
4006
4065
|
return remoteEnabled === true ? `Remote / ${base}` : base;
|
|
4007
4066
|
}
|
|
4008
4067
|
function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fast, cwd, stats, contextWindow, displayContextWindow = 0, compactBoundaryTokens = 0, autoCompactTokenLimit = 0, rawContextWindow, resizeEpoch, agentRevision = "", agentWorkers = [], agentJobs = [], activeTools = null, initialLine = "", workflow = null, remoteEnabled = false, themeEpoch = 0 }) {
|
|
4009
|
-
const [line, setLine] =
|
|
4068
|
+
const [line, setLine] = useState2(() => normalizeStatusLine(initialLine || localBootStatusLine({
|
|
4010
4069
|
provider,
|
|
4011
4070
|
model,
|
|
4012
4071
|
effort,
|
|
@@ -4021,19 +4080,19 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
|
|
|
4021
4080
|
agentJobs,
|
|
4022
4081
|
activeTools
|
|
4023
4082
|
})));
|
|
4024
|
-
const [refreshTick, setRefreshTick] =
|
|
4025
|
-
const statuslineArgsRef =
|
|
4026
|
-
const bootFullDoneRef =
|
|
4027
|
-
const mountAtRef =
|
|
4028
|
-
const lineRef =
|
|
4029
|
-
const bootFullNextAttemptAtRef =
|
|
4030
|
-
const bootFullRetryBackoffMsRef =
|
|
4031
|
-
const renderEffectIdRef =
|
|
4032
|
-
const lastImmediateArgsRef =
|
|
4033
|
-
const themeEpochRef =
|
|
4034
|
-
const lastRawFullLineRef =
|
|
4035
|
-
const lastRawFullLineCacheKeyRef =
|
|
4036
|
-
const lastRawFullLineAtRef =
|
|
4083
|
+
const [refreshTick, setRefreshTick] = useState2(0);
|
|
4084
|
+
const statuslineArgsRef = useRef3(null);
|
|
4085
|
+
const bootFullDoneRef = useRef3(false);
|
|
4086
|
+
const mountAtRef = useRef3(Date.now());
|
|
4087
|
+
const lineRef = useRef3("");
|
|
4088
|
+
const bootFullNextAttemptAtRef = useRef3(0);
|
|
4089
|
+
const bootFullRetryBackoffMsRef = useRef3(STATUSLINE_BOOT_FULL_RETRY_MS);
|
|
4090
|
+
const renderEffectIdRef = useRef3(0);
|
|
4091
|
+
const lastImmediateArgsRef = useRef3(null);
|
|
4092
|
+
const themeEpochRef = useRef3(themeEpoch);
|
|
4093
|
+
const lastRawFullLineRef = useRef3("");
|
|
4094
|
+
const lastRawFullLineCacheKeyRef = useRef3("");
|
|
4095
|
+
const lastRawFullLineAtRef = useRef3(0);
|
|
4037
4096
|
const statuslineArgs = {
|
|
4038
4097
|
sessionId,
|
|
4039
4098
|
clientHostPid,
|
|
@@ -4074,17 +4133,13 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
|
|
|
4074
4133
|
String(statsForSignature.currentContextSource || "")
|
|
4075
4134
|
].join("|");
|
|
4076
4135
|
const refreshMs = hasActiveStatuslineWork(line, agentWorkers, agentJobs, activeTools) ? STATUSLINE_ACTIVE_REFRESH_MS : STATUSLINE_REFRESH_MS;
|
|
4077
|
-
|
|
4136
|
+
useEffect2(() => {
|
|
4078
4137
|
scheduleStatuslineModulePrewarm();
|
|
4079
4138
|
}, []);
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
timer.unref?.();
|
|
4085
|
-
return () => clearInterval(timer);
|
|
4086
|
-
}, [refreshMs]);
|
|
4087
|
-
useEffect(() => {
|
|
4139
|
+
useSharedTick(refreshMs, true, () => {
|
|
4140
|
+
setRefreshTick((tick) => (tick + 1) % 1e6);
|
|
4141
|
+
});
|
|
4142
|
+
useEffect2(() => {
|
|
4088
4143
|
let alive = true;
|
|
4089
4144
|
let bootRetryTimer = null;
|
|
4090
4145
|
const effectId = renderEffectIdRef.current + 1;
|
|
@@ -4146,7 +4201,7 @@ ${grafted}` : cachedL1;
|
|
|
4146
4201
|
autoCompactTokenLimit: args.autoCompactTokenLimit,
|
|
4147
4202
|
stats: args.stats
|
|
4148
4203
|
};
|
|
4149
|
-
const
|
|
4204
|
+
const timer2 = setTimeout(() => {
|
|
4150
4205
|
if (bootFullDoneRef.current !== true) {
|
|
4151
4206
|
if (!bootFullRenderEligible(mountAtRef.current, lineRef.current, args.agentWorkers, args.agentJobs, args.activeTools)) {
|
|
4152
4207
|
const active = hasActiveStatuslineWork(lineRef.current, args.agentWorkers, args.agentJobs, args.activeTools);
|
|
@@ -4192,10 +4247,10 @@ ${grafted}` : cachedL1;
|
|
|
4192
4247
|
resetStatuslineModuleLoad();
|
|
4193
4248
|
});
|
|
4194
4249
|
}, STATUSLINE_RENDER_DEBOUNCE_MS);
|
|
4195
|
-
|
|
4250
|
+
timer2.unref?.();
|
|
4196
4251
|
return () => {
|
|
4197
4252
|
alive = false;
|
|
4198
|
-
clearTimeout(
|
|
4253
|
+
clearTimeout(timer2);
|
|
4199
4254
|
clearTimeout(bootRetryTimer);
|
|
4200
4255
|
};
|
|
4201
4256
|
}, [sessionId, clientHostPid, provider, model, effort, fast, cwd, statsSignature, contextWindow, displayContextWindow, compactBoundaryTokens, autoCompactTokenLimit, rawContextWindow, resizeEpoch, agentRevision, agentWorkersSignature, agentJobsSignature, activeToolsSignature, refreshTick, themeEpoch]);
|
|
@@ -4212,16 +4267,16 @@ ${grafted}` : cachedL1;
|
|
|
4212
4267
|
var StatusLine = React2.memo(StatusLineView);
|
|
4213
4268
|
|
|
4214
4269
|
// src/tui/components/PromptInput.jsx
|
|
4215
|
-
import React3, { useEffect as
|
|
4270
|
+
import React3, { useEffect as useEffect3, useLayoutEffect, useState as useState3, useRef as useRef4 } from "react";
|
|
4216
4271
|
import { Box as Box3, Text as Text3, useInput, usePaste, useStdin } from "../../../vendor/ink/build/index.js";
|
|
4217
4272
|
|
|
4218
4273
|
// src/tui/display-width.mjs
|
|
4219
4274
|
import stringWidth from "string-width";
|
|
4220
4275
|
var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
4221
4276
|
function isProblemCodePoint(cp) {
|
|
4222
|
-
return cp >= 9312 && cp <= 9471 || cp >=
|
|
4277
|
+
return cp >= 9312 && cp <= 9471 || cp >= 8596 && cp <= 8703;
|
|
4223
4278
|
}
|
|
4224
|
-
var PROBLEM_RE = /[\
|
|
4279
|
+
var PROBLEM_RE = /[\u2194-\u21ff\u2460-\u24ff]/;
|
|
4225
4280
|
function resolveAmbiguousWidePolicy(env = process.env, platform = process.platform) {
|
|
4226
4281
|
const override = env?.MIXDOG_TUI_AMBIGUOUS_WIDE;
|
|
4227
4282
|
if (override === "1") return true;
|
|
@@ -4577,29 +4632,29 @@ function PromptInput({
|
|
|
4577
4632
|
mouseSelectionRef,
|
|
4578
4633
|
suppressShiftNavRef
|
|
4579
4634
|
}) {
|
|
4580
|
-
const [draft, setDraft] =
|
|
4635
|
+
const [draft, setDraft] = useState3(() => {
|
|
4581
4636
|
const value2 = String(initialValue || "");
|
|
4582
4637
|
return { value: value2, cursor: value2.length, selectionAnchor: null };
|
|
4583
4638
|
});
|
|
4584
|
-
const [, bumpCursorAnchorEpoch] =
|
|
4585
|
-
const draftRef =
|
|
4586
|
-
const lastReportedValueRef =
|
|
4587
|
-
const pasteGenerationRef =
|
|
4639
|
+
const [, bumpCursorAnchorEpoch] = useState3(0);
|
|
4640
|
+
const draftRef = useRef4(draft);
|
|
4641
|
+
const lastReportedValueRef = useRef4(draft.value);
|
|
4642
|
+
const pasteGenerationRef = useRef4(0);
|
|
4588
4643
|
if (valueRef) valueRef.current = draftRef.current.value;
|
|
4589
4644
|
const { isRawModeSupported } = useStdin();
|
|
4590
|
-
const boxRef =
|
|
4591
|
-
const cursorEnabledRef =
|
|
4592
|
-
const contentWidthRef =
|
|
4593
|
-
const preferredColumnRef =
|
|
4594
|
-
const mouseExtendCoalesceRef =
|
|
4595
|
-
const undoRef =
|
|
4645
|
+
const boxRef = useRef4(null);
|
|
4646
|
+
const cursorEnabledRef = useRef4(false);
|
|
4647
|
+
const contentWidthRef = useRef4(80);
|
|
4648
|
+
const preferredColumnRef = useRef4(null);
|
|
4649
|
+
const mouseExtendCoalesceRef = useRef4({ pendingNext: null, timer: null, t: 0 });
|
|
4650
|
+
const undoRef = useRef4({ past: [], future: [], lastPushAt: 0, lastValue: null });
|
|
4596
4651
|
const { value, cursor } = draft;
|
|
4597
4652
|
draftRef.current = draft;
|
|
4598
4653
|
if (selectionRef) {
|
|
4599
4654
|
const range = selectionRange(draft);
|
|
4600
4655
|
selectionRef.current = range ? { range, text: mask ? "" : draft.value.slice(range.start, range.end) } : null;
|
|
4601
4656
|
}
|
|
4602
|
-
const flushThrottleRef =
|
|
4657
|
+
const flushThrottleRef = useRef4({ lastAt: 0, timer: null });
|
|
4603
4658
|
const flushImmediate = () => {
|
|
4604
4659
|
let node = boxRef.current;
|
|
4605
4660
|
for (let i = 0; node && i < 64; i++) {
|
|
@@ -4816,7 +4871,7 @@ function PromptInput({
|
|
|
4816
4871
|
}
|
|
4817
4872
|
};
|
|
4818
4873
|
}
|
|
4819
|
-
|
|
4874
|
+
useEffect3(() => () => {
|
|
4820
4875
|
const state = mouseExtendCoalesceRef.current;
|
|
4821
4876
|
if (state.timer) clearTimeout(state.timer);
|
|
4822
4877
|
state.timer = null;
|
|
@@ -4879,13 +4934,13 @@ function PromptInput({
|
|
|
4879
4934
|
apply(handled);
|
|
4880
4935
|
}
|
|
4881
4936
|
};
|
|
4882
|
-
|
|
4937
|
+
useEffect3(() => {
|
|
4883
4938
|
if (!draftOverride || typeof draftOverride.value !== "string") return;
|
|
4884
4939
|
pasteGenerationRef.current += 1;
|
|
4885
4940
|
commitDraft({ value: draftOverride.value, cursor: draftOverride.value.length, selectionAnchor: null }, { skipHistory: true });
|
|
4886
4941
|
resetUndo();
|
|
4887
4942
|
}, [draftOverride?.id]);
|
|
4888
|
-
|
|
4943
|
+
useEffect3(() => () => {
|
|
4889
4944
|
pasteGenerationRef.current += 1;
|
|
4890
4945
|
if (selectionRef) selectionRef.current = null;
|
|
4891
4946
|
}, [selectionRef]);
|
|
@@ -5270,7 +5325,7 @@ function QueuedCommands({ queued, columns, compact = false }) {
|
|
|
5270
5325
|
}
|
|
5271
5326
|
|
|
5272
5327
|
// src/tui/components/Picker.jsx
|
|
5273
|
-
import React6, { useState as
|
|
5328
|
+
import React6, { useState as useState4, useCallback, useEffect as useEffect4, useRef as useRef5 } from "react";
|
|
5274
5329
|
import { Box as Box6, Text as Text6, useInput as useInput2 } from "../../../vendor/ink/build/index.js";
|
|
5275
5330
|
import stringWidth2 from "string-width";
|
|
5276
5331
|
|
|
@@ -5388,20 +5443,20 @@ function Picker({
|
|
|
5388
5443
|
themeEpoch = 0
|
|
5389
5444
|
}) {
|
|
5390
5445
|
const visibleLimit = Math.max(1, Math.floor(Number(visibleCount) || MAX_VISIBLE));
|
|
5391
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5446
|
+
const [selectedIndex, setSelectedIndex] = useState4(() => Math.max(0, Math.min(Number(initialIndex) || 0, Math.max(0, items.length - 1))));
|
|
5392
5447
|
const confirmButtons = Array.isArray(confirmBar?.buttons) ? confirmBar.buttons.filter(Boolean) : [];
|
|
5393
5448
|
const hasConfirm = confirmButtons.length > 0;
|
|
5394
|
-
const [confirmFocus, setConfirmFocus] =
|
|
5395
|
-
const lastTabAtRef =
|
|
5396
|
-
|
|
5449
|
+
const [confirmFocus, setConfirmFocus] = useState4(-1);
|
|
5450
|
+
const lastTabAtRef = useRef5(0);
|
|
5451
|
+
useEffect4(() => {
|
|
5397
5452
|
setConfirmFocus(-1);
|
|
5398
5453
|
}, [confirmButtons.length, confirmBar]);
|
|
5399
|
-
const [selectionMemo] =
|
|
5400
|
-
|
|
5454
|
+
const [selectionMemo] = useState4(() => ({ value: null, initialIndex }));
|
|
5455
|
+
useEffect4(() => {
|
|
5401
5456
|
const item = items[selectedIndex];
|
|
5402
5457
|
if (item && item.value != null) selectionMemo.value = item.value;
|
|
5403
5458
|
}, [items, selectedIndex, selectionMemo]);
|
|
5404
|
-
|
|
5459
|
+
useEffect4(() => {
|
|
5405
5460
|
setSelectedIndex((i) => {
|
|
5406
5461
|
if (selectionMemo.value != null) {
|
|
5407
5462
|
const found = items.findIndex((entry) => entry && entry.value === selectionMemo.value);
|
|
@@ -5412,7 +5467,7 @@ function Picker({
|
|
|
5412
5467
|
return Math.min(Math.max(0, i), Math.max(0, items.length - 1));
|
|
5413
5468
|
});
|
|
5414
5469
|
}, [items, selectionMemo, initialIndex]);
|
|
5415
|
-
|
|
5470
|
+
useEffect4(() => {
|
|
5416
5471
|
if (initialIndex == null) {
|
|
5417
5472
|
selectionMemo.initialIndex = null;
|
|
5418
5473
|
return;
|
|
@@ -5421,7 +5476,7 @@ function Picker({
|
|
|
5421
5476
|
selectionMemo.initialIndex = initialIndex;
|
|
5422
5477
|
setSelectedIndex(Math.max(0, Math.min(Number(initialIndex) || 0, Math.max(0, items.length - 1))));
|
|
5423
5478
|
}, [initialIndex, items.length, selectionMemo]);
|
|
5424
|
-
|
|
5479
|
+
useEffect4(() => {
|
|
5425
5480
|
if (typeof onHighlight !== "function") return;
|
|
5426
5481
|
const item = items[selectedIndex];
|
|
5427
5482
|
if (item) onHighlight(item.value, item, selectedIndex);
|
|
@@ -6048,7 +6103,7 @@ function ContextPanel({ rows, title = "Context Usage", columns = 80, fillHeight
|
|
|
6048
6103
|
}
|
|
6049
6104
|
|
|
6050
6105
|
// src/tui/components/UsagePanel.jsx
|
|
6051
|
-
import React9, { useEffect as
|
|
6106
|
+
import React9, { useEffect as useEffect5, useState as useState5 } from "react";
|
|
6052
6107
|
import { Box as Box9, Text as Text9, useInput as useInput3 } from "../../../vendor/ink/build/index.js";
|
|
6053
6108
|
import stringWidth5 from "string-width";
|
|
6054
6109
|
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -6265,11 +6320,11 @@ function UsagePanel({ dashboard, loading = false, columns = 80, fillHeight = fal
|
|
|
6265
6320
|
const hasMeasuredRows = Number(panelRows) > 0;
|
|
6266
6321
|
const maxProviderRows = hasMeasuredRows ? Math.max(1, Math.floor(panelRows) - 6) : rows.length;
|
|
6267
6322
|
const maxScrollOffset = Math.max(0, rows.length - maxProviderRows);
|
|
6268
|
-
const [scrollOffset, setScrollOffset] =
|
|
6323
|
+
const [scrollOffset, setScrollOffset] = useState5(0);
|
|
6269
6324
|
const visibleRows = hasMeasuredRows ? rows.slice(scrollOffset, scrollOffset + maxProviderRows) : rows;
|
|
6270
6325
|
const scrollable = rows.length > maxProviderRows;
|
|
6271
6326
|
const helpText = `${scrollable || isLoading || isChecking ? "\u2191/\u2193 Scroll \xB7 PgUp/PgDn Page \xB7 " : ""}Esc Back \xB7 /usage refresh`;
|
|
6272
|
-
|
|
6327
|
+
useEffect5(() => {
|
|
6273
6328
|
setScrollOffset((offset) => Math.min(Math.max(0, offset), maxScrollOffset));
|
|
6274
6329
|
}, [maxScrollOffset]);
|
|
6275
6330
|
useInput3((input, key) => {
|
|
@@ -6342,7 +6397,7 @@ function UsagePanel({ dashboard, loading = false, columns = 80, fillHeight = fal
|
|
|
6342
6397
|
}
|
|
6343
6398
|
|
|
6344
6399
|
// src/tui/components/TextEntryPanel.jsx
|
|
6345
|
-
import React10, { useEffect as
|
|
6400
|
+
import React10, { useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState6 } from "react";
|
|
6346
6401
|
import { Box as Box10, Text as Text10, useInput as useInput4, usePaste as usePaste2, useStdin as useStdin2 } from "../../../vendor/ink/build/index.js";
|
|
6347
6402
|
import stringWidth6 from "string-width";
|
|
6348
6403
|
|
|
@@ -6555,13 +6610,13 @@ function TextEntryPanel({
|
|
|
6555
6610
|
onSubmit,
|
|
6556
6611
|
onCancel
|
|
6557
6612
|
}) {
|
|
6558
|
-
const [draft, setDraft] =
|
|
6559
|
-
const [, bumpCursorAnchorEpoch] =
|
|
6560
|
-
const draftRef =
|
|
6561
|
-
const boxRef =
|
|
6562
|
-
const cursorEnabledRef =
|
|
6563
|
-
const contentWidthRef =
|
|
6564
|
-
const preferredColumnRef =
|
|
6613
|
+
const [draft, setDraft] = useState6(() => ({ value: String(initialValue || ""), cursor: String(initialValue || "").length, selectionAnchor: null }));
|
|
6614
|
+
const [, bumpCursorAnchorEpoch] = useState6(0);
|
|
6615
|
+
const draftRef = useRef6(draft);
|
|
6616
|
+
const boxRef = useRef6(null);
|
|
6617
|
+
const cursorEnabledRef = useRef6(false);
|
|
6618
|
+
const contentWidthRef = useRef6(80);
|
|
6619
|
+
const preferredColumnRef = useRef6(null);
|
|
6565
6620
|
const { isRawModeSupported } = useStdin2();
|
|
6566
6621
|
draftRef.current = draft;
|
|
6567
6622
|
const flushImmediate = () => {
|
|
@@ -6597,13 +6652,13 @@ function TextEntryPanel({
|
|
|
6597
6652
|
commitDraft(moveCursor(current, moved.cursor, { extend }), { keepPreferredColumn: true });
|
|
6598
6653
|
return true;
|
|
6599
6654
|
};
|
|
6600
|
-
|
|
6655
|
+
useEffect6(() => {
|
|
6601
6656
|
const value = String(initialValue || "");
|
|
6602
6657
|
commitDraft({ value, cursor: value.length, selectionAnchor: null });
|
|
6603
6658
|
}, [title, initialValue]);
|
|
6604
6659
|
const labelCells = stringWidth6(String(promptLabel || ""));
|
|
6605
6660
|
const contentCells = Math.max(1, columns - 4 - labelCells);
|
|
6606
|
-
|
|
6661
|
+
useEffect6(() => {
|
|
6607
6662
|
if (!multiline) {
|
|
6608
6663
|
onContentRowsChange?.(1);
|
|
6609
6664
|
return;
|
|
@@ -7381,7 +7436,7 @@ function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
|
|
|
7381
7436
|
}
|
|
7382
7437
|
let stdout = "";
|
|
7383
7438
|
let settled = false;
|
|
7384
|
-
const
|
|
7439
|
+
const timer2 = setTimeout(() => {
|
|
7385
7440
|
if (settled) return;
|
|
7386
7441
|
settled = true;
|
|
7387
7442
|
try {
|
|
@@ -7390,20 +7445,20 @@ function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
|
|
|
7390
7445
|
}
|
|
7391
7446
|
resolve5({ ok: false, code: -1, stdout: "", error: new Error("dialog timed out") });
|
|
7392
7447
|
}, timeoutMs);
|
|
7393
|
-
|
|
7448
|
+
timer2.unref?.();
|
|
7394
7449
|
child.stdout.on("data", (chunk) => {
|
|
7395
7450
|
stdout += chunk.toString("utf8");
|
|
7396
7451
|
});
|
|
7397
7452
|
child.on("error", (error) => {
|
|
7398
7453
|
if (settled) return;
|
|
7399
7454
|
settled = true;
|
|
7400
|
-
clearTimeout(
|
|
7455
|
+
clearTimeout(timer2);
|
|
7401
7456
|
resolve5({ ok: false, code: -1, stdout: "", error });
|
|
7402
7457
|
});
|
|
7403
7458
|
child.on("close", (code) => {
|
|
7404
7459
|
if (settled) return;
|
|
7405
7460
|
settled = true;
|
|
7406
|
-
clearTimeout(
|
|
7461
|
+
clearTimeout(timer2);
|
|
7407
7462
|
resolve5({ ok: code === 0, code, stdout });
|
|
7408
7463
|
});
|
|
7409
7464
|
});
|
|
@@ -7989,7 +8044,7 @@ function copyToClipboard(text) {
|
|
|
7989
8044
|
import stringWidth11 from "string-width";
|
|
7990
8045
|
|
|
7991
8046
|
// src/tui/app/use-mouse-input.mjs
|
|
7992
|
-
import { useCallback as useCallback2, useEffect as
|
|
8047
|
+
import { useCallback as useCallback2, useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
7993
8048
|
|
|
7994
8049
|
// src/tui/markdown/measure-rendered-rows.mjs
|
|
7995
8050
|
import stripAnsi4 from "strip-ansi";
|
|
@@ -8454,6 +8509,25 @@ function collectFlatBodyLines(text, codeBlock, bandWidth) {
|
|
|
8454
8509
|
}
|
|
8455
8510
|
return lines;
|
|
8456
8511
|
}
|
|
8512
|
+
function normalizeCodePlain(text) {
|
|
8513
|
+
return String(text ?? "").replace(/\r\n?/g, "\n").replace(/\t/g, " ").replace(/[\u0000-\u0009\u000b-\u001f\u007f]/g, " ");
|
|
8514
|
+
}
|
|
8515
|
+
function collectPlainBodyLines(text, codeBlock, bandWidth) {
|
|
8516
|
+
const max = Math.max(1, bandWidth);
|
|
8517
|
+
const lines = [];
|
|
8518
|
+
for (const line of String(text ?? "").split(EOL)) {
|
|
8519
|
+
if (!line) {
|
|
8520
|
+
lines.push("");
|
|
8521
|
+
continue;
|
|
8522
|
+
}
|
|
8523
|
+
if (line.length <= max && displayWidth(line) <= max) {
|
|
8524
|
+
lines.push(codeBlock(line));
|
|
8525
|
+
} else {
|
|
8526
|
+
for (const seg of wrapCodeLine(codeBlock(line), max)) lines.push(seg);
|
|
8527
|
+
}
|
|
8528
|
+
}
|
|
8529
|
+
return lines;
|
|
8530
|
+
}
|
|
8457
8531
|
function collectHighlightedBodyLines(text, hljsLang, bandWidth) {
|
|
8458
8532
|
const highlighted = highlightCodeText(text, hljsLang);
|
|
8459
8533
|
const { codeBlock } = colorizers();
|
|
@@ -8469,19 +8543,24 @@ function renderCodeBlock(token, width = 0) {
|
|
|
8469
8543
|
const { codeBlock } = colorizers();
|
|
8470
8544
|
const c = extraColorizers();
|
|
8471
8545
|
const lang = normalizeLang(token.lang);
|
|
8472
|
-
const text = normalizeCodeText(decodeEntities(token.text ?? ""));
|
|
8473
8546
|
const renderWidth = Math.max(8, Number(width) || 80);
|
|
8474
8547
|
const contentWidth = Math.max(1, renderWidth - CODE_GUTTER.length);
|
|
8475
8548
|
let bodyLines;
|
|
8476
|
-
if (
|
|
8477
|
-
|
|
8549
|
+
if (token.plain) {
|
|
8550
|
+
const text = normalizeCodePlain(decodeEntities(token.text ?? ""));
|
|
8551
|
+
bodyLines = collectPlainBodyLines(text, codeBlock, contentWidth);
|
|
8478
8552
|
} else {
|
|
8479
|
-
const
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
bodyLines = collectHighlightedBodyLines(text, hljsLang, contentWidth);
|
|
8553
|
+
const text = normalizeCodeText(decodeEntities(token.text ?? ""));
|
|
8554
|
+
if (DIFF_LANGS.has(lang) || !lang && looksLikeUnifiedDiff(text)) {
|
|
8555
|
+
bodyLines = collectDiffBodyLines(text, c, contentWidth);
|
|
8483
8556
|
} else {
|
|
8484
|
-
|
|
8557
|
+
const hljsLang = resolveHljsLanguage(lang);
|
|
8558
|
+
const family = LANG_FAMILY[lang];
|
|
8559
|
+
if (hljsLang && family !== "md") {
|
|
8560
|
+
bodyLines = collectHighlightedBodyLines(text, hljsLang, contentWidth);
|
|
8561
|
+
} else {
|
|
8562
|
+
bodyLines = collectFlatBodyLines(text, codeBlock, contentWidth);
|
|
8563
|
+
}
|
|
8485
8564
|
}
|
|
8486
8565
|
}
|
|
8487
8566
|
const indentedBody = bodyLines.map((line) => `${CODE_GUTTER}${line ?? ""}`);
|
|
@@ -8903,6 +8982,38 @@ function trimPartialClosingFences(tokens) {
|
|
|
8903
8982
|
if (lastLine.length >= marker.length || lastLine !== marker[0].repeat(lastLine.length)) return;
|
|
8904
8983
|
token.text = String(token.text ?? "").slice(0, -lastLine.length).replace(/\n$/, "");
|
|
8905
8984
|
}
|
|
8985
|
+
var OPEN_FENCE_RE = /^( {0,3})(`{3,}|~{3,})(.*)$/;
|
|
8986
|
+
var CLOSE_FENCE_RE = /^ {0,3}([`~]+)[ \t]*$/;
|
|
8987
|
+
function isClosingFence(line, char, openLen) {
|
|
8988
|
+
const m = CLOSE_FENCE_RE.exec(line);
|
|
8989
|
+
if (!m) return false;
|
|
8990
|
+
const run = m[1];
|
|
8991
|
+
return run.startsWith(char.repeat(openLen));
|
|
8992
|
+
}
|
|
8993
|
+
function findOpenFenceStart(text) {
|
|
8994
|
+
const value = String(text ?? "");
|
|
8995
|
+
let open = null;
|
|
8996
|
+
let start = 0;
|
|
8997
|
+
for (let i = 0; i <= value.length; i++) {
|
|
8998
|
+
if (i !== value.length && value[i] !== "\n") continue;
|
|
8999
|
+
const line = value.slice(start, i);
|
|
9000
|
+
if (!open) {
|
|
9001
|
+
const m = OPEN_FENCE_RE.exec(line);
|
|
9002
|
+
if (m) {
|
|
9003
|
+
const char = m[2][0];
|
|
9004
|
+
const info = m[3];
|
|
9005
|
+
if (!(char === "`" && info.indexOf("`") !== -1)) {
|
|
9006
|
+
open = { index: start, indent: m[1].length, char, len: m[2].length, lang: info.trim() };
|
|
9007
|
+
}
|
|
9008
|
+
}
|
|
9009
|
+
} else if (isClosingFence(line, open.char, open.len)) {
|
|
9010
|
+
open = null;
|
|
9011
|
+
}
|
|
9012
|
+
start = i + 1;
|
|
9013
|
+
}
|
|
9014
|
+
if (!open || open.indent !== 0) return null;
|
|
9015
|
+
return { index: open.index, lang: open.lang };
|
|
9016
|
+
}
|
|
8906
9017
|
|
|
8907
9018
|
// src/tui/markdown/render-ansi.mjs
|
|
8908
9019
|
var TOKEN_CACHE_MAX = 500;
|
|
@@ -8932,6 +9043,23 @@ function lexMarkdown(content, { trimPartialFences = false } = {}) {
|
|
|
8932
9043
|
}];
|
|
8933
9044
|
}
|
|
8934
9045
|
if (trimPartialFences) {
|
|
9046
|
+
const open = findOpenFenceStart(text);
|
|
9047
|
+
if (open) {
|
|
9048
|
+
const post = text.slice(open.index);
|
|
9049
|
+
const nl = post.indexOf("\n");
|
|
9050
|
+
const codeToken = {
|
|
9051
|
+
type: "code",
|
|
9052
|
+
raw: post,
|
|
9053
|
+
lang: open.lang,
|
|
9054
|
+
text: nl === -1 ? "" : post.slice(nl + 1),
|
|
9055
|
+
plain: true
|
|
9056
|
+
};
|
|
9057
|
+
const pre = text.slice(0, open.index);
|
|
9058
|
+
const tokens3 = pre ? marked.lexer(pre) : [];
|
|
9059
|
+
tokens3.push(codeToken);
|
|
9060
|
+
trimPartialClosingFences(tokens3);
|
|
9061
|
+
return tokens3;
|
|
9062
|
+
}
|
|
8935
9063
|
const tokens2 = marked.lexer(text);
|
|
8936
9064
|
trimPartialClosingFences(tokens2);
|
|
8937
9065
|
return tokens2;
|
|
@@ -9077,6 +9205,21 @@ function resolveStreamingMarkdownParts(text, streamKey) {
|
|
|
9077
9205
|
if (!t.startsWith(stablePrefix)) {
|
|
9078
9206
|
stablePrefix = "";
|
|
9079
9207
|
}
|
|
9208
|
+
const open = findOpenFenceStart(t);
|
|
9209
|
+
if (open) {
|
|
9210
|
+
let openPrefix = t.substring(0, open.index);
|
|
9211
|
+
if (isWhitespaceOnlyText(openPrefix)) openPrefix = "";
|
|
9212
|
+
if (key && openPrefix) touchStablePrefixKey(key, openPrefix);
|
|
9213
|
+
else if (key) stablePrefixByStreamKey.delete(key);
|
|
9214
|
+
const unstableSuffix2 = t.substring(openPrefix.length);
|
|
9215
|
+
return {
|
|
9216
|
+
plain: false,
|
|
9217
|
+
openFence: true,
|
|
9218
|
+
stablePrefix: openPrefix,
|
|
9219
|
+
unstableSuffix: unstableSuffix2,
|
|
9220
|
+
unstableForRender: unstableSuffix2
|
|
9221
|
+
};
|
|
9222
|
+
}
|
|
9080
9223
|
try {
|
|
9081
9224
|
configureMarked();
|
|
9082
9225
|
const boundary = stablePrefix.length;
|
|
@@ -9831,6 +9974,8 @@ function positiveIntEnv(name, fallback) {
|
|
|
9831
9974
|
var TRANSCRIPT_WINDOW_MIN_ITEMS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_WINDOW_MIN_ITEMS", 12);
|
|
9832
9975
|
var TRANSCRIPT_WINDOW_OVERSCAN_ROWS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_OVERSCAN_ROWS", 16);
|
|
9833
9976
|
var TRANSCRIPT_WINDOW_MAX_ITEMS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_WINDOW_ITEMS", 80);
|
|
9977
|
+
var TRANSCRIPT_WINDOW_TAIL_OVERSCAN_ROWS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_TAIL_OVERSCAN_ROWS", 4);
|
|
9978
|
+
var TRANSCRIPT_WINDOW_TAIL_MAX_ITEMS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_WINDOW_TAIL_ITEMS", 20);
|
|
9834
9979
|
var SELECTION_PAINT_INTERVAL_MS = positiveIntEnv("MIXDOG_TUI_SELECTION_PAINT_MS", 24);
|
|
9835
9980
|
var SCROLL_COALESCE_MS = positiveIntEnv("MIXDOG_TUI_SCROLL_COALESCE_MS", 16);
|
|
9836
9981
|
var PROMPT_HISTORY_LIMIT = 50;
|
|
@@ -10449,12 +10594,14 @@ function transcriptRenderWindow(items, { scrollOffset = 0, viewportHeight = 24,
|
|
|
10449
10594
|
return { startIndex: 0, endIndex: itemCount, items: allItems, bottomSpacerRows: 0, totalRows, maxScrollRows, effectiveScrollOffset };
|
|
10450
10595
|
}
|
|
10451
10596
|
const minItems = Math.min(TRANSCRIPT_WINDOW_MIN_ITEMS, itemCount);
|
|
10452
|
-
const
|
|
10597
|
+
const atTail = effectiveScrollOffset === 0;
|
|
10598
|
+
const overscanRows = atTail ? TRANSCRIPT_WINDOW_TAIL_OVERSCAN_ROWS : TRANSCRIPT_WINDOW_OVERSCAN_ROWS;
|
|
10599
|
+
const maxItems = Math.max(minItems, atTail ? TRANSCRIPT_WINDOW_TAIL_MAX_ITEMS : TRANSCRIPT_WINDOW_MAX_ITEMS);
|
|
10453
10600
|
const prefixRows = fallbackIndex.prefixRows;
|
|
10454
10601
|
const visibleTop = Math.max(0, totalRows - effectiveScrollOffset - viewRows);
|
|
10455
10602
|
const visibleBottom = Math.min(totalRows, totalRows - effectiveScrollOffset);
|
|
10456
|
-
const desiredTop = Math.max(0, visibleTop -
|
|
10457
|
-
const desiredBottom = Math.min(totalRows, visibleBottom +
|
|
10603
|
+
const desiredTop = Math.max(0, visibleTop - overscanRows);
|
|
10604
|
+
const desiredBottom = Math.min(totalRows, visibleBottom + overscanRows);
|
|
10458
10605
|
let startIndex = Math.max(0, upperBound(prefixRows, desiredTop) - 1);
|
|
10459
10606
|
let endIndex = Math.min(itemCount, Math.max(startIndex + 1, lowerBound(prefixRows, Math.max(desiredBottom, desiredTop + 1))));
|
|
10460
10607
|
while (endIndex - startIndex < minItems && startIndex > 0) startIndex--;
|
|
@@ -10462,9 +10609,10 @@ function transcriptRenderWindow(items, { scrollOffset = 0, viewportHeight = 24,
|
|
|
10462
10609
|
if (endIndex - startIndex > maxItems) {
|
|
10463
10610
|
const visibleStartIndex = Math.max(0, upperBound(prefixRows, visibleTop) - 1);
|
|
10464
10611
|
const visibleEndIndex = Math.min(itemCount, Math.max(visibleStartIndex + 1, lowerBound(prefixRows, Math.max(visibleBottom, visibleTop + 1))));
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10612
|
+
const effectiveMaxItems = Math.max(maxItems, visibleEndIndex - visibleStartIndex);
|
|
10613
|
+
startIndex = Math.max(0, Math.min(visibleStartIndex, itemCount - effectiveMaxItems));
|
|
10614
|
+
endIndex = Math.min(itemCount, Math.max(visibleEndIndex, startIndex + effectiveMaxItems));
|
|
10615
|
+
if (endIndex - startIndex > effectiveMaxItems) startIndex = Math.max(0, endIndex - effectiveMaxItems);
|
|
10468
10616
|
}
|
|
10469
10617
|
const bottomSpacerRows = Math.max(0, totalRows - (prefixRows[endIndex] || totalRows));
|
|
10470
10618
|
return {
|
|
@@ -10513,8 +10661,8 @@ function useMouseInput({
|
|
|
10513
10661
|
setMeasuredRowsVersion,
|
|
10514
10662
|
clearStitchBuffer
|
|
10515
10663
|
}) {
|
|
10516
|
-
const zoomTimerRef =
|
|
10517
|
-
const edgeAutoscrollRef =
|
|
10664
|
+
const zoomTimerRef = useRef7(null);
|
|
10665
|
+
const edgeAutoscrollRef = useRef7({ dir: 0, timer: null, noMove: 0 });
|
|
10518
10666
|
const passthroughCtrlWheelZoom = useCallback2(() => {
|
|
10519
10667
|
if (!stdout?.write) return;
|
|
10520
10668
|
try {
|
|
@@ -10537,10 +10685,10 @@ function useMouseInput({
|
|
|
10537
10685
|
zoomTimerRef.current = setTimeout(() => restore(0), 700);
|
|
10538
10686
|
zoomTimerRef.current.unref?.();
|
|
10539
10687
|
}, [stdout, zoomTimerRef]);
|
|
10540
|
-
|
|
10688
|
+
useEffect7(() => () => {
|
|
10541
10689
|
if (zoomTimerRef.current) clearTimeout(zoomTimerRef.current);
|
|
10542
10690
|
}, [zoomTimerRef]);
|
|
10543
|
-
|
|
10691
|
+
useEffect7(() => {
|
|
10544
10692
|
if (!inkInput || !isRawModeSupported) return void 0;
|
|
10545
10693
|
const WHEEL_SGR = /\x1b\[<(\d+);/;
|
|
10546
10694
|
const linearSelection = (a, b) => {
|
|
@@ -10917,7 +11065,7 @@ function useMouseInput({
|
|
|
10917
11065
|
}
|
|
10918
11066
|
|
|
10919
11067
|
// src/tui/app/use-transcript-scroll.mjs
|
|
10920
|
-
import { useCallback as useCallback3, useEffect as
|
|
11068
|
+
import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef8 } from "react";
|
|
10921
11069
|
function useTranscriptScroll({
|
|
10922
11070
|
store,
|
|
10923
11071
|
frameColumns,
|
|
@@ -10937,13 +11085,13 @@ function useTranscriptScroll({
|
|
|
10937
11085
|
selectionLayoutRef,
|
|
10938
11086
|
selectionTextRef
|
|
10939
11087
|
}) {
|
|
10940
|
-
const scrollAnimationRef =
|
|
10941
|
-
const selectionPaintRef =
|
|
10942
|
-
const scrollCoalesceRef =
|
|
10943
|
-
const selectionTextTimerRef =
|
|
10944
|
-
const stitchBufferRef =
|
|
10945
|
-
const stitchHarvestTimerRef =
|
|
10946
|
-
const stitchHarvestScrollRef =
|
|
11088
|
+
const scrollAnimationRef = useRef8(null);
|
|
11089
|
+
const selectionPaintRef = useRef8({ t: 0, rect: null, pending: null, timer: null });
|
|
11090
|
+
const scrollCoalesceRef = useRef8({ pendingRows: 0, timer: null });
|
|
11091
|
+
const selectionTextTimerRef = useRef8(null);
|
|
11092
|
+
const stitchBufferRef = useRef8(/* @__PURE__ */ new Map());
|
|
11093
|
+
const stitchHarvestTimerRef = useRef8(null);
|
|
11094
|
+
const stitchHarvestScrollRef = useRef8(0);
|
|
10947
11095
|
const clearStitchBuffer = useCallback3(() => {
|
|
10948
11096
|
stitchBufferRef.current.clear();
|
|
10949
11097
|
if (stitchHarvestTimerRef.current) {
|
|
@@ -11210,14 +11358,14 @@ function useTranscriptScroll({
|
|
|
11210
11358
|
if (lr != null && Number.isFinite(lr.x2)) return Math.max(0, lr.x2);
|
|
11211
11359
|
return Math.max(0, frameColumns - 1);
|
|
11212
11360
|
}, [store, frameColumns]);
|
|
11213
|
-
const gridSelectionActiveRef =
|
|
11361
|
+
const gridSelectionActiveRef = useRef8(() => {
|
|
11214
11362
|
const drag = dragRef.current;
|
|
11215
11363
|
if (!drag || drag.active) return false;
|
|
11216
11364
|
if (drag.region !== "transcript" && drag.region !== "status") return false;
|
|
11217
11365
|
const rect = drag.rect;
|
|
11218
11366
|
return Boolean(rect) && !(rect.x1 === rect.x2 && rect.y1 === rect.y2);
|
|
11219
11367
|
});
|
|
11220
|
-
|
|
11368
|
+
useEffect8(() => () => {
|
|
11221
11369
|
const paintState = selectionPaintRef.current;
|
|
11222
11370
|
if (paintState.timer) clearTimeout(paintState.timer);
|
|
11223
11371
|
paintState.timer = null;
|
|
@@ -11421,7 +11569,7 @@ function useTranscriptScroll({
|
|
|
11421
11569
|
}
|
|
11422
11570
|
|
|
11423
11571
|
// src/tui/app/use-transcript-window.mjs
|
|
11424
|
-
import { useCallback as useCallback4, useEffect as
|
|
11572
|
+
import { useCallback as useCallback4, useEffect as useEffect9, useLayoutEffect as useLayoutEffect3, useMemo, useRef as useRef9, useState as useState7 } from "react";
|
|
11425
11573
|
function useTranscriptWindow({
|
|
11426
11574
|
items,
|
|
11427
11575
|
themeEpoch,
|
|
@@ -11450,14 +11598,14 @@ function useTranscriptWindow({
|
|
|
11450
11598
|
measuredRowsVersion,
|
|
11451
11599
|
setMeasuredRowsVersion
|
|
11452
11600
|
}) {
|
|
11453
|
-
const transcriptTotalRowsRef =
|
|
11454
|
-
const preservedScrollDeltaRef =
|
|
11455
|
-
const committedMaxScrollRowsRef =
|
|
11456
|
-
const incrementalRowIndexCacheRef =
|
|
11457
|
-
const prevViewportGeomRef =
|
|
11458
|
-
const transcriptItemElsRef =
|
|
11459
|
-
const transcriptMeasureRefCache =
|
|
11460
|
-
const transcriptMeasureItemsRef =
|
|
11601
|
+
const transcriptTotalRowsRef = useRef9(0);
|
|
11602
|
+
const preservedScrollDeltaRef = useRef9(0);
|
|
11603
|
+
const committedMaxScrollRowsRef = useRef9(0);
|
|
11604
|
+
const incrementalRowIndexCacheRef = useRef9(null);
|
|
11605
|
+
const prevViewportGeomRef = useRef9({ contentHeight: 0, floatingPanelRows: 0 });
|
|
11606
|
+
const transcriptItemElsRef = useRef9(/* @__PURE__ */ new Map());
|
|
11607
|
+
const transcriptMeasureRefCache = useRef9(/* @__PURE__ */ new Map());
|
|
11608
|
+
const transcriptMeasureItemsRef = useRef9(/* @__PURE__ */ new Map());
|
|
11461
11609
|
const transcriptMeasureRef = useCallback4((item) => {
|
|
11462
11610
|
if (!TRANSCRIPT_MEASURED_ROWS || !item || item.id == null) return void 0;
|
|
11463
11611
|
if (shouldSuppressFullyFailedToolItem(item)) {
|
|
@@ -11855,13 +12003,13 @@ function useTranscriptWindow({
|
|
|
11855
12003
|
dragRef.current = { ...dragRef.current, rect: clippedRect };
|
|
11856
12004
|
paintSelectionRect(clippedRect, { rememberText: false, immediate: true });
|
|
11857
12005
|
}, [transcriptContentHeight, transcriptWindow.totalRows, transcriptWindow.effectiveScrollOffset, withSelectionClip, paintSelectionRect]);
|
|
11858
|
-
|
|
12006
|
+
useEffect9(() => {
|
|
11859
12007
|
if (!dragRef.current.rect) return;
|
|
11860
12008
|
const clippedRect = withSelectionClip(dragRef.current.rect);
|
|
11861
12009
|
dragRef.current = { ...dragRef.current, rect: clippedRect };
|
|
11862
12010
|
paintSelectionRect(clippedRect, { rememberText: false, immediate: true });
|
|
11863
12011
|
}, [themeEpoch, withSelectionClip, paintSelectionRect]);
|
|
11864
|
-
|
|
12012
|
+
useEffect9(() => {
|
|
11865
12013
|
const maxRows = Math.max(0, Number(transcriptWindow.maxScrollRows) || 0);
|
|
11866
12014
|
if (scrollTargetRef.current <= maxRows && scrollPositionRef.current <= maxRows && scrollOffset <= maxRows) return;
|
|
11867
12015
|
stopSmoothScroll();
|
|
@@ -17635,16 +17783,16 @@ function reconcileWithDisk(filePath, pend) {
|
|
|
17635
17783
|
function scheduleWriteBehind(filePath) {
|
|
17636
17784
|
if (!filePath) return;
|
|
17637
17785
|
if (pendingTimers.has(filePath)) clearTimeout(pendingTimers.get(filePath));
|
|
17638
|
-
const
|
|
17786
|
+
const timer2 = setTimeout(() => {
|
|
17639
17787
|
void writeBehindFlush(filePath);
|
|
17640
17788
|
}, WRITE_BEHIND_MS);
|
|
17641
|
-
if (typeof
|
|
17642
|
-
pendingTimers.set(filePath,
|
|
17789
|
+
if (typeof timer2.unref === "function") timer2.unref();
|
|
17790
|
+
pendingTimers.set(filePath, timer2);
|
|
17643
17791
|
}
|
|
17644
17792
|
async function writeBehindFlush(filePath) {
|
|
17645
|
-
const
|
|
17646
|
-
if (
|
|
17647
|
-
clearTimeout(
|
|
17793
|
+
const timer2 = pendingTimers.get(filePath);
|
|
17794
|
+
if (timer2) {
|
|
17795
|
+
clearTimeout(timer2);
|
|
17648
17796
|
pendingTimers.delete(filePath);
|
|
17649
17797
|
}
|
|
17650
17798
|
const pend = pendingAppends.get(filePath);
|
|
@@ -17660,7 +17808,7 @@ async function writeBehindFlush(filePath) {
|
|
|
17660
17808
|
}
|
|
17661
17809
|
}
|
|
17662
17810
|
function flushPromptHistory() {
|
|
17663
|
-
for (const
|
|
17811
|
+
for (const timer2 of pendingTimers.values()) clearTimeout(timer2);
|
|
17664
17812
|
pendingTimers.clear();
|
|
17665
17813
|
for (const [filePath, pend] of pendingAppends) {
|
|
17666
17814
|
if (!pend.length) continue;
|
|
@@ -18170,7 +18318,7 @@ function NoticeMessage({ text, tone, columns = 80 }) {
|
|
|
18170
18318
|
}
|
|
18171
18319
|
|
|
18172
18320
|
// src/tui/components/ToolExecution.jsx
|
|
18173
|
-
import React16
|
|
18321
|
+
import React16 from "react";
|
|
18174
18322
|
import { Box as Box14, Text as Text16 } from "../../../vendor/ink/build/index.js";
|
|
18175
18323
|
import stringWidth9 from "string-width";
|
|
18176
18324
|
|
|
@@ -18641,15 +18789,12 @@ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
|
18641
18789
|
var TOOL_BLINK_MS = 500;
|
|
18642
18790
|
var TOOL_BLINK_LIMIT_MS = 3e3;
|
|
18643
18791
|
var TOOL_PENDING_SHOW_DELAY_MS = 1e3;
|
|
18792
|
+
var TOOL_ANIM_TICK_MS = TOOL_BLINK_MS;
|
|
18644
18793
|
function statusCopy(name, label, count, doneCount, pending, isError, args = {}) {
|
|
18645
18794
|
return formatToolActionHeader(name, args, { pending, count });
|
|
18646
18795
|
}
|
|
18647
18796
|
function ToolExecution({ name, args, result, rawResult, isError, errorCount, expanded, columns = 80, attached = false, count = 1, completedCount = 0, startedAt = 0, completedAt = 0, aggregate = false, categories = {}, doneCategories = null, headerFinalized = true, deferredDisplayReady = false }) {
|
|
18648
18797
|
const rowWidth = Math.max(1, Number(columns || 80));
|
|
18649
|
-
const [blinkOn, setBlinkOn] = useState7(true);
|
|
18650
|
-
const [blinkExpired, setBlinkExpired] = useState7(false);
|
|
18651
|
-
const [pendingDelayElapsed, setPendingDelayElapsed] = useState7(false);
|
|
18652
|
-
const [, setElapsedTick] = useState7(0);
|
|
18653
18798
|
const groupCount = Math.max(1, Number(count || 1));
|
|
18654
18799
|
const doneCount = Math.max(0, Math.min(groupCount, Number(completedCount || (result == null ? 0 : groupCount))));
|
|
18655
18800
|
const rt = result == null ? null : String(result).replace(/\s+$/, "");
|
|
@@ -18657,13 +18802,19 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18657
18802
|
const pending = doneCount < groupCount;
|
|
18658
18803
|
const startedAtMs = Number(startedAt || 0);
|
|
18659
18804
|
const completedAtMs = Number(completedAt || 0);
|
|
18660
|
-
const
|
|
18805
|
+
const nowMs = Date.now();
|
|
18806
|
+
useSharedTick(TOOL_ANIM_TICK_MS, pending);
|
|
18807
|
+
const pendingAgeMs = pending && startedAtMs ? Math.max(0, nowMs - startedAtMs) : 0;
|
|
18808
|
+
const pendingDelayElapsed = pending ? !startedAtMs || pendingAgeMs >= TOOL_PENDING_SHOW_DELAY_MS : false;
|
|
18661
18809
|
const hasVisibleProgress = doneCount > 0 || Boolean(String(rt || "").trim());
|
|
18662
18810
|
const pendingDisplayReady = !pending || !startedAtMs || pendingDelayElapsed || pendingAgeMs >= TOOL_PENDING_SHOW_DELAY_MS || hasVisibleProgress || deferredDisplayReady;
|
|
18811
|
+
const blinkActive = pending && pendingDisplayReady;
|
|
18812
|
+
const blinkExpired = blinkActive && startedAtMs > 0 && nowMs - startedAtMs >= TOOL_BLINK_LIMIT_MS;
|
|
18813
|
+
const blinkOn = !blinkActive || blinkExpired ? true : Math.floor(nowMs / TOOL_BLINK_MS) % 2 === 0;
|
|
18663
18814
|
const headerPending = pending || headerFinalized === false;
|
|
18664
18815
|
const hasResult = result != null && Boolean(String(rt || "").trim());
|
|
18665
18816
|
const hasRawResult = rawResult != null && Boolean(String(rawRt || "").trim());
|
|
18666
|
-
const elapsedMs = startedAtMs ? Math.max(0, (pending ?
|
|
18817
|
+
const elapsedMs = startedAtMs ? Math.max(0, (pending ? nowMs : completedAtMs || nowMs) - startedAtMs) : 0;
|
|
18667
18818
|
const elapsed = elapsedMs >= 1e3 ? formatElapsed(elapsedMs) : "";
|
|
18668
18819
|
const failedCount = clampFailureCount(errorCount, groupCount, isError);
|
|
18669
18820
|
const displayGroupCount = groupCount;
|
|
@@ -18673,53 +18824,6 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18673
18824
|
(v) => (v && typeof v === "object" ? Number(v.count || 0) : Number(v || 0)) > 0
|
|
18674
18825
|
);
|
|
18675
18826
|
const displayDoneCategories = hasDoneCounts ? normalizedDoneCategories : displayCategories;
|
|
18676
|
-
useEffect9(() => {
|
|
18677
|
-
if (!pending) {
|
|
18678
|
-
setPendingDelayElapsed(false);
|
|
18679
|
-
return void 0;
|
|
18680
|
-
}
|
|
18681
|
-
const started = Number(startedAt || 0);
|
|
18682
|
-
if (!started) {
|
|
18683
|
-
setPendingDelayElapsed(true);
|
|
18684
|
-
return void 0;
|
|
18685
|
-
}
|
|
18686
|
-
const remaining = TOOL_PENDING_SHOW_DELAY_MS - Math.max(0, Date.now() - started);
|
|
18687
|
-
if (remaining <= 0) {
|
|
18688
|
-
setPendingDelayElapsed(true);
|
|
18689
|
-
return void 0;
|
|
18690
|
-
}
|
|
18691
|
-
setPendingDelayElapsed(false);
|
|
18692
|
-
const timer = setTimeout(() => setPendingDelayElapsed(true), remaining);
|
|
18693
|
-
return () => clearTimeout(timer);
|
|
18694
|
-
}, [pending, startedAt]);
|
|
18695
|
-
useEffect9(() => {
|
|
18696
|
-
if (!pending || !pendingDisplayReady || blinkExpired) {
|
|
18697
|
-
setBlinkOn(true);
|
|
18698
|
-
return void 0;
|
|
18699
|
-
}
|
|
18700
|
-
const timer = setInterval(() => setBlinkOn((on) => !on), TOOL_BLINK_MS);
|
|
18701
|
-
return () => clearInterval(timer);
|
|
18702
|
-
}, [pending, pendingDisplayReady, blinkExpired]);
|
|
18703
|
-
useEffect9(() => {
|
|
18704
|
-
if (!pending || !pendingDisplayReady) {
|
|
18705
|
-
setBlinkExpired(false);
|
|
18706
|
-
return void 0;
|
|
18707
|
-
}
|
|
18708
|
-
const started = Number(startedAt || 0);
|
|
18709
|
-
const remaining = TOOL_BLINK_LIMIT_MS - (started ? Math.max(0, Date.now() - started) : 0);
|
|
18710
|
-
if (remaining <= 0) {
|
|
18711
|
-
setBlinkExpired(true);
|
|
18712
|
-
return void 0;
|
|
18713
|
-
}
|
|
18714
|
-
setBlinkExpired(false);
|
|
18715
|
-
const timer = setTimeout(() => setBlinkExpired(true), remaining);
|
|
18716
|
-
return () => clearTimeout(timer);
|
|
18717
|
-
}, [pending, pendingDisplayReady, startedAt]);
|
|
18718
|
-
useEffect9(() => {
|
|
18719
|
-
if (!pending || !pendingDisplayReady || !startedAtMs) return void 0;
|
|
18720
|
-
const timer = setInterval(() => setElapsedTick((tick) => (tick + 1) % 1e6), 1e3);
|
|
18721
|
-
return () => clearInterval(timer);
|
|
18722
|
-
}, [pending, pendingDisplayReady, startedAtMs]);
|
|
18723
18827
|
if (pending && !pendingDisplayReady) {
|
|
18724
18828
|
const placeholderNormalizedName = String(formatToolSurface(name, args)?.normalizedName || "").toLowerCase();
|
|
18725
18829
|
const placeholderSingleRow = !aggregate && (SKILL_SURFACE_NAMES2.has(placeholderNormalizedName) || isAgentTool(placeholderNormalizedName));
|
|
@@ -19128,7 +19232,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19128
19232
|
const { stdout } = useStdout2();
|
|
19129
19233
|
const [exiting, setExiting] = useState8(false);
|
|
19130
19234
|
const [tuiReady, setTuiReady] = useState8(false);
|
|
19131
|
-
const exitRequestedRef =
|
|
19235
|
+
const exitRequestedRef = useRef10(false);
|
|
19132
19236
|
const [resizeState, setResizeState] = useState8(() => ({ ...terminalSize(stdout), epoch: 0 }));
|
|
19133
19237
|
const [panelTransitionEpoch, setPanelTransitionEpoch] = useState8(0);
|
|
19134
19238
|
const [panelInkMaskEpoch, setPanelInkMaskEpoch] = useState8(0);
|
|
@@ -19136,22 +19240,22 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19136
19240
|
const rightSafetyColumns = windowsLikeTerminal ? 1 : 0;
|
|
19137
19241
|
const frameColumns = Math.max(1, resizeState.columns - rightSafetyColumns);
|
|
19138
19242
|
const [scrollOffset, setScrollOffset] = useState8(0);
|
|
19139
|
-
const scrollPositionRef =
|
|
19140
|
-
const scrollTargetRef =
|
|
19141
|
-
const maxScrollRowsRef =
|
|
19142
|
-
const transcriptBottomSlackRowsRef =
|
|
19143
|
-
const transcriptAnchorRef =
|
|
19144
|
-
const transcriptAnchorDirtyRef =
|
|
19145
|
-
const transcriptGeomRef =
|
|
19243
|
+
const scrollPositionRef = useRef10(0);
|
|
19244
|
+
const scrollTargetRef = useRef10(0);
|
|
19245
|
+
const maxScrollRowsRef = useRef10(0);
|
|
19246
|
+
const transcriptBottomSlackRowsRef = useRef10(0);
|
|
19247
|
+
const transcriptAnchorRef = useRef10(null);
|
|
19248
|
+
const transcriptAnchorDirtyRef = useRef10(false);
|
|
19249
|
+
const transcriptGeomRef = useRef10({ prefixRows: null, totalRows: 0, viewRows: 1 });
|
|
19146
19250
|
const [measuredRowsVersion, setMeasuredRowsVersion] = useState8(0);
|
|
19147
|
-
const followingRef =
|
|
19148
|
-
const lastItemsCountRef =
|
|
19149
|
-
const pickerOpenedFromEnterRef =
|
|
19150
|
-
const pickerOpenedFromEnterTimerRef =
|
|
19151
|
-
const projectPickerRef =
|
|
19251
|
+
const followingRef = useRef10(false);
|
|
19252
|
+
const lastItemsCountRef = useRef10(0);
|
|
19253
|
+
const pickerOpenedFromEnterRef = useRef10(false);
|
|
19254
|
+
const pickerOpenedFromEnterTimerRef = useRef10(null);
|
|
19255
|
+
const projectPickerRef = useRef10(null);
|
|
19152
19256
|
const buildProjectPickerState = (opts) => projectPickerRef.current.buildProjectPickerState(opts);
|
|
19153
19257
|
const [picker, setPickerState] = useState8(null);
|
|
19154
|
-
const livePickerRef =
|
|
19258
|
+
const livePickerRef = useRef10(null);
|
|
19155
19259
|
const setPicker = useCallback6((next) => {
|
|
19156
19260
|
livePickerRef.current = typeof next === "function" ? next(livePickerRef.current) : next;
|
|
19157
19261
|
setPickerState((prev) => {
|
|
@@ -19173,14 +19277,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19173
19277
|
livePickerRef.current = picker;
|
|
19174
19278
|
const [contextPanel, setContextPanel] = useState8(null);
|
|
19175
19279
|
const [usagePanel, setUsagePanel] = useState8(null);
|
|
19176
|
-
const usageRequestRef =
|
|
19177
|
-
const settingsHeavyCacheRef =
|
|
19280
|
+
const usageRequestRef = useRef10(0);
|
|
19281
|
+
const settingsHeavyCacheRef = useRef10(null);
|
|
19178
19282
|
const closeUsagePanel = useCallback6(() => {
|
|
19179
19283
|
usageRequestRef.current += 1;
|
|
19180
19284
|
setUsagePanel(null);
|
|
19181
19285
|
}, []);
|
|
19182
19286
|
const [providerPrompt, setProviderPrompt] = useState8(null);
|
|
19183
|
-
const oauthSubmitRef =
|
|
19287
|
+
const oauthSubmitRef = useRef10(false);
|
|
19184
19288
|
const [channelPrompt, setChannelPrompt] = useState8(null);
|
|
19185
19289
|
const [hookPrompt, setHookPrompt] = useState8(null);
|
|
19186
19290
|
const [settingsPrompt, setSettingsPrompt] = useState8(null);
|
|
@@ -19202,7 +19306,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19202
19306
|
pickFolder
|
|
19203
19307
|
});
|
|
19204
19308
|
projectPickerRef.current = projectPicker;
|
|
19205
|
-
const initialPickerBuiltRef =
|
|
19309
|
+
const initialPickerBuiltRef = useRef10(false);
|
|
19206
19310
|
if (!initialPickerBuiltRef.current) {
|
|
19207
19311
|
initialPickerBuiltRef.current = true;
|
|
19208
19312
|
let onboardingOwnsScreen = false;
|
|
@@ -19310,30 +19414,30 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19310
19414
|
const toolApproval = state.toolApproval || null;
|
|
19311
19415
|
const [promptDraft, setPromptDraft] = useState8("");
|
|
19312
19416
|
const [promptDraftOverride, setPromptDraftOverride] = useState8(null);
|
|
19313
|
-
const promptLayoutValueRef =
|
|
19417
|
+
const promptLayoutValueRef = useRef10("");
|
|
19314
19418
|
const [, setPromptLayoutRows] = useState8(1);
|
|
19315
19419
|
const [textEntryLayoutRows, setTextEntryLayoutRows] = useState8(1);
|
|
19316
19420
|
const [, setPastedImages] = useState8({});
|
|
19317
|
-
const pastedImagesRef =
|
|
19318
|
-
const nextPastedImageIdRef =
|
|
19421
|
+
const pastedImagesRef = useRef10({});
|
|
19422
|
+
const nextPastedImageIdRef = useRef10(1);
|
|
19319
19423
|
const [, setPastedTexts] = useState8({});
|
|
19320
|
-
const pastedTextsRef =
|
|
19321
|
-
const nextPastedTextIdRef =
|
|
19322
|
-
const promptValueRef =
|
|
19323
|
-
const promptSelectionRef =
|
|
19324
|
-
const promptBoxRectRef =
|
|
19325
|
-
const promptMouseSelectionRef =
|
|
19326
|
-
const promptHistoryNavRef =
|
|
19327
|
-
const promptHistoryDraftChangeRef =
|
|
19424
|
+
const pastedTextsRef = useRef10({});
|
|
19425
|
+
const nextPastedTextIdRef = useRef10(1);
|
|
19426
|
+
const promptValueRef = useRef10("");
|
|
19427
|
+
const promptSelectionRef = useRef10(null);
|
|
19428
|
+
const promptBoxRectRef = useRef10(null);
|
|
19429
|
+
const promptMouseSelectionRef = useRef10(null);
|
|
19430
|
+
const promptHistoryNavRef = useRef10({ active: false, index: -1, seed: "", lastValue: "" });
|
|
19431
|
+
const promptHistoryDraftChangeRef = useRef10(false);
|
|
19328
19432
|
const [promptHint, setPromptHint] = useState8("");
|
|
19329
19433
|
const [promptHintTone, setPromptHintTone] = useState8("info");
|
|
19330
19434
|
const [welcomePromptHintDismissed, setWelcomePromptHintDismissed] = useState8(false);
|
|
19331
19435
|
const [conditionalWelcomePromptHint, setConditionalWelcomePromptHint] = useState8("");
|
|
19332
|
-
const welcomePromptHintRef =
|
|
19436
|
+
const welcomePromptHintRef = useRef10(null);
|
|
19333
19437
|
if (welcomePromptHintRef.current === null) {
|
|
19334
19438
|
welcomePromptHintRef.current = randomWelcomePromptHint();
|
|
19335
19439
|
}
|
|
19336
|
-
const welcomePromptHintVisibleRef =
|
|
19440
|
+
const welcomePromptHintVisibleRef = useRef10(false);
|
|
19337
19441
|
const dismissWelcomePromptHint = useCallback6(() => {
|
|
19338
19442
|
if (!welcomePromptHintVisibleRef.current) return;
|
|
19339
19443
|
setWelcomePromptHintDismissed((dismissed) => dismissed || true);
|
|
@@ -19341,15 +19445,15 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19341
19445
|
const toastErrorSignature = useMemo2(() => (state.toasts || []).filter((toast) => toast?.tone === "error").map((toast) => `${toast.id || ""}:${toast.text || ""}`).join("|"), [state.toasts]);
|
|
19342
19446
|
const [slashIndex, setSlashIndex] = useState8(0);
|
|
19343
19447
|
const [slashDismissedFor, setSlashDismissedFor] = useState8("");
|
|
19344
|
-
const slashPaletteRef =
|
|
19345
|
-
const workflowTabCycleRef =
|
|
19346
|
-
const scrollFocusRef =
|
|
19347
|
-
const onboardingStartedRef =
|
|
19348
|
-
const onboardingRef =
|
|
19349
|
-
const providerModelsCacheRef =
|
|
19350
|
-
const searchModelsCacheRef =
|
|
19351
|
-
const modelPickerRequestRef =
|
|
19352
|
-
const onboardingPrefetchSeqRef =
|
|
19448
|
+
const slashPaletteRef = useRef10({ open: false, count: 0 });
|
|
19449
|
+
const workflowTabCycleRef = useRef10({ pending: false, lastAt: 0 });
|
|
19450
|
+
const scrollFocusRef = useRef10({});
|
|
19451
|
+
const onboardingStartedRef = useRef10(false);
|
|
19452
|
+
const onboardingRef = useRef10({ defaultRoute: null, searchRoute: null, agentRoutes: {}, agents: [], providerModels: [] });
|
|
19453
|
+
const providerModelsCacheRef = useRef10({ models: null, at: 0 });
|
|
19454
|
+
const searchModelsCacheRef = useRef10({ models: null, at: 0 });
|
|
19455
|
+
const modelPickerRequestRef = useRef10(0);
|
|
19456
|
+
const onboardingPrefetchSeqRef = useRef10(0);
|
|
19353
19457
|
const clearModelCaches = useCallback6((scope = "all") => {
|
|
19354
19458
|
if (scope === "all" || scope === "provider") {
|
|
19355
19459
|
providerModelsCacheRef.current = { models: null, at: 0 };
|
|
@@ -19514,14 +19618,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19514
19618
|
runDoctor: (...a) => store.runDoctor?.(...a),
|
|
19515
19619
|
requestExit: (...a) => requestExit(...a)
|
|
19516
19620
|
});
|
|
19517
|
-
const promptHintTimerRef =
|
|
19518
|
-
const promptHintActiveRef =
|
|
19519
|
-
const dragRef =
|
|
19520
|
-
const transcriptViewportRef =
|
|
19521
|
-
const panelTransitionRef =
|
|
19522
|
-
const panelCloseInkMaskRowsRef =
|
|
19523
|
-
const projectBootInputLatchRef =
|
|
19524
|
-
const frameRowsRef =
|
|
19621
|
+
const promptHintTimerRef = useRef10(null);
|
|
19622
|
+
const promptHintActiveRef = useRef10(false);
|
|
19623
|
+
const dragRef = useRef10({ anchor: null, anchorScroll: 0, last: null, active: false, rect: null, region: null, anchorSpan: null });
|
|
19624
|
+
const transcriptViewportRef = useRef10({ top: 0, bottom: 0 });
|
|
19625
|
+
const panelTransitionRef = useRef10({ signature: "", reserve: 0, clearRows: 0, guardRows: 0, epoch: 0 });
|
|
19626
|
+
const panelCloseInkMaskRowsRef = useRef10(0);
|
|
19627
|
+
const projectBootInputLatchRef = useRef10(false);
|
|
19628
|
+
const frameRowsRef = useRef10(24);
|
|
19525
19629
|
const STATUSLINE_BAND_ROWS = 3;
|
|
19526
19630
|
const promptContentColumns = Math.max(1, frameColumns - 4);
|
|
19527
19631
|
const syncPromptLayoutRows = useCallback6((value) => {
|
|
@@ -19598,9 +19702,9 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19598
19702
|
alive = false;
|
|
19599
19703
|
};
|
|
19600
19704
|
}, [store, state.provider, state.model, state.workflow?.id, toastErrorSignature]);
|
|
19601
|
-
const selectionLayoutRef =
|
|
19602
|
-
const selectionTextRef =
|
|
19603
|
-
const lastClickRef =
|
|
19705
|
+
const selectionLayoutRef = useRef10(null);
|
|
19706
|
+
const selectionTextRef = useRef10("");
|
|
19707
|
+
const lastClickRef = useRef10({ x: -1, y: -1, t: 0, count: 0 });
|
|
19604
19708
|
const showSelectionCopyHint = useCallback6((text, tone = "plain") => {
|
|
19605
19709
|
if (promptHintTimerRef.current) clearTimeout(promptHintTimerRef.current);
|
|
19606
19710
|
promptHintActiveRef.current = true;
|
|
@@ -19614,8 +19718,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19614
19718
|
}, 2200);
|
|
19615
19719
|
}, []);
|
|
19616
19720
|
useEffect10(() => {
|
|
19617
|
-
const
|
|
19618
|
-
return () => clearTimeout(
|
|
19721
|
+
const timer2 = setTimeout(() => setTuiReady(true), 0);
|
|
19722
|
+
return () => clearTimeout(timer2);
|
|
19619
19723
|
}, []);
|
|
19620
19724
|
useEffect10(() => {
|
|
19621
19725
|
if (!stdout) return void 0;
|
|
@@ -19868,14 +19972,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19868
19972
|
}, 2e3);
|
|
19869
19973
|
hardExitTimer.unref?.();
|
|
19870
19974
|
setTimeout(() => {
|
|
19871
|
-
let
|
|
19975
|
+
let timer2 = null;
|
|
19872
19976
|
Promise.race([
|
|
19873
19977
|
Promise.resolve(store.dispose?.("cli-react-exit", { detach: true })),
|
|
19874
19978
|
new Promise((resolve5) => {
|
|
19875
|
-
|
|
19979
|
+
timer2 = setTimeout(resolve5, 350);
|
|
19876
19980
|
})
|
|
19877
19981
|
]).finally(() => {
|
|
19878
|
-
if (
|
|
19982
|
+
if (timer2) clearTimeout(timer2);
|
|
19879
19983
|
exit();
|
|
19880
19984
|
});
|
|
19881
19985
|
}, 60);
|
|
@@ -21106,8 +21210,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
21106
21210
|
return void 0;
|
|
21107
21211
|
}
|
|
21108
21212
|
if (!hadTransitionClearance) return void 0;
|
|
21109
|
-
const
|
|
21110
|
-
return () => clearTimeout(
|
|
21213
|
+
const timer2 = setTimeout(() => setPanelTransitionEpoch((epoch) => epoch + 1), 0);
|
|
21214
|
+
return () => clearTimeout(timer2);
|
|
21111
21215
|
}, [panelLayoutSignature, bottomClusterRows, panelTransitionClearRows, panelTransitionGuardRows]);
|
|
21112
21216
|
useEffect10(() => {
|
|
21113
21217
|
panelTransitionRef.current.tailId = latestTranscriptItem?.id ?? null;
|
|
@@ -23581,9 +23685,9 @@ function createSessionFlow(bag) {
|
|
|
23581
23685
|
}
|
|
23582
23686
|
if (compactType) {
|
|
23583
23687
|
const clearPromise = runtime.clear({ compactType, requireCompactSuccess: true });
|
|
23584
|
-
let
|
|
23688
|
+
let timer2 = null;
|
|
23585
23689
|
const timeout = new Promise((_, reject) => {
|
|
23586
|
-
|
|
23690
|
+
timer2 = setTimeout(
|
|
23587
23691
|
() => reject(new Error(`compaction timed out after ${AUTO_CLEAR_COMPACT_TIMEOUT_MS}ms; auto-clear deferred to next idle`)),
|
|
23588
23692
|
AUTO_CLEAR_COMPACT_TIMEOUT_MS
|
|
23589
23693
|
);
|
|
@@ -23608,7 +23712,7 @@ function createSessionFlow(bag) {
|
|
|
23608
23712
|
});
|
|
23609
23713
|
throw raceError;
|
|
23610
23714
|
} finally {
|
|
23611
|
-
if (
|
|
23715
|
+
if (timer2) clearTimeout(timer2);
|
|
23612
23716
|
}
|
|
23613
23717
|
} else {
|
|
23614
23718
|
await runtime.clear({});
|
|
@@ -26062,13 +26166,13 @@ async function createEngineSession({
|
|
|
26062
26166
|
const value = String(text ?? "").trim();
|
|
26063
26167
|
if (!value) return null;
|
|
26064
26168
|
set({ toasts: [...state.toasts.filter((toast) => toast.id !== id), { id, text: value, tone }] });
|
|
26065
|
-
const
|
|
26066
|
-
toastTimers.delete(
|
|
26169
|
+
const timer2 = setTimeout(() => {
|
|
26170
|
+
toastTimers.delete(timer2);
|
|
26067
26171
|
if (flags.disposed) return;
|
|
26068
26172
|
set({ toasts: state.toasts.filter((toast) => toast.id !== id) });
|
|
26069
26173
|
}, ttlMs);
|
|
26070
|
-
toastTimers.add(
|
|
26071
|
-
|
|
26174
|
+
toastTimers.add(timer2);
|
|
26175
|
+
timer2.unref?.();
|
|
26072
26176
|
return id;
|
|
26073
26177
|
};
|
|
26074
26178
|
const pushNotice = (text, tone = "info", options = {}) => {
|
|
@@ -26137,8 +26241,8 @@ async function createEngineSession({
|
|
|
26137
26241
|
}, 2e3);
|
|
26138
26242
|
lifecycle.runtimePulseTimer.unref?.();
|
|
26139
26243
|
function clearToastTimers() {
|
|
26140
|
-
for (const
|
|
26141
|
-
clearTimeout(
|
|
26244
|
+
for (const timer2 of toastTimers) {
|
|
26245
|
+
clearTimeout(timer2);
|
|
26142
26246
|
}
|
|
26143
26247
|
toastTimers.clear();
|
|
26144
26248
|
}
|
|
@@ -26255,21 +26359,21 @@ function waitWithTimeout(promise, timeoutMs, label = "cleanup") {
|
|
|
26255
26359
|
const ms = Math.max(1, Math.floor(Number(timeoutMs) || 1));
|
|
26256
26360
|
return new Promise((resolve5, reject) => {
|
|
26257
26361
|
let settled = false;
|
|
26258
|
-
const
|
|
26362
|
+
const timer2 = setTimeout(() => {
|
|
26259
26363
|
if (settled) return;
|
|
26260
26364
|
settled = true;
|
|
26261
26365
|
reject(new Error(`${label} timed out after ${ms}ms`));
|
|
26262
26366
|
}, ms);
|
|
26263
|
-
if (typeof
|
|
26367
|
+
if (typeof timer2.unref === "function") timer2.unref();
|
|
26264
26368
|
Promise.resolve(promise).then((value) => {
|
|
26265
26369
|
if (settled) return;
|
|
26266
26370
|
settled = true;
|
|
26267
|
-
clearTimeout(
|
|
26371
|
+
clearTimeout(timer2);
|
|
26268
26372
|
resolve5(value);
|
|
26269
26373
|
}).catch((error) => {
|
|
26270
26374
|
if (settled) return;
|
|
26271
26375
|
settled = true;
|
|
26272
|
-
clearTimeout(
|
|
26376
|
+
clearTimeout(timer2);
|
|
26273
26377
|
reject(error);
|
|
26274
26378
|
});
|
|
26275
26379
|
});
|
|
@@ -26406,7 +26510,7 @@ function installTuiLoopProbe() {
|
|
|
26406
26510
|
if (!LOOP_PROBE_ENABLED) return () => {
|
|
26407
26511
|
};
|
|
26408
26512
|
let last = performance3.now();
|
|
26409
|
-
const
|
|
26513
|
+
const timer2 = setInterval(() => {
|
|
26410
26514
|
const now = performance3.now();
|
|
26411
26515
|
const drift = now - last - LOOP_PROBE_INTERVAL_MS;
|
|
26412
26516
|
last = now;
|
|
@@ -26418,10 +26522,10 @@ function installTuiLoopProbe() {
|
|
|
26418
26522
|
}
|
|
26419
26523
|
}
|
|
26420
26524
|
}, LOOP_PROBE_INTERVAL_MS);
|
|
26421
|
-
|
|
26525
|
+
timer2.unref?.();
|
|
26422
26526
|
return () => {
|
|
26423
26527
|
try {
|
|
26424
|
-
clearInterval(
|
|
26528
|
+
clearInterval(timer2);
|
|
26425
26529
|
} catch {
|
|
26426
26530
|
}
|
|
26427
26531
|
};
|
|
@@ -26454,7 +26558,7 @@ function installTuiPerfProbe() {
|
|
|
26454
26558
|
stallMs: PERF_STALL_THRESHOLD_MS,
|
|
26455
26559
|
renderGapMs: PERF_RENDER_GAP_MS
|
|
26456
26560
|
});
|
|
26457
|
-
const
|
|
26561
|
+
const timer2 = setInterval(() => {
|
|
26458
26562
|
const now = performance3.now();
|
|
26459
26563
|
const elapsed = now - last;
|
|
26460
26564
|
const lagMs = elapsed - PERF_STALL_INTERVAL_MS;
|
|
@@ -26472,10 +26576,10 @@ function installTuiPerfProbe() {
|
|
|
26472
26576
|
heapMb: (mem.heapUsed / 1048576).toFixed(1)
|
|
26473
26577
|
});
|
|
26474
26578
|
}, PERF_STALL_INTERVAL_MS);
|
|
26475
|
-
|
|
26579
|
+
timer2.unref?.();
|
|
26476
26580
|
return () => {
|
|
26477
26581
|
try {
|
|
26478
|
-
clearInterval(
|
|
26582
|
+
clearInterval(timer2);
|
|
26479
26583
|
} catch {
|
|
26480
26584
|
}
|
|
26481
26585
|
};
|
|
@@ -26573,19 +26677,19 @@ function dumpActiveHandles(label) {
|
|
|
26573
26677
|
}
|
|
26574
26678
|
}
|
|
26575
26679
|
function waitWithTimeout2(promise, ms) {
|
|
26576
|
-
let
|
|
26680
|
+
let timer2 = null;
|
|
26577
26681
|
return Promise.race([
|
|
26578
26682
|
Promise.resolve(promise).then(() => true),
|
|
26579
26683
|
new Promise((resolve5) => {
|
|
26580
|
-
|
|
26684
|
+
timer2 = setTimeout(() => resolve5(false), ms);
|
|
26581
26685
|
})
|
|
26582
26686
|
]).finally(() => {
|
|
26583
|
-
if (
|
|
26687
|
+
if (timer2) clearTimeout(timer2);
|
|
26584
26688
|
});
|
|
26585
26689
|
}
|
|
26586
26690
|
function scheduleHardExit(code = 0) {
|
|
26587
26691
|
if (!EXIT_HARD_ENABLED) return;
|
|
26588
|
-
const
|
|
26692
|
+
const timer2 = setTimeout(() => {
|
|
26589
26693
|
dumpActiveHandles("hard-exit");
|
|
26590
26694
|
try {
|
|
26591
26695
|
process.stdout.write(`${TERMINAL_MODE_RESET}${TERMINAL_OSC_RESET_BG}`);
|
|
@@ -26593,7 +26697,7 @@ function scheduleHardExit(code = 0) {
|
|
|
26593
26697
|
}
|
|
26594
26698
|
process.exit(code);
|
|
26595
26699
|
}, EXIT_HARD_DELAY_MS);
|
|
26596
|
-
|
|
26700
|
+
timer2.unref?.();
|
|
26597
26701
|
}
|
|
26598
26702
|
function resolveTuiStderrLogPath() {
|
|
26599
26703
|
return process.env.MIXDOG_TUI_STDERR_LOG || join10(process.env.MIXDOG_RUNTIME_ROOT || join10(tmpdir2(), "mixdog"), "mixdog-tui.stderr.log");
|