@a5c-ai/agent-mux-tui 0.4.1 → 0.4.10-staging.89ec25ae
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 +69 -9
- package/dist/app.d.ts +21 -3
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +249 -77
- package/dist/app.js.map +1 -1
- package/dist/bin/amux-tui.d.ts +0 -1
- package/dist/bin/amux-tui.js +68 -6
- package/dist/bin/amux-tui.js.map +1 -1
- package/dist/bin/runtime.d.ts +31 -0
- package/dist/bin/runtime.d.ts.map +1 -0
- package/dist/bin/runtime.js +78 -0
- package/dist/bin/runtime.js.map +1 -0
- package/dist/command-palette.d.ts +3 -2
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +14 -7
- package/dist/command-palette.js.map +1 -1
- package/dist/event-stream.d.ts +0 -1
- package/dist/external-plugins.d.ts +0 -1
- package/dist/external-plugins.d.ts.map +1 -1
- package/dist/external-plugins.js +5 -1
- package/dist/external-plugins.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/kanban-control-plane.d.ts +44 -0
- package/dist/kanban-control-plane.d.ts.map +1 -0
- package/dist/kanban-control-plane.js +30 -0
- package/dist/kanban-control-plane.js.map +1 -0
- package/dist/layout.d.ts +24 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +64 -0
- package/dist/layout.js.map +1 -0
- package/dist/model-picker.d.ts +4 -2
- package/dist/model-picker.d.ts.map +1 -1
- package/dist/model-picker.js +9 -5
- package/dist/model-picker.js.map +1 -1
- package/dist/plugin.d.ts +26 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/adapters-view.d.ts +0 -1
- package/dist/plugins/agents-view.d.ts +0 -1
- package/dist/plugins/agents-view.d.ts.map +1 -1
- package/dist/plugins/agents-view.js +59 -15
- package/dist/plugins/agents-view.js.map +1 -1
- package/dist/plugins/approval.d.ts +0 -1
- package/dist/plugins/auth-view.d.ts +0 -1
- package/dist/plugins/chat-view.d.ts +0 -1
- package/dist/plugins/config-view.d.ts +0 -1
- package/dist/plugins/control.d.ts +0 -1
- package/dist/plugins/cost-alerts.d.ts +0 -1
- package/dist/plugins/cost-view.d.ts +0 -1
- package/dist/plugins/cost.d.ts +0 -1
- package/dist/plugins/diff.d.ts +0 -1
- package/dist/plugins/diff.js.map +1 -1
- package/dist/plugins/doctor-view.d.ts +0 -1
- package/dist/plugins/fallback.d.ts +0 -1
- package/dist/plugins/file-ops.d.ts +0 -1
- package/dist/plugins/help-view.d.ts +0 -1
- package/dist/plugins/help-view.d.ts.map +1 -1
- package/dist/plugins/help-view.js +5 -3
- package/dist/plugins/help-view.js.map +1 -1
- package/dist/plugins/hooks-view.d.ts +0 -1
- package/dist/plugins/hooks-view.js.map +1 -1
- package/dist/plugins/image.d.ts +0 -1
- package/dist/plugins/kanban-view.d.ts +4 -0
- package/dist/plugins/kanban-view.d.ts.map +1 -0
- package/dist/plugins/kanban-view.js +480 -0
- package/dist/plugins/kanban-view.js.map +1 -0
- package/dist/plugins/lifecycle.d.ts +0 -1
- package/dist/plugins/mcp-view.d.ts +0 -1
- package/dist/plugins/mcp-view.d.ts.map +1 -1
- package/dist/plugins/mcp-view.js +10 -7
- package/dist/plugins/mcp-view.js.map +1 -1
- package/dist/plugins/mcp.d.ts +0 -1
- package/dist/plugins/models-view.d.ts +0 -1
- package/dist/plugins/models-view.d.ts.map +1 -1
- package/dist/plugins/models-view.js +10 -4
- package/dist/plugins/models-view.js.map +1 -1
- package/dist/plugins/observability-view.d.ts +20 -1
- package/dist/plugins/observability-view.d.ts.map +1 -1
- package/dist/plugins/observability-view.js +62 -48
- package/dist/plugins/observability-view.js.map +1 -1
- package/dist/plugins/plugin-skill.d.ts +0 -1
- package/dist/plugins/plugins-view.d.ts +0 -1
- package/dist/plugins/plugins-view.d.ts.map +1 -1
- package/dist/plugins/plugins-view.js +2 -14
- package/dist/plugins/plugins-view.js.map +1 -1
- package/dist/plugins/profiles-view.d.ts +0 -1
- package/dist/plugins/session-detail-view.d.ts +1 -2
- package/dist/plugins/session-detail-view.d.ts.map +1 -1
- package/dist/plugins/session-detail-view.js +19 -14
- package/dist/plugins/session-detail-view.js.map +1 -1
- package/dist/plugins/session-lifecycle.d.ts +0 -1
- package/dist/plugins/sessions-view.d.ts +0 -1
- package/dist/plugins/sessions-view.d.ts.map +1 -1
- package/dist/plugins/sessions-view.js +25 -11
- package/dist/plugins/sessions-view.js.map +1 -1
- package/dist/plugins/shell.d.ts +0 -1
- package/dist/plugins/skills-view.d.ts +0 -1
- package/dist/plugins/skills-view.d.ts.map +1 -1
- package/dist/plugins/skills-view.js +54 -9
- package/dist/plugins/skills-view.js.map +1 -1
- package/dist/plugins/subagent.d.ts +0 -1
- package/dist/plugins/text-delta.d.ts +0 -1
- package/dist/plugins/tool-call.d.ts +0 -1
- package/dist/plugins/tool-call.d.ts.map +1 -1
- package/dist/plugins/tool-call.js +89 -17
- package/dist/plugins/tool-call.js.map +1 -1
- package/dist/plugins/workspaces-view.d.ts +4 -0
- package/dist/plugins/workspaces-view.d.ts.map +1 -0
- package/dist/plugins/workspaces-view.js +382 -0
- package/dist/plugins/workspaces-view.js.map +1 -0
- package/dist/prompt-history-store.d.ts +0 -1
- package/dist/prompt-input.d.ts +27 -2
- package/dist/prompt-input.d.ts.map +1 -1
- package/dist/prompt-input.js +195 -12
- package/dist/prompt-input.js.map +1 -1
- package/dist/registry.d.ts +2 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +2 -1
- package/dist/registry.js.map +1 -1
- package/dist/session-dispatch.d.ts +35 -0
- package/dist/session-dispatch.d.ts.map +1 -0
- package/dist/session-dispatch.js +129 -0
- package/dist/session-dispatch.js.map +1 -0
- package/package.json +15 -10
- package/specs/kanban-workspaces-spec.md +293 -0
- package/specs/kanban-workspaces-subtasks.md +180 -0
package/README.md
CHANGED
|
@@ -12,6 +12,38 @@ npm i -g @a5c-ai/agent-mux-tui
|
|
|
12
12
|
amux-tui
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Development
|
|
16
|
+
|
|
17
|
+
Package-level `build` and `test` scripts are routed through the repo-root
|
|
18
|
+
`scripts/agent-mux-build.cjs` helper so `@a5c-ai/agent-mux-tui` runs with its
|
|
19
|
+
agent-mux prerequisites built in dependency order and its tests executed from
|
|
20
|
+
the repo root. Use `npm run build:local --workspace=@a5c-ai/agent-mux-tui`
|
|
21
|
+
when you only want the package-local TypeScript compile.
|
|
22
|
+
|
|
23
|
+
Run `npm run verify:release --workspace=@a5c-ai/agent-mux-tui` after a build to
|
|
24
|
+
confirm the packed package surface still includes the README-linked kanban/workspaces
|
|
25
|
+
planning artifacts.
|
|
26
|
+
|
|
27
|
+
## Kanban/workspaces surface
|
|
28
|
+
|
|
29
|
+
The package now ships first-phase `kanban` and `workspaces` views backed by the
|
|
30
|
+
shared kanban/workspace control plane.
|
|
31
|
+
|
|
32
|
+
- Runtime today: `kanban` view (list-first issue browsing, issue detail,
|
|
33
|
+
keyboard-driven backlog actions through an injected control plane, and linked
|
|
34
|
+
workspace/session handoff)
|
|
35
|
+
- Runtime today: `workspaces` view (workspace inventory, linked issue/session/run
|
|
36
|
+
context, lifecycle actions routed through the injected control plane, and
|
|
37
|
+
issue/session handoff back into the rest of the TUI)
|
|
38
|
+
|
|
39
|
+
- Spec: [`specs/kanban-workspaces-spec.md`](specs/kanban-workspaces-spec.md)
|
|
40
|
+
- Backlog decomposition: [`specs/kanban-workspaces-subtasks.md`](specs/kanban-workspaces-subtasks.md)
|
|
41
|
+
- Cross-package design note:
|
|
42
|
+
[`../../docs/agent-mux/archive/design/20-tui-kanban-workspaces.md`](../../docs/agent-mux/archive/design/20-tui-kanban-workspaces.md)
|
|
43
|
+
|
|
44
|
+
The planning documents remain useful design references for follow-on expansion
|
|
45
|
+
of both surfaces.
|
|
46
|
+
|
|
15
47
|
## Writing a plugin
|
|
16
48
|
|
|
17
49
|
```ts
|
|
@@ -70,6 +102,12 @@ The built-in plugins (`text-delta`, `thinking-delta`, `tool-call`,
|
|
|
70
102
|
`tool-error`, `cost`, `chat-view`, `sessions-view`, `fallback`) are all
|
|
71
103
|
implemented through these same extension points — use them as references.
|
|
72
104
|
|
|
105
|
+
The logs / observability view is part of the supported built-in surface. Press
|
|
106
|
+
`l` to open it. The metrics header aggregates the full buffered `EventStream`,
|
|
107
|
+
the global `/` filter only narrows the visible log rows, and pressing `e`
|
|
108
|
+
exports the full buffered stream to `session-log-<timestamp>.json` in the
|
|
109
|
+
current working directory.
|
|
110
|
+
|
|
73
111
|
## View hotkeys
|
|
74
112
|
|
|
75
113
|
| Key | View | Purpose |
|
|
@@ -81,15 +119,37 @@ implemented through these same extension points — use them as references.
|
|
|
81
119
|
| `5` | models | model registry per adapter |
|
|
82
120
|
| `6` | profiles | run-options profiles |
|
|
83
121
|
| `7` | plugins | native plugins per adapter |
|
|
84
|
-
| `8` |
|
|
122
|
+
| `8` | kanban | backlog/board browsing + issue actions |
|
|
123
|
+
| `W` | workspaces | workspace lifecycle inventory + actions |
|
|
85
124
|
| `9` | help | keybindings + tips |
|
|
86
125
|
| `0` | mcp | registered MCP servers |
|
|
87
126
|
| `-` | doctor | capability matrix / diagnostics |
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
127
|
+
| `l` | logs | observability metrics + filtered log stream |
|
|
128
|
+
| `A` | auth | auth status per adapter |
|
|
129
|
+
| `C` | config | config view |
|
|
130
|
+
| `K` | skills | installed skills (d: delete, r: refresh) |
|
|
131
|
+
| `G` | agents | installed sub-agents (d: delete, r: refresh) |
|
|
132
|
+
| `H` | hooks | registered hooks (d: remove, r: refresh) |
|
|
133
|
+
|
|
134
|
+
Global: chat composer auto-focuses in `chat`; `Esc` temporarily dismisses it;
|
|
135
|
+
`/` filter, `:` / Ctrl-K palette, `m` model picker, `N` agent picker, `P`
|
|
136
|
+
profile picker, `i` interrupt, `y`/`n` approval, `q` quit.
|
|
137
|
+
|
|
138
|
+
Inside `kanban`, press `w` to jump to the linked workspace for the selected
|
|
139
|
+
issue. Inside `workspaces`, press `g` to jump back to the linked issue. If you
|
|
140
|
+
open `session-detail` from either view, `b`/`Esc` returns to the originating
|
|
141
|
+
surface instead of always falling back to `sessions`.
|
|
142
|
+
|
|
143
|
+
## Logs / Observability View
|
|
144
|
+
|
|
145
|
+
The `logs` view is a built-in tab registered as `builtin:observability-view`.
|
|
146
|
+
|
|
147
|
+
- Hotkey: `l`
|
|
148
|
+
- Purpose: show aggregated tokens, cost, latency, error count, and tool-call
|
|
149
|
+
count for the buffered event stream.
|
|
150
|
+
- Filtering: the global `/` filter applies to the log rows in this view,
|
|
151
|
+
including `type:<prefix>` filters. Metrics continue to reflect the full
|
|
152
|
+
buffered stream so you can inspect a subset without losing session totals.
|
|
153
|
+
- Export: press `e` while the view is active to write the full buffered event
|
|
154
|
+
stream as pretty-printed JSON to `session-log-<timestamp>.json` in the
|
|
155
|
+
current working directory.
|
package/dist/app.d.ts
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
-
import type { AgentMuxClient } from '@a5c-ai/agent-mux';
|
|
1
|
+
import type { AgentMuxClient, DeferredPromptTarget, RunHandle } from '@a5c-ai/agent-mux';
|
|
2
2
|
import type { TuiPlugin } from './plugin.js';
|
|
3
|
+
import type { KanbanControlPlane } from './kanban-control-plane.js';
|
|
3
4
|
export interface AppProps {
|
|
4
5
|
client: AgentMuxClient;
|
|
5
6
|
plugins: TuiPlugin[];
|
|
7
|
+
kanban?: KanbanControlPlane;
|
|
6
8
|
defaultAgent?: string;
|
|
9
|
+
initialViewId?: string;
|
|
10
|
+
disableChatAutoPrompt?: boolean;
|
|
7
11
|
}
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
type ActiveRunCommand = {
|
|
13
|
+
kind: 'send';
|
|
14
|
+
prompt: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'queue';
|
|
17
|
+
prompt: string;
|
|
18
|
+
when: DeferredPromptTarget;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'steer';
|
|
21
|
+
prompt: string;
|
|
22
|
+
when: DeferredPromptTarget;
|
|
23
|
+
};
|
|
24
|
+
export declare function parseActiveRunCommand(prompt: string): ActiveRunCommand | null;
|
|
25
|
+
export declare function dispatchPromptToActiveRun(handle: RunHandle, prompt: string): Promise<string>;
|
|
26
|
+
export declare function App({ client, plugins, kanban, defaultAgent, initialViewId, disableChatAutoPrompt, }: AppProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export {};
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAc,oBAAoB,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAErG,OAAO,KAAK,EAAE,SAAS,EAA+B,MAAM,aAAa,CAAC;AAa1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,KAAK,gBAAgB,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,oBAAoB,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAElE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAc7E;AAeD,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAelG;AA6BD,wBAAgB,GAAG,CAAC,EAClB,MAAM,EACN,OAAO,EACP,MAAM,EACN,YAAuB,EACvB,aAAsB,EACtB,qBAA6B,GAC9B,EAAE,QAAQ,2CA4rBV"}
|
package/dist/app.js
CHANGED
|
@@ -1,24 +1,96 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
3
|
-
import { Box, Text, useInput,
|
|
3
|
+
import { Box, Text, useApp, useInput, useStdout } from 'ink';
|
|
4
4
|
import { logger } from '@a5c-ai/agent-mux-observability';
|
|
5
5
|
import { createRegistry, createContext, loadPlugins } from './registry.js';
|
|
6
6
|
import { EventStream } from './event-stream.js';
|
|
7
|
-
import { PromptInput } from './prompt-input.js';
|
|
7
|
+
import { PromptInput, createEmptyPromptInputState, insertPromptInput, } from './prompt-input.js';
|
|
8
8
|
import { CommandPalette } from './command-palette.js';
|
|
9
9
|
import { ModelPicker } from './model-picker.js';
|
|
10
10
|
import { loadHistory, appendHistory } from './prompt-history-store.js';
|
|
11
|
+
import { resolveSessionDispatchPlan } from './session-dispatch.js';
|
|
12
|
+
import { createViewport, packSegments } from './layout.js';
|
|
13
|
+
export function parseActiveRunCommand(prompt) {
|
|
14
|
+
if (prompt.startsWith('/queue ')) {
|
|
15
|
+
const body = prompt.slice('/queue '.length).trim();
|
|
16
|
+
return body ? { kind: 'queue', prompt: body, when: 'next-turn' } : null;
|
|
17
|
+
}
|
|
18
|
+
if (prompt.startsWith('/steer-tool ')) {
|
|
19
|
+
const body = prompt.slice('/steer-tool '.length).trim();
|
|
20
|
+
return body ? { kind: 'steer', prompt: body, when: 'after-tool' } : null;
|
|
21
|
+
}
|
|
22
|
+
if (prompt.startsWith('/steer ')) {
|
|
23
|
+
const body = prompt.slice('/steer '.length).trim();
|
|
24
|
+
return body ? { kind: 'steer', prompt: body, when: 'after-response' } : null;
|
|
25
|
+
}
|
|
26
|
+
return { kind: 'send', prompt };
|
|
27
|
+
}
|
|
28
|
+
function describeDeferredTarget(target) {
|
|
29
|
+
switch (target) {
|
|
30
|
+
case 'after-tool':
|
|
31
|
+
return 'the next tool result';
|
|
32
|
+
case 'after-response':
|
|
33
|
+
return 'the next agent response';
|
|
34
|
+
case 'next-turn':
|
|
35
|
+
return 'the next turn';
|
|
36
|
+
default:
|
|
37
|
+
return 'a later event';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function dispatchPromptToActiveRun(handle, prompt) {
|
|
41
|
+
const command = parseActiveRunCommand(prompt);
|
|
42
|
+
if (!command) {
|
|
43
|
+
return 'Active-run command requires a message.';
|
|
44
|
+
}
|
|
45
|
+
if (command.kind === 'queue') {
|
|
46
|
+
await handle.queue(command.prompt, { when: command.when });
|
|
47
|
+
return `Queued for ${describeDeferredTarget(command.when)}…`;
|
|
48
|
+
}
|
|
49
|
+
if (command.kind === 'steer') {
|
|
50
|
+
await handle.steer(command.prompt, { when: command.when });
|
|
51
|
+
return `Steering after ${describeDeferredTarget(command.when)}…`;
|
|
52
|
+
}
|
|
53
|
+
await handle.send(command.prompt);
|
|
54
|
+
return 'Sending to active run…';
|
|
55
|
+
}
|
|
11
56
|
function pickRenderers(renderers, ev) {
|
|
12
57
|
const specific = renderers.find((r) => r.id !== 'fallback' && r.match(ev));
|
|
13
58
|
if (specific)
|
|
14
59
|
return specific;
|
|
15
60
|
return renderers.find((r) => r.id === 'fallback');
|
|
16
61
|
}
|
|
17
|
-
|
|
62
|
+
function SegmentLines({ lines }) {
|
|
63
|
+
return (_jsx(Box, { flexDirection: "column", children: lines.map((line, lineIndex) => (_jsx(Box, { children: line.map((segment, segmentIndex) => (_jsxs(Text, { color: segment.color, dimColor: segment.dimColor, children: [segmentIndex > 0 ? ' · ' : '', segment.text] }, segment.key))) }, `line:${lineIndex}`))) }));
|
|
64
|
+
}
|
|
65
|
+
export function App({ client, plugins, kanban, defaultAgent = 'claude', initialViewId = 'chat', disableChatAutoPrompt = false, }) {
|
|
18
66
|
const { exit } = useApp();
|
|
67
|
+
const { stdout } = useStdout();
|
|
68
|
+
const [stdoutDimensions, setStdoutDimensions] = useState(() => ({
|
|
69
|
+
width: stdout?.columns ?? 80,
|
|
70
|
+
height: stdout?.rows ?? 24,
|
|
71
|
+
}));
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!stdout)
|
|
74
|
+
return;
|
|
75
|
+
const sync = () => {
|
|
76
|
+
setStdoutDimensions({
|
|
77
|
+
width: stdout.columns ?? 80,
|
|
78
|
+
height: stdout.rows ?? 24,
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
sync();
|
|
82
|
+
stdout.on('resize', sync);
|
|
83
|
+
return () => {
|
|
84
|
+
stdout.off('resize', sync);
|
|
85
|
+
};
|
|
86
|
+
}, [stdout]);
|
|
87
|
+
const viewport = useMemo(() => createViewport(stdoutDimensions.width, stdoutDimensions.height), [stdoutDimensions.height, stdoutDimensions.width]);
|
|
19
88
|
const [status, setStatus] = useState('');
|
|
20
|
-
const [
|
|
89
|
+
const [pluginLoadVersion, setPluginLoadVersion] = useState(0);
|
|
90
|
+
const [activeId, setActiveId] = useState(initialViewId);
|
|
21
91
|
const [promptMode, setPromptMode] = useState(false);
|
|
92
|
+
const [chatPromptDismissed, setChatPromptDismissed] = useState(false);
|
|
93
|
+
const [chatPromptState, setChatPromptState] = useState(() => createEmptyPromptInputState());
|
|
22
94
|
const [pendingResume, setPendingResume] = useState(null);
|
|
23
95
|
const currentHandleRef = React.useRef(null);
|
|
24
96
|
const pendingApprovalRef = React.useRef(null);
|
|
@@ -27,6 +99,9 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
27
99
|
const [filter, setFilter] = useState('');
|
|
28
100
|
const [paletteMode, setPaletteMode] = useState(false);
|
|
29
101
|
const [selection, setSelection] = useState(undefined);
|
|
102
|
+
const [issueSelection, setIssueSelection] = useState(undefined);
|
|
103
|
+
const [workspaceSelection, setWorkspaceSelection] = useState(undefined);
|
|
104
|
+
const [returnViewId, setReturnViewId] = useState('sessions');
|
|
30
105
|
const [modelPickerMode, setModelPickerMode] = useState(false);
|
|
31
106
|
const [currentModel, setCurrentModel] = useState(undefined);
|
|
32
107
|
const [agentPickerMode, setAgentPickerMode] = useState(false);
|
|
@@ -59,6 +134,7 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
59
134
|
deny: 'magenta',
|
|
60
135
|
};
|
|
61
136
|
const [execMode, setExecMode] = useState('normal');
|
|
137
|
+
const activeViewIdRef = React.useRef(initialViewId);
|
|
62
138
|
function cycleExecMode() {
|
|
63
139
|
setExecMode((m) => {
|
|
64
140
|
const i = EXEC_MODES.indexOf(m);
|
|
@@ -85,28 +161,35 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
85
161
|
const r = createRegistry();
|
|
86
162
|
const s = new EventStream();
|
|
87
163
|
const ctx = createContext(client, r, (ev) => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
setPendingResume({ agent: ev.agent, sessionId: ev.sessionId });
|
|
94
|
-
setStatus(`Resuming ${ev.agent}/${ev.sessionId} — type to send next message`);
|
|
95
|
-
void loadSessionTranscript(ev.agent, ev.sessionId);
|
|
96
|
-
}
|
|
97
|
-
if (ev.type === 'session:detail') {
|
|
98
|
-
setSelection({ agent: ev.agent, sessionId: ev.sessionId });
|
|
99
|
-
}
|
|
100
|
-
if (ev.type === 'session:diff') {
|
|
101
|
-
void handleSessionDiff(ev.agent, ev.sessionId);
|
|
102
|
-
}
|
|
103
|
-
}, s);
|
|
104
|
-
void loadPlugins(plugins, ctx);
|
|
164
|
+
void handleInternalEvent(ev, activeViewIdRef.current);
|
|
165
|
+
}, s, kanban);
|
|
166
|
+
void loadPlugins(plugins, ctx).then(() => {
|
|
167
|
+
setPluginLoadVersion((version) => version + 1);
|
|
168
|
+
});
|
|
105
169
|
return { registry: r, stream: s };
|
|
106
170
|
}, [client, plugins]);
|
|
171
|
+
const active = registry.views.find((v) => v.id === activeId) ?? registry.views[0];
|
|
172
|
+
activeViewIdRef.current = active?.id ?? activeId;
|
|
107
173
|
useInput((input, key) => {
|
|
108
174
|
if (promptMode || filterMode || paletteMode || modelPickerMode || profilePickerMode || agentPickerMode)
|
|
109
175
|
return; // child input owns keys while open
|
|
176
|
+
if (active?.id === 'chat' && chatPromptDismissed) {
|
|
177
|
+
if (key.escape) {
|
|
178
|
+
setChatPromptDismissed(false);
|
|
179
|
+
setChatPromptState(createEmptyPromptInputState());
|
|
180
|
+
const fallbackView = registry.views.find((view) => view.id !== 'chat');
|
|
181
|
+
if (fallbackView) {
|
|
182
|
+
setActiveId(fallbackView.id);
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (input && !key.ctrl && !key.meta) {
|
|
187
|
+
setChatPromptState((current) => insertPromptInput(current, input));
|
|
188
|
+
setChatPromptDismissed(false);
|
|
189
|
+
setPromptMode(true);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
110
193
|
if (input === 'q' || (key.ctrl && input === 'c')) {
|
|
111
194
|
exit();
|
|
112
195
|
return;
|
|
@@ -175,24 +258,24 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
175
258
|
registerEventRenderer: () => { },
|
|
176
259
|
registerCommand: () => { },
|
|
177
260
|
registerPromptHandler: () => { },
|
|
178
|
-
emit: (e) =>
|
|
179
|
-
if (e.type === 'status')
|
|
180
|
-
setStatus(e.message);
|
|
181
|
-
if (e.type === 'event')
|
|
182
|
-
stream.push(e.event);
|
|
183
|
-
},
|
|
261
|
+
emit: (e) => void handleInternalEvent(e, activeViewIdRef.current),
|
|
184
262
|
});
|
|
185
263
|
}
|
|
186
264
|
}
|
|
187
265
|
});
|
|
188
|
-
const
|
|
266
|
+
const navLines = useMemo(() => packSegments(registry.views.map((v) => ({
|
|
267
|
+
key: v.id,
|
|
268
|
+
text: `[${v.hotkey ?? '?'}] ${v.title}`,
|
|
269
|
+
color: v.id === active?.id ? 'green' : 'gray',
|
|
270
|
+
})), viewport.width), [active?.id, registry.views, viewport.width]);
|
|
189
271
|
// Chat view has an always-on inline prompt. Auto-focus when entering chat
|
|
190
|
-
// and no modal is open.
|
|
191
|
-
//
|
|
192
|
-
// clears it. This is the simplest model that keeps global navigation working.
|
|
272
|
+
// and no modal is open. The draft itself lives at the app level so Esc can
|
|
273
|
+
// temporarily dismiss the composer without losing the current prompt.
|
|
193
274
|
useEffect(() => {
|
|
194
|
-
if (
|
|
275
|
+
if (!disableChatAutoPrompt &&
|
|
276
|
+
active?.id === 'chat' &&
|
|
195
277
|
!promptMode &&
|
|
278
|
+
!chatPromptDismissed &&
|
|
196
279
|
!filterMode &&
|
|
197
280
|
!paletteMode &&
|
|
198
281
|
!modelPickerMode &&
|
|
@@ -200,31 +283,59 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
200
283
|
!agentPickerMode) {
|
|
201
284
|
setPromptMode(true);
|
|
202
285
|
}
|
|
203
|
-
if (active?.id !== 'chat'
|
|
204
|
-
|
|
286
|
+
if (active?.id !== 'chat') {
|
|
287
|
+
if (promptMode) {
|
|
288
|
+
setPromptMode(false);
|
|
289
|
+
}
|
|
290
|
+
if (chatPromptDismissed) {
|
|
291
|
+
setChatPromptDismissed(false);
|
|
292
|
+
}
|
|
205
293
|
}
|
|
206
|
-
}, [active?.id, filterMode, paletteMode, modelPickerMode, profilePickerMode, agentPickerMode, promptMode]);
|
|
294
|
+
}, [active?.id, chatPromptDismissed, disableChatAutoPrompt, filterMode, paletteMode, modelPickerMode, profilePickerMode, agentPickerMode, promptMode]);
|
|
207
295
|
const ActiveView = active?.component;
|
|
208
296
|
const viewEmit = (ev) => {
|
|
209
|
-
|
|
297
|
+
void handleInternalEvent(ev, active?.id);
|
|
298
|
+
};
|
|
299
|
+
async function handleInternalEvent(ev, sourceViewId) {
|
|
300
|
+
if (ev.type === 'status') {
|
|
210
301
|
setStatus(ev.message);
|
|
211
|
-
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (ev.type === 'event') {
|
|
305
|
+
stream.push(ev.event);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (ev.type === 'view:switch') {
|
|
212
309
|
setActiveId(ev.id);
|
|
213
|
-
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (ev.type === 'issue:select') {
|
|
313
|
+
setIssueSelection({ issueId: ev.issueId, projectId: ev.projectId });
|
|
314
|
+
setActiveId(ev.viewId ?? 'kanban');
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (ev.type === 'workspace:select') {
|
|
318
|
+
setWorkspaceSelection({ workspacePath: ev.workspacePath });
|
|
319
|
+
setActiveId(ev.viewId ?? 'workspaces');
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (ev.type === 'session:select') {
|
|
214
323
|
setPendingResume({ agent: ev.agent, sessionId: ev.sessionId });
|
|
215
324
|
setStatus(`Resuming ${ev.agent}/${ev.sessionId} — type to send next message`);
|
|
216
|
-
|
|
325
|
+
await loadSessionTranscript(ev.agent, ev.sessionId);
|
|
217
326
|
setActiveId('chat');
|
|
327
|
+
return;
|
|
218
328
|
}
|
|
219
|
-
|
|
329
|
+
if (ev.type === 'session:detail') {
|
|
330
|
+
const nextReturnViewId = sourceViewId && sourceViewId !== 'session-detail' ? sourceViewId : activeViewIdRef.current;
|
|
220
331
|
setSelection({ agent: ev.agent, sessionId: ev.sessionId });
|
|
332
|
+
setReturnViewId(nextReturnViewId && nextReturnViewId !== 'session-detail' ? nextReturnViewId : 'sessions');
|
|
333
|
+
return;
|
|
221
334
|
}
|
|
222
|
-
|
|
223
|
-
|
|
335
|
+
if (ev.type === 'session:diff') {
|
|
336
|
+
await handleSessionDiff(ev.agent, ev.sessionId);
|
|
224
337
|
}
|
|
225
|
-
|
|
226
|
-
stream.push(ev.event);
|
|
227
|
-
};
|
|
338
|
+
}
|
|
228
339
|
async function loadSessionTranscript(agent, sessionId) {
|
|
229
340
|
try {
|
|
230
341
|
const full = await client.sessions.get(agent, sessionId);
|
|
@@ -235,7 +346,7 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
235
346
|
stream.push({
|
|
236
347
|
runId: 'transcript',
|
|
237
348
|
agent,
|
|
238
|
-
timestamp: (m.timestamp ??
|
|
349
|
+
timestamp: new Date(m.timestamp ?? Date.now()).toISOString(),
|
|
239
350
|
type: 'text_delta',
|
|
240
351
|
delta: `[${m.role}] ${m.content}\n`,
|
|
241
352
|
});
|
|
@@ -260,7 +371,7 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
260
371
|
const sum = result.summary;
|
|
261
372
|
const head = result.operations
|
|
262
373
|
.slice(0, 30)
|
|
263
|
-
.map((o, i) => `${i + 1}. ${o.
|
|
374
|
+
.map((o, i) => `${i + 1}. ${o.type}`)
|
|
264
375
|
.join('\n');
|
|
265
376
|
const text = `\n--- session diff ---\n` +
|
|
266
377
|
`A: ${left.agent}/${left.sessionId}\n` +
|
|
@@ -293,6 +404,7 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
293
404
|
async function handlePromptSubmit(prompt) {
|
|
294
405
|
if (!prompt.trim())
|
|
295
406
|
return;
|
|
407
|
+
setChatPromptDismissed(false);
|
|
296
408
|
setPromptHistory((h) => {
|
|
297
409
|
const next = h.filter((p) => p !== prompt);
|
|
298
410
|
next.push(prompt);
|
|
@@ -309,8 +421,7 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
309
421
|
// fresh process — keeps follow-ups inside the same session & context.
|
|
310
422
|
if (currentHandleRef.current) {
|
|
311
423
|
try {
|
|
312
|
-
setStatus(
|
|
313
|
-
await currentHandleRef.current.send(prompt);
|
|
424
|
+
setStatus(await dispatchPromptToActiveRun(currentHandleRef.current, prompt));
|
|
314
425
|
}
|
|
315
426
|
catch (e) {
|
|
316
427
|
setStatus(`send failed: ${e.message}`);
|
|
@@ -323,19 +434,20 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
323
434
|
? (await client.profiles.apply(currentProfile))
|
|
324
435
|
: {};
|
|
325
436
|
const selectedAgent = currentModel?.agent ?? currentAgent ?? baseOpts.agent ?? defaultAgent;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
333
|
-
const runOpts = {
|
|
334
|
-
agent: pendingResume?.agent ?? selectedAgent,
|
|
437
|
+
const explicitHarnessSelection = Boolean(currentModel || currentAgent);
|
|
438
|
+
const requestedAgent = pendingResume && !explicitHarnessSelection ? pendingResume.agent : selectedAgent;
|
|
439
|
+
const sessionPlan = await resolveSessionDispatchPlan({
|
|
440
|
+
sessions: client.sessions,
|
|
441
|
+
pendingResume,
|
|
442
|
+
requestedAgent,
|
|
335
443
|
prompt,
|
|
444
|
+
});
|
|
445
|
+
const runOpts = {
|
|
446
|
+
agent: sessionPlan.agent,
|
|
447
|
+
prompt: sessionPlan.prompt,
|
|
336
448
|
};
|
|
337
|
-
if (
|
|
338
|
-
runOpts.sessionId =
|
|
449
|
+
if (sessionPlan.sessionId)
|
|
450
|
+
runOpts.sessionId = sessionPlan.sessionId;
|
|
339
451
|
if (currentModel)
|
|
340
452
|
runOpts.model = currentModel.modelId;
|
|
341
453
|
else if (baseOpts.model)
|
|
@@ -354,6 +466,9 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
354
466
|
}
|
|
355
467
|
else
|
|
356
468
|
runOpts.approvalMode = 'prompt';
|
|
469
|
+
if (sessionPlan.migration) {
|
|
470
|
+
setStatus(`Forking ${sessionPlan.migration.sourceAgent}/${sessionPlan.migration.sourceSessionId} into ${sessionPlan.agent} via transcript import…`);
|
|
471
|
+
}
|
|
357
472
|
const resumedAgent = runOpts.agent;
|
|
358
473
|
let resumedSessionId = runOpts.sessionId;
|
|
359
474
|
const handle = client.run(runOpts);
|
|
@@ -406,31 +521,91 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
406
521
|
}
|
|
407
522
|
}
|
|
408
523
|
}
|
|
409
|
-
|
|
524
|
+
const footerSegments = [
|
|
525
|
+
{
|
|
526
|
+
key: 'controls',
|
|
527
|
+
text: viewport.isVeryNarrow
|
|
528
|
+
? '/ filter, : palette'
|
|
529
|
+
: 'shift+tab: mode, /: filter, :: palette',
|
|
530
|
+
dimColor: true,
|
|
531
|
+
},
|
|
532
|
+
{ key: 'mode', text: `mode=${execMode}`, color: EXEC_MODE_COLORS[execMode] },
|
|
533
|
+
{ key: 'agent', text: `agent=${currentAgent ?? defaultAgent}`, color: 'cyan' },
|
|
534
|
+
];
|
|
535
|
+
if (filter) {
|
|
536
|
+
footerSegments.push({
|
|
537
|
+
key: 'filter',
|
|
538
|
+
text: viewport.isVeryNarrow ? `filter=${filter}` : `filter="${filter}"`,
|
|
539
|
+
color: 'cyan',
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
if (currentModel) {
|
|
543
|
+
footerSegments.push({
|
|
544
|
+
key: 'model',
|
|
545
|
+
text: `model=${currentModel.agent}/${currentModel.modelId}`,
|
|
546
|
+
color: 'magenta',
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
if (currentProfile) {
|
|
550
|
+
footerSegments.push({ key: 'profile', text: `profile=${currentProfile}`, color: 'blue' });
|
|
551
|
+
}
|
|
552
|
+
if (currentHandleRef.current) {
|
|
553
|
+
footerSegments.push({ key: 'interrupt', text: 'i: interrupt', color: 'yellow' });
|
|
554
|
+
if (!viewport.isVeryNarrow) {
|
|
555
|
+
footerSegments.push({
|
|
556
|
+
key: 'deferred',
|
|
557
|
+
text: '/queue, /steer, /steer-tool',
|
|
558
|
+
color: 'yellow',
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (pendingApproval) {
|
|
563
|
+
footerSegments.push({ key: 'approval', text: 'y/n: approve', color: 'yellow' });
|
|
564
|
+
}
|
|
565
|
+
footerSegments.push({ key: 'quit', text: 'q: quit', dimColor: true });
|
|
566
|
+
if (!viewport.isVeryNarrow && registry.views.length > 1) {
|
|
567
|
+
footerSegments.push({
|
|
568
|
+
key: 'views',
|
|
569
|
+
text: registry.views
|
|
570
|
+
.filter((v) => v.hotkey)
|
|
571
|
+
.map((v) => `${v.hotkey}:${v.title.toLowerCase()}`)
|
|
572
|
+
.join(' '),
|
|
573
|
+
dimColor: true,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
if (!viewport.isShort && !viewport.isVeryNarrow && registry.commands.length > 0) {
|
|
577
|
+
footerSegments.push({
|
|
578
|
+
key: 'commands',
|
|
579
|
+
text: registry.commands.map((c) => `${c.hotkey}:${c.label}`).join(' '),
|
|
580
|
+
dimColor: true,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
const footerLines = packSegments(footerSegments, viewport.width);
|
|
584
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SegmentLines, { lines: navLines }), _jsx(Box, { borderStyle: "single", flexDirection: "column", paddingX: 1, children: ViewWithRenderers ? (_jsx(ViewWithRenderers, { client: client, kanban: kanban, active: true, eventStream: stream, emit: viewEmit, pluginEpoch: pluginLoadVersion, viewport: viewport, filter: filter || undefined, selection: selection, issueSelection: issueSelection, workspaceSelection: workspaceSelection, returnViewId: returnViewId, activeSessions: activeSessions })) : (_jsx(Text, { dimColor: true, children: "No views registered." })) }), pendingApproval ? (_jsx(Box, { children: _jsxs(Text, { color: pendingApproval.riskLevel === 'high' ? 'red' : 'yellow', children: ["[approval ", pendingApproval.riskLevel, "] ", pendingApproval.action, " \u2014 y: approve \u00B7 n: deny"] }) })) : null, filterMode ? (_jsx(PromptInput, { label: "filter (substring or `type:<prefix>`)> ", onSubmit: (v) => {
|
|
410
585
|
setFilter(v);
|
|
411
586
|
setFilterMode(false);
|
|
412
587
|
setStatus(v ? `Filter: ${v}` : 'Filter cleared.');
|
|
413
|
-
}, onCancel: () => setFilterMode(false) })) : null, profilePickerMode ? (_jsx(ModelPicker, { models: availableProfiles.map((n) => ({ agent: 'profile', modelId: n })), onCancel: () => setProfilePickerMode(false), onPick: (m) => {
|
|
588
|
+
}, onCancel: () => setFilterMode(false) })) : null, profilePickerMode ? (_jsx(ModelPicker, { models: availableProfiles.map((n) => ({ agent: 'profile', modelId: n })), maxItems: viewport.overlayRowLimit, width: viewport.contentWidth, title: "profile", onCancel: () => setProfilePickerMode(false), onPick: (m) => {
|
|
414
589
|
setCurrentProfile(m.modelId);
|
|
415
590
|
setProfilePickerMode(false);
|
|
416
591
|
setStatus(`Profile: ${m.modelId}`);
|
|
417
|
-
} })) : null, modelPickerMode ? (_jsx(ModelPicker, { models: availableModels, onCancel: () => setModelPickerMode(false), onPick: (m) => {
|
|
592
|
+
} })) : null, modelPickerMode ? (_jsx(ModelPicker, { models: availableModels, maxItems: viewport.overlayRowLimit, width: viewport.contentWidth, onCancel: () => setModelPickerMode(false), onPick: (m) => {
|
|
418
593
|
setCurrentModel(m);
|
|
419
594
|
setModelPickerMode(false);
|
|
420
595
|
setStatus(`Model: ${m.agent}/${m.modelId}`);
|
|
421
|
-
} })) : null, agentPickerMode ? (_jsx(ModelPicker, { models: (() => {
|
|
596
|
+
} })) : null, agentPickerMode ? (_jsx(ModelPicker, { title: "agent", models: (() => {
|
|
422
597
|
try {
|
|
423
598
|
return client.adapters.list().map((a) => ({ agent: a.agent, modelId: a.displayName }));
|
|
424
599
|
}
|
|
425
600
|
catch {
|
|
426
601
|
return [];
|
|
427
602
|
}
|
|
428
|
-
})(), onCancel: () => setAgentPickerMode(false), onPick: (m) => {
|
|
603
|
+
})(), maxItems: viewport.overlayRowLimit, width: viewport.contentWidth, onCancel: () => setAgentPickerMode(false), onPick: (m) => {
|
|
429
604
|
setCurrentAgent(m.agent);
|
|
430
605
|
setCurrentModel(undefined);
|
|
431
606
|
setAgentPickerMode(false);
|
|
432
607
|
setStatus(`Agent: ${m.agent}`);
|
|
433
|
-
} })) : null, paletteMode ? (_jsx(CommandPalette, { views: registry.views, commands: registry.commands, onCancel: () => setPaletteMode(false), onPick: (a) => {
|
|
608
|
+
} })) : null, paletteMode ? (_jsx(CommandPalette, { views: registry.views, commands: registry.commands, maxItems: viewport.overlayRowLimit, width: viewport.contentWidth, onCancel: () => setPaletteMode(false), onPick: (a) => {
|
|
434
609
|
setPaletteMode(false);
|
|
435
610
|
if (a.id.startsWith('view:')) {
|
|
436
611
|
setActiveId(a.id.slice('view:'.length));
|
|
@@ -446,18 +621,15 @@ export function App({ client, plugins, defaultAgent = 'claude' }) {
|
|
|
446
621
|
registerEventRenderer: () => { },
|
|
447
622
|
registerCommand: () => { },
|
|
448
623
|
registerPromptHandler: () => { },
|
|
449
|
-
emit: (e) =>
|
|
450
|
-
if (e.type === 'status')
|
|
451
|
-
setStatus(e.message);
|
|
452
|
-
if (e.type === 'event')
|
|
453
|
-
stream.push(e.event);
|
|
454
|
-
},
|
|
624
|
+
emit: (e) => void handleInternalEvent(e, activeViewIdRef.current),
|
|
455
625
|
});
|
|
456
626
|
}
|
|
457
627
|
}
|
|
458
|
-
} })) : null, _jsxs(Box, { flexDirection: "column", children: [status ? _jsx(Text, { dimColor: true, children: status }) : null,
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
628
|
+
} })) : null, _jsxs(Box, { flexDirection: "column", children: [status ? _jsx(Text, { dimColor: true, children: status }) : null, _jsx(SegmentLines, { lines: footerLines }), promptMode ? (_jsx(PromptInput, { label: "> ", labelColor: EXEC_MODE_COLORS[execMode], onSubmit: handlePromptSubmit, initialState: chatPromptState, onStateChange: setChatPromptState, onCancel: () => {
|
|
629
|
+
setPromptMode(false);
|
|
630
|
+
if (active?.id === 'chat') {
|
|
631
|
+
setChatPromptDismissed(true);
|
|
632
|
+
}
|
|
633
|
+
}, onShiftTab: cycleExecMode, history: promptHistory })) : null] })] }));
|
|
462
634
|
}
|
|
463
635
|
//# sourceMappingURL=app.js.map
|