mindweave 3.0.1 → 3.2.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 +36 -4
- package/dist/alternator/chassis/lsp.js +36 -4
- package/dist/alternator/chassis/lsp.js.map +1 -1
- package/dist/alternator/chassis/provision.js +54 -6
- package/dist/alternator/chassis/provision.js.map +1 -1
- package/dist/alternator/chassis/servers.js +34 -21
- package/dist/alternator/chassis/servers.js.map +1 -1
- package/dist/cli/App.js +118 -16
- package/dist/cli/App.js.map +1 -1
- package/dist/cli/attachments.js +37 -1
- package/dist/cli/attachments.js.map +1 -1
- package/dist/cli/bootstrap.js +25 -3
- package/dist/cli/bootstrap.js.map +1 -1
- package/dist/cli/components/BlockView.js +5 -5
- package/dist/cli/components/BlockView.js.map +1 -1
- package/dist/cli/components/CommandGroup.js +100 -0
- package/dist/cli/components/CommandGroup.js.map +1 -0
- package/dist/cli/components/MarathonBox.js +2 -1
- package/dist/cli/components/MarathonBox.js.map +1 -1
- package/dist/cli/components/TipLine.js +1 -0
- package/dist/cli/components/TipLine.js.map +1 -1
- package/dist/cli/components/ToolLine.js +69 -14
- package/dist/cli/components/ToolLine.js.map +1 -1
- package/dist/cli/components/WorkGroup.js +115 -0
- package/dist/cli/components/WorkGroup.js.map +1 -0
- package/dist/cli/diffWrap.js +24 -0
- package/dist/cli/diffWrap.js.map +1 -0
- package/dist/cli/expandHits.js +74 -0
- package/dist/cli/expandHits.js.map +1 -0
- package/dist/cli/marathonUi.js +2 -0
- package/dist/cli/marathonUi.js.map +1 -1
- package/dist/cli/mouse.js +16 -8
- package/dist/cli/mouse.js.map +1 -1
- package/dist/cli/scrollGlide.js +72 -0
- package/dist/cli/scrollGlide.js.map +1 -0
- package/dist/cli/scrollPill.js +21 -1
- package/dist/cli/scrollPill.js.map +1 -1
- package/dist/cli/selfUpdate.js +7 -0
- package/dist/cli/selfUpdate.js.map +1 -1
- package/dist/cli/terminalRestore.js +1 -1
- package/dist/cli/terminalRestore.js.map +1 -1
- package/dist/cli/toolDisplay.js +14 -10
- package/dist/cli/toolDisplay.js.map +1 -1
- package/dist/cli/transcript.js +100 -29
- package/dist/cli/transcript.js.map +1 -1
- package/dist/cli/uiLines.js +60 -0
- package/dist/cli/uiLines.js.map +1 -0
- package/dist/cli/wheelStep.js +29 -0
- package/dist/cli/wheelStep.js.map +1 -0
- package/dist/cli/workLines.js +84 -0
- package/dist/cli/workLines.js.map +1 -0
- package/dist/core/permissions.js +26 -1
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/spendView.js +8 -1
- package/dist/core/spendView.js.map +1 -1
- package/dist/core/turnRunner.js +36 -8
- package/dist/core/turnRunner.js.map +1 -1
- package/dist/drivers/deepseek/inlineTools.js +36 -2
- package/dist/drivers/deepseek/inlineTools.js.map +1 -1
- package/dist/drivers/ollama/endpoint.js +25 -0
- package/dist/drivers/ollama/endpoint.js.map +1 -1
- package/dist/dynamo/engine.js +414 -88
- package/dist/dynamo/engine.js.map +1 -1
- package/dist/dynamo/guard.js +11 -4
- package/dist/dynamo/guard.js.map +1 -1
- package/dist/dynamo/hooks.js +209 -0
- package/dist/dynamo/hooks.js.map +1 -0
- package/dist/dynamo/marathon.js +67 -10
- package/dist/dynamo/marathon.js.map +1 -1
- package/dist/dynamo/todoReminder.js +26 -0
- package/dist/dynamo/todoReminder.js.map +1 -0
- package/dist/governor/forbidden.js +19 -0
- package/dist/governor/forbidden.js.map +1 -1
- package/dist/governor/freshness.js +1 -1
- package/dist/governor/freshness.js.map +1 -1
- package/dist/governor/index.js +8 -1
- package/dist/governor/index.js.map +1 -1
- package/dist/governor/rules.js +1 -0
- package/dist/governor/rules.js.map +1 -1
- package/dist/governor/write.js +50 -2
- package/dist/governor/write.js.map +1 -1
- package/dist/mcp/config.js +0 -41
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/projectApproval.js +117 -0
- package/dist/mcp/projectApproval.js.map +1 -0
- package/dist/mcp/resultStore.js +19 -5
- package/dist/mcp/resultStore.js.map +1 -1
- package/dist/mcp/transport/stdio.js +4 -1
- package/dist/mcp/transport/stdio.js.map +1 -1
- package/dist/memory/allMetas.js +1 -1
- package/dist/memory/allMetas.js.map +1 -1
- package/dist/memory/autoMemory.js +1 -0
- package/dist/memory/autoMemory.js.map +1 -1
- package/dist/memory/clearedArchive.js +120 -0
- package/dist/memory/clearedArchive.js.map +1 -0
- package/dist/memory/compaction.js +178 -15
- package/dist/memory/compaction.js.map +1 -1
- package/dist/memory/counters.js +64 -0
- package/dist/memory/counters.js.map +1 -0
- package/dist/memory/projectNotes.js +61 -13
- package/dist/memory/projectNotes.js.map +1 -1
- package/dist/memory/rewind.js +6 -2
- package/dist/memory/rewind.js.map +1 -1
- package/dist/memory/session.js +13 -5
- package/dist/memory/session.js.map +1 -1
- package/dist/memory/sessionMemory.js +6 -2
- package/dist/memory/sessionMemory.js.map +1 -1
- package/dist/memory/store.js +3 -0
- package/dist/memory/store.js.map +1 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/memory/workingSet.js +0 -375
- package/dist/memory/workingSet.js.map +1 -1
- package/dist/project/context.js +54 -7
- package/dist/project/context.js.map +1 -1
- package/dist/tools/approval.js +19 -4
- package/dist/tools/approval.js.map +1 -1
- package/dist/tools/backgroundShells.js +8 -1
- package/dist/tools/backgroundShells.js.map +1 -1
- package/dist/tools/browser.js +2 -1
- package/dist/tools/browser.js.map +1 -1
- package/dist/tools/checkpoints.js +28 -4
- package/dist/tools/checkpoints.js.map +1 -1
- package/dist/tools/childEnv.js +50 -0
- package/dist/tools/childEnv.js.map +1 -0
- package/dist/tools/codeIntel.js +4 -1
- package/dist/tools/codeIntel.js.map +1 -1
- package/dist/tools/commandParse.js +238 -0
- package/dist/tools/commandParse.js.map +1 -0
- package/dist/tools/commandPolicy.js +283 -0
- package/dist/tools/commandPolicy.js.map +1 -0
- package/dist/tools/detail.js +113 -1
- package/dist/tools/detail.js.map +1 -1
- package/dist/tools/edit.js +2 -1
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/editRipple.js +18 -3
- package/dist/tools/editRipple.js.map +1 -1
- package/dist/tools/editTarget.js +11 -6
- package/dist/tools/editTarget.js.map +1 -1
- package/dist/tools/governorTools.js +61 -1
- package/dist/tools/governorTools.js.map +1 -1
- package/dist/tools/grep.js +13 -5
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/guard.js +249 -2
- package/dist/tools/guard.js.map +1 -1
- package/dist/tools/invisible.js +62 -0
- package/dist/tools/invisible.js.map +1 -0
- package/dist/tools/killTree.js +3 -0
- package/dist/tools/killTree.js.map +1 -1
- package/dist/tools/readFile.js +11 -3
- package/dist/tools/readFile.js.map +1 -1
- package/dist/tools/readSymbol.js +5 -24
- package/dist/tools/readSymbol.js.map +1 -1
- package/dist/tools/rebuildDetail.js +155 -0
- package/dist/tools/rebuildDetail.js.map +1 -0
- package/dist/tools/replaceSymbol.js +1 -1
- package/dist/tools/replaceSymbol.js.map +1 -1
- package/dist/tools/ripgrepShell.js +56 -0
- package/dist/tools/ripgrepShell.js.map +1 -0
- package/dist/tools/runCommand.js +77 -18
- package/dist/tools/runCommand.js.map +1 -1
- package/dist/tools/runsLater.js +50 -0
- package/dist/tools/runsLater.js.map +1 -0
- package/dist/tools/serverConsent.js +27 -0
- package/dist/tools/serverConsent.js.map +1 -0
- package/dist/tools/ui.js +81 -1
- package/dist/tools/ui.js.map +1 -1
- package/dist/tools/untrusted.js +22 -1
- package/dist/tools/untrusted.js.map +1 -1
- package/dist/tools/viewImage.js +2 -2
- package/dist/tools/viewImage.js.map +1 -1
- package/dist/tools/webFetch.js +76 -8
- package/dist/tools/webFetch.js.map +1 -1
- package/dist/tools/workspace.js +87 -24
- package/dist/tools/workspace.js.map +1 -1
- package/dist/tools/writeFile.js +9 -4
- package/dist/tools/writeFile.js.map +1 -1
- package/package.json +4 -4
package/dist/cli/App.js
CHANGED
|
@@ -70,21 +70,25 @@ import { PromptInput } from "./components/PromptInput.js";
|
|
|
70
70
|
import { Picker } from "./components/Picker.js";
|
|
71
71
|
import { ApprovalBox } from "./components/ApprovalBox.js";
|
|
72
72
|
import { BlockView } from "./components/BlockView.js";
|
|
73
|
-
import { initialState, reduce, trimNarration } from "./transcript.js";
|
|
73
|
+
import { initialState, lastExpandable, reduce, trimNarration } from "./transcript.js";
|
|
74
74
|
import { isTight } from "./blockSpacing.js";
|
|
75
75
|
import { parseScreenArg, screenChoices, screenNotice, startupMode } from "./screenMode.js";
|
|
76
76
|
import { applyScreenMode } from "./screenShell.js";
|
|
77
77
|
import { saveScreenMode } from "./screenStore.js";
|
|
78
78
|
import { needsMeasure, pruneHeights } from "./blockHeights.js";
|
|
79
|
+
import { expandableAt, hitBlock, hitItem } from "./expandHits.js";
|
|
80
|
+
import { readFileSync, statSync } from "node:fs";
|
|
81
|
+
import { compactUiDetail, rebuildFull, writtenFromDisk } from "../tools/rebuildDetail.js";
|
|
82
|
+
import { shownText } from "../memory/compaction.js";
|
|
79
83
|
import { BASE_COMMANDS } from "./commands.js";
|
|
80
|
-
import { dismissMarathon } from "../core/turnRunner.js";
|
|
84
|
+
import { attachmentBudget, dismissMarathon } from "../core/turnRunner.js";
|
|
81
85
|
import { describeMarathonEvent, resumeMarathon, startMarathon } from "../dynamo/marathon.js";
|
|
82
86
|
import { MarathonBox } from "./components/MarathonBox.js";
|
|
83
87
|
import { isFinished, isLive, marathonUiReduce } from "./marathonUi.js";
|
|
84
88
|
import { manualCommand, refusalReason } from "./selfUpdate.js";
|
|
85
89
|
import { currentInstall, requestRestart, runUpdate } from "./updateRunner.js";
|
|
86
90
|
import { enableMouse, readMouse, readWheel } from "./mouse.js";
|
|
87
|
-
import { wheelLines
|
|
91
|
+
import { wheelLines } from "./wheelStep.js";
|
|
88
92
|
import { applySelection, ctrlCShouldCopy, isEmpty, selectionText } from "./selection.js";
|
|
89
93
|
import { latestScreen, repaintOverlay, setFrameOverlay } from "./framebuffer/overlay.js";
|
|
90
94
|
import { requestFullRepaint } from "./framebuffer/writer.js";
|
|
@@ -322,6 +326,14 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
322
326
|
/** The chip's cells in SCREEN coordinates, for the pointer handler. Null when there is
|
|
323
327
|
* nothing to click. Published by the render, read between frames. */
|
|
324
328
|
const pillHit = useRef(null);
|
|
329
|
+
// Where the layout's first row sits on the screen, published each render for the pointer.
|
|
330
|
+
const frameTopRef = useRef(0);
|
|
331
|
+
// The row the pointer is over, if it can be pressed. State rather than a ref: it is what
|
|
332
|
+
// the row draws from. Only set when it CHANGES, so a pointer crossing the screen is not
|
|
333
|
+
// a redraw per cell.
|
|
334
|
+
const [hoverId, setHoverId] = useState(null);
|
|
335
|
+
// Set when a row is opened or folded, read once by the height effect.
|
|
336
|
+
const rowToggled = useRef(false);
|
|
325
337
|
const [footerHeight, setFooterHeight] = useState(0);
|
|
326
338
|
// The chat viewport's REAL height. Yoga decides it now (flexGrow beside a
|
|
327
339
|
// flexShrink:0 footer); this is read back purely so the scroll maths knows how
|
|
@@ -616,6 +628,9 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
616
628
|
const withEarlier = (session) => [...(session.earlier ?? []), ...session.transcript];
|
|
617
629
|
function showResumed(transcript) {
|
|
618
630
|
const readThisTurn = new Set(); // for narrationShown, as live
|
|
631
|
+
// Each call's name and arguments by id, so a result from a session saved before the uncut
|
|
632
|
+
// text was kept can have it put back together (see rebuildDetail.ts).
|
|
633
|
+
const callsById = new Map();
|
|
619
634
|
for (const e of transcript) {
|
|
620
635
|
if (e.role === "user") {
|
|
621
636
|
if (!e.synthetic)
|
|
@@ -638,11 +653,13 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
638
653
|
const calls = (e.toolCalls ?? []).map((c) => ({ name: c.name, args: parseToolArgs(c.arguments) }));
|
|
639
654
|
const shown = calls.length === 0 || narrationShown(calls, readThisTurn);
|
|
640
655
|
noteReads(calls, readThisTurn);
|
|
641
|
-
|
|
656
|
+
const said = shownText(e);
|
|
657
|
+
if (shown && said && said.trim()) {
|
|
642
658
|
const intermediate = calls.length > 0;
|
|
643
|
-
dispatch({ type: "say", text: intermediate ? trimNarration(
|
|
659
|
+
dispatch({ type: "say", text: intermediate ? trimNarration(said) : said });
|
|
644
660
|
}
|
|
645
661
|
for (const call of e.toolCalls ?? []) {
|
|
662
|
+
callsById.set(call.id, { name: call.name, args: parseToolArgs(call.arguments) });
|
|
646
663
|
// The spawn itself is drawn by its sub-agent block live, never as a raw row.
|
|
647
664
|
// Replaying it as one puts a `● SpawnSubagent(...)` in a resumed transcript
|
|
648
665
|
// that was not in the live one.
|
|
@@ -669,6 +686,28 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
669
686
|
ok: e.isError === undefined ? !e.content.startsWith("Error:") : !e.isError,
|
|
670
687
|
summary: e.summary,
|
|
671
688
|
detail: e.detail,
|
|
689
|
+
...(() => {
|
|
690
|
+
const call = callsById.get(e.toolCallId);
|
|
691
|
+
// A UI test saved before it became a short row comes back as the short row.
|
|
692
|
+
if (call?.name === "ui" && !e.detailFull) {
|
|
693
|
+
const compact = compactUiDetail(e.detail, e.isError);
|
|
694
|
+
if (compact)
|
|
695
|
+
return { detail: compact.detail, ...(compact.detailFull ? { detailFull: compact.detailFull } : {}) };
|
|
696
|
+
}
|
|
697
|
+
let full = e.detailFull ?? rebuildFull(call?.name, call?.args, e);
|
|
698
|
+
// A write whose text was cleared to save context has only the file left to open.
|
|
699
|
+
if (!full && call?.name === "write_file" && typeof call.args?.path === "string") {
|
|
700
|
+
try {
|
|
701
|
+
const abs = resolve(session.current?.cwd ?? process.cwd(), call.args.path);
|
|
702
|
+
if (statSync(abs).size < 2_000_000)
|
|
703
|
+
full = writtenFromDisk(e.detail, readFileSync(abs, "utf8"));
|
|
704
|
+
}
|
|
705
|
+
catch {
|
|
706
|
+
// gone or unreadable: the row stays as it was saved
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return full ? { detailFull: full } : {};
|
|
710
|
+
})(),
|
|
672
711
|
...(e.detailKind ? { detailKind: e.detailKind } : {}),
|
|
673
712
|
...(e.quiet ? { quiet: true } : {}),
|
|
674
713
|
...(e.displayName ? { name: e.displayName } : {}),
|
|
@@ -1086,8 +1125,6 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1086
1125
|
// counter kept climbing — and every one of those phantom lines then had to be
|
|
1087
1126
|
// scrolled back down before the view moved at all. A flick or two past the top bought
|
|
1088
1127
|
// a second of a wheel that did nothing, which reads as the app having frozen.
|
|
1089
|
-
/** The wheel gesture in progress, for scroll acceleration (wheelAccel.ts). */
|
|
1090
|
-
const wheelRef = useRef(wheelStart());
|
|
1091
1128
|
const scrollBy = useCallback((lines) => {
|
|
1092
1129
|
// Repaint the whole next frame. A scroll can move a row out from under the
|
|
1093
1130
|
// framebuffer's model (see requestFullRepaint), which is what left a transcript row
|
|
@@ -1150,6 +1187,17 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1150
1187
|
requestFullRepaint();
|
|
1151
1188
|
setScrollUp(0);
|
|
1152
1189
|
}
|
|
1190
|
+
// The keyboard way to the same thing a click does, for terminals that do not send
|
|
1191
|
+
// the mouse. It acts on the newest row that has more to show.
|
|
1192
|
+
else if (key.ctrl && _input === "o") {
|
|
1193
|
+
if (shell === "inline" && !reading) {
|
|
1194
|
+
note("Scroll up first (PageUp or the wheel), then press ctrl+O to open the newest long output.");
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
const target = lastExpandable(stateRef.current);
|
|
1198
|
+
if (target !== null)
|
|
1199
|
+
toggleRow(target);
|
|
1200
|
+
}
|
|
1153
1201
|
else if (key.home && key.ctrl) {
|
|
1154
1202
|
requestFullRepaint();
|
|
1155
1203
|
setScrollUp(maxScrollRef.current);
|
|
@@ -1256,7 +1304,18 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1256
1304
|
// Ordinary growth, not a resize — see growScroll for why this has to hold the
|
|
1257
1305
|
// reader's absolute position rather than leave `scrollUp` where it was.
|
|
1258
1306
|
const prev = prevContentHeight.current;
|
|
1259
|
-
|
|
1307
|
+
let next = growScroll(scrollUp, prev, height);
|
|
1308
|
+
// A row was just opened or folded. The row the reader pressed stays where it is and
|
|
1309
|
+
// the change happens below it: opening from the bottom would otherwise follow the
|
|
1310
|
+
// new end of the output and carry the row's own first line off the top, and folding
|
|
1311
|
+
// would slide the view up by what was taken away.
|
|
1312
|
+
if (rowToggled.current && height !== prev) {
|
|
1313
|
+
if (height > prev && scrollUp === 0)
|
|
1314
|
+
next = height - prev;
|
|
1315
|
+
else if (height < prev)
|
|
1316
|
+
next = Math.max(0, scrollUp - (prev - height));
|
|
1317
|
+
}
|
|
1318
|
+
rowToggled.current = false;
|
|
1260
1319
|
if (next !== scrollUp)
|
|
1261
1320
|
setScrollUp(next);
|
|
1262
1321
|
}
|
|
@@ -1412,12 +1471,11 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1412
1471
|
// Content moves out from under a selection when the view scrolls, so the
|
|
1413
1472
|
// highlight would be sitting on text that is no longer the text it copied.
|
|
1414
1473
|
clearSelection();
|
|
1415
|
-
// Signed notch count, then the distance for it
|
|
1416
|
-
//
|
|
1474
|
+
// Signed notch count, then the distance for it. The same distance for every notch,
|
|
1475
|
+
// applied at once (wheelStep.ts): how far the view goes follows how far the wheel
|
|
1476
|
+
// was turned, and nothing else.
|
|
1417
1477
|
const net = notches.reduce((n, dir) => n + (dir === "up" ? 1 : -1), 0);
|
|
1418
|
-
const
|
|
1419
|
-
wheelRef.current = step.state;
|
|
1420
|
-
const lines = step.lines;
|
|
1478
|
+
const lines = wheelLines(net);
|
|
1421
1479
|
// The wheel is how anyone actually scrolls, so in the inline shell it is what
|
|
1422
1480
|
// opens the reading view. Turning it UP is the gesture: the reader is going back
|
|
1423
1481
|
// through the conversation and wants the prompt to stay where they can type into
|
|
@@ -1437,7 +1495,13 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1437
1495
|
// in a chunk is on screen at the end of it, so the ones before it are painted for
|
|
1438
1496
|
// nobody. Cleared by press and release, which do their own painting.
|
|
1439
1497
|
let pendingDrag = false;
|
|
1498
|
+
let lastMove = null;
|
|
1440
1499
|
for (const event of events) {
|
|
1500
|
+
if (event.kind === "move") {
|
|
1501
|
+
// Only the last position in a chunk matters; a burst is one lookup.
|
|
1502
|
+
lastMove = event;
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1441
1505
|
if (event.kind === "press") {
|
|
1442
1506
|
pendingDrag = false;
|
|
1443
1507
|
// The chip is a button. It is the only thing on screen that says what it does,
|
|
@@ -1478,6 +1542,14 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1478
1542
|
selection.current = null;
|
|
1479
1543
|
repaintOverlay();
|
|
1480
1544
|
setFrameOverlay(null);
|
|
1545
|
+
// A click on a row that has more to show opens it, and a second click folds
|
|
1546
|
+
// it. Tested BEFORE the caret, because a click on the transcript is never on
|
|
1547
|
+
// the input anyway, and a click that does nothing is the worse outcome.
|
|
1548
|
+
const hitId = expandableAt(event.y, frameTopRef.current, chatRef.current);
|
|
1549
|
+
if (hitId !== null) {
|
|
1550
|
+
toggleRow(hitId);
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
1481
1553
|
placeCaretAt(event.x, event.y);
|
|
1482
1554
|
continue;
|
|
1483
1555
|
}
|
|
@@ -1495,11 +1567,30 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1495
1567
|
}
|
|
1496
1568
|
if (pendingDrag)
|
|
1497
1569
|
repaintOverlay();
|
|
1570
|
+
// The pointer, resting or moving: light the row under it if it can be pressed, and put
|
|
1571
|
+
// the light out when it leaves. A press or a wheel turn clears it too, below, because
|
|
1572
|
+
// the content moves out from under a pointer that did not.
|
|
1573
|
+
if (lastMove) {
|
|
1574
|
+
const over = expandableAt(lastMove.y, frameTopRef.current, chatRef.current);
|
|
1575
|
+
setHoverId((h) => (h === over ? h : over));
|
|
1576
|
+
}
|
|
1577
|
+
else if (notches.length > 0 || events.some((e) => e.kind === "press")) {
|
|
1578
|
+
setHoverId((h) => (h === null ? h : null));
|
|
1579
|
+
}
|
|
1498
1580
|
// A real keystroke, so put any highlight away — the same as a terminal's own
|
|
1499
1581
|
// selection does the moment you type.
|
|
1500
1582
|
if (events.length === 0 && notches.length === 0)
|
|
1501
1583
|
clearSelection();
|
|
1502
1584
|
}, { isActive: true });
|
|
1585
|
+
/** Open or fold a row, keeping it where the reader pressed it. See the height effect. */
|
|
1586
|
+
function toggleRow(hit) {
|
|
1587
|
+
rowToggled.current = true;
|
|
1588
|
+
// A hit on one command inside a commands row opens that command; anything else is a row.
|
|
1589
|
+
if (hitItem(hit) >= 0)
|
|
1590
|
+
dispatch({ type: "toggleItem", id: hitBlock(hit), index: hitItem(hit) });
|
|
1591
|
+
else
|
|
1592
|
+
dispatch({ type: "toggleExpand", id: hit });
|
|
1593
|
+
}
|
|
1503
1594
|
function endTurn() {
|
|
1504
1595
|
releaseLiveGates();
|
|
1505
1596
|
if (turnStart.current != null)
|
|
@@ -1850,7 +1941,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1850
1941
|
// Dropped files are carried in the buffer as short handles. Put the real paths back
|
|
1851
1942
|
// before anything is resolved against the disk, and hand the same handle back as the
|
|
1852
1943
|
// label so the chat shows what the user typed rather than a third name for the file.
|
|
1853
|
-
const { modelText, displayText, notes, images } = await resolveAttachments(expandHandles(text, dropHandles.current), s.cwd, canSeeImages, (abs) => dropHandles.current.labelFor(abs));
|
|
1944
|
+
const { modelText, displayText, notes, images } = await resolveAttachments(expandHandles(text, dropHandles.current), s.cwd, canSeeImages, (abs) => dropHandles.current.labelFor(abs), attachmentBudget(s.modelConfig.model));
|
|
1854
1945
|
// Restore any collapsed pastes into the model's copy only (the chat keeps chips).
|
|
1855
1946
|
return { content: expandPastes(modelText), displayText, notes, images };
|
|
1856
1947
|
}
|
|
@@ -1957,6 +2048,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1957
2048
|
summary: e.summary,
|
|
1958
2049
|
detail: e.detail,
|
|
1959
2050
|
detailKind: e.detailKind,
|
|
2051
|
+
detailFull: e.detailFull,
|
|
1960
2052
|
quiet: e.quiet,
|
|
1961
2053
|
action: e.displayKind,
|
|
1962
2054
|
name: e.displayName,
|
|
@@ -2536,6 +2628,14 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2536
2628
|
return;
|
|
2537
2629
|
}
|
|
2538
2630
|
const install = currentInstall();
|
|
2631
|
+
if (install.kind === "app") {
|
|
2632
|
+
const latest = await checkForUpdate({ readCacheImpl: () => null });
|
|
2633
|
+
const current = appVersion();
|
|
2634
|
+
note(latest
|
|
2635
|
+
? `${refusalReason(install)} v${latest} is out (this is v${current}); update the app from Settings, About.`
|
|
2636
|
+
: `Already on the latest${current ? ` (v${current})` : ""}. ${refusalReason(install)}`);
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2539
2639
|
if (install.kind !== "global") {
|
|
2540
2640
|
// Never guessed at. Overwriting a working tree or another project's dependency
|
|
2541
2641
|
// is the one thing this command must not do; see selfUpdate.ts.
|
|
@@ -3570,6 +3670,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3570
3670
|
newReplies: countNewReplies(allBlocks, scrollMark.current),
|
|
3571
3671
|
overlayOpen: overlay !== null,
|
|
3572
3672
|
width,
|
|
3673
|
+
maxScroll,
|
|
3573
3674
|
});
|
|
3574
3675
|
/**
|
|
3575
3676
|
* The chip's cells in SCREEN coordinates, so a click can be tested against them.
|
|
@@ -3586,6 +3687,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3586
3687
|
* measure nothing for itself.
|
|
3587
3688
|
*/
|
|
3588
3689
|
const frameTop = readingInline ? Math.max(0, rows - frameHeight) : 0;
|
|
3690
|
+
frameTopRef.current = frameTop;
|
|
3589
3691
|
pillHit.current = pill !== null && pillRow.current !== null ? pillBounds(pill, width, frameTop + pillRow.current) : null;
|
|
3590
3692
|
// The whole conversation stays reachable: the cap is only a safety valve (see
|
|
3591
3693
|
// SCROLLBACK_BLOCKS). Blocks outside the window cost one number each here and nothing
|
|
@@ -3692,13 +3794,13 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3692
3794
|
_jsx(Box, { ref: (node) => {
|
|
3693
3795
|
if (node && needsMeasure(blockHeights.current.get(b.id), b))
|
|
3694
3796
|
toMeasure.current.set(b, node);
|
|
3695
|
-
}, flexShrink: 0, flexDirection: "column", children: _jsx(BlockView, { block: b, columns: width, tightTop: isTight(allBlocks, offset + idx) }) }, b.id));
|
|
3797
|
+
}, flexShrink: 0, flexDirection: "column", children: _jsx(BlockView, { block: b, columns: width, tightTop: isTight(allBlocks, offset + idx), hovered: hoverId !== null && hitBlock(hoverId) === b.id && hitItem(hoverId) < 0, hoveredItem: hoverId !== null && hitBlock(hoverId) === b.id ? hitItem(hoverId) : -1 }) }, b.id));
|
|
3696
3798
|
}), padMiddle > 0 ? _jsx(Box, { flexShrink: 0, height: padMiddle }) : null, rendered.slice(known).map((b, i) => {
|
|
3697
3799
|
const idx = known + i;
|
|
3698
3800
|
return (_jsx(Box, { ref: (node) => {
|
|
3699
3801
|
if (node && needsMeasure(blockHeights.current.get(b.id), b))
|
|
3700
3802
|
toMeasure.current.set(b, node);
|
|
3701
|
-
}, flexShrink: 0, flexDirection: "column", children: _jsx(BlockView, { block: b, columns: width, tightTop: isTight(allBlocks, offset + idx) }) }, b.id));
|
|
3803
|
+
}, flexShrink: 0, flexDirection: "column", children: _jsx(BlockView, { block: b, columns: width, tightTop: isTight(allBlocks, offset + idx), hovered: hoverId !== null && hitBlock(hoverId) === b.id && hitItem(hoverId) < 0, hoveredItem: hoverId !== null && hitBlock(hoverId) === b.id ? hitItem(hoverId) : -1 }) }, b.id));
|
|
3702
3804
|
})] }) }), pill ? (_jsx(Box
|
|
3703
3805
|
// Measured so a CLICK can find it. The row is the only part of the chip's
|
|
3704
3806
|
// position the layout owns — the columns fall out of centring, which
|