@deepseek-harness-tui/dsh-tui 0.7.0 → 0.7.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/README.md +9 -6
- package/bin/dsh-tui.js +67 -14
- package/cordis.patch.yml +10 -0
- package/lib/types/commands.js +1 -1
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +1 -1
- package/lib/types/components/design-system/ThemedText.d.ts +2 -2
- package/lib/types/components/design-system/ThemedText.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedText.js +1 -3
- package/lib/types/components/sessions/SessionListRow.d.ts +26 -0
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionListRow.js +35 -0
- package/lib/types/components/sessions/SessionPreview.d.ts +25 -0
- package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionPreview.js +60 -0
- package/lib/types/dsh-adapter/channel.d.ts +23 -4
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +190 -75
- package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/index.js +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +8 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.js +1 -1
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +10 -1
- package/lib/types/dsh-adapter/packaged-presets.d.ts +22 -0
- package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/packaged-presets.js +90 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +60 -4
- package/lib/types/dsh-adapter/presets.d.ts +16 -0
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +23 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts +29 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/digest.js +224 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts +100 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/frames.js +246 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts +50 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/header.js +62 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts +17 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/index.js +15 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts +41 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/list.js +213 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts +48 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/store.js +164 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts +142 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/types.js +14 -0
- package/lib/types/i18n.d.ts +122 -14
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +37 -6
- package/lib/types/screens/Chat.d.ts +3 -2
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +28 -144
- package/lib/types/screens/SessionBrowser.d.ts +37 -0
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -0
- package/lib/types/screens/SessionBrowser.js +437 -0
- package/lib/types/sessionHistory.d.ts +0 -8
- package/lib/types/sessionHistory.d.ts.map +1 -1
- package/lib/types/sessions/format.d.ts +117 -0
- package/lib/types/sessions/format.d.ts.map +1 -0
- package/lib/types/sessions/format.js +244 -0
- package/lib/types/sessions/view.d.ts +125 -0
- package/lib/types/sessions/view.d.ts.map +1 -0
- package/lib/types/sessions/view.js +222 -0
- package/package.json +12 -5
- package/presets/liangshen/.dsh-tui-managed.json +5 -0
- package/presets/liangshen/agent.cordis.yml +389 -0
- package/presets/liangshen/compaction-epoch.mjs +81 -0
- package/presets/liangshen/custom-bash.mjs +126 -0
- package/presets/liangshen/instruction-hint.mjs +181 -0
- package/presets/liangshen/preset.yml +3 -0
- package/presets/liangshen/skill-search.mjs +142 -0
- package/presets/liangshen/tool-bootstrap.mjs +300 -0
- package/lib/invariant.js +0 -28
- package/lib/types/components/ResumePicker.d.ts +0 -26
- package/lib/types/components/ResumePicker.d.ts.map +0 -1
- package/lib/types/components/ResumePicker.js +0 -50
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type
|
|
2
|
+
import { type Channel } from '../dsh-adapter/channel.js';
|
|
3
3
|
import type { QuestionStore } from '../dsh-adapter/questions.js';
|
|
4
4
|
import { ApprovalStore } from '../dsh-adapter/approvals.js';
|
|
5
5
|
export declare function Chat({ channel, questionStore, approvalStore, onExit, onUpdate, fullscreen, trajectorySeen: trajectorySeenProp, }: {
|
|
@@ -16,7 +16,8 @@ export declare function Chat({ channel, questionStore, approvalStore, onExit, on
|
|
|
16
16
|
onUpdate?: () => void;
|
|
17
17
|
/**
|
|
18
18
|
* True when the host already wrapped this tree in `<AlternateScreen>`
|
|
19
|
-
* (`fullscreen: true`).
|
|
19
|
+
* (`fullscreen: true`). Both full-screen surfaces need this — the trajectory
|
|
20
|
+
* scene and the session browser: entering the alt
|
|
20
21
|
* screen a second time is harmless, but the inner unmount's DEC 1049 exit
|
|
21
22
|
* would drop the whole app back to the main screen.
|
|
22
23
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,OAAO,EAAqB,KAAK,OAAO,EAAsD,MAAM,2BAA2B,CAAA;AAC/H,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAEhE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AA0H3D,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,aAAa,EACb,aAAa,EACb,MAAM,EACN,QAAQ,EACR,UAAkB,EAClB,cAAc,EAAE,kBAAkB,GACnC,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,aAAa,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,qBAgzDA"}
|
|
@@ -5,6 +5,8 @@ import { AlternateScreen, Box, Text, useInput, ScrollBox, useTheme, useTerminalS
|
|
|
5
5
|
import { POINTER } from '../cc/figures.js';
|
|
6
6
|
import { isMod, isPlainReturnInput, modLabel } from '../utils/modifiers.js';
|
|
7
7
|
import { formatTokens } from '../cc/format.js';
|
|
8
|
+
import { homeDir } from '../utils/paths.js';
|
|
9
|
+
import { sessionCwdMatches } from '../dsh-adapter/channel.js';
|
|
8
10
|
import { runProviderWizard } from '../dsh-adapter/providerWizard.js';
|
|
9
11
|
import { ApprovalStore } from '../dsh-adapter/approvals.js';
|
|
10
12
|
import { AskUserQuestionPanel } from '../components/questions/AskUserQuestionPanel.js';
|
|
@@ -24,7 +26,7 @@ import { StatusLine } from './StatusLine.js';
|
|
|
24
26
|
import { WorkingSpinner, useThinkingStatus } from '../components/WorkingSpinner.js';
|
|
25
27
|
import { ActivityLine, contextPressurePct } from '../components/ActivityLine.js';
|
|
26
28
|
import { ModelPicker } from '../components/ModelPicker.js';
|
|
27
|
-
import {
|
|
29
|
+
import { SessionBrowser } from './SessionBrowser.js';
|
|
28
30
|
import { WorkspacePicker } from '../components/WorkspacePicker.js';
|
|
29
31
|
import { WorkspaceFlowPicker } from '../components/WorkspaceFlowPicker.js';
|
|
30
32
|
import { ActivityPicker } from '../components/ActivityPicker.js';
|
|
@@ -150,12 +152,9 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
150
152
|
const [modelPickerOpen, setModelPickerOpen] = React.useState(false);
|
|
151
153
|
const [models, setModels] = React.useState([]);
|
|
152
154
|
const [modelIndex, setModelIndex] = React.useState(0);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const [
|
|
156
|
-
/** `/resume` session management (issue #112): plain selection, a delete
|
|
157
|
-
* confirmation (ctrl+d), or the inline rename input (ctrl+r). */
|
|
158
|
-
const [resumeMode, setResumeMode] = React.useState('list');
|
|
155
|
+
/** `/resume` opens the session browser, a screen rather than a panel. It
|
|
156
|
+
* owns its own selection, filters and keyboard — Chat only opens it. */
|
|
157
|
+
const [browserOpen, setBrowserOpen] = React.useState(false);
|
|
159
158
|
const [workspacePickerOpen, setWorkspacePickerOpen] = React.useState(false);
|
|
160
159
|
const [workspaceTargets, setWorkspaceTargets] = React.useState([]);
|
|
161
160
|
const [workspaceIndex, setWorkspaceIndex] = React.useState(0);
|
|
@@ -165,7 +164,6 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
165
164
|
const [workspaceFlowInput, setWorkspaceFlowInput] = React.useState(null);
|
|
166
165
|
const workspaceFlowRequestRef = React.useRef(0);
|
|
167
166
|
const workspaceFlowAbortRef = React.useRef(null);
|
|
168
|
-
const [resumeRenameText, setResumeRenameText] = React.useState('');
|
|
169
167
|
/** `/activity` indicator picker (pi extension's interactive select). */
|
|
170
168
|
const [activityPickerOpen, setActivityPickerOpen] = React.useState(false);
|
|
171
169
|
const [activityIndex, setActivityIndex] = React.useState(0);
|
|
@@ -663,22 +661,10 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
663
661
|
}
|
|
664
662
|
case 'resume': {
|
|
665
663
|
setHelpOpen(false);
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
// otherwise the fresh empty session of this launch always tops the
|
|
671
|
-
// list as an unopenable row.
|
|
672
|
-
const pickable = sessions.filter(session => session.id !== channel.agentId);
|
|
673
|
-
setResumeSessions(pickable);
|
|
674
|
-
if (pickable.length === 0) {
|
|
675
|
-
channel.notify(t('resume-none-in-cwd'));
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
setResumeMode('list');
|
|
679
|
-
setResumePickerOpen(true);
|
|
680
|
-
setResumeIndex(0);
|
|
681
|
-
})();
|
|
664
|
+
// The browser opens immediately and loads its own list. Waiting for
|
|
665
|
+
// the listing here would make `/resume` feel slower the more history
|
|
666
|
+
// a project has, which is exactly backwards.
|
|
667
|
+
setBrowserOpen(true);
|
|
682
668
|
return true;
|
|
683
669
|
}
|
|
684
670
|
case 'workspace': {
|
|
@@ -1182,6 +1168,11 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
1182
1168
|
// not block the event from reaching the panel.
|
|
1183
1169
|
if (btw !== null)
|
|
1184
1170
|
return;
|
|
1171
|
+
// Same for the session browser: it renders instead of the conversation,
|
|
1172
|
+
// so every key belongs to it — including the plain letters that drive its
|
|
1173
|
+
// search box, which Chat would otherwise route into the prompt.
|
|
1174
|
+
if (browserOpen)
|
|
1175
|
+
return;
|
|
1185
1176
|
// The questionnaire / approval panel owns the keyboard while one is
|
|
1186
1177
|
// pending (the panel's own useInput handles ↑/↓/Space/Tab/Enter/Esc;
|
|
1187
1178
|
// the prompt input is unmounted, so nothing else should see these keys).
|
|
@@ -1407,123 +1398,6 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
1407
1398
|
}
|
|
1408
1399
|
return;
|
|
1409
1400
|
}
|
|
1410
|
-
if (resumePickerOpen) {
|
|
1411
|
-
const resumeSession = resumeSessions[resumeIndex];
|
|
1412
|
-
// Session management modes (issue #112): the list keys stay untouched
|
|
1413
|
-
// until ctrl+d/ctrl+r switch into a sub-mode, each with Enter/Esc.
|
|
1414
|
-
if (resumeMode === 'confirm-delete') {
|
|
1415
|
-
if (plainReturn) {
|
|
1416
|
-
setResumeMode('list');
|
|
1417
|
-
// oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime guard: out-of-range index on an empty list
|
|
1418
|
-
if (resumeSession) {
|
|
1419
|
-
const target = resumeSession;
|
|
1420
|
-
void (async () => {
|
|
1421
|
-
const ok = await channel.deleteSession(target.id);
|
|
1422
|
-
if (!ok) {
|
|
1423
|
-
channel.notify(t('resume-delete-failed', { name: target.title || target.id }), { color: 'error' });
|
|
1424
|
-
return;
|
|
1425
|
-
}
|
|
1426
|
-
channel.notify(t('resume-deleted', { name: target.title || target.id }));
|
|
1427
|
-
// Refresh right away so the row disappears in place; closing
|
|
1428
|
-
// the picker when nothing resumable remains.
|
|
1429
|
-
const sessions = await channel.listSessions();
|
|
1430
|
-
const pickable = sessions.filter(session => session.id !== channel.agentId);
|
|
1431
|
-
setResumeSessions(pickable);
|
|
1432
|
-
if (pickable.length === 0) {
|
|
1433
|
-
setResumePickerOpen(false);
|
|
1434
|
-
}
|
|
1435
|
-
else {
|
|
1436
|
-
setResumeIndex(index => Math.min(index, pickable.length - 1));
|
|
1437
|
-
}
|
|
1438
|
-
})();
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
else if (key.escape) {
|
|
1442
|
-
setResumeMode('list');
|
|
1443
|
-
}
|
|
1444
|
-
return;
|
|
1445
|
-
}
|
|
1446
|
-
if (resumeMode === 'rename') {
|
|
1447
|
-
if (plainReturn) {
|
|
1448
|
-
setResumeMode('list');
|
|
1449
|
-
const title = resumeRenameText.trim();
|
|
1450
|
-
// oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime guard: out-of-range index on an empty list
|
|
1451
|
-
if (resumeSession && title.length > 0) {
|
|
1452
|
-
const target = resumeSession;
|
|
1453
|
-
void (async () => {
|
|
1454
|
-
const ok = await channel.renameSessionTo(target.id, title);
|
|
1455
|
-
if (!ok) {
|
|
1456
|
-
channel.notify(t('resume-rename-failed', { name: target.title || target.id }), { color: 'error' });
|
|
1457
|
-
return;
|
|
1458
|
-
}
|
|
1459
|
-
channel.notify(t('rename-done', { title }));
|
|
1460
|
-
// Re-list so the row reflects the persisted state, but patch
|
|
1461
|
-
// the renamed row's title explicitly: listSessions resolves
|
|
1462
|
-
// persisted titles only within the MRU top SESSION_TITLE_DEPTH
|
|
1463
|
-
// window, and a freshly renamed row must never snap back to
|
|
1464
|
-
// the basename fallback in between.
|
|
1465
|
-
const sessions = await channel.listSessions();
|
|
1466
|
-
const next = sessions
|
|
1467
|
-
.filter(session => session.id !== channel.agentId)
|
|
1468
|
-
.map(session => (session.id === target.id ? { ...session, title } : session));
|
|
1469
|
-
setResumeSessions(next);
|
|
1470
|
-
// Re-anchor focus on the renamed row: renameSessionTo touches
|
|
1471
|
-
// MRU, so the re-listed order shifts — a kept index would
|
|
1472
|
-
// silently point at a DIFFERENT session, and a following
|
|
1473
|
-
// Enter/ctrl+d would act on the wrong one (review leftover).
|
|
1474
|
-
const anchored = next.findIndex(session => session.id === target.id);
|
|
1475
|
-
if (anchored >= 0)
|
|
1476
|
-
setResumeIndex(anchored);
|
|
1477
|
-
})();
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
else if (key.escape) {
|
|
1481
|
-
setResumeMode('list');
|
|
1482
|
-
}
|
|
1483
|
-
else if (key.backspace) {
|
|
1484
|
-
setResumeRenameText(text => text.slice(0, -1));
|
|
1485
|
-
}
|
|
1486
|
-
else if (!key.ctrl && !key.meta && !key.super && input) {
|
|
1487
|
-
// Single-line title: pasted newlines collapse to spaces.
|
|
1488
|
-
setResumeRenameText(text => text + input.replace(/[\r\n]+/g, ' '));
|
|
1489
|
-
}
|
|
1490
|
-
return;
|
|
1491
|
-
}
|
|
1492
|
-
if (key.upArrow) {
|
|
1493
|
-
setResumeIndex(index => (index <= 0 ? resumeSessions.length - 1 : index - 1));
|
|
1494
|
-
}
|
|
1495
|
-
else if (key.downArrow) {
|
|
1496
|
-
setResumeIndex(index => (index >= resumeSessions.length - 1 ? 0 : index + 1));
|
|
1497
|
-
}
|
|
1498
|
-
else if (plainReturn) {
|
|
1499
|
-
// oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime guard: out-of-range index on an empty list
|
|
1500
|
-
if (resumeSession) {
|
|
1501
|
-
// Enter switches the live agent to the persisted session right
|
|
1502
|
-
// away (the history replays into the transcript); the resume.txt
|
|
1503
|
-
// launcher marker is refreshed by resumeTo so `--resume` on the
|
|
1504
|
-
// next launch opens the same session.
|
|
1505
|
-
setResumePickerOpen(false);
|
|
1506
|
-
void channel.resumeTo(resumeSession.id).then((ok) => {
|
|
1507
|
-
if (ok)
|
|
1508
|
-
channel.notify(t('resume-resumed'));
|
|
1509
|
-
});
|
|
1510
|
-
}
|
|
1511
|
-
else {
|
|
1512
|
-
setResumePickerOpen(false);
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
else if (key.escape) {
|
|
1516
|
-
setResumePickerOpen(false);
|
|
1517
|
-
}
|
|
1518
|
-
else if (isMod(key) && input === 'd' && resumeSession) {
|
|
1519
|
-
setResumeMode('confirm-delete');
|
|
1520
|
-
}
|
|
1521
|
-
else if (isMod(key) && input === 'r' && resumeSession) {
|
|
1522
|
-
setResumeRenameText(resumeSession.title || '');
|
|
1523
|
-
setResumeMode('rename');
|
|
1524
|
-
}
|
|
1525
|
-
return;
|
|
1526
|
-
}
|
|
1527
1401
|
if (modelPickerOpen) {
|
|
1528
1402
|
if (key.upArrow) {
|
|
1529
1403
|
setModelIndex(index => (index <= 0 ? models.length - 1 : index - 1));
|
|
@@ -1815,8 +1689,18 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
1815
1689
|
// Working-activity line (spinner slot): context-pressure prefix shares the
|
|
1816
1690
|
// StatusLine thresholds (amber ≥ 80, red ≥ 95).
|
|
1817
1691
|
const activityWarnPct = contextPressurePct(channel.lastUsage, channel.contextWindow);
|
|
1692
|
+
// The browser is a screen, not an overlay: it REPLACES the conversation
|
|
1693
|
+
// rather than floating above it. Rendering it as an early return (after
|
|
1694
|
+
// every hook above has run) is what makes that literal — there is no
|
|
1695
|
+
// transcript underneath to be repainted, scrolled, or bled through.
|
|
1696
|
+
if (browserOpen) {
|
|
1697
|
+
const browser = (_jsx(SessionBrowser, { channel: channel, home: homeDir(), sameProject: sessionCwdMatches, onClose: () => setBrowserOpen(false) }));
|
|
1698
|
+
// Inline hosts enter the alternate screen for the duration; full-screen
|
|
1699
|
+
// hosts are already in it and must not nest a second one.
|
|
1700
|
+
return fullscreen ? browser : _jsx(AlternateScreen, { children: browser });
|
|
1701
|
+
}
|
|
1818
1702
|
/** Prompt input is inert while a modal dialog owns the keyboard. */
|
|
1819
|
-
const promptSelectionActive = selectionActive || modelPickerOpen ||
|
|
1703
|
+
const promptSelectionActive = selectionActive || modelPickerOpen || workspacePickerOpen || workspaceFlow !== null || activityPickerOpen ||
|
|
1820
1704
|
effortSliderOpen || presetPickerOpen || themePickerOpen || thinkingOpen || historyOpen || rewindOpen || searchOpen ||
|
|
1821
1705
|
btw !== null;
|
|
1822
1706
|
// The trajectory scene replaces the conversation for as long as it is open.
|
|
@@ -1837,7 +1721,7 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
1837
1721
|
// 常驻、只移除其普通子节点,blit 解毒不触发,被覆盖的转录行会在
|
|
1838
1722
|
// blit-skip 后留空(Esc 关 picker 一片空白的根因)。
|
|
1839
1723
|
const dialogOverlayOpen = thinkingOpen || (workspacePickerOpen && workspaceTargets.length > 0) || workspaceFlow !== null ||
|
|
1840
|
-
|
|
1724
|
+
modelPickerOpen ||
|
|
1841
1725
|
activityPickerOpen || (effortSliderOpen && effortOptions.length > 1) ||
|
|
1842
1726
|
(presetPickerOpen && presetOptions.length > 0) || themePickerOpen || historyOpen ||
|
|
1843
1727
|
rewindOpen || searchOpen;
|
|
@@ -1862,7 +1746,7 @@ export function Chat({ channel, questionStore, approvalStore, onExit, onUpdate,
|
|
|
1862
1746
|
band: wakeBand,
|
|
1863
1747
|
hint: trajectorySeen ? undefined : `${modLabel}t`,
|
|
1864
1748
|
tick: Math.floor(wakeTime / 120),
|
|
1865
|
-
} }), dialogOverlayOpen && (_jsxs(OverlayAbove, { maxHeight: Math.max(terminalRows - 8, 8), children: [thinkingOpen && (_jsx(ThinkingToggle, { currentValue: thinkingVisible, focusIndex: thinkingFocus, confirmationPending: thinkingConfirm })), workspacePickerOpen && workspaceTargets.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(WorkspacePicker, { targets: workspaceTargets, focusIndex: workspaceIndex, currentCwd: channel.cwd }) })), workspaceFlow !== null && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(WorkspaceFlowPicker, { title: workspaceFlow.title, choices: workspaceFlow.choices, focusIndex: workspaceFlowIndex, busy: workspaceFlowBusy, input: workspaceFlowInput }) })),
|
|
1749
|
+
} }), dialogOverlayOpen && (_jsxs(OverlayAbove, { maxHeight: Math.max(terminalRows - 8, 8), children: [thinkingOpen && (_jsx(ThinkingToggle, { currentValue: thinkingVisible, focusIndex: thinkingFocus, confirmationPending: thinkingConfirm })), workspacePickerOpen && workspaceTargets.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(WorkspacePicker, { targets: workspaceTargets, focusIndex: workspaceIndex, currentCwd: channel.cwd }) })), workspaceFlow !== null && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(WorkspaceFlowPicker, { title: workspaceFlow.title, choices: workspaceFlow.choices, focusIndex: workspaceFlowIndex, busy: workspaceFlowBusy, input: workspaceFlowInput }) })), modelPickerOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: models.length === 0 ? (_jsx(ModelPickerLoading, {})) : (_jsx(ModelPicker, { models: models, focusIndex: modelIndex, currentModel: `${channel.provider}/${channel.model}` })) })), activityPickerOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(ActivityPicker, { focusIndex: activityIndex, currentPreset: channel.activityFrames }) })), effortSliderOpen && effortOptions.length > 1 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(EffortSlider, { options: effortOptions, focusIndex: effortIndex, currentId: channel.reasoningEffort }) })), presetPickerOpen && presetOptions.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(PresetPicker, { presets: presetOptions, focusIndex: presetIndex, currentPreset: channel.agentPreset }) })), themePickerOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(ThemePicker, { focusIndex: themeIndex, currentTheme: themeName }) })), historyOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(HistorySearchDialog, { query: historyQuery, cursorOffset: historyCursor, matches: historyMatches, focusIndex: historyFocus }) })), rewindOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(RewindPicker, { rows: rewindRows, focusIndex: rewindIndex, confirmRow: rewindConfirm }) })), searchOpen && _jsx(TranscriptSearchBar, { query: searchQuery, cursorOffset: searchCursor, count: searchCount, current: searchCurrent })] }))] })] }));
|
|
1866
1750
|
}
|
|
1867
1751
|
/**
|
|
1868
1752
|
* The pinned prompt header shown above the ScrollBox while the user has
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Channel } from '../dsh-adapter/channel.js';
|
|
3
|
+
/**
|
|
4
|
+
* The session browser — `/resume` as a screen of its own.
|
|
5
|
+
*
|
|
6
|
+
* The old picker was a panel of eight titles and a timestamp, and the reason
|
|
7
|
+
* it could not be more than that was never layout: the data behind it was five
|
|
8
|
+
* fields wide. With every session now arriving classified, sized, dated and
|
|
9
|
+
* attributed, the surface that shows them can do the job a person actually
|
|
10
|
+
* came for — find one conversation among many.
|
|
11
|
+
*
|
|
12
|
+
* What that means concretely:
|
|
13
|
+
*
|
|
14
|
+
* - Search is always live. There is no mode to enter; typing filters, because
|
|
15
|
+
* the list is the search results.
|
|
16
|
+
* - Delegated sub-agent runs are folded away by default and revealed under
|
|
17
|
+
* their parents on demand. They are not noise to be deleted — they are the
|
|
18
|
+
* model's own work, and it is worth being able to open one — but they are
|
|
19
|
+
* not what "resume a conversation" means, and there are five of them for
|
|
20
|
+
* every conversation.
|
|
21
|
+
* - Sessions that hold no conversation are never listed, only counted, with
|
|
22
|
+
* one action to clear them.
|
|
23
|
+
* - The preview shows the end of a session, so "is this the one I was in the
|
|
24
|
+
* middle of" is answerable without resuming it.
|
|
25
|
+
*
|
|
26
|
+
* Every one of those reads bounded data, so the screen behaves the same on a
|
|
27
|
+
* fifty-session history as on a five-session one.
|
|
28
|
+
*/
|
|
29
|
+
export declare function SessionBrowser({ channel, home, sameProject, onClose, }: {
|
|
30
|
+
channel: Channel;
|
|
31
|
+
/** Home directory, for collapsing project paths to `~`. */
|
|
32
|
+
home: string;
|
|
33
|
+
/** Whether a stored cwd belongs to the same project as the live session. */
|
|
34
|
+
sameProject: (a: string, b: string) => boolean;
|
|
35
|
+
onClose: () => void;
|
|
36
|
+
}): React.ReactNode;
|
|
37
|
+
//# sourceMappingURL=SessionBrowser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionBrowser.d.ts","sourceRoot":"","sources":["../../../src/screens/SessionBrowser.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAsBzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAoDxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,WAAW,EACX,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAA;IACZ,4EAA4E;IAC5E,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;IAC9C,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,GAAG,KAAK,CAAC,SAAS,CAydlB"}
|