zidane 5.1.0 → 5.1.2
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/dist/chat.d.ts +3 -3
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/index.js +1 -1
- package/dist/{login-BiuHyuEh.js → login-B7b7NNJQ.js} +2 -1
- package/dist/login-B7b7NNJQ.js.map +1 -0
- package/dist/{theme-CcGLMJrn.d.ts → tool-formatters-D7cN3T_W.d.ts} +203 -14
- package/dist/tool-formatters-D7cN3T_W.d.ts.map +1 -0
- package/dist/tui.d.ts +9 -33
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +49 -385
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-BzOIM6Of.js → turn-operations-2cgnXS2d.js} +515 -32
- package/dist/turn-operations-2cgnXS2d.js.map +1 -0
- package/package.json +1 -1
- package/dist/login-BiuHyuEh.js.map +0 -1
- package/dist/theme-CcGLMJrn.d.ts.map +0 -1
- package/dist/turn-operations-BzOIM6Of.js.map +0 -1
package/dist/tui.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { S as resolvePersistDir, b as cleanupPersistedSession, d as createAgent } from "./tools-d1yeA6xK.js";
|
|
2
2
|
import { s as McpOAuthProvider, t as connectMcpServers } from "./mcp-BgwK6ySj.js";
|
|
3
|
-
import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-
|
|
3
|
+
import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-B7b7NNJQ.js";
|
|
4
4
|
import { n as formatTokenUsage } from "./stats-DvCtBRwK.js";
|
|
5
5
|
import { n as loadSession, t as createSession } from "./session-pS4Vt4dl.js";
|
|
6
6
|
import { createTuiStore } from "./session/sqlite.js";
|
|
7
|
-
import { $ as
|
|
7
|
+
import { $ as getMcpAuthStatus, $t as toolResultText, A as isOnSafelist, An as tryOpenBrowser, B as filterModelCatalog, Bt as eventsFromTurns, C as writeSessionExport, Cn as createFilesCompletionProvider, Ct as SettingsProvider, E as useSafeModeQueue, Ft as ConfigProvider, Gt as listSessionMeta, H as buildMcpServers, Ht as isTurnHighlighted, I as splitPromptSegments, It as useConfig, Jn as modelSupportsReasoning, Kn as getContextWindow, L as runOAuthLogin, Lt as resolveConfig, Mn as detectAuth, O as addToSafelist, Ot as resolveChipColor, P as suggestSafelistEntry, Q as useMcpAuthState, Qn as piIdOf, Qt as toolCallPreview, R as supportsOAuth, Rn as setProviderCredential, St as SETTINGS_TOGGLES, T as useSafeModeActions, Tt as useSettings, Ut as isVisible, V as indexOfEntry, Vt as isEditErrorResult, W as discoverProjectMcps, Wt as lastContextSizeFromTurns, X as McpAuthProvider, Xt as stripSpawnTokensLine, Yt as selectableTurnIds, Z as useMcpAuthDispatch, _ as useStreamBuffer, _t as shortId, a as TOOL_DISPLAY, an as filetypeFromPath, at as createInteractionTools, b as discoverProjectSkills, bn as createSkillsCompletionProvider, bt as DEFAULT_SETTINGS, c as ThemeProvider, cn as findGitRoot, ct as pendingInteractionsFromTurns, d as useSurfaces, dt as useInteractionsQueue, en as turnSelectionOwnership, fn as ensureKeybindingsFile, g as turnContextSize, gr as buildPlanSystem, gt as fmtTokens, h as finalizeStreamingMarkdownForOwner, hr as buildBuildSystem, ht as compactPath, i as turnAsText, in as extractEditPayload, ir as accentColor, it as buildResumedToolResultsTurn, jn as shouldAutoCompact, k as getSafelist, kn as useCompletion, kt as resolveTheme, l as useColors, m as finalizeStreamingMarkdown, mn as matchesBinding, mt as ageString, n as deleteTurnSafely, nt as InteractionsProvider, o as displayNameFor, p as useTheme, pt as generateSessionTitle, q as createFileMcpCredentialStore, qt as marginTopFor, r as truncateTurnsAt, s as formatToolCall, st as makeRequestInteraction, tn as buildUnifiedDiff, u as useSelectStyle, ut as useInteractionsActions, v as buildSkillsConfig, vt as listProjectFiles, w as SafeModeProvider, wt as clampFps, xn as uniqueSkillNamesFromReferences, xt as SETTINGS_CHOICES, y as defaultSkillScanPaths, yt as useEnabledToggleSet, z as buildModelCatalog, zt as deriveSessionTitle } from "./turn-operations-2cgnXS2d.js";
|
|
8
8
|
import { Buffer } from "node:buffer";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { createContext, createElement, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
11
|
-
import { Fragment, jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
12
11
|
import { RGBA, SyntaxStyle, addDefaultParsers, createCliRenderer, defaultTextareaKeyBindings, getTreeSitterClient } from "@opentui/core";
|
|
13
12
|
import { createRoot, useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/react";
|
|
13
|
+
import { Fragment, jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
14
14
|
//#region src/tui/modal.tsx
|
|
15
15
|
const ModalContext = createContext(null);
|
|
16
16
|
function ModalRoot({ children }) {
|
|
@@ -204,19 +204,6 @@ function EmptyState$1() {
|
|
|
204
204
|
})]
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Resolve a profile's `accent` token to a concrete theme color via the
|
|
209
|
-
* caller's color palette. Exposed for the Footer badge so all surfaces
|
|
210
|
-
* stay in sync with the picker's row tinting.
|
|
211
|
-
*/
|
|
212
|
-
function accentColor(accent, COLOR) {
|
|
213
|
-
switch (accent) {
|
|
214
|
-
case "brand": return COLOR.brand;
|
|
215
|
-
case "warn": return COLOR.warn;
|
|
216
|
-
case "model": return COLOR.model;
|
|
217
|
-
default: return COLOR.accent;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
207
|
//#endregion
|
|
221
208
|
//#region src/tui/theme.ts
|
|
222
209
|
/**
|
|
@@ -360,250 +347,6 @@ function useChipHighlights(textareaRef, references, chipStyle) {
|
|
|
360
347
|
]);
|
|
361
348
|
}
|
|
362
349
|
//#endregion
|
|
363
|
-
//#region src/tui/tool-formatters.ts
|
|
364
|
-
const TOOL_DISPLAY = {
|
|
365
|
-
read_file: {
|
|
366
|
-
displayName: "Read",
|
|
367
|
-
format: (input) => {
|
|
368
|
-
const path = stringField(input, "path");
|
|
369
|
-
if (!path) return null;
|
|
370
|
-
const meta = [];
|
|
371
|
-
const offset = numberField(input, "offset");
|
|
372
|
-
const limit = numberField(input, "limit");
|
|
373
|
-
if (offset !== void 0 && limit !== void 0 && limit > 0) meta.push(`L${offset}–${offset + limit - 1}`);
|
|
374
|
-
else if (offset !== void 0) meta.push(`from L${offset}`);
|
|
375
|
-
else if (limit !== void 0 && limit > 0) meta.push(`${limit} lines`);
|
|
376
|
-
return {
|
|
377
|
-
target: path,
|
|
378
|
-
meta
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
},
|
|
382
|
-
list_files: {
|
|
383
|
-
displayName: "List",
|
|
384
|
-
format: (input) => {
|
|
385
|
-
return { target: stringField(input, "path") ?? "." };
|
|
386
|
-
}
|
|
387
|
-
},
|
|
388
|
-
glob: {
|
|
389
|
-
displayName: "Glob",
|
|
390
|
-
format: (input) => {
|
|
391
|
-
const pattern = stringField(input, "pattern");
|
|
392
|
-
if (!pattern) return null;
|
|
393
|
-
const meta = [];
|
|
394
|
-
const limit = numberField(input, "limit");
|
|
395
|
-
if (limit !== void 0) meta.push(`limit ${limit}`);
|
|
396
|
-
return {
|
|
397
|
-
target: pattern,
|
|
398
|
-
meta
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
grep: {
|
|
403
|
-
displayName: "Grep",
|
|
404
|
-
format: (input) => {
|
|
405
|
-
const pattern = stringField(input, "pattern");
|
|
406
|
-
if (!pattern) return null;
|
|
407
|
-
const target = `/${pattern}/`;
|
|
408
|
-
const meta = [];
|
|
409
|
-
const path = stringField(input, "path");
|
|
410
|
-
if (path && path !== ".") meta.push(`in ${path}`);
|
|
411
|
-
const glob = stringField(input, "glob");
|
|
412
|
-
if (glob) meta.push(glob);
|
|
413
|
-
const type = stringField(input, "type");
|
|
414
|
-
if (type) meta.push(`type:${type}`);
|
|
415
|
-
if (input["-i"] === true) meta.push("case-insensitive");
|
|
416
|
-
const mode = stringField(input, "output_mode");
|
|
417
|
-
if (mode && mode !== "files_with_matches") meta.push(mode);
|
|
418
|
-
return {
|
|
419
|
-
target,
|
|
420
|
-
meta
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
},
|
|
424
|
-
shell: {
|
|
425
|
-
displayName: "Shell",
|
|
426
|
-
format: (input) => {
|
|
427
|
-
const command = stringField(input, "command");
|
|
428
|
-
if (!command) return null;
|
|
429
|
-
return { target: truncate(command.trim(), 200) };
|
|
430
|
-
}
|
|
431
|
-
},
|
|
432
|
-
edit: {
|
|
433
|
-
displayName: "Edit",
|
|
434
|
-
format: (input) => {
|
|
435
|
-
const path = stringField(input, "path");
|
|
436
|
-
if (!path) return null;
|
|
437
|
-
return {
|
|
438
|
-
target: path,
|
|
439
|
-
meta: input.replace_all === true ? ["replace all"] : []
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
},
|
|
443
|
-
multi_edit: {
|
|
444
|
-
displayName: "Multi-edit",
|
|
445
|
-
format: (input) => {
|
|
446
|
-
const path = stringField(input, "path");
|
|
447
|
-
if (!path) return null;
|
|
448
|
-
const edits = Array.isArray(input.edits) ? input.edits.length : 0;
|
|
449
|
-
return {
|
|
450
|
-
target: path,
|
|
451
|
-
meta: edits > 0 ? [`${edits} hunk${edits === 1 ? "" : "s"}`] : []
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
},
|
|
455
|
-
write_file: {
|
|
456
|
-
displayName: "Write",
|
|
457
|
-
format: (input) => {
|
|
458
|
-
const path = stringField(input, "path");
|
|
459
|
-
if (!path) return null;
|
|
460
|
-
const content = stringField(input, "content");
|
|
461
|
-
const meta = [];
|
|
462
|
-
if (content !== void 0) {
|
|
463
|
-
const bytes = byteLengthUtf8(content);
|
|
464
|
-
meta.push(`${formatBytes(bytes)}`);
|
|
465
|
-
}
|
|
466
|
-
return {
|
|
467
|
-
target: path,
|
|
468
|
-
meta
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
},
|
|
472
|
-
spawn: {
|
|
473
|
-
displayName: "Spawn",
|
|
474
|
-
format: (input) => {
|
|
475
|
-
const task = stringField(input, "task");
|
|
476
|
-
if (!task) return null;
|
|
477
|
-
return { target: truncate(task.replace(/\s+/g, " ").trim(), 120) };
|
|
478
|
-
}
|
|
479
|
-
},
|
|
480
|
-
tool_search: {
|
|
481
|
-
displayName: "Search tools",
|
|
482
|
-
format: (input) => {
|
|
483
|
-
const query = stringField(input, "query");
|
|
484
|
-
const names = Array.isArray(input.names) ? input.names.length : 0;
|
|
485
|
-
if (query) return { target: `“${query}”` };
|
|
486
|
-
if (names > 0) return { target: `${names} tool${names === 1 ? "" : "s"}` };
|
|
487
|
-
return null;
|
|
488
|
-
}
|
|
489
|
-
},
|
|
490
|
-
skills_use: {
|
|
491
|
-
displayName: "Activate skill",
|
|
492
|
-
format: (input) => {
|
|
493
|
-
const name = stringField(input, "name");
|
|
494
|
-
if (!name) return null;
|
|
495
|
-
return { target: name };
|
|
496
|
-
}
|
|
497
|
-
},
|
|
498
|
-
skills_read: {
|
|
499
|
-
displayName: "Read skill",
|
|
500
|
-
format: (input) => {
|
|
501
|
-
const name = stringField(input, "name");
|
|
502
|
-
const path = stringField(input, "path");
|
|
503
|
-
if (!name) return null;
|
|
504
|
-
return { target: path ? `${name}/${path}` : name };
|
|
505
|
-
}
|
|
506
|
-
},
|
|
507
|
-
skills_run_script: {
|
|
508
|
-
displayName: "Run script",
|
|
509
|
-
format: (input) => {
|
|
510
|
-
const name = stringField(input, "name");
|
|
511
|
-
const script = stringField(input, "script");
|
|
512
|
-
if (!name || !script) return null;
|
|
513
|
-
const meta = [`skill ${name}`];
|
|
514
|
-
const args = Array.isArray(input.args) ? input.args : null;
|
|
515
|
-
if (args && args.length > 0) meta.push(truncate(args.map(String).join(" "), 80));
|
|
516
|
-
return {
|
|
517
|
-
target: script,
|
|
518
|
-
meta
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
},
|
|
522
|
-
ask_user: {
|
|
523
|
-
displayName: "Ask user",
|
|
524
|
-
format: (input) => {
|
|
525
|
-
const questions = Array.isArray(input.questions) ? input.questions.length : 0;
|
|
526
|
-
if (questions === 0) return null;
|
|
527
|
-
return { target: `${questions} question${questions === 1 ? "" : "s"}` };
|
|
528
|
-
}
|
|
529
|
-
},
|
|
530
|
-
present_plan: {
|
|
531
|
-
displayName: "Present plan",
|
|
532
|
-
format: (input) => {
|
|
533
|
-
const title = stringField(input, "title");
|
|
534
|
-
if (!title) return null;
|
|
535
|
-
return { target: title };
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
/**
|
|
540
|
-
* Resolve the display verb for a tool. Native tools use their curated
|
|
541
|
-
* entry from {@link TOOL_DISPLAY}; everything else gets a Title-Case
|
|
542
|
-
* version of the raw name (`my_host_tool` → `My Host Tool`) so an MCP /
|
|
543
|
-
* host tool still reads cleanly in the transcript.
|
|
544
|
-
*
|
|
545
|
-
* MCP convention: every tool surfaced by `mcp/connectMcpServers` is
|
|
546
|
-
* namespaced as `mcp_<server>_<tool>` (see `src/mcp/index.ts`). The
|
|
547
|
-
* `mcp_` prefix is plumbing — strip it before title-casing so the
|
|
548
|
-
* label reads as `Github Create Issue` instead of `Mcp Github Create
|
|
549
|
-
* Issue`. The server name becomes the leading words, which doubles as
|
|
550
|
-
* a free visual grouping affordance ("everything starting with
|
|
551
|
-
* `Github` came from the github MCP server").
|
|
552
|
-
*/
|
|
553
|
-
function displayNameFor(name) {
|
|
554
|
-
const entry = TOOL_DISPLAY[name];
|
|
555
|
-
if (entry) return entry.displayName;
|
|
556
|
-
return titleCase(name.startsWith("mcp_") ? name.slice(4) : name);
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* Run a tool's curated formatter and return the result, or `null` when
|
|
560
|
-
* no formatter is registered / the input shape doesn't match. Renderer
|
|
561
|
-
* decides what to do with `null` — typically: show `↳ <displayName>`
|
|
562
|
-
* with no target / meta tail.
|
|
563
|
-
*/
|
|
564
|
-
function formatToolCall(name, input) {
|
|
565
|
-
const entry = TOOL_DISPLAY[name];
|
|
566
|
-
if (!entry) return null;
|
|
567
|
-
try {
|
|
568
|
-
return entry.format(input);
|
|
569
|
-
} catch {
|
|
570
|
-
return null;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
function stringField(input, key) {
|
|
574
|
-
const v = input[key];
|
|
575
|
-
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
576
|
-
}
|
|
577
|
-
function numberField(input, key) {
|
|
578
|
-
const v = input[key];
|
|
579
|
-
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
580
|
-
}
|
|
581
|
-
/** `snake_case` / `kebab-case` / lowercase → `Title Case`. */
|
|
582
|
-
function titleCase(s) {
|
|
583
|
-
return s.split(/[-_\s]+/).filter(Boolean).map((w) => w[0]?.toUpperCase() + w.slice(1).toLowerCase()).join(" ");
|
|
584
|
-
}
|
|
585
|
-
function truncate(s, max) {
|
|
586
|
-
return s.length <= max ? s : `${s.slice(0, max - 1)}…`;
|
|
587
|
-
}
|
|
588
|
-
function byteLengthUtf8(s) {
|
|
589
|
-
let bytes = 0;
|
|
590
|
-
for (let i = 0; i < s.length; i++) {
|
|
591
|
-
const code = s.charCodeAt(i);
|
|
592
|
-
if (code < 128) bytes += 1;
|
|
593
|
-
else if (code < 2048) bytes += 2;
|
|
594
|
-
else if (code >= 55296 && code <= 56319) {
|
|
595
|
-
bytes += 4;
|
|
596
|
-
i++;
|
|
597
|
-
} else bytes += 3;
|
|
598
|
-
}
|
|
599
|
-
return bytes;
|
|
600
|
-
}
|
|
601
|
-
function formatBytes(bytes) {
|
|
602
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
603
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
604
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
605
|
-
}
|
|
606
|
-
//#endregion
|
|
607
350
|
//#region src/tui/components.tsx
|
|
608
351
|
/**
|
|
609
352
|
* Memoized so a flush that mutates only the trailing event doesn't force the
|
|
@@ -967,39 +710,19 @@ function StatusSpinner({ color }) {
|
|
|
967
710
|
children: useSpinnerFrame()
|
|
968
711
|
});
|
|
969
712
|
}
|
|
970
|
-
/**
|
|
971
|
-
* Minimum scrollbar thumb size, in half-block units (OpenTUI's
|
|
972
|
-
* `SliderRenderable` renders the vertical thumb at 2 half-blocks per
|
|
973
|
-
* character cell). `8` half-blocks = 4 character cells — always large
|
|
974
|
-
* enough to read + grab with the mouse, never so large that it
|
|
975
|
-
* dominates the track on short transcripts.
|
|
976
|
-
*/
|
|
977
|
-
const MIN_THUMB_HALF_BLOCKS = 8;
|
|
978
713
|
function Transcript({ events, settings, selectedTurnId = null }) {
|
|
714
|
+
const COLOR = useColors();
|
|
979
715
|
const items = useMemo(() => partitionTranscript(events, settings), [events, settings]);
|
|
716
|
+
const ownership = useMemo(() => turnSelectionOwnership(events), [events]);
|
|
980
717
|
const scrollboxRef = useRef(null);
|
|
981
|
-
useEffect(() => {
|
|
982
|
-
const scrollbox = scrollboxRef.current;
|
|
983
|
-
if (!scrollbox) return;
|
|
984
|
-
const slider = scrollbox.verticalScrollBar?.slider;
|
|
985
|
-
if (!slider || typeof slider.getVirtualThumbSize !== "function") return;
|
|
986
|
-
const original = slider.getVirtualThumbSize.bind(slider);
|
|
987
|
-
slider.getVirtualThumbSize = function() {
|
|
988
|
-
const upstream = original();
|
|
989
|
-
const virtualTrackSize = slider.height * 2;
|
|
990
|
-
return Math.min(virtualTrackSize, Math.max(MIN_THUMB_HALF_BLOCKS, upstream));
|
|
991
|
-
};
|
|
992
|
-
return () => {
|
|
993
|
-
slider.getVirtualThumbSize = original;
|
|
994
|
-
};
|
|
995
|
-
}, []);
|
|
996
718
|
const anchors = useMemo(() => computeTurnAnchors(items), [items]);
|
|
997
719
|
useEffect(() => {
|
|
998
720
|
if (!selectedTurnId) return;
|
|
999
721
|
const scrollbox = scrollboxRef.current;
|
|
1000
722
|
if (!scrollbox) return;
|
|
1001
723
|
const handle = requestAnimationFrame(() => {
|
|
1002
|
-
|
|
724
|
+
const ownsLast = anchors.lastTurnId !== void 0 && ownership.get(anchors.lastTurnId) === selectedTurnId;
|
|
725
|
+
if (selectedTurnId === anchors.lastTurnId || ownsLast) {
|
|
1003
726
|
scrollbox.scrollTop = scrollbox.scrollHeight;
|
|
1004
727
|
return;
|
|
1005
728
|
}
|
|
@@ -1007,7 +730,11 @@ function Transcript({ events, settings, selectedTurnId = null }) {
|
|
|
1007
730
|
if (id) scrollbox.scrollChildIntoView(id);
|
|
1008
731
|
});
|
|
1009
732
|
return () => cancelAnimationFrame(handle);
|
|
1010
|
-
}, [
|
|
733
|
+
}, [
|
|
734
|
+
selectedTurnId,
|
|
735
|
+
anchors,
|
|
736
|
+
ownership
|
|
737
|
+
]);
|
|
1011
738
|
if (items.length === 0) return /* @__PURE__ */ jsx(EmptyState, {});
|
|
1012
739
|
return /* @__PURE__ */ jsx("scrollbox", {
|
|
1013
740
|
ref: scrollboxRef,
|
|
@@ -1019,10 +746,17 @@ function Transcript({ events, settings, selectedTurnId = null }) {
|
|
|
1019
746
|
},
|
|
1020
747
|
stickyScroll: true,
|
|
1021
748
|
stickyStart: "bottom",
|
|
749
|
+
verticalScrollbarOptions: {
|
|
750
|
+
width: 1,
|
|
751
|
+
trackOptions: {
|
|
752
|
+
backgroundColor: "transparent",
|
|
753
|
+
foregroundColor: COLOR.mute
|
|
754
|
+
}
|
|
755
|
+
},
|
|
1022
756
|
children: items.map((item, i) => item.kind === "event" ? /* @__PURE__ */ jsx(EventLine, {
|
|
1023
757
|
event: item.event,
|
|
1024
758
|
previous: item.previous,
|
|
1025
|
-
selected:
|
|
759
|
+
selected: isTurnHighlighted(item.event, selectedTurnId, ownership),
|
|
1026
760
|
anchorId: anchors.ids[i][0]
|
|
1027
761
|
}, i) : /* @__PURE__ */ jsx(SubagentBlock, {
|
|
1028
762
|
events: item.events,
|
|
@@ -1068,51 +802,6 @@ function computeTurnAnchors(items) {
|
|
|
1068
802
|
};
|
|
1069
803
|
}
|
|
1070
804
|
/**
|
|
1071
|
-
* Per-event visibility — filters honor user toggles and the
|
|
1072
|
-
* `hideSubagentOutput` setting. When subagent output is hidden:
|
|
1073
|
-
* - Child-agent events are filtered down to the `spawn-start` /
|
|
1074
|
-
* `spawn-end` markers so the user still sees "🌱 working… 🌳 done".
|
|
1075
|
-
* - The parent's `tool-result` for `spawn` is hidden too. Its body
|
|
1076
|
-
* duplicates `spawn-end`'s stats line *and* the parent's next markdown
|
|
1077
|
-
* turn ("Here's what the sub-agent found: …"). Showing it again
|
|
1078
|
-
* produced an extra `┃ [sub-agent child-1] Completed …` block that
|
|
1079
|
-
* the user just wanted gone.
|
|
1080
|
-
*
|
|
1081
|
-
* Exported so the visibility matrix can be unit-tested without rendering.
|
|
1082
|
-
*/
|
|
1083
|
-
/** Tools whose `tool-result` event is suppressed when `showEditDiffs` is on. */
|
|
1084
|
-
const EDIT_TOOL_NAMES = new Set([
|
|
1085
|
-
"edit",
|
|
1086
|
-
"multi_edit",
|
|
1087
|
-
"write_file"
|
|
1088
|
-
]);
|
|
1089
|
-
function isVisible(event, settings) {
|
|
1090
|
-
if (settings.hideSubagentOutput) {
|
|
1091
|
-
if (isChild(event)) return event.kind === "spawn-start" || event.kind === "spawn-end";
|
|
1092
|
-
if (event.kind === "tool-result" && event.tool === "spawn") return false;
|
|
1093
|
-
}
|
|
1094
|
-
if (settings.showEditDiffs && event.kind === "tool-result" && event.tool && EDIT_TOOL_NAMES.has(event.tool) && !isEditErrorResult(event.text)) return false;
|
|
1095
|
-
switch (event.kind) {
|
|
1096
|
-
case "thinking": return settings.showThinking;
|
|
1097
|
-
case "tool": return settings.toolCallDisplay !== "hidden";
|
|
1098
|
-
case "tool-result": return settings.showToolResults;
|
|
1099
|
-
default: return true;
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
/**
|
|
1103
|
-
* Recognize a tool-result body as a failure. The three edit tools all
|
|
1104
|
-
* return short, deterministic error prefixes on the failure path:
|
|
1105
|
-
* - `edit` → "Edit error: …"
|
|
1106
|
-
* - `multi_edit` → "multi_edit error: …"
|
|
1107
|
-
* - `write_file` → succeeds on permission errors only via exception →
|
|
1108
|
-
* the loop wraps as "Tool failed: …" so we match that prefix too.
|
|
1109
|
-
*
|
|
1110
|
-
* Exported for unit-testability of the visibility matrix.
|
|
1111
|
-
*/
|
|
1112
|
-
function isEditErrorResult(text) {
|
|
1113
|
-
return text.startsWith("Edit error:") || text.startsWith("multi_edit error:") || text.startsWith("Tool failed:");
|
|
1114
|
-
}
|
|
1115
|
-
/**
|
|
1116
805
|
* Walk the visible-event list once and group consecutive child events
|
|
1117
806
|
* (`depth > 0`) into runs so we can wrap each run in a single bordered
|
|
1118
807
|
* subagent box.
|
|
@@ -1238,55 +927,6 @@ function rowStyle(paddingLeft) {
|
|
|
1238
927
|
alignSelf: "stretch"
|
|
1239
928
|
};
|
|
1240
929
|
}
|
|
1241
|
-
/**
|
|
1242
|
-
* Default top-margin per kind. Spacing intent:
|
|
1243
|
-
* - `info` / `markdown` / `tool` / `error` / `spawn-start` open a new block
|
|
1244
|
-
* so they each get one row of breathing room above.
|
|
1245
|
-
* - `thinking` / `tool-result` / `spawn-end` continue the previous block
|
|
1246
|
-
* and stay flush.
|
|
1247
|
-
*
|
|
1248
|
-
* Context-aware overrides live in `marginTopFor` — e.g. consecutive tool
|
|
1249
|
-
* round-trips collapse to a tight list regardless of whether outputs are shown.
|
|
1250
|
-
*/
|
|
1251
|
-
const MARGIN_TOP = {
|
|
1252
|
-
"separator": 0,
|
|
1253
|
-
"user-prompt": 1,
|
|
1254
|
-
"info": 1,
|
|
1255
|
-
"thinking": 0,
|
|
1256
|
-
"tool": 1,
|
|
1257
|
-
"tool-result": 0,
|
|
1258
|
-
"error": 1,
|
|
1259
|
-
"markdown": 1,
|
|
1260
|
-
"spawn-start": 1,
|
|
1261
|
-
"spawn-end": 0,
|
|
1262
|
-
"compact-summary": 1
|
|
1263
|
-
};
|
|
1264
|
-
const TOOL_KINDS = new Set(["tool", "tool-result"]);
|
|
1265
|
-
/**
|
|
1266
|
-
* Resolve the top margin for an event given the one rendered just before it.
|
|
1267
|
-
*
|
|
1268
|
-
* Context-aware rules:
|
|
1269
|
-
*
|
|
1270
|
-
* - A `tool` / `tool-result` event right after another `tool` / `tool-result`
|
|
1271
|
-
* collapses to a tight list — call→result pairs and back-to-back calls
|
|
1272
|
-
* read as one logical block.
|
|
1273
|
-
* - A parent-level event (`depth === 0`) right after a subagent event
|
|
1274
|
-
* (`depth > 0`) collapses too. The subagent's `🌳` end marker (and, in
|
|
1275
|
-
* show mode, the subagent box's bottom border) already provides the
|
|
1276
|
-
* separation; adding the event's default `marginTop` on top would
|
|
1277
|
-
* produce the visible "line jump" between a subagent's outcome and the
|
|
1278
|
-
* parent's follow-up. Either form of marker is enough — we don't want
|
|
1279
|
-
* both.
|
|
1280
|
-
*
|
|
1281
|
-
* Exported so the spacing matrix can be unit-tested without rendering.
|
|
1282
|
-
*/
|
|
1283
|
-
function marginTopFor(event, previous) {
|
|
1284
|
-
if (TOOL_KINDS.has(event.kind) && previous && TOOL_KINDS.has(previous.kind)) return 0;
|
|
1285
|
-
const eventDepth = event.depth ?? 0;
|
|
1286
|
-
const previousDepth = previous?.depth ?? 0;
|
|
1287
|
-
if (eventDepth === 0 && previousDepth > 0) return 0;
|
|
1288
|
-
return MARGIN_TOP[event.kind] ?? 0;
|
|
1289
|
-
}
|
|
1290
930
|
function EventLineImpl({ event, depthOffset = 0 }) {
|
|
1291
931
|
const COLOR = useColors();
|
|
1292
932
|
const { settings } = useSettings();
|
|
@@ -6487,6 +6127,11 @@ function AppShell() {
|
|
|
6487
6127
|
useEffect(() => {
|
|
6488
6128
|
safeModeEnabledRef.current = settings.safeMode;
|
|
6489
6129
|
}, [settings.safeMode]);
|
|
6130
|
+
useEffect(() => {
|
|
6131
|
+
const fps = clampFps(settings.targetFps);
|
|
6132
|
+
if (renderer.targetFps !== fps) renderer.targetFps = fps;
|
|
6133
|
+
if (renderer.maxFps !== fps) renderer.maxFps = fps;
|
|
6134
|
+
}, [renderer, settings.targetFps]);
|
|
6490
6135
|
const persistToolResultsRef = useRef(settings.persistToolResults);
|
|
6491
6136
|
useEffect(() => {
|
|
6492
6137
|
persistToolResultsRef.current = settings.persistToolResults;
|
|
@@ -7586,7 +7231,7 @@ function AppShell() {
|
|
|
7586
7231
|
})();
|
|
7587
7232
|
}, [modal, config.paths.userDir]);
|
|
7588
7233
|
const hasMultipleAgents = useMemo(() => Object.keys(agentRegistry).length > 1, [agentRegistry]);
|
|
7589
|
-
const turnIds = useMemo(() => selectableTurnIds(events), [events]);
|
|
7234
|
+
const turnIds = useMemo(() => selectableTurnIds(events, settings), [events, settings]);
|
|
7590
7235
|
/** Drop the selection if its turn disappeared (session swap, history reset). */
|
|
7591
7236
|
useEffect(() => {
|
|
7592
7237
|
if (selectedTurnId && !turnIds.includes(selectedTurnId)) setSelectedTurnId(null);
|
|
@@ -8992,6 +8637,13 @@ let runTuiInvoked = false;
|
|
|
8992
8637
|
* Env-var overrides (handy when launching from CI or restricted shells):
|
|
8993
8638
|
* - `ZIDANE_STORAGE_DIR` — sets `storageDir`
|
|
8994
8639
|
* - `ZIDANE_PREFIX` — sets `prefix`
|
|
8640
|
+
* - `ZIDANE_DEBUG` — enables `gatherStats` on the renderer and dumps the
|
|
8641
|
+
* final fps / frametime distribution to stderr on exit. Useful when
|
|
8642
|
+
* tuning the renderer fps setting or comparing terminal emulators.
|
|
8643
|
+
*
|
|
8644
|
+
* Renderer fps lives in Settings (`targetFps`, cycle `30 / 60 / 120`);
|
|
8645
|
+
* the on-disk value seeds the renderer at boot and `AppShell` mirrors
|
|
8646
|
+
* subsequent flips onto the live renderer.
|
|
8995
8647
|
*
|
|
8996
8648
|
* Hosts building on top of `zidane/tui` typically want their own env vars
|
|
8997
8649
|
* (e.g. `MYAPP_STORAGE_DIR`); read them in your launch script and forward
|
|
@@ -9025,15 +8677,27 @@ async function runTui(options = {}) {
|
|
|
9025
8677
|
const exited = new Promise((resolve) => {
|
|
9026
8678
|
done = resolve;
|
|
9027
8679
|
});
|
|
9028
|
-
|
|
8680
|
+
const bootFps = clampFps(config.initialSettings.targetFps ?? DEFAULT_SETTINGS.targetFps);
|
|
8681
|
+
const gatherStats = !!process.env.ZIDANE_DEBUG;
|
|
8682
|
+
const renderer = await createCliRenderer({
|
|
9029
8683
|
exitOnCtrlC: true,
|
|
9030
8684
|
onDestroy: () => done(),
|
|
9031
|
-
debounceDelay: 0
|
|
9032
|
-
|
|
8685
|
+
debounceDelay: 0,
|
|
8686
|
+
targetFps: bootFps,
|
|
8687
|
+
maxFps: bootFps,
|
|
8688
|
+
gatherStats
|
|
8689
|
+
});
|
|
8690
|
+
createRoot(renderer).render(/* @__PURE__ */ jsx(App, { config }));
|
|
9033
8691
|
await exited;
|
|
8692
|
+
if (gatherStats) try {
|
|
8693
|
+
const s = renderer.getStats();
|
|
8694
|
+
process.stderr.write(`[zidane/tui] render stats: fps=${s.fps.toFixed(1)} avgFrame=${s.averageFrameTime.toFixed(2)}ms min=${s.minFrameTime.toFixed(2)}ms max=${s.maxFrameTime.toFixed(2)}ms frames=${s.frameCount}\n`);
|
|
8695
|
+
} catch (err) {
|
|
8696
|
+
process.stderr.write(`[zidane/tui] render stats unavailable: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
8697
|
+
}
|
|
9034
8698
|
process.exit(0);
|
|
9035
8699
|
}
|
|
9036
8700
|
//#endregion
|
|
9037
|
-
export { AgentPickerModal, App, AuthScreen, ChatScreen, CompletionPopup, EffortPickerModal, Footer, InteractionBlock, McpsSettingsModal, Modal, ModalRoot, ModelPickerModal, SessionDetailsModal, SessionsScreen, SettingsModal, SkillsSettingsModal, Spinner, StatusSpinner, TitleOverlay, ToggleListModal, Transcript, TurnDetailsModal, accentColor, buildMdStyle, hintsLength, isVisible, marginTopFor, onInputSubmit, renderHintSpans, runTui, splitPromptSegments, useMdStyle, useModal, useModalAwareFocus };
|
|
8701
|
+
export { AgentPickerModal, App, AuthScreen, ChatScreen, CompletionPopup, EffortPickerModal, Footer, InteractionBlock, McpsSettingsModal, Modal, ModalRoot, ModelPickerModal, SessionDetailsModal, SessionsScreen, SettingsModal, SkillsSettingsModal, Spinner, StatusSpinner, TOOL_DISPLAY, TitleOverlay, ToggleListModal, Transcript, TurnDetailsModal, accentColor, buildMdStyle, displayNameFor, formatToolCall, hintsLength, isEditErrorResult, isTurnHighlighted, isVisible, marginTopFor, onInputSubmit, renderHintSpans, runTui, selectableTurnIds, splitPromptSegments, turnSelectionOwnership, useMdStyle, useModal, useModalAwareFocus };
|
|
9038
8702
|
|
|
9039
8703
|
//# sourceMappingURL=tui.js.map
|