@agents-ensemble/cli 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -3
- package/config.example.yaml +13 -0
- package/dist/async-operator-input.js +1 -1
- package/dist/async-operator-input.js.map +1 -1
- package/dist/display/session-display-reducer.d.ts.map +1 -1
- package/dist/display/session-display-reducer.js +0 -11
- package/dist/display/session-display-reducer.js.map +1 -1
- package/dist/inline-markdown.d.ts +3 -1
- package/dist/inline-markdown.d.ts.map +1 -1
- package/dist/inline-markdown.js +232 -33
- package/dist/inline-markdown.js.map +1 -1
- package/dist/issue-command.d.ts.map +1 -1
- package/dist/issue-command.js +8 -4
- package/dist/issue-command.js.map +1 -1
- package/dist/prompt-operator-input.d.ts +1 -1
- package/dist/prompt-operator-input.d.ts.map +1 -1
- package/dist/prompt-operator-input.js +1 -1
- package/dist/prompt-operator-input.js.map +1 -1
- package/dist/session-log-lines.d.ts.map +1 -1
- package/dist/session-log-lines.js +64 -8
- package/dist/session-log-lines.js.map +1 -1
- package/dist/session-sinks.d.ts.map +1 -1
- package/dist/session-sinks.js +2 -10
- package/dist/session-sinks.js.map +1 -1
- package/dist/tui/activity-log.d.ts +8 -0
- package/dist/tui/activity-log.d.ts.map +1 -1
- package/dist/tui/activity-log.js +16 -0
- package/dist/tui/activity-log.js.map +1 -1
- package/dist/tui/compute-operator-input-cursor-y.d.ts +4 -0
- package/dist/tui/compute-operator-input-cursor-y.d.ts.map +1 -1
- package/dist/tui/compute-operator-input-cursor-y.js +4 -2
- package/dist/tui/compute-operator-input-cursor-y.js.map +1 -1
- package/dist/tui/create-issue-session-tui-host.d.ts.map +1 -1
- package/dist/tui/create-issue-session-tui-host.js +44 -8
- package/dist/tui/create-issue-session-tui-host.js.map +1 -1
- package/dist/tui/create-issue-session-tui-host.test.js +19 -5
- package/dist/tui/create-issue-session-tui-host.test.js.map +1 -1
- package/dist/tui/issue-session-tui-stream.d.ts +6 -1
- package/dist/tui/issue-session-tui-stream.d.ts.map +1 -1
- package/dist/tui/issue-session-tui-stream.js +55 -17
- package/dist/tui/issue-session-tui-stream.js.map +1 -1
- package/dist/tui/issue-session-tui-stream.test.js +345 -11
- package/dist/tui/issue-session-tui-stream.test.js.map +1 -1
- package/dist/tui/issue-session-tui.d.ts +11 -5
- package/dist/tui/issue-session-tui.d.ts.map +1 -1
- package/dist/tui/issue-session-tui.js +60 -30
- package/dist/tui/issue-session-tui.js.map +1 -1
- package/dist/tui/issue-session-tui.test.js +268 -30
- package/dist/tui/issue-session-tui.test.js.map +1 -1
- package/dist/tui/open-questions-pane.d.ts.map +1 -1
- package/dist/tui/open-questions-pane.js +30 -12
- package/dist/tui/open-questions-pane.js.map +1 -1
- package/dist/tui/pane-layout.d.ts +22 -0
- package/dist/tui/pane-layout.d.ts.map +1 -0
- package/dist/tui/pane-layout.js +74 -0
- package/dist/tui/pane-layout.js.map +1 -0
- package/dist/tui/stream-scrollback.d.ts +29 -0
- package/dist/tui/stream-scrollback.d.ts.map +1 -0
- package/dist/tui/stream-scrollback.js +35 -0
- package/dist/tui/stream-scrollback.js.map +1 -0
- package/dist/tui/titled-border-pane.d.ts +2 -1
- package/dist/tui/titled-border-pane.d.ts.map +1 -1
- package/dist/tui/titled-border-pane.js +2 -2
- package/dist/tui/titled-border-pane.js.map +1 -1
- package/dist/tui/tui-layout-constants.d.ts +11 -6
- package/dist/tui/tui-layout-constants.d.ts.map +1 -1
- package/dist/tui/tui-layout-constants.js +11 -6
- package/dist/tui/tui-layout-constants.js.map +1 -1
- package/dist/tui/tui-resize-recovery.d.ts +24 -0
- package/dist/tui/tui-resize-recovery.d.ts.map +1 -0
- package/dist/tui/tui-resize-recovery.js +34 -0
- package/dist/tui/tui-resize-recovery.js.map +1 -0
- package/dist/tui/tui-terminal-size.d.ts +45 -0
- package/dist/tui/tui-terminal-size.d.ts.map +1 -0
- package/dist/tui/tui-terminal-size.js +195 -0
- package/dist/tui/tui-terminal-size.js.map +1 -0
- package/dist/tui/tui-view-model.d.ts +2 -0
- package/dist/tui/tui-view-model.d.ts.map +1 -1
- package/dist/tui/tui-view-model.js +7 -0
- package/dist/tui/tui-view-model.js.map +1 -1
- package/dist/tui/wrap-text-to-width.d.ts.map +1 -1
- package/dist/tui/wrap-text-to-width.js +4 -1
- package/dist/tui/wrap-text-to-width.js.map +1 -1
- package/docs/config.md +67 -3
- package/docs/settings.md +17 -4
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue-session-tui-stream.d.ts","sourceRoot":"","sources":["../../src/tui/issue-session-tui-stream.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA0B7E,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"issue-session-tui-stream.d.ts","sourceRoot":"","sources":["../../src/tui/issue-session-tui-stream.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA0B7E,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAKtC,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,YAAY,CAAC;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;IACzC,+EAA+E;IAC/E,eAAe,CAAC,EAAE,qBAAqB,CAAC;CACzC;AA0CD,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,aAAsB,EACtB,iBAAiB,EACjB,eAAe,GAChB,EAAE,0BAA0B,2CA0O5B"}
|
|
@@ -13,27 +13,31 @@ import { buildActivityLogDisplayLines } from './activity-log.js';
|
|
|
13
13
|
import { getPaneContentWidth, wrapTextToWidth } from './wrap-text-to-width.js';
|
|
14
14
|
import { resolveStreamPaneHeights } from './stream-layout.js';
|
|
15
15
|
import { TitledBorderPane } from './titled-border-pane.js';
|
|
16
|
-
|
|
16
|
+
import { useTuiTerminalSize, } from './tui-terminal-size.js';
|
|
17
|
+
import { createStreamScrollbackState, reduceStreamScrollback, } from './stream-scrollback.js';
|
|
18
|
+
function useStreamContentWidth(columns) {
|
|
17
19
|
return getPaneContentWidth({
|
|
18
|
-
columns
|
|
20
|
+
columns,
|
|
19
21
|
paddingX: PANE_PADDING_X,
|
|
20
22
|
borderWidth: ROUND_BORDER_WIDTH,
|
|
21
23
|
});
|
|
22
24
|
}
|
|
23
|
-
function useStreamActivityLogWidth() {
|
|
24
|
-
return Math.max(1,
|
|
25
|
+
function useStreamActivityLogWidth(columns) {
|
|
26
|
+
return Math.max(1, columns);
|
|
25
27
|
}
|
|
26
|
-
function StaticActivityLog({ activityLog, contentWidth, }) {
|
|
27
|
-
return (_jsx(Static, { items: activityLog, children: (entry, index) => (_jsx(Box, { flexDirection: "column", children: buildActivityLogDisplayLines([entry], contentWidth).map((line, lineIndex) => (_jsx(ActivityLogDisplayLineRow, { line: line }, `activity-line-${index}-${lineIndex}`))) }, `activity-log-${index}`)) }));
|
|
28
|
+
function StaticActivityLog({ activityLog, contentWidth, replayGeneration, issueLinkMode, }) {
|
|
29
|
+
return (_jsx(Static, { items: activityLog, children: (entry, index) => (_jsx(Box, { flexDirection: "column", children: buildActivityLogDisplayLines([entry], contentWidth).map((line, lineIndex) => (_jsx(ActivityLogDisplayLineRow, { line: line, issueLinkMode: issueLinkMode }, `activity-line-${index}-${lineIndex}`))) }, `activity-log-${index}`)) }, replayGeneration));
|
|
28
30
|
}
|
|
29
|
-
export function IssueSessionTuiStream({ viewModel, onSubmit, issueUrl, issueLinkMode = 'osc8', }) {
|
|
31
|
+
export function IssueSessionTuiStream({ viewModel, onSubmit, issueUrl, issueLinkMode = 'osc8', terminalSizeStore, scrollbackEvent, }) {
|
|
30
32
|
const snapshot = useSyncExternalStore(viewModel.subscribe, viewModel.getSnapshot, viewModel.getSnapshot);
|
|
31
33
|
const [inputValue, setInputValue] = useState('');
|
|
32
34
|
const [inputDisplayLineCount, setInputDisplayLineCount] = useState(1);
|
|
33
35
|
const [selectedQuestionIndex, setSelectedQuestionIndex] = useState(0);
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
36
|
+
const [scrollbackState, setScrollbackState] = useState(() => createStreamScrollbackState(snapshot.activityLog.length));
|
|
37
|
+
const terminalSize = useTuiTerminalSize(terminalSizeStore);
|
|
38
|
+
const { columns: terminalColumns, rows: terminalRows } = terminalSize;
|
|
39
|
+
const contentWidth = useStreamContentWidth(terminalColumns);
|
|
40
|
+
const activityLogContentWidth = useStreamActivityLogWidth(terminalColumns);
|
|
37
41
|
const operatorPrompt = 'operator> ';
|
|
38
42
|
const maxInputDisplayLines = computeMaxInputDisplayLines(terminalRows);
|
|
39
43
|
const openQuestions = getTuiOpenQuestions(snapshot);
|
|
@@ -56,7 +60,18 @@ export function IssueSessionTuiStream({ viewModel, onSubmit, issueUrl, issueLink
|
|
|
56
60
|
postLoopWaiting: snapshot.postLoopWaiting,
|
|
57
61
|
shuttingDown: snapshot.shuttingDown,
|
|
58
62
|
});
|
|
59
|
-
const
|
|
63
|
+
const committedActivityCount = Math.min(scrollbackState.detached
|
|
64
|
+
? scrollbackState.committedActivityCount
|
|
65
|
+
: snapshot.activityLog.length, snapshot.activityLog.length);
|
|
66
|
+
const pendingActivityCount = scrollbackState.detached
|
|
67
|
+
? Math.max(0, snapshot.activityLog.length - committedActivityCount)
|
|
68
|
+
: 0;
|
|
69
|
+
const scrollbackHint = pendingActivityCount > 0
|
|
70
|
+
? `${pendingActivityCount} 件の新着 · End で最新へ`
|
|
71
|
+
: undefined;
|
|
72
|
+
const contextHintLines = scrollbackHint
|
|
73
|
+
? [scrollbackHint, ...operatorInputDisplay.hintLines]
|
|
74
|
+
: operatorInputDisplay.hintLines;
|
|
60
75
|
const visibleInputDisplayLineCount = Math.min(inputDisplayLineCount, maxInputDisplayLines);
|
|
61
76
|
const hintLineCount = contextHintLines.reduce((lineCount, line) => lineCount + wrapTextToWidth(line, contentWidth).length, 0);
|
|
62
77
|
const desiredInputPaneHeight = computeInputPaneHeight({
|
|
@@ -86,16 +101,39 @@ export function IssueSessionTuiStream({ viewModel, onSubmit, issueUrl, issueLink
|
|
|
86
101
|
useEffect(() => {
|
|
87
102
|
setSelectedQuestionIndex((current) => clampOpenQuestionSelectionIndex(current, openQuestions.length));
|
|
88
103
|
}, [openQuestions]);
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!scrollbackEvent) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// The prop is an edge-triggered event. A future adapter should replace it
|
|
109
|
+
// when it observes another viewport transition.
|
|
110
|
+
setScrollbackState((current) => reduceStreamScrollback(current, scrollbackEvent, snapshot.activityLog.length));
|
|
111
|
+
}, [scrollbackEvent]);
|
|
112
|
+
const dispatchScrollbackEvent = (event) => {
|
|
113
|
+
setScrollbackState((current) => reduceStreamScrollback(current, event, snapshot.activityLog.length));
|
|
114
|
+
};
|
|
89
115
|
useInput((_input, key) => {
|
|
90
|
-
if (openQuestions.length
|
|
116
|
+
if (openQuestions.length > 0 && key.shift) {
|
|
117
|
+
if (key.upArrow) {
|
|
118
|
+
setSelectedQuestionIndex((current) => advanceOpenQuestionSelection(current, 'up', openQuestions.length));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (key.downArrow) {
|
|
122
|
+
setSelectedQuestionIndex((current) => advanceOpenQuestionSelection(current, 'down', openQuestions.length));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const scrollWithModifier = key.ctrl;
|
|
127
|
+
const scrollWithoutModifier = inputValue.length === 0;
|
|
128
|
+
if (!scrollWithModifier && !scrollWithoutModifier) {
|
|
91
129
|
return;
|
|
92
130
|
}
|
|
93
|
-
if (key.
|
|
94
|
-
|
|
131
|
+
if (key.pageUp) {
|
|
132
|
+
dispatchScrollbackEvent({ type: 'detached', source: 'keyboard' });
|
|
95
133
|
return;
|
|
96
134
|
}
|
|
97
|
-
if (key.
|
|
98
|
-
|
|
135
|
+
if (key.end) {
|
|
136
|
+
dispatchScrollbackEvent({ type: 'follow', source: 'keyboard' });
|
|
99
137
|
}
|
|
100
138
|
});
|
|
101
139
|
const handleSubmit = (value) => {
|
|
@@ -112,6 +150,6 @@ export function IssueSessionTuiStream({ viewModel, onSubmit, issueUrl, issueLink
|
|
|
112
150
|
setInputValue('');
|
|
113
151
|
setInputDisplayLineCount(1);
|
|
114
152
|
};
|
|
115
|
-
return (_jsxs(_Fragment, { children: [_jsx(StaticActivityLog, { activityLog: snapshot.activityLog, contentWidth: activityLogContentWidth }), _jsxs(Box, { flexDirection: "column", width:
|
|
153
|
+
return (_jsxs(_Fragment, { children: [_jsx(StaticActivityLog, { activityLog: snapshot.activityLog.slice(0, committedActivityCount), contentWidth: activityLogContentWidth, replayGeneration: snapshot.activityLogGeneration, issueLinkMode: issueLinkMode }), _jsxs(Box, { flexDirection: "column", width: terminalColumns, height: streamPaneHeights.dynamicFrameHeight, overflowX: "visible", overflowY: "hidden", children: [operatorInputDisplay.mode === 'withQuestions' ? (_jsx(OpenQuestionsPane, { layout: streamOpenQuestionsLayout, terminalColumns: terminalColumns, issueLinkMode: issueLinkMode })) : null, _jsxs(TitledBorderPane, { title: INPUT_PANE_TITLE, borderStyle: "single", borderColor: INPUT_PANE_BORDER_COLOR, paddingX: PANE_PADDING_X, height: streamPaneHeights.inputPaneHeight, terminalColumns: terminalColumns, children: [contextHintLines.map((line, index) => (_jsx(WrappedTextLines, { text: line, width: contentWidth, color: INPUT_PANE_HINT_COLOR }, `context-hint-${index}`))), _jsx(OperatorTextArea, { value: inputValue, onChange: setInputValue, onSubmit: handleSubmit, focus: !snapshot.shuttingDown, contentWidth: contentWidth, promptPrefix: operatorPrompt, maxDisplayLines: maxInputDisplayLines, onDisplayLineCountChange: handleDisplayLineCountChange, cursorStart: cursorStart })] }), _jsx(WorkerStatusPane, { workers: snapshot.displayState.workers, dispatchHold: snapshot.displayState.dispatchHold, height: streamPaneHeights.workerPaneHeight, terminalColumns: terminalColumns, issueUrl: issueUrl, issueLinkMode: issueLinkMode })] })] }));
|
|
116
154
|
}
|
|
117
155
|
//# sourceMappingURL=issue-session-tui-stream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue-session-tui-stream.js","sourceRoot":"","sources":["../../src/tui/issue-session-tui-stream.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,QAAQ,EACR,oBAAoB,GACrB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,mBAAmB,EAAqB,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC7F,OAAO,EACL,+BAA+B,GAEhC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,4BAA4B,EAAyB,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"issue-session-tui-stream.js","sourceRoot":"","sources":["../../src/tui/issue-session-tui-stream.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
3
|
vi.mock('./operator-text-area.js', () => import('./operator-text-area.test-double.js'));
|
|
4
|
+
import { EventEmitter } from 'node:events';
|
|
5
|
+
import { PassThrough } from 'node:stream';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { render as renderInk } from 'ink';
|
|
4
8
|
import { cleanup, render } from 'ink-testing-library';
|
|
5
9
|
import { IssueSessionTuiStream } from './issue-session-tui-stream.js';
|
|
6
10
|
import { createTuiViewModel } from './tui-view-model.js';
|
|
7
11
|
import { flushInkStdin, INK_TEST_KEYS } from './ink-test-keys.js';
|
|
12
|
+
import { createTuiResizeController, createTuiTerminalSizeStore, TUI_RESIZE_SETTLE_MS, TUI_RESIZE_SHRINK_COALESCE_MS, } from './tui-terminal-size.js';
|
|
13
|
+
import { createTuiResizeRecovery, TUI_FULL_REDRAW_SEQUENCE, } from './tui-resize-recovery.js';
|
|
14
|
+
class ResizeSource extends EventEmitter {
|
|
15
|
+
columns = 80;
|
|
16
|
+
rows = 24;
|
|
17
|
+
}
|
|
18
|
+
class FakeTtyStdout extends PassThrough {
|
|
19
|
+
isTTY = true;
|
|
20
|
+
columns = 120;
|
|
21
|
+
rows = 32;
|
|
22
|
+
}
|
|
23
|
+
class FakeTtyStdin extends PassThrough {
|
|
24
|
+
isTTY = true;
|
|
25
|
+
isRaw = false;
|
|
26
|
+
setRawMode(value) {
|
|
27
|
+
this.isRaw = value;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
ref() {
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
unref() {
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function waitFor(milliseconds) {
|
|
38
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
39
|
+
}
|
|
8
40
|
function createOpenQuestion(overrides) {
|
|
9
41
|
return {
|
|
10
42
|
responseType: 'text',
|
|
@@ -29,6 +61,7 @@ describe('IssueSessionTuiStream', () => {
|
|
|
29
61
|
});
|
|
30
62
|
afterEach(() => {
|
|
31
63
|
cleanup();
|
|
64
|
+
vi.useRealTimers();
|
|
32
65
|
vi.restoreAllMocks();
|
|
33
66
|
});
|
|
34
67
|
it('renders activity as unframed static output and keeps the lower panes framed', () => {
|
|
@@ -54,7 +87,44 @@ describe('IssueSessionTuiStream', () => {
|
|
|
54
87
|
expect(frame.indexOf('Open questions')).toBeLessThan(frame.indexOf('Operator input'));
|
|
55
88
|
expect(frame.indexOf('Operator input')).toBeLessThan(frame.indexOf('Workers'));
|
|
56
89
|
expect(frame).toContain('inq-1 (1/1) への回答');
|
|
57
|
-
expect(frame).not.toContain('任意のタイミングで入力 · /exit で終了');
|
|
90
|
+
expect(frame).not.toContain('任意のタイミングで入力 · /reconnect で再接続 · /exit で終了');
|
|
91
|
+
});
|
|
92
|
+
it('keeps compact rows visible when long selected detail reaches the cap', async () => {
|
|
93
|
+
const viewModel = createTuiViewModel();
|
|
94
|
+
viewModel.setDisplayState({
|
|
95
|
+
workers: {},
|
|
96
|
+
conductorOutput: null,
|
|
97
|
+
openQuestions: [
|
|
98
|
+
createOpenQuestion({
|
|
99
|
+
id: 'inq-long',
|
|
100
|
+
question: `question-start ${'word '.repeat(100)}`,
|
|
101
|
+
context: `first-detail-start ${'context '.repeat(80)}context-tail`,
|
|
102
|
+
}),
|
|
103
|
+
createOpenQuestion({
|
|
104
|
+
id: 'inq-compact-1',
|
|
105
|
+
question: 'Second question',
|
|
106
|
+
context: 'second-detail',
|
|
107
|
+
}),
|
|
108
|
+
createOpenQuestion({
|
|
109
|
+
id: 'inq-compact-2',
|
|
110
|
+
question: 'Third question',
|
|
111
|
+
}),
|
|
112
|
+
],
|
|
113
|
+
dispatchHold: { hold: false, heldEventCount: 0 },
|
|
114
|
+
});
|
|
115
|
+
const { stdin, lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, onSubmit: () => { } }));
|
|
116
|
+
const initialFrame = lastFrame() ?? '';
|
|
117
|
+
expect(initialFrame).toContain('first-detail-start');
|
|
118
|
+
expect(initialFrame).toContain('inq-compact-1');
|
|
119
|
+
expect(initialFrame).toContain('inq-compact-2');
|
|
120
|
+
stdin.write(INK_TEST_KEYS.shiftDownArrow);
|
|
121
|
+
await flushInkStdin();
|
|
122
|
+
const switchedFrame = lastFrame() ?? '';
|
|
123
|
+
expect(switchedFrame).toContain('▸ inq-compact-1');
|
|
124
|
+
expect(switchedFrame).toContain('second-detail');
|
|
125
|
+
expect(switchedFrame).toContain('inq-long');
|
|
126
|
+
expect(switchedFrame).toContain('inq-compact-2');
|
|
127
|
+
expect(switchedFrame).not.toContain('first-detail-start');
|
|
58
128
|
});
|
|
59
129
|
it('renders inline Markdown in static activity output', () => {
|
|
60
130
|
const viewModel = createTuiViewModel();
|
|
@@ -65,6 +135,34 @@ describe('IssueSessionTuiStream', () => {
|
|
|
65
135
|
expect(frame).not.toContain('**');
|
|
66
136
|
expect(frame).not.toContain('`');
|
|
67
137
|
});
|
|
138
|
+
it('renders nested Markdown links in stream activity and open questions', () => {
|
|
139
|
+
const viewModel = createTuiViewModel();
|
|
140
|
+
viewModel.setDisplayState({
|
|
141
|
+
workers: {},
|
|
142
|
+
conductorOutput: null,
|
|
143
|
+
openQuestions: [
|
|
144
|
+
createOpenQuestion({
|
|
145
|
+
id: 'inq-stream-link',
|
|
146
|
+
question: '- **choose** [this](https://example.com/question)',
|
|
147
|
+
context: 'Use `safe` [docs](https://example.com/context).',
|
|
148
|
+
}),
|
|
149
|
+
],
|
|
150
|
+
dispatchHold: { hold: false, heldEventCount: 0 },
|
|
151
|
+
});
|
|
152
|
+
viewModel.appendActivityLog('conductor', '- **bold** [activity](https://example.com/activity)\n - `nested`');
|
|
153
|
+
const { lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, issueLinkMode: "osc8", onSubmit: () => { } }));
|
|
154
|
+
const frame = lastFrame() ?? '';
|
|
155
|
+
const visibleFrame = frame.replace(/\u001b\]8;;[^\u0007]*\u0007/g, '');
|
|
156
|
+
expect(visibleFrame).toContain('[conductor]');
|
|
157
|
+
expect(visibleFrame).toContain('- bold activity');
|
|
158
|
+
expect(visibleFrame).toContain('▸ inq-stream-link [text] - choose this');
|
|
159
|
+
expect(visibleFrame).toContain(' Use safe docs.');
|
|
160
|
+
expect(frame).toContain('\u001b]8;;https://example.com/activity\u0007');
|
|
161
|
+
expect(frame).toContain('\u001b]8;;https://example.com/question\u0007');
|
|
162
|
+
expect(frame).toContain('\u001b]8;;https://example.com/context\u0007');
|
|
163
|
+
expect(frame).not.toContain('**');
|
|
164
|
+
expect(frame).not.toContain('`');
|
|
165
|
+
});
|
|
68
166
|
it('shows dispatch hold count in the Workers pane title', () => {
|
|
69
167
|
const viewModel = createTuiViewModel();
|
|
70
168
|
viewModel.setDisplayState({
|
|
@@ -82,14 +180,14 @@ describe('IssueSessionTuiStream', () => {
|
|
|
82
180
|
const { lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, issueUrl: "https://github.com/otolab/agents-ensemble/issues/261", issueLinkMode: "label", onSubmit: () => { } }));
|
|
83
181
|
const frame = lastFrame() ?? '';
|
|
84
182
|
const operatorInputIndex = frame.indexOf('Operator input');
|
|
85
|
-
const instructionHintIndex = frame.indexOf('追加指示を入力するか /exit で終了');
|
|
183
|
+
const instructionHintIndex = frame.indexOf('追加指示を入力するか /reconnect で再接続 · /exit で終了');
|
|
86
184
|
const workersIndex = frame.indexOf('Workers');
|
|
87
185
|
expect(operatorInputIndex).toBeGreaterThanOrEqual(0);
|
|
88
186
|
expect(frame).not.toContain('Open questions');
|
|
89
187
|
expect(frame).not.toContain('(未回答なし)');
|
|
90
188
|
expect(instructionHintIndex).toBeGreaterThan(operatorInputIndex);
|
|
91
189
|
expect(workersIndex).toBeGreaterThan(operatorInputIndex);
|
|
92
|
-
expect(frame).toContain('追加指示を入力するか /exit で終了');
|
|
190
|
+
expect(frame).toContain('追加指示を入力するか /reconnect で再接続 · /exit で終了');
|
|
93
191
|
expect(frame).toContain('otolab/agents-ensemble#261');
|
|
94
192
|
expect(frame).not.toContain('post-loop 待機中');
|
|
95
193
|
expect(Math.max(...frame.split('\n').map((line) => line.trimEnd().length))).toBeLessThanOrEqual(80);
|
|
@@ -254,7 +352,7 @@ describe('IssueSessionTuiStream', () => {
|
|
|
254
352
|
viewModel.setPostLoopWaiting(true);
|
|
255
353
|
await flushInkStdin();
|
|
256
354
|
expectCanonicalIssueLink();
|
|
257
|
-
expect(lastFrame() ?? '').toContain('追加指示を入力するか /exit で終了');
|
|
355
|
+
expect(lastFrame() ?? '').toContain('追加指示を入力するか /reconnect で再接続 · /exit で終了');
|
|
258
356
|
viewModel.setShuttingDown(true);
|
|
259
357
|
await flushInkStdin();
|
|
260
358
|
expectCanonicalIssueLink();
|
|
@@ -264,7 +362,7 @@ describe('IssueSessionTuiStream', () => {
|
|
|
264
362
|
const viewModel = createTuiViewModel();
|
|
265
363
|
const { lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, issueUrl: "https://github.com/otolab/agents-ensemble/issues/261", issueLinkMode: "label", onSubmit: () => { } }));
|
|
266
364
|
const frame = lastFrame() ?? '';
|
|
267
|
-
expect(frame).toContain('任意のタイミングで入力 · /exit で終了');
|
|
365
|
+
expect(frame).toContain('任意のタイミングで入力 · /reconnect で再接続 · /exit で終了');
|
|
268
366
|
expect(frame).not.toContain('自律ターン');
|
|
269
367
|
});
|
|
270
368
|
it('omits the open-question pane and keeps ordinary input available', async () => {
|
|
@@ -272,9 +370,9 @@ describe('IssueSessionTuiStream', () => {
|
|
|
272
370
|
const onSubmit = vi.fn();
|
|
273
371
|
const { stdin, lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, issueUrl: "https://github.com/otolab/agents-ensemble/issues/263", issueLinkMode: "label", onSubmit: onSubmit }));
|
|
274
372
|
expect(lastFrame() ?? '').not.toContain('Open questions');
|
|
275
|
-
expect(lastFrame() ?? '').toContain('任意のタイミングで入力 · /exit で終了');
|
|
373
|
+
expect(lastFrame() ?? '').toContain('任意のタイミングで入力 · /reconnect で再接続 · /exit で終了');
|
|
276
374
|
expect(lastFrame() ?? '').toContain('otolab/agents-ensemble#263');
|
|
277
|
-
expect(lastFrame() ?? '').not.toContain('otolab/agents-ensemble#263 — 任意のタイミングで入力 · /exit で終了');
|
|
375
|
+
expect(lastFrame() ?? '').not.toContain('otolab/agents-ensemble#263 — 任意のタイミングで入力 · /reconnect で再接続 · /exit で終了');
|
|
278
376
|
stdin.write('follow-up');
|
|
279
377
|
await flushInkStdin();
|
|
280
378
|
stdin.write('\r');
|
|
@@ -292,6 +390,242 @@ describe('IssueSessionTuiStream', () => {
|
|
|
292
390
|
expect(frame).toContain('[harness] first');
|
|
293
391
|
expect(frame).toContain('[observation] second');
|
|
294
392
|
});
|
|
393
|
+
it('holds new activity while detached and flushes the pending suffix once on End', async () => {
|
|
394
|
+
const viewModel = createTuiViewModel({ activityLogWindowSize: null });
|
|
395
|
+
viewModel.appendActivityLog('harness', 'committed');
|
|
396
|
+
const { stdin, lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, onSubmit: () => { } }));
|
|
397
|
+
stdin.write(INK_TEST_KEYS.pageUp);
|
|
398
|
+
await flushInkStdin();
|
|
399
|
+
viewModel.appendActivityLog('harness', 'pending-one');
|
|
400
|
+
viewModel.appendActivityLog('harness', 'pending-two');
|
|
401
|
+
await flushInkStdin();
|
|
402
|
+
const detachedFrame = lastFrame() ?? '';
|
|
403
|
+
expect(detachedFrame).not.toContain('[harness] pending-one');
|
|
404
|
+
expect(detachedFrame).not.toContain('[harness] pending-two');
|
|
405
|
+
expect(detachedFrame).toContain('2 件の新着 · End で最新へ');
|
|
406
|
+
stdin.write(INK_TEST_KEYS.end);
|
|
407
|
+
await flushInkStdin();
|
|
408
|
+
const followedFrame = lastFrame() ?? '';
|
|
409
|
+
expect(followedFrame).toContain('[harness] pending-one');
|
|
410
|
+
expect(followedFrame).toContain('[harness] pending-two');
|
|
411
|
+
expect((followedFrame.match(/\[harness\] pending-one/g) ?? []).length).toBe(1);
|
|
412
|
+
expect((followedFrame.match(/\[harness\] pending-two/g) ?? []).length).toBe(1);
|
|
413
|
+
expect(followedFrame).not.toContain('2 件の新着 · End で最新へ');
|
|
414
|
+
});
|
|
415
|
+
it('requires Ctrl+PgUp while input has text, matching the pane key contract', async () => {
|
|
416
|
+
const viewModel = createTuiViewModel({ activityLogWindowSize: null });
|
|
417
|
+
viewModel.appendActivityLog('harness', 'committed');
|
|
418
|
+
const { stdin, lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, onSubmit: () => { } }));
|
|
419
|
+
stdin.write('typed');
|
|
420
|
+
await flushInkStdin();
|
|
421
|
+
stdin.write(INK_TEST_KEYS.pageUp);
|
|
422
|
+
await flushInkStdin();
|
|
423
|
+
viewModel.appendActivityLog('harness', 'plain-page-up-followed');
|
|
424
|
+
await flushInkStdin();
|
|
425
|
+
expect(lastFrame() ?? '').toContain('[harness] plain-page-up-followed');
|
|
426
|
+
expect(lastFrame() ?? '').not.toContain('新着 · End で最新へ');
|
|
427
|
+
stdin.write(INK_TEST_KEYS.ctrlPageUp);
|
|
428
|
+
await flushInkStdin();
|
|
429
|
+
viewModel.appendActivityLog('harness', 'ctrl-page-up-pending');
|
|
430
|
+
await flushInkStdin();
|
|
431
|
+
const detachedFrame = lastFrame() ?? '';
|
|
432
|
+
expect(detachedFrame).toContain('typed');
|
|
433
|
+
expect(detachedFrame).not.toContain('[harness] ctrl-page-up-pending');
|
|
434
|
+
expect(detachedFrame).toContain('1 件の新着 · End で最新へ');
|
|
435
|
+
});
|
|
436
|
+
it('settles the live frame without replaying stream Static history', async () => {
|
|
437
|
+
vi.useFakeTimers();
|
|
438
|
+
const source = new ResizeSource();
|
|
439
|
+
const terminalSizeStore = createTuiTerminalSizeStore(source);
|
|
440
|
+
const viewModel = createTuiViewModel();
|
|
441
|
+
viewModel.appendActivityLog('harness', 'before resize');
|
|
442
|
+
const { lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, terminalSizeStore: terminalSizeStore, onSubmit: () => { } }));
|
|
443
|
+
const countHistory = (frame) => (frame.match(/\[harness\] before resize/g) ?? []).length;
|
|
444
|
+
expect(countHistory(lastFrame() ?? '')).toBe(1);
|
|
445
|
+
source.columns = 40;
|
|
446
|
+
source.rows = 12;
|
|
447
|
+
source.emit('resize');
|
|
448
|
+
await vi.advanceTimersByTimeAsync(TUI_RESIZE_SHRINK_COALESCE_MS);
|
|
449
|
+
const frame = lastFrame() ?? '';
|
|
450
|
+
expect(countHistory(frame)).toBe(1);
|
|
451
|
+
expect(Math.max(...frame.split('\n').map((line) => line.trimEnd().length))).toBeLessThanOrEqual(40);
|
|
452
|
+
terminalSizeStore.dispose();
|
|
453
|
+
});
|
|
454
|
+
it('coalesces staged same-row shrink before redrawing the stream frame', async () => {
|
|
455
|
+
vi.useFakeTimers();
|
|
456
|
+
const source = new ResizeSource();
|
|
457
|
+
source.columns = 120;
|
|
458
|
+
source.rows = 32;
|
|
459
|
+
const resizeController = createTuiResizeController(source);
|
|
460
|
+
const terminalSizeStore = resizeController.terminalSize;
|
|
461
|
+
const viewModel = createTuiViewModel();
|
|
462
|
+
viewModel.appendActivityLog('harness', 'before staged resize');
|
|
463
|
+
const { lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, terminalSizeStore: terminalSizeStore, onSubmit: () => { } }));
|
|
464
|
+
const initialFrame = lastFrame() ?? '';
|
|
465
|
+
const countHistory = (frame) => (frame.match(/\[harness\] before staged resize/g) ?? []).length;
|
|
466
|
+
const inkResizeNotifications = [];
|
|
467
|
+
const inkResizeListener = () => {
|
|
468
|
+
inkResizeNotifications.push({
|
|
469
|
+
terminalColumns: terminalSizeStore.getSnapshot().columns,
|
|
470
|
+
proxyColumns: resizeController.stdout.columns,
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
resizeController.stdout.on('resize', inkResizeListener);
|
|
474
|
+
const unsubscribe = terminalSizeStore.subscribe(() => {
|
|
475
|
+
expect(resizeController.stdout.columns).toBeGreaterThanOrEqual(terminalSizeStore.getSnapshot().columns);
|
|
476
|
+
});
|
|
477
|
+
try {
|
|
478
|
+
expect(countHistory(initialFrame)).toBe(1);
|
|
479
|
+
for (const columns of [110, 100, 90, 80, 70]) {
|
|
480
|
+
source.columns = columns;
|
|
481
|
+
source.rows = 32;
|
|
482
|
+
source.emit('resize');
|
|
483
|
+
await vi.advanceTimersByTimeAsync(TUI_RESIZE_SETTLE_MS);
|
|
484
|
+
expect(inkResizeNotifications).toEqual([]);
|
|
485
|
+
expect(lastFrame()).toBe(initialFrame);
|
|
486
|
+
expect(countHistory(lastFrame() ?? '')).toBe(1);
|
|
487
|
+
}
|
|
488
|
+
source.columns = 60;
|
|
489
|
+
source.rows = 32;
|
|
490
|
+
source.emit('resize');
|
|
491
|
+
await vi.advanceTimersByTimeAsync(TUI_RESIZE_SHRINK_COALESCE_MS);
|
|
492
|
+
const finalFrame = lastFrame() ?? '';
|
|
493
|
+
expect(inkResizeNotifications).toEqual([
|
|
494
|
+
{ terminalColumns: 60, proxyColumns: 120 },
|
|
495
|
+
]);
|
|
496
|
+
expect(finalFrame).not.toBe(initialFrame);
|
|
497
|
+
expect(countHistory(finalFrame)).toBe(1);
|
|
498
|
+
expect(Math.max(...finalFrame.split('\n').map((line) => line.trimEnd().length))).toBeLessThanOrEqual(60);
|
|
499
|
+
expect(finalFrame).toContain('Workers');
|
|
500
|
+
expect(finalFrame).toContain('operator>');
|
|
501
|
+
}
|
|
502
|
+
finally {
|
|
503
|
+
unsubscribe();
|
|
504
|
+
resizeController.dispose();
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
it('clears and replays retained stream history once per settled shrink', async () => {
|
|
508
|
+
const source = new FakeTtyStdout();
|
|
509
|
+
const stdin = new FakeTtyStdin();
|
|
510
|
+
const stderr = new PassThrough();
|
|
511
|
+
const output = [];
|
|
512
|
+
source.on('data', (chunk) => {
|
|
513
|
+
output.push(chunk.toString());
|
|
514
|
+
});
|
|
515
|
+
const viewModel = createTuiViewModel({ activityLogWindowSize: null });
|
|
516
|
+
viewModel.appendActivityLog('observation', 'before-resize');
|
|
517
|
+
let ink;
|
|
518
|
+
let recovery;
|
|
519
|
+
const resizeController = createTuiResizeController(source, TUI_RESIZE_SETTLE_MS, (event) => {
|
|
520
|
+
recovery?.recover(event);
|
|
521
|
+
});
|
|
522
|
+
recovery = createTuiResizeRecovery({
|
|
523
|
+
clearInk: () => {
|
|
524
|
+
ink?.clear();
|
|
525
|
+
},
|
|
526
|
+
write: (data) => {
|
|
527
|
+
resizeController.stdout.write(data);
|
|
528
|
+
},
|
|
529
|
+
requestActivityLogReplay: () => {
|
|
530
|
+
viewModel.requestActivityLogReplay();
|
|
531
|
+
},
|
|
532
|
+
});
|
|
533
|
+
ink = renderInk(React.createElement(IssueSessionTuiStream, {
|
|
534
|
+
viewModel,
|
|
535
|
+
terminalSizeStore: resizeController.terminalSize,
|
|
536
|
+
onSubmit: () => { },
|
|
537
|
+
}), {
|
|
538
|
+
stdout: resizeController.stdout,
|
|
539
|
+
stdin: stdin,
|
|
540
|
+
stderr: stderr,
|
|
541
|
+
alternateScreen: false,
|
|
542
|
+
interactive: true,
|
|
543
|
+
patchConsole: false,
|
|
544
|
+
maxFps: 60,
|
|
545
|
+
});
|
|
546
|
+
try {
|
|
547
|
+
await ink.waitUntilRenderFlush();
|
|
548
|
+
const firstOutputLength = output.join('').length;
|
|
549
|
+
source.columns = 60;
|
|
550
|
+
source.emit('resize');
|
|
551
|
+
await waitFor(TUI_RESIZE_SHRINK_COALESCE_MS + 80);
|
|
552
|
+
await ink.waitUntilRenderFlush();
|
|
553
|
+
const firstRecoveryOutput = output.join('').slice(firstOutputLength);
|
|
554
|
+
const firstResetIndex = firstRecoveryOutput.indexOf(TUI_FULL_REDRAW_SEQUENCE);
|
|
555
|
+
const firstMarkerIndex = firstRecoveryOutput.indexOf('[observation] before-resize');
|
|
556
|
+
expect(firstRecoveryOutput.split(TUI_FULL_REDRAW_SEQUENCE)).toHaveLength(2);
|
|
557
|
+
expect(firstResetIndex).toBeGreaterThanOrEqual(0);
|
|
558
|
+
expect(firstResetIndex).toBeLessThan(firstMarkerIndex);
|
|
559
|
+
expect(firstRecoveryOutput.match(/\[observation\] before-resize/g)).toHaveLength(1);
|
|
560
|
+
expect(viewModel.getSnapshot().activityLogGeneration).toBe(1);
|
|
561
|
+
viewModel.appendActivityLog('observation', 'after-resize');
|
|
562
|
+
await ink.waitUntilRenderFlush();
|
|
563
|
+
const secondOutputLength = output.join('').length;
|
|
564
|
+
source.columns = 40;
|
|
565
|
+
source.emit('resize');
|
|
566
|
+
await waitFor(TUI_RESIZE_SHRINK_COALESCE_MS + 80);
|
|
567
|
+
await ink.waitUntilRenderFlush();
|
|
568
|
+
const secondRecoveryOutput = output.join('').slice(secondOutputLength);
|
|
569
|
+
const secondResetIndex = secondRecoveryOutput.indexOf(TUI_FULL_REDRAW_SEQUENCE);
|
|
570
|
+
const secondMarkerIndex = secondRecoveryOutput.indexOf('[observation] before-resize');
|
|
571
|
+
expect(secondRecoveryOutput.split(TUI_FULL_REDRAW_SEQUENCE)).toHaveLength(2);
|
|
572
|
+
expect(secondResetIndex).toBeGreaterThanOrEqual(0);
|
|
573
|
+
expect(secondResetIndex).toBeLessThan(secondMarkerIndex);
|
|
574
|
+
expect(secondRecoveryOutput.match(/\[observation\] before-resize/g)).toHaveLength(1);
|
|
575
|
+
expect(secondRecoveryOutput.match(/\[observation\] after-resize/g)).toHaveLength(1);
|
|
576
|
+
expect(viewModel.getSnapshot().activityLogGeneration).toBe(2);
|
|
577
|
+
}
|
|
578
|
+
finally {
|
|
579
|
+
recovery.dispose();
|
|
580
|
+
ink.unmount();
|
|
581
|
+
await ink.waitUntilExit();
|
|
582
|
+
resizeController.dispose();
|
|
583
|
+
stdin.destroy();
|
|
584
|
+
stderr.destroy();
|
|
585
|
+
source.destroy();
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
it('applies a stream width increase after a pending shrink through the normal path', async () => {
|
|
589
|
+
vi.useFakeTimers();
|
|
590
|
+
const source = new ResizeSource();
|
|
591
|
+
source.columns = 120;
|
|
592
|
+
source.rows = 32;
|
|
593
|
+
const resizeController = createTuiResizeController(source);
|
|
594
|
+
const terminalSizeStore = resizeController.terminalSize;
|
|
595
|
+
const viewModel = createTuiViewModel();
|
|
596
|
+
const { lastFrame } = render(_jsx(IssueSessionTuiStream, { viewModel: viewModel, terminalSizeStore: terminalSizeStore, onSubmit: () => { } }));
|
|
597
|
+
const resizeNotifications = [];
|
|
598
|
+
resizeController.stdout.on('resize', () => {
|
|
599
|
+
resizeNotifications.push({
|
|
600
|
+
terminalColumns: terminalSizeStore.getSnapshot().columns,
|
|
601
|
+
proxyColumns: resizeController.stdout.columns,
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
try {
|
|
605
|
+
source.columns = 60;
|
|
606
|
+
source.rows = 32;
|
|
607
|
+
source.emit('resize');
|
|
608
|
+
await vi.advanceTimersByTimeAsync(TUI_RESIZE_SHRINK_COALESCE_MS);
|
|
609
|
+
const shrunkFrame = lastFrame() ?? '';
|
|
610
|
+
const shrunkWidth = Math.max(...shrunkFrame.split('\n').map((line) => line.trimEnd().length));
|
|
611
|
+
source.columns = 120;
|
|
612
|
+
source.rows = 32;
|
|
613
|
+
source.emit('resize');
|
|
614
|
+
await vi.advanceTimersByTimeAsync(TUI_RESIZE_SETTLE_MS);
|
|
615
|
+
const grownFrame = lastFrame() ?? '';
|
|
616
|
+
const grownWidth = Math.max(...grownFrame.split('\n').map((line) => line.trimEnd().length));
|
|
617
|
+
expect(terminalSizeStore.getSnapshot().columns).toBe(120);
|
|
618
|
+
expect(resizeNotifications).toEqual([
|
|
619
|
+
{ terminalColumns: 60, proxyColumns: 120 },
|
|
620
|
+
{ terminalColumns: 120, proxyColumns: 120 },
|
|
621
|
+
]);
|
|
622
|
+
expect(grownFrame).not.toBe(shrunkFrame);
|
|
623
|
+
expect(grownWidth).toBeGreaterThan(shrunkWidth);
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
resizeController.dispose();
|
|
627
|
+
}
|
|
628
|
+
});
|
|
295
629
|
it('submits input for the selected open question', async () => {
|
|
296
630
|
const viewModel = createTuiViewModel();
|
|
297
631
|
viewModel.setDisplayState({
|
|
@@ -342,7 +676,7 @@ describe('IssueSessionTuiStream', () => {
|
|
|
342
676
|
const initialFrame = lastFrame() ?? '';
|
|
343
677
|
expect(initialFrame).toContain('Open questions');
|
|
344
678
|
expect(initialFrame).toContain('inq-resumed-1');
|
|
345
|
-
expect(initialFrame).not.toContain('任意のタイミングで入力 · /exit で終了');
|
|
679
|
+
expect(initialFrame).not.toContain('任意のタイミングで入力 · /reconnect で再接続 · /exit で終了');
|
|
346
680
|
if (selectedIndex === 1) {
|
|
347
681
|
stdin.write(INK_TEST_KEYS.shiftDownArrow);
|
|
348
682
|
await flushInkStdin();
|
|
@@ -356,16 +690,16 @@ describe('IssueSessionTuiStream', () => {
|
|
|
356
690
|
});
|
|
357
691
|
if (restoredQuestions.length === 1) {
|
|
358
692
|
expect(lastFrame() ?? '').not.toContain('Open questions');
|
|
359
|
-
expect(lastFrame() ?? '').toContain('任意のタイミングで入力 · /exit で終了');
|
|
693
|
+
expect(lastFrame() ?? '').toContain('任意のタイミングで入力 · /reconnect で再接続 · /exit で終了');
|
|
360
694
|
}
|
|
361
695
|
else {
|
|
362
696
|
expect(lastFrame() ?? '').toContain('Open questions');
|
|
363
697
|
expect(lastFrame() ?? '').toContain('inq-resumed-1');
|
|
364
|
-
expect(lastFrame() ?? '').not.toContain('任意のタイミングで入力 · /exit で終了');
|
|
698
|
+
expect(lastFrame() ?? '').not.toContain('任意のタイミングで入力 · /reconnect で再接続 · /exit で終了');
|
|
365
699
|
setOperatorQuestions([]);
|
|
366
700
|
await flushInkStdin();
|
|
367
701
|
expect(lastFrame() ?? '').not.toContain('Open questions');
|
|
368
|
-
expect(lastFrame() ?? '').toContain('任意のタイミングで入力 · /exit で終了');
|
|
702
|
+
expect(lastFrame() ?? '').toContain('任意のタイミングで入力 · /reconnect で再接続 · /exit で終了');
|
|
369
703
|
}
|
|
370
704
|
});
|
|
371
705
|
it('keeps Shift+arrow open-question selection in stream mode', async () => {
|