repoburg 1.3.202 → 1.3.209
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/agent-pane/backend/dist/index.js +2103 -32003
- package/agent-pane/backend/dist/server.js +45313 -0
- package/backend/dist/src/agent-runtime/agent-runtime.types.d.ts +1 -0
- package/backend/dist/src/llm-orchestration/hooks/frontend-notification.hook.js +6 -6
- package/backend/dist/src/llm-orchestration/hooks/frontend-notification.hook.js.map +1 -1
- package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js +1 -0
- package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js.map +1 -1
- package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
- package/daemon/dist/agentPane.js +128 -0
- package/daemon/dist/agentPane.spec.js +183 -0
- package/daemon/dist/index.js +28 -8
- package/daemon/package.json +1 -1
- package/model-selector-scroll.png +0 -0
- package/package.json +10 -2
- package/agent-pane/.prettierignore +0 -5
- package/agent-pane/.prettierrc +0 -8
- package/agent-pane/backend/coverage/base.css +0 -224
- package/agent-pane/backend/coverage/block-navigation.js +0 -87
- package/agent-pane/backend/coverage/clover.xml +0 -887
- package/agent-pane/backend/coverage/coverage-final.json +0 -10
- package/agent-pane/backend/coverage/favicon.png +0 -0
- package/agent-pane/backend/coverage/index.html +0 -176
- package/agent-pane/backend/coverage/prettify.css +0 -1
- package/agent-pane/backend/coverage/prettify.js +0 -2
- package/agent-pane/backend/coverage/sort-arrow-sprite.png +0 -0
- package/agent-pane/backend/coverage/sorter.js +0 -210
- package/agent-pane/backend/coverage/src/codex/codex-provider.ts.html +0 -1402
- package/agent-pane/backend/coverage/src/codex/index.html +0 -131
- package/agent-pane/backend/coverage/src/codex/item-mapper.ts.html +0 -331
- package/agent-pane/backend/coverage/src/db/index.html +0 -131
- package/agent-pane/backend/coverage/src/db/index.ts.html +0 -436
- package/agent-pane/backend/coverage/src/db/schema.ts.html +0 -175
- package/agent-pane/backend/coverage/src/index.html +0 -116
- package/agent-pane/backend/coverage/src/index.ts.html +0 -262
- package/agent-pane/backend/coverage/src/routes/index.html +0 -146
- package/agent-pane/backend/coverage/src/routes/sessions.ts.html +0 -991
- package/agent-pane/backend/coverage/src/routes/turns.ts.html +0 -256
- package/agent-pane/backend/coverage/src/routes/workspaces.ts.html +0 -406
- package/agent-pane/backend/coverage/src/ws/index.html +0 -116
- package/agent-pane/backend/coverage/src/ws/index.ts.html +0 -238
- package/agent-pane/backend/data.db +0 -0
- package/agent-pane/backend/data.db-shm +0 -0
- package/agent-pane/backend/data.db-wal +0 -0
- package/agent-pane/backend/package-lock.json +0 -5945
- package/agent-pane/backend/package.json +0 -33
- package/agent-pane/backend/scripts/backfill-from-jsonl.ts +0 -206
- package/agent-pane/backend/scripts/import-session-from-jsonl.ts +0 -264
- package/agent-pane/backend/src/claude/claude-message-mapper.test.ts +0 -271
- package/agent-pane/backend/src/claude/claude-message-mapper.ts +0 -259
- package/agent-pane/backend/src/claude/claude-provider.test.ts +0 -455
- package/agent-pane/backend/src/claude/claude-provider.ts +0 -1319
- package/agent-pane/backend/src/claude/claude-types.ts +0 -45
- package/agent-pane/backend/src/codex/app-server-client.test.ts +0 -183
- package/agent-pane/backend/src/codex/app-server-client.ts +0 -247
- package/agent-pane/backend/src/codex/app-server-types.ts +0 -259
- package/agent-pane/backend/src/codex/codex-provider-runtime-config.test.ts +0 -56
- package/agent-pane/backend/src/codex/codex-provider.test.ts +0 -60
- package/agent-pane/backend/src/codex/codex-provider.ts +0 -619
- package/agent-pane/backend/src/codex/item-mapper.test.ts +0 -221
- package/agent-pane/backend/src/codex/item-mapper.ts +0 -86
- package/agent-pane/backend/src/codex/shared-app-server.ts +0 -25
- package/agent-pane/backend/src/db/index.test.ts +0 -125
- package/agent-pane/backend/src/db/index.ts +0 -236
- package/agent-pane/backend/src/db/schema.ts +0 -34
- package/agent-pane/backend/src/index.ts +0 -89
- package/agent-pane/backend/src/integration/__fixtures__/cancel-turn.json +0 -29
- package/agent-pane/backend/src/integration/__fixtures__/delta-streaming.json +0 -40
- package/agent-pane/backend/src/integration/__fixtures__/failed-turn.json +0 -16
- package/agent-pane/backend/src/integration/__fixtures__/mcp-tool-call.json +0 -60
- package/agent-pane/backend/src/integration/__fixtures__/missing-item-started.json +0 -34
- package/agent-pane/backend/src/integration/__fixtures__/multi-item-turn.json +0 -85
- package/agent-pane/backend/src/integration/__fixtures__/multi-turn.json +0 -48
- package/agent-pane/backend/src/integration/__fixtures__/simple-turn.json +0 -35
- package/agent-pane/backend/src/integration/delta-streaming.test.ts +0 -130
- package/agent-pane/backend/src/integration/helpers/app-server-mock.ts +0 -272
- package/agent-pane/backend/src/integration/helpers/test-app.ts +0 -110
- package/agent-pane/backend/src/integration/helpers/ws-capture.ts +0 -64
- package/agent-pane/backend/src/integration/item-lifecycle.test.ts +0 -133
- package/agent-pane/backend/src/integration/mcp-tool-call.test.ts +0 -102
- package/agent-pane/backend/src/integration/missing-item-started.test.ts +0 -91
- package/agent-pane/backend/src/integration/multi-turn.test.ts +0 -121
- package/agent-pane/backend/src/integration/session-operations.test.ts +0 -175
- package/agent-pane/backend/src/integration/threads-claude-fork.test.ts +0 -241
- package/agent-pane/backend/src/integration/threads.test.ts +0 -309
- package/agent-pane/backend/src/integration/title-generation.test.ts +0 -109
- package/agent-pane/backend/src/integration/turn-cancel.test.ts +0 -111
- package/agent-pane/backend/src/integration/turn-failure.test.ts +0 -97
- package/agent-pane/backend/src/integration/turn-lifecycle.test.ts +0 -141
- package/agent-pane/backend/src/providers/index.ts +0 -16
- package/agent-pane/backend/src/providers/registry.test.ts +0 -92
- package/agent-pane/backend/src/providers/registry.ts +0 -43
- package/agent-pane/backend/src/providers/runtime-provider.ts +0 -49
- package/agent-pane/backend/src/repoburg/action-mapping.test.ts +0 -122
- package/agent-pane/backend/src/repoburg/action-mapping.ts +0 -294
- package/agent-pane/backend/src/repoburg/agent-runtime-client.test.ts +0 -271
- package/agent-pane/backend/src/repoburg/agent-runtime-client.ts +0 -285
- package/agent-pane/backend/src/repoburg/backend-resolver.test.ts +0 -356
- package/agent-pane/backend/src/repoburg/backend-resolver.ts +0 -261
- package/agent-pane/backend/src/repoburg/repoburg-client.test.ts +0 -180
- package/agent-pane/backend/src/repoburg/repoburg-client.ts +0 -194
- package/agent-pane/backend/src/repoburg/repoburg-provider.test.ts +0 -780
- package/agent-pane/backend/src/repoburg/repoburg-provider.ts +0 -471
- package/agent-pane/backend/src/repoburg/turn-projector.test.ts +0 -621
- package/agent-pane/backend/src/repoburg/turn-projector.ts +0 -637
- package/agent-pane/backend/src/routes/files.ts +0 -31
- package/agent-pane/backend/src/routes/mcp.ts +0 -33
- package/agent-pane/backend/src/routes/models.test.ts +0 -92
- package/agent-pane/backend/src/routes/models.ts +0 -16
- package/agent-pane/backend/src/routes/plugins.ts +0 -40
- package/agent-pane/backend/src/routes/providers.test.ts +0 -86
- package/agent-pane/backend/src/routes/providers.ts +0 -86
- package/agent-pane/backend/src/routes/sessions.test.ts +0 -772
- package/agent-pane/backend/src/routes/sessions.ts +0 -565
- package/agent-pane/backend/src/routes/skills.test.ts +0 -114
- package/agent-pane/backend/src/routes/skills.ts +0 -70
- package/agent-pane/backend/src/routes/threads.ts +0 -323
- package/agent-pane/backend/src/routes/turns.test.ts +0 -196
- package/agent-pane/backend/src/routes/turns.ts +0 -103
- package/agent-pane/backend/src/routes/workspaces.test.ts +0 -197
- package/agent-pane/backend/src/routes/workspaces.ts +0 -128
- package/agent-pane/backend/src/ws/index.test.ts +0 -107
- package/agent-pane/backend/src/ws/index.ts +0 -51
- package/agent-pane/backend/tsconfig.json +0 -25
- package/agent-pane/backend/vitest.config.ts +0 -20
- package/agent-pane/eslint.config.js +0 -33
- package/agent-pane/frontend/coverage/base.css +0 -224
- package/agent-pane/frontend/coverage/block-navigation.js +0 -87
- package/agent-pane/frontend/coverage/clover.xml +0 -2962
- package/agent-pane/frontend/coverage/coverage-final.json +0 -37
- package/agent-pane/frontend/coverage/favicon.png +0 -0
- package/agent-pane/frontend/coverage/index.html +0 -251
- package/agent-pane/frontend/coverage/prettify.css +0 -1
- package/agent-pane/frontend/coverage/prettify.js +0 -2
- package/agent-pane/frontend/coverage/sort-arrow-sprite.png +0 -0
- package/agent-pane/frontend/coverage/sorter.js +0 -210
- package/agent-pane/frontend/coverage/src/App.tsx.html +0 -217
- package/agent-pane/frontend/coverage/src/AppInitializer.tsx.html +0 -223
- package/agent-pane/frontend/coverage/src/api/client.ts.html +0 -187
- package/agent-pane/frontend/coverage/src/api/index.html +0 -161
- package/agent-pane/frontend/coverage/src/api/sessions.ts.html +0 -316
- package/agent-pane/frontend/coverage/src/api/websocket.ts.html +0 -544
- package/agent-pane/frontend/coverage/src/api/workspaces.ts.html +0 -199
- package/agent-pane/frontend/coverage/src/components/CollapsibleToolCalls.tsx.html +0 -226
- package/agent-pane/frontend/coverage/src/components/CommandInputBar.tsx.html +0 -382
- package/agent-pane/frontend/coverage/src/components/CommandPalette.tsx.html +0 -715
- package/agent-pane/frontend/coverage/src/components/IdeWorkspace.tsx.html +0 -160
- package/agent-pane/frontend/coverage/src/components/ModelSelector.tsx.html +0 -478
- package/agent-pane/frontend/coverage/src/components/NewSessionDialog.tsx.html +0 -472
- package/agent-pane/frontend/coverage/src/components/RenderArea.tsx.html +0 -574
- package/agent-pane/frontend/coverage/src/components/RightPanel.tsx.html +0 -205
- package/agent-pane/frontend/coverage/src/components/RuntimeIcon.tsx.html +0 -133
- package/agent-pane/frontend/coverage/src/components/SessionTabs.tsx.html +0 -217
- package/agent-pane/frontend/coverage/src/components/StatusBar.tsx.html +0 -271
- package/agent-pane/frontend/coverage/src/components/StatusDot.tsx.html +0 -109
- package/agent-pane/frontend/coverage/src/components/Toast.tsx.html +0 -313
- package/agent-pane/frontend/coverage/src/components/TurnContent.tsx.html +0 -1216
- package/agent-pane/frontend/coverage/src/components/WorkspaceSidebar.tsx.html +0 -1138
- package/agent-pane/frontend/coverage/src/components/index.html +0 -326
- package/agent-pane/frontend/coverage/src/components/panels/ChangesPanel.tsx.html +0 -247
- package/agent-pane/frontend/coverage/src/components/panels/LogsPanel.tsx.html +0 -160
- package/agent-pane/frontend/coverage/src/components/panels/MetricsPanel.tsx.html +0 -427
- package/agent-pane/frontend/coverage/src/components/panels/TurnsPanel.tsx.html +0 -397
- package/agent-pane/frontend/coverage/src/components/panels/index.html +0 -161
- package/agent-pane/frontend/coverage/src/constants/index.html +0 -116
- package/agent-pane/frontend/coverage/src/constants/models.ts.html +0 -406
- package/agent-pane/frontend/coverage/src/hooks/index.html +0 -146
- package/agent-pane/frontend/coverage/src/hooks/useEscapeKey.ts.html +0 -124
- package/agent-pane/frontend/coverage/src/hooks/useOutsideClick.ts.html +0 -142
- package/agent-pane/frontend/coverage/src/hooks/useSessionStatus.ts.html +0 -187
- package/agent-pane/frontend/coverage/src/index.html +0 -146
- package/agent-pane/frontend/coverage/src/main.tsx.html +0 -115
- package/agent-pane/frontend/coverage/src/store/index.html +0 -116
- package/agent-pane/frontend/coverage/src/store/store.ts.html +0 -2404
- package/agent-pane/frontend/coverage/src/test/component-helpers.ts.html +0 -190
- package/agent-pane/frontend/coverage/src/test/fixtures.ts.html +0 -367
- package/agent-pane/frontend/coverage/src/test/index.html +0 -131
- package/agent-pane/frontend/coverage/src/types/index.html +0 -116
- package/agent-pane/frontend/coverage/src/types/index.ts.html +0 -466
- package/agent-pane/frontend/coverage/src/utils/changes.ts.html +0 -202
- package/agent-pane/frontend/coverage/src/utils/duration.ts.html +0 -127
- package/agent-pane/frontend/coverage/src/utils/index.html +0 -131
- package/agent-pane/frontend/dist/assets/index-CJxD4YmJ.js +0 -370
- package/agent-pane/frontend/dist/assets/index-r-xwfOKQ.css +0 -1
- package/agent-pane/frontend/dist/index.html +0 -19
- package/agent-pane/frontend/index.html +0 -18
- package/agent-pane/frontend/package-lock.json +0 -4211
- package/agent-pane/frontend/package.json +0 -35
- package/agent-pane/frontend/src/App.test.tsx +0 -74
- package/agent-pane/frontend/src/App.tsx +0 -44
- package/agent-pane/frontend/src/AppInitializer.test.tsx +0 -142
- package/agent-pane/frontend/src/AppInitializer.tsx +0 -46
- package/agent-pane/frontend/src/__tests__/ThreadsPanel.test.tsx +0 -192
- package/agent-pane/frontend/src/api/client.test.ts +0 -126
- package/agent-pane/frontend/src/api/client.ts +0 -34
- package/agent-pane/frontend/src/api/models.ts +0 -7
- package/agent-pane/frontend/src/api/providers.ts +0 -17
- package/agent-pane/frontend/src/api/sessions.test.ts +0 -246
- package/agent-pane/frontend/src/api/sessions.ts +0 -97
- package/agent-pane/frontend/src/api/skills.ts +0 -12
- package/agent-pane/frontend/src/api/websocket.test.ts +0 -288
- package/agent-pane/frontend/src/api/websocket.ts +0 -240
- package/agent-pane/frontend/src/api/workspaces.test.ts +0 -112
- package/agent-pane/frontend/src/api/workspaces.ts +0 -38
- package/agent-pane/frontend/src/components/AskUserItem.test.tsx +0 -143
- package/agent-pane/frontend/src/components/AskUserItem.tsx +0 -207
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.test.tsx +0 -49
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.tsx +0 -56
- package/agent-pane/frontend/src/components/CommandInputBar.test.tsx +0 -133
- package/agent-pane/frontend/src/components/CommandInputBar.tsx +0 -584
- package/agent-pane/frontend/src/components/CommandPalette.test.tsx +0 -226
- package/agent-pane/frontend/src/components/CommandPalette.tsx +0 -194
- package/agent-pane/frontend/src/components/CommentableText.test.tsx +0 -196
- package/agent-pane/frontend/src/components/CommentableText.tsx +0 -379
- package/agent-pane/frontend/src/components/IdeWorkspace.test.tsx +0 -39
- package/agent-pane/frontend/src/components/IdeWorkspace.tsx +0 -51
- package/agent-pane/frontend/src/components/ModelSelector.test.tsx +0 -129
- package/agent-pane/frontend/src/components/ModelSelector.tsx +0 -191
- package/agent-pane/frontend/src/components/NewSessionDialog.test.tsx +0 -95
- package/agent-pane/frontend/src/components/NewSessionDialog.tsx +0 -129
- package/agent-pane/frontend/src/components/RenderArea.test.tsx +0 -160
- package/agent-pane/frontend/src/components/RenderArea.tsx +0 -170
- package/agent-pane/frontend/src/components/ResizeHandle.tsx +0 -29
- package/agent-pane/frontend/src/components/RightPanel.test.tsx +0 -43
- package/agent-pane/frontend/src/components/RightPanel.tsx +0 -46
- package/agent-pane/frontend/src/components/RuntimeIcon.test.tsx +0 -32
- package/agent-pane/frontend/src/components/RuntimeIcon.tsx +0 -16
- package/agent-pane/frontend/src/components/SessionTabs.test.tsx +0 -72
- package/agent-pane/frontend/src/components/SessionTabs.tsx +0 -49
- package/agent-pane/frontend/src/components/StatusBar.test.tsx +0 -56
- package/agent-pane/frontend/src/components/StatusBar.tsx +0 -129
- package/agent-pane/frontend/src/components/StatusDot.test.tsx +0 -25
- package/agent-pane/frontend/src/components/StatusDot.tsx +0 -8
- package/agent-pane/frontend/src/components/Toast.test.tsx +0 -79
- package/agent-pane/frontend/src/components/Toast.tsx +0 -76
- package/agent-pane/frontend/src/components/TurnContent.test.tsx +0 -377
- package/agent-pane/frontend/src/components/TurnContent.tsx +0 -594
- package/agent-pane/frontend/src/components/WorkspaceSidebar.test.tsx +0 -297
- package/agent-pane/frontend/src/components/WorkspaceSidebar.tsx +0 -351
- package/agent-pane/frontend/src/components/input/OptionSubPopup.tsx +0 -32
- package/agent-pane/frontend/src/components/input/SkillMentionPopup.test.tsx +0 -63
- package/agent-pane/frontend/src/components/input/SkillMentionPopup.tsx +0 -78
- package/agent-pane/frontend/src/components/input/SlashPopup.test.tsx +0 -96
- package/agent-pane/frontend/src/components/input/SlashPopup.tsx +0 -82
- package/agent-pane/frontend/src/components/panels/ChangesPanel.test.tsx +0 -61
- package/agent-pane/frontend/src/components/panels/ChangesPanel.tsx +0 -54
- package/agent-pane/frontend/src/components/panels/TasksPanel.test.tsx +0 -97
- package/agent-pane/frontend/src/components/panels/TasksPanel.tsx +0 -208
- package/agent-pane/frontend/src/components/panels/ThreadsPanel.tsx +0 -382
- package/agent-pane/frontend/src/components/panels/TurnsPanel.test.tsx +0 -131
- package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +0 -131
- package/agent-pane/frontend/src/constants/models.test.ts +0 -97
- package/agent-pane/frontend/src/constants/models.ts +0 -166
- package/agent-pane/frontend/src/hooks/useEscapeKey.test.ts +0 -42
- package/agent-pane/frontend/src/hooks/useEscapeKey.ts +0 -13
- package/agent-pane/frontend/src/hooks/useOutsideClick.test.ts +0 -92
- package/agent-pane/frontend/src/hooks/useOutsideClick.ts +0 -19
- package/agent-pane/frontend/src/hooks/useProvider.ts +0 -44
- package/agent-pane/frontend/src/hooks/useResizable.test.ts +0 -97
- package/agent-pane/frontend/src/hooks/useResizable.ts +0 -75
- package/agent-pane/frontend/src/hooks/useSessionStatus.test.ts +0 -116
- package/agent-pane/frontend/src/hooks/useSessionStatus.ts +0 -34
- package/agent-pane/frontend/src/main.test.tsx +0 -37
- package/agent-pane/frontend/src/main.tsx +0 -10
- package/agent-pane/frontend/src/store/sessionSlice.ts +0 -440
- package/agent-pane/frontend/src/store/store.test.ts +0 -1338
- package/agent-pane/frontend/src/store/store.ts +0 -37
- package/agent-pane/frontend/src/store/threadSlice.ts +0 -136
- package/agent-pane/frontend/src/store/uiSlice.ts +0 -133
- package/agent-pane/frontend/src/store/workspaceSlice.ts +0 -77
- package/agent-pane/frontend/src/store/wsSlice.ts +0 -368
- package/agent-pane/frontend/src/styles/app.css +0 -627
- package/agent-pane/frontend/src/styles/tokens.css +0 -62
- package/agent-pane/frontend/src/test/component-helpers.ts +0 -38
- package/agent-pane/frontend/src/test/fixtures.ts +0 -101
- package/agent-pane/frontend/src/test-setup.ts +0 -5
- package/agent-pane/frontend/src/types/index.test.ts +0 -128
- package/agent-pane/frontend/src/types/index.ts +0 -134
- package/agent-pane/frontend/src/utils/changes.test.ts +0 -81
- package/agent-pane/frontend/src/utils/changes.ts +0 -39
- package/agent-pane/frontend/src/utils/duration.test.ts +0 -76
- package/agent-pane/frontend/src/utils/duration.ts +0 -14
- package/agent-pane/frontend/tsconfig.json +0 -25
- package/agent-pane/frontend/vite.config.ts +0 -22
- package/agent-pane/frontend/vitest.config.ts +0 -29
- package/agent-pane/package-lock.json +0 -1527
- package/agent-pane/package.json +0 -19
- package/agent-pane/shared/provider-types.ts +0 -67
- package/agent-pane/shared/types.ts +0 -184
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { useState, useRef, useEffect } from 'react';
|
|
2
|
-
import { useStore } from '../store/store.ts';
|
|
3
|
-
import { PROVIDERS, getModels, getReasoningLevels, getDefaultReasoning } from '../constants/models.ts';
|
|
4
|
-
import { useOutsideClick } from '../hooks/useOutsideClick.ts';
|
|
5
|
-
import { useEscapeKey } from '../hooks/useEscapeKey.ts';
|
|
6
|
-
import { ChevronDown, Check, Lock } from 'lucide-react';
|
|
7
|
-
import type { ModelInfo } from '../types/index.ts';
|
|
8
|
-
|
|
9
|
-
export default function ModelSelector() {
|
|
10
|
-
const { activeSessionId, sessions, updateModelConfig } = useStore();
|
|
11
|
-
const showModelSelector = useStore((s) => s.showModelSelector);
|
|
12
|
-
const setShowModelSelector = useStore((s) => s.setShowModelSelector);
|
|
13
|
-
const session = sessions.find((s) => s.id === activeSessionId);
|
|
14
|
-
const storeModels = useStore((s) => s.models);
|
|
15
|
-
const modelsLoading = useStore((s) => s.modelsLoading);
|
|
16
|
-
const loadModels = useStore((s) => s.loadModels);
|
|
17
|
-
|
|
18
|
-
const [open, setOpen] = useState(false);
|
|
19
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
20
|
-
|
|
21
|
-
useOutsideClick(ref, () => setOpen(false), open);
|
|
22
|
-
useEscapeKey(() => setOpen(false), open);
|
|
23
|
-
|
|
24
|
-
// Open when triggered by /model command
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (showModelSelector) {
|
|
27
|
-
setOpen(true);
|
|
28
|
-
setShowModelSelector(false);
|
|
29
|
-
}
|
|
30
|
-
}, [showModelSelector, setShowModelSelector]);
|
|
31
|
-
|
|
32
|
-
// Load dynamic models on mount
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if (session?.runtime_type) {
|
|
35
|
-
loadModels(session.runtime_type);
|
|
36
|
-
}
|
|
37
|
-
}, [session?.runtime_type, loadModels]);
|
|
38
|
-
|
|
39
|
-
if (!session) return null;
|
|
40
|
-
|
|
41
|
-
const providerId = session.runtime_type;
|
|
42
|
-
const currentModel = session.model ?? '';
|
|
43
|
-
const currentReasoning = session.model_reasoning_effort ?? '';
|
|
44
|
-
|
|
45
|
-
// Use dynamic models from store, fallback to hardcoded
|
|
46
|
-
const dynamicModels: ModelInfo[] = storeModels.length > 0 ? storeModels : [];
|
|
47
|
-
const fallbackModels = getModels(providerId);
|
|
48
|
-
const baseModels = dynamicModels.length > 0 ? dynamicModels : fallbackModels;
|
|
49
|
-
|
|
50
|
-
// Prepend current model if not in the list (e.g. custom or Ollama models)
|
|
51
|
-
const models: ModelInfo[] = [...baseModels];
|
|
52
|
-
if (currentModel && !models.some((m) => m.slug === currentModel)) {
|
|
53
|
-
const ref = fallbackModels[0];
|
|
54
|
-
models.unshift({
|
|
55
|
-
slug: currentModel,
|
|
56
|
-
displayName: currentModel,
|
|
57
|
-
description: 'Custom model',
|
|
58
|
-
defaultReasoning: ref?.defaultReasoning ?? 'medium',
|
|
59
|
-
reasoningLevels: ref?.reasoningLevels ?? [],
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Find a model by slug
|
|
64
|
-
function findModel(slug: string): ModelInfo | undefined {
|
|
65
|
-
return models.find((m) => m.slug === slug);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Get reasoning levels for a model
|
|
69
|
-
function levelsFor(slug: string): { effort: string; label: string }[] {
|
|
70
|
-
if (dynamicModels.length > 0) {
|
|
71
|
-
return findModel(slug)?.reasoningLevels ?? [];
|
|
72
|
-
}
|
|
73
|
-
return getReasoningLevels(providerId, slug);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Get default reasoning for a model
|
|
77
|
-
function defaultFor(slug: string): string | undefined {
|
|
78
|
-
if (dynamicModels.length > 0) {
|
|
79
|
-
return findModel(slug)?.defaultReasoning;
|
|
80
|
-
}
|
|
81
|
-
return getDefaultReasoning(providerId, slug);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const provider = PROVIDERS.find((p) => p.id === providerId);
|
|
85
|
-
const modelDef = findModel(currentModel);
|
|
86
|
-
const reasoningLevels = currentModel ? levelsFor(currentModel) : [];
|
|
87
|
-
|
|
88
|
-
const providerLabel = provider?.label ?? providerId;
|
|
89
|
-
const modelLabel = modelDef?.displayName ?? (currentModel || 'Default');
|
|
90
|
-
const reasoningDef = reasoningLevels.find((r) => r.effort === currentReasoning);
|
|
91
|
-
const reasoningLabel = reasoningDef?.label ?? (currentReasoning || 'Default');
|
|
92
|
-
|
|
93
|
-
function handleSelectModel(modelSlug: string) {
|
|
94
|
-
const levels = levelsFor(modelSlug);
|
|
95
|
-
const stillSupported = levels.some((r) => r.effort === currentReasoning);
|
|
96
|
-
const newReasoning = stillSupported
|
|
97
|
-
? currentReasoning
|
|
98
|
-
: (defaultFor(modelSlug) ?? levels[0]?.effort ?? '');
|
|
99
|
-
updateModelConfig(modelSlug, newReasoning);
|
|
100
|
-
setOpen(false);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function handleSelectReasoning(effort: string) {
|
|
104
|
-
updateModelConfig(currentModel, effort);
|
|
105
|
-
setOpen(false);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<div className="model-selector" ref={ref}>
|
|
110
|
-
<button className="model-selector-btn" onClick={() => setOpen(!open)} title="Change model and thinking level">
|
|
111
|
-
<span className="model-selector-provider">{providerLabel}</span>
|
|
112
|
-
<span className="model-selector-sep">›</span>
|
|
113
|
-
<span className="model-selector-model">{modelLabel}</span>
|
|
114
|
-
{reasoningLabel !== 'Default' && (
|
|
115
|
-
<>
|
|
116
|
-
<span className="model-selector-sep">›</span>
|
|
117
|
-
<span className="model-selector-reasoning">{reasoningLabel}</span>
|
|
118
|
-
</>
|
|
119
|
-
)}
|
|
120
|
-
<ChevronDown size={12} className="model-selector-chevron" />
|
|
121
|
-
</button>
|
|
122
|
-
|
|
123
|
-
{open && (
|
|
124
|
-
<div className="model-selector-popover">
|
|
125
|
-
{/* Column 1: Provider */}
|
|
126
|
-
<div className="model-selector-column">
|
|
127
|
-
<div className="model-selector-col-header">Provider</div>
|
|
128
|
-
{PROVIDERS.map((p) => (
|
|
129
|
-
<div
|
|
130
|
-
key={p.id}
|
|
131
|
-
className={`model-selector-item ${p.id === providerId ? 'selected' : ''} ${!p.available ? 'disabled' : ''}`}
|
|
132
|
-
onClick={() => {
|
|
133
|
-
if (!p.available) return;
|
|
134
|
-
if (p.id === providerId) { setOpen(false); return; }
|
|
135
|
-
// Switch provider — clear model and reasoning, keep popover open
|
|
136
|
-
updateModelConfig('', '', p.id);
|
|
137
|
-
}}
|
|
138
|
-
>
|
|
139
|
-
<span>{p.label}</span>
|
|
140
|
-
{!p.available && (
|
|
141
|
-
<span className="model-selector-coming-soon">
|
|
142
|
-
<Lock size={10} /> Soon
|
|
143
|
-
</span>
|
|
144
|
-
)}
|
|
145
|
-
{p.id === providerId && <Check size={12} />}
|
|
146
|
-
</div>
|
|
147
|
-
))}
|
|
148
|
-
</div>
|
|
149
|
-
|
|
150
|
-
{/* Column 2: Model */}
|
|
151
|
-
<div className="model-selector-column">
|
|
152
|
-
<div className="model-selector-col-header">Model</div>
|
|
153
|
-
{modelsLoading && <div className="model-selector-empty">Loading models…</div>}
|
|
154
|
-
{!modelsLoading && models.length === 0 && (
|
|
155
|
-
<div className="model-selector-empty">No models available</div>
|
|
156
|
-
)}
|
|
157
|
-
{models.map((m) => (
|
|
158
|
-
<div
|
|
159
|
-
key={m.slug}
|
|
160
|
-
className={`model-selector-item ${m.slug === currentModel ? 'selected' : ''}`}
|
|
161
|
-
onClick={() => handleSelectModel(m.slug)}
|
|
162
|
-
>
|
|
163
|
-
<div className="model-selector-item-main">
|
|
164
|
-
<span className="model-selector-item-name">{m.displayName}</span>
|
|
165
|
-
<span className="model-selector-item-desc">{m.description}</span>
|
|
166
|
-
</div>
|
|
167
|
-
{m.slug === currentModel && <Check size={12} />}
|
|
168
|
-
</div>
|
|
169
|
-
))}
|
|
170
|
-
</div>
|
|
171
|
-
|
|
172
|
-
{/* Column 3: Thinking Level */}
|
|
173
|
-
<div className="model-selector-column">
|
|
174
|
-
<div className="model-selector-col-header">Thinking</div>
|
|
175
|
-
{reasoningLevels.length === 0 && <div className="model-selector-empty">Select a model first</div>}
|
|
176
|
-
{reasoningLevels.map((r) => (
|
|
177
|
-
<div
|
|
178
|
-
key={r.effort}
|
|
179
|
-
className={`model-selector-item ${r.effort === currentReasoning ? 'selected' : ''}`}
|
|
180
|
-
onClick={() => handleSelectReasoning(r.effort)}
|
|
181
|
-
>
|
|
182
|
-
<span>{r.label}</span>
|
|
183
|
-
{r.effort === currentReasoning && <Check size={12} />}
|
|
184
|
-
</div>
|
|
185
|
-
))}
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
)}
|
|
189
|
-
</div>
|
|
190
|
-
);
|
|
191
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
|
|
3
|
-
import NewSessionDialog from './NewSessionDialog.tsx';
|
|
4
|
-
import { resetStore } from '../test/component-helpers.ts';
|
|
5
|
-
|
|
6
|
-
describe('NewSessionDialog', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
resetStore();
|
|
9
|
-
});
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
cleanup();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('returns null when showNewSessionDialog is false', () => {
|
|
15
|
-
const { container } = render(<NewSessionDialog />);
|
|
16
|
-
expect(container.firstChild).toBeNull();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('renders form with title, model, and cwd inputs when visible', () => {
|
|
20
|
-
resetStore({ showNewSessionDialog: true });
|
|
21
|
-
render(<NewSessionDialog />);
|
|
22
|
-
expect(screen.getByPlaceholderText('e.g. refactor auth module')).toBeInTheDocument();
|
|
23
|
-
expect(screen.getByPlaceholderText('e.g. /Users/me/projects/my-app')).toBeInTheDocument();
|
|
24
|
-
expect(screen.getByText('Create')).toBeInTheDocument();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('model select shows codex models', () => {
|
|
28
|
-
resetStore({ showNewSessionDialog: true });
|
|
29
|
-
render(<NewSessionDialog />);
|
|
30
|
-
const modelSelect = document.querySelector('select') as HTMLSelectElement;
|
|
31
|
-
// Default option + 4 codex models
|
|
32
|
-
expect(modelSelect.options.length).toBe(5);
|
|
33
|
-
expect(Array.from(modelSelect.options).map((o) => o.textContent)).toEqual(
|
|
34
|
-
expect.arrayContaining(['Default (Codex picks)', 'GPT-5.6-Sol — Flagship — complex, open-ended work']),
|
|
35
|
-
);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('selecting a model shows reasoning levels', () => {
|
|
39
|
-
resetStore({ showNewSessionDialog: true });
|
|
40
|
-
render(<NewSessionDialog />);
|
|
41
|
-
const modelSelect = document.querySelector('select') as HTMLSelectElement;
|
|
42
|
-
fireEvent.change(modelSelect, { target: { value: 'gpt-5.6-sol' } });
|
|
43
|
-
// Reasoning level select should appear (second select)
|
|
44
|
-
const reasoningSelect = document.querySelectorAll('select')[1];
|
|
45
|
-
expect(reasoningSelect).toBeTruthy();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('changing model to one without current reasoning level switches to default', () => {
|
|
49
|
-
resetStore({
|
|
50
|
-
showNewSessionDialog: true,
|
|
51
|
-
lastModelConfig: { provider: 'codex', model: 'gpt-5.6-sol', reasoning: 'ultra' },
|
|
52
|
-
});
|
|
53
|
-
render(<NewSessionDialog />);
|
|
54
|
-
// gpt-5.6-sol has reasoning 'ultra', but gpt-5.5 only goes up to 'xhigh'
|
|
55
|
-
// First select gpt-5.6-sol to get reasoning levels visible
|
|
56
|
-
const selects = document.querySelectorAll('select');
|
|
57
|
-
const modelSelect = selects[0];
|
|
58
|
-
fireEvent.change(modelSelect, { target: { value: 'gpt-5.6-sol' } });
|
|
59
|
-
// Now select gpt-5.5
|
|
60
|
-
fireEvent.change(modelSelect, { target: { value: 'gpt-5.5' } });
|
|
61
|
-
// gpt-5.5 defaultReasoning is 'medium'
|
|
62
|
-
const reasoningSelect = document.querySelectorAll('select')[1] as HTMLSelectElement;
|
|
63
|
-
expect(reasoningSelect.value).toBe('medium');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('Create button calls createSession with form values', async () => {
|
|
67
|
-
const createSession = vi.fn().mockResolvedValue(undefined);
|
|
68
|
-
resetStore({ showNewSessionDialog: true, createSession });
|
|
69
|
-
render(<NewSessionDialog />);
|
|
70
|
-
const titleInput = screen.getByPlaceholderText('e.g. refactor auth module');
|
|
71
|
-
fireEvent.change(titleInput, { target: { value: 'My Session' } });
|
|
72
|
-
fireEvent.click(screen.getByText('Create'));
|
|
73
|
-
expect(createSession).toHaveBeenCalledWith(expect.objectContaining({
|
|
74
|
-
title: 'My Session',
|
|
75
|
-
runtime_type: 'codex',
|
|
76
|
-
}));
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('ESC closes dialog', () => {
|
|
80
|
-
const setShowNewSessionDialog = vi.fn();
|
|
81
|
-
resetStore({ showNewSessionDialog: true, setShowNewSessionDialog });
|
|
82
|
-
render(<NewSessionDialog />);
|
|
83
|
-
fireEvent.keyDown(document.querySelector('.dialog')!, { key: 'Escape' });
|
|
84
|
-
expect(setShowNewSessionDialog).toHaveBeenCalledWith(false);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('overlay click closes dialog', () => {
|
|
88
|
-
const setShowNewSessionDialog = vi.fn();
|
|
89
|
-
resetStore({ showNewSessionDialog: true, setShowNewSessionDialog });
|
|
90
|
-
render(<NewSessionDialog />);
|
|
91
|
-
const overlay = document.querySelector('.dialog-overlay')!;
|
|
92
|
-
fireEvent.click(overlay);
|
|
93
|
-
expect(setShowNewSessionDialog).toHaveBeenCalledWith(false);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { useStore } from '../store/store.ts';
|
|
3
|
-
import { X } from 'lucide-react';
|
|
4
|
-
import { getModels, getReasoningLevels, getDefaultReasoning } from '../constants/models.ts';
|
|
5
|
-
|
|
6
|
-
export default function NewSessionDialog() {
|
|
7
|
-
const { showNewSessionDialog, setShowNewSessionDialog, createSession, lastModelConfig } = useStore();
|
|
8
|
-
|
|
9
|
-
const initialProvider = lastModelConfig?.provider ?? 'codex';
|
|
10
|
-
const initialModel = lastModelConfig?.model ?? '';
|
|
11
|
-
const initialReasoning = lastModelConfig?.reasoning ?? '';
|
|
12
|
-
|
|
13
|
-
const [title, setTitle] = useState('');
|
|
14
|
-
const [model, setModel] = useState(initialModel);
|
|
15
|
-
const [reasoning, setReasoning] = useState(initialReasoning);
|
|
16
|
-
const [cwd, setCwd] = useState('');
|
|
17
|
-
|
|
18
|
-
if (!showNewSessionDialog) return null;
|
|
19
|
-
|
|
20
|
-
const models = getModels(initialProvider);
|
|
21
|
-
const reasoningLevels = model ? getReasoningLevels(initialProvider, model) : [];
|
|
22
|
-
|
|
23
|
-
function handleSubmit() {
|
|
24
|
-
createSession({
|
|
25
|
-
title: title.trim() || undefined,
|
|
26
|
-
model: model || undefined,
|
|
27
|
-
model_reasoning_effort: reasoning || undefined,
|
|
28
|
-
cwd: cwd.trim() || undefined,
|
|
29
|
-
runtime_type: initialProvider,
|
|
30
|
-
});
|
|
31
|
-
setTitle('');
|
|
32
|
-
setCwd('');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function handleKeyDown(e: React.KeyboardEvent) {
|
|
36
|
-
if (e.key === 'Escape') {
|
|
37
|
-
setShowNewSessionDialog(false);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function handleModelChange(slug: string) {
|
|
42
|
-
setModel(slug);
|
|
43
|
-
// If current reasoning is not supported by new model, switch to default
|
|
44
|
-
const levels = getReasoningLevels(initialProvider, slug);
|
|
45
|
-
const stillSupported = levels.some((r) => r.effort === reasoning);
|
|
46
|
-
if (!stillSupported) {
|
|
47
|
-
setReasoning(getDefaultReasoning(initialProvider, slug) ?? levels[0]?.effort ?? '');
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<div className="dialog-overlay" onClick={() => setShowNewSessionDialog(false)}>
|
|
53
|
-
<div className="dialog" onClick={(e) => e.stopPropagation()} onKeyDown={handleKeyDown}>
|
|
54
|
-
<div className="dialog-header">
|
|
55
|
-
<span className="dialog-title">New Session</span>
|
|
56
|
-
<button className="dialog-close" onClick={() => setShowNewSessionDialog(false)}>
|
|
57
|
-
<X size={16} />
|
|
58
|
-
</button>
|
|
59
|
-
</div>
|
|
60
|
-
<div className="dialog-body">
|
|
61
|
-
<label className="dialog-label">
|
|
62
|
-
Title (optional)
|
|
63
|
-
<input
|
|
64
|
-
className="dialog-input"
|
|
65
|
-
type="text"
|
|
66
|
-
value={title}
|
|
67
|
-
onChange={(e) => setTitle(e.target.value)}
|
|
68
|
-
placeholder="e.g. refactor auth module"
|
|
69
|
-
autoFocus
|
|
70
|
-
/>
|
|
71
|
-
</label>
|
|
72
|
-
|
|
73
|
-
<label className="dialog-label">
|
|
74
|
-
Model
|
|
75
|
-
<select
|
|
76
|
-
className="dialog-input dialog-select"
|
|
77
|
-
value={model}
|
|
78
|
-
onChange={(e) => handleModelChange(e.target.value)}
|
|
79
|
-
>
|
|
80
|
-
<option value="">Default (Codex picks)</option>
|
|
81
|
-
{models.map((m) => (
|
|
82
|
-
<option key={m.slug} value={m.slug}>
|
|
83
|
-
{m.displayName} — {m.description}
|
|
84
|
-
</option>
|
|
85
|
-
))}
|
|
86
|
-
</select>
|
|
87
|
-
</label>
|
|
88
|
-
|
|
89
|
-
{reasoningLevels.length > 0 && (
|
|
90
|
-
<label className="dialog-label">
|
|
91
|
-
Thinking Level
|
|
92
|
-
<select
|
|
93
|
-
className="dialog-input dialog-select"
|
|
94
|
-
value={reasoning}
|
|
95
|
-
onChange={(e) => setReasoning(e.target.value)}
|
|
96
|
-
>
|
|
97
|
-
<option value="">Default ({getDefaultReasoning(initialProvider, model)})</option>
|
|
98
|
-
{reasoningLevels.map((r) => (
|
|
99
|
-
<option key={r.effort} value={r.effort}>
|
|
100
|
-
{r.label}
|
|
101
|
-
</option>
|
|
102
|
-
))}
|
|
103
|
-
</select>
|
|
104
|
-
</label>
|
|
105
|
-
)}
|
|
106
|
-
|
|
107
|
-
<label className="dialog-label">
|
|
108
|
-
Working Directory (optional)
|
|
109
|
-
<input
|
|
110
|
-
className="dialog-input"
|
|
111
|
-
type="text"
|
|
112
|
-
value={cwd}
|
|
113
|
-
onChange={(e) => setCwd(e.target.value)}
|
|
114
|
-
placeholder="e.g. /Users/me/projects/my-app"
|
|
115
|
-
/>
|
|
116
|
-
</label>
|
|
117
|
-
</div>
|
|
118
|
-
<div className="dialog-footer">
|
|
119
|
-
<button className="dialog-btn-cancel" onClick={() => setShowNewSessionDialog(false)}>
|
|
120
|
-
Cancel
|
|
121
|
-
</button>
|
|
122
|
-
<button className="dialog-btn-create" onClick={handleSubmit}>
|
|
123
|
-
Create
|
|
124
|
-
</button>
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import { render, screen, cleanup } from '@testing-library/react';
|
|
3
|
-
import RenderArea from './RenderArea.tsx';
|
|
4
|
-
import { resetStore } from '../test/component-helpers.ts';
|
|
5
|
-
import { makeDetail, makeTurn, makeItem } from '../test/fixtures.ts';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
describe('RenderArea', () => {
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
resetStore();
|
|
11
|
-
});
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
cleanup();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('shows "No session selected" when no active session and not loading', () => {
|
|
17
|
-
render(<RenderArea />);
|
|
18
|
-
expect(screen.getByText('No session selected')).toBeInTheDocument();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('shows skeleton bars when loading and no detail', () => {
|
|
22
|
-
resetStore({ loading: true });
|
|
23
|
-
render(<RenderArea />);
|
|
24
|
-
expect(document.querySelector('.skeleton-bars')).toBeInTheDocument();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('renders turns when session has detail', () => {
|
|
28
|
-
const turn = makeTurn('t1', 's1', { user_prompt: 'Hello agent' });
|
|
29
|
-
const detail = makeDetail('s1', [turn]);
|
|
30
|
-
resetStore({ activeSessionId: 's1', sessionDetails: { s1: detail } });
|
|
31
|
-
render(<RenderArea />);
|
|
32
|
-
expect(screen.getByText('Hello agent')).toBeInTheDocument();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('shows "No turns yet" when session has no turns', () => {
|
|
36
|
-
const detail = makeDetail('s1', []);
|
|
37
|
-
resetStore({ activeSessionId: 's1', sessionDetails: { s1: detail } });
|
|
38
|
-
render(<RenderArea />);
|
|
39
|
-
expect(screen.getByText('No turns yet. Send a message below to start.')).toBeInTheDocument();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('shows submitting prompt at top while waiting', () => {
|
|
43
|
-
const detail = makeDetail('s1', []);
|
|
44
|
-
resetStore({ activeSessionId: 's1', sessionDetails: { s1: detail }, submittingPrompt: 'pending msg' });
|
|
45
|
-
render(<RenderArea />);
|
|
46
|
-
expect(screen.getByText('pending msg')).toBeInTheDocument();
|
|
47
|
-
expect(screen.getByText('Sending…')).toBeInTheDocument();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('changes tab shows file changes when present', () => {
|
|
51
|
-
const item = makeItem('i1', 't1', 'file_change');
|
|
52
|
-
item.data = { changes: [{ path: 'src/app.ts', kind: 'modify' }] };
|
|
53
|
-
const turn = makeTurn('t1', 's1', { items: [item] });
|
|
54
|
-
const detail = makeDetail('s1', [turn]);
|
|
55
|
-
resetStore({ activeSessionId: 's1', sessionDetails: { s1: detail }, activeRightTab: 'changes' });
|
|
56
|
-
render(<RenderArea />);
|
|
57
|
-
expect(screen.getByText(/app\.ts/)).toBeInTheDocument();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('changes tab shows "No file changes" when empty', () => {
|
|
61
|
-
const detail = makeDetail('s1', [makeTurn('t1', 's1')]);
|
|
62
|
-
resetStore({ activeSessionId: 's1', sessionDetails: { s1: detail }, activeRightTab: 'changes' });
|
|
63
|
-
render(<RenderArea />);
|
|
64
|
-
expect(screen.getByText('No file changes in this scope.')).toBeInTheDocument();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('changes tab shows "No session selected" when no session', () => {
|
|
68
|
-
resetStore({ activeRightTab: 'changes' });
|
|
69
|
-
render(<RenderArea />);
|
|
70
|
-
expect(screen.getByText('No session selected')).toBeInTheDocument();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// --- selectedChangePath scroll effect ---
|
|
74
|
-
|
|
75
|
-
it('selectedChangePath scrolls to diff element and clears path', () => {
|
|
76
|
-
// Stub scrollIntoView on Element prototype (jsdom doesn't implement it)
|
|
77
|
-
const proto = Element.prototype as unknown as { scrollIntoView: unknown };
|
|
78
|
-
const orig = proto.scrollIntoView;
|
|
79
|
-
proto.scrollIntoView = vi.fn();
|
|
80
|
-
|
|
81
|
-
vi.useFakeTimers();
|
|
82
|
-
const item = makeItem('i1', 't1', 'file_change');
|
|
83
|
-
item.data = { changes: [{ path: 'src/app.ts', kind: 'modify' }] };
|
|
84
|
-
const turn = makeTurn('t1', 's1', { items: [item] });
|
|
85
|
-
const detail = makeDetail('s1', [turn]);
|
|
86
|
-
const setSelectedChangePath = vi.fn();
|
|
87
|
-
resetStore({
|
|
88
|
-
activeSessionId: 's1',
|
|
89
|
-
sessionDetails: { s1: detail },
|
|
90
|
-
activeRightTab: 'changes',
|
|
91
|
-
selectedChangePath: 'src/app.ts',
|
|
92
|
-
setSelectedChangePath,
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
render(<RenderArea />);
|
|
96
|
-
vi.advanceTimersByTime(100);
|
|
97
|
-
|
|
98
|
-
expect(setSelectedChangePath).toHaveBeenCalledWith(null);
|
|
99
|
-
vi.useRealTimers();
|
|
100
|
-
proto.scrollIntoView = orig;
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// --- changes tab edge cases ---
|
|
104
|
-
|
|
105
|
-
it('changes tab shows "files" plural when multiple changes', () => {
|
|
106
|
-
const item = makeItem('i1', 't1', 'file_change');
|
|
107
|
-
item.data = {
|
|
108
|
-
changes: [
|
|
109
|
-
{ path: 'src/a.ts', kind: 'add' },
|
|
110
|
-
{ path: 'src/b.ts', kind: 'delete' },
|
|
111
|
-
],
|
|
112
|
-
};
|
|
113
|
-
const turn = makeTurn('t1', 's1', { items: [item] });
|
|
114
|
-
const detail = makeDetail('s1', [turn]);
|
|
115
|
-
resetStore({ activeSessionId: 's1', sessionDetails: { s1: detail }, activeRightTab: 'changes' });
|
|
116
|
-
render(<RenderArea />);
|
|
117
|
-
expect(screen.getByText('2 files')).toBeInTheDocument();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('changes tab shows add and delete status badges', () => {
|
|
121
|
-
const item = makeItem('i1', 't1', 'file_change');
|
|
122
|
-
item.data = {
|
|
123
|
-
changes: [
|
|
124
|
-
{ path: 'src/new.ts', kind: 'add' },
|
|
125
|
-
{ path: 'src/old.ts', kind: 'delete' },
|
|
126
|
-
],
|
|
127
|
-
};
|
|
128
|
-
const turn = makeTurn('t1', 's1', { items: [item] });
|
|
129
|
-
const detail = makeDetail('s1', [turn]);
|
|
130
|
-
resetStore({ activeSessionId: 's1', sessionDetails: { s1: detail }, activeRightTab: 'changes' });
|
|
131
|
-
render(<RenderArea />);
|
|
132
|
-
expect(screen.getByText('Added')).toBeInTheDocument();
|
|
133
|
-
expect(screen.getByText('Deleted')).toBeInTheDocument();
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('changes tab shows directory path split', () => {
|
|
137
|
-
const item = makeItem('i1', 't1', 'file_change');
|
|
138
|
-
item.data = { changes: [{ path: 'src/components/Foo.tsx', kind: 'modify' }] };
|
|
139
|
-
const turn = makeTurn('t1', 's1', { items: [item] });
|
|
140
|
-
const detail = makeDetail('s1', [turn]);
|
|
141
|
-
resetStore({ activeSessionId: 's1', sessionDetails: { s1: detail }, activeRightTab: 'changes' });
|
|
142
|
-
render(<RenderArea />);
|
|
143
|
-
expect(screen.getByText('Foo.tsx')).toBeInTheDocument();
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// --- loading + detail edge case ---
|
|
147
|
-
|
|
148
|
-
it('loading with existing detail renders turns, not skeleton', () => {
|
|
149
|
-
const turn = makeTurn('t1', 's1', { user_prompt: 'Hello' });
|
|
150
|
-
const detail = makeDetail('s1', [turn]);
|
|
151
|
-
resetStore({
|
|
152
|
-
activeSessionId: 's1',
|
|
153
|
-
sessionDetails: { s1: detail },
|
|
154
|
-
loading: true,
|
|
155
|
-
});
|
|
156
|
-
render(<RenderArea />);
|
|
157
|
-
expect(screen.getByText('Hello')).toBeInTheDocument();
|
|
158
|
-
expect(document.querySelector('.skeleton-bars')).not.toBeInTheDocument();
|
|
159
|
-
});
|
|
160
|
-
});
|