ompchamber 3.1.1 → 3.2.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/dist/client/index.html +1 -1
- package/dist/client/index.js +279 -280
- package/dist/client/static/js/index-01dv383b.css +1 -0
- package/dist/client/static/js/{index-qhzsybkn.js → index-1q6165r3.js} +641 -641
- package/package.json +1 -1
- package/src/cli/lib/output.js +15 -8
- package/src/client/components/common/Modal.tsx +14 -4
- package/src/client/components/{layout/session-sidebar/SubagentList.tsx → common/subagent-list/index.tsx} +8 -10
- package/src/client/components/layout/session-sidebar/CategoryItem.tsx +6 -30
- package/src/client/components/layout/session-sidebar/Toolbar.tsx +11 -5
- package/src/client/components/layout/session-sidebar/index.tsx +4 -2
- package/src/client/components/mobile/LayoutWrapper.tsx +6 -0
- package/src/client/components/mobile/SessionSidebar.tsx +4 -0
- package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +66 -29
- package/src/client/components/mobile/mobile-session-sidebar/SessionRow.tsx +50 -12
- package/src/client/components/mobile/mobile-session-sidebar/Toolbar.tsx +21 -1
- package/src/client/components/settings/Modal.tsx +35 -6
- package/src/client/components/settings/SettingsSidebarList.tsx +1 -1
- package/src/client/components/settings/categories/AgentSettings.tsx +45 -28
- package/src/client/components/settings/categories/CommandSettings.tsx +45 -28
- package/src/client/components/settings/categories/McpSettings.tsx +48 -31
- package/src/client/components/settings/categories/ProjectSettings.tsx +34 -19
- package/src/client/components/settings/categories/ProviderSettings.tsx +77 -53
- package/src/client/components/settings/categories/SkillSettings.tsx +34 -17
- package/src/client/components/settings/categories/UsageSettings.tsx +53 -39
- package/src/client/components/settings/categories/behavior-settings/Editor.tsx +7 -7
- package/src/client/components/settings/categories/mcp-settings/ImportModal.tsx +1 -1
- package/src/client/components/settings/categories/mcp-settings/SidebarList.tsx +1 -1
- package/src/client/components/settings/categories/project-settings/SidebarList.tsx +1 -1
- package/src/client/components/settings/categories/provider-settings/AuthSection.tsx +8 -3
- package/src/client/components/settings/categories/provider-settings/DeleteProviderModal.tsx +1 -1
- package/src/client/components/settings/categories/provider-settings/ModelsList.tsx +9 -4
- package/src/client/components/settings/categories/provider-settings/SidebarList.tsx +1 -1
- package/src/client/components/settings/categories/skill-catalog/AddSourceModal.tsx +1 -1
- package/src/client/components/settings/categories/skill-settings/SidebarList.tsx +1 -1
- package/src/client/components/settings/categories/usage-settings/SidebarList.tsx +1 -1
- package/src/client/components/settings/master-detail/index.tsx +68 -0
- package/src/client/components/workspace/SearchPanel.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/SubagentView.tsx +7 -1
- package/src/client/components/workspace/chat-timeline/SystemNotice.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/TaskResultContent.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/index.tsx +1 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/hashline-patch/index.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/AstEdit.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Bash.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Edit.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Read.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/SearchTool.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.tsx +2 -1
- package/src/client/components/workspace/diff-panel/SplitView.tsx +31 -5
- package/src/client/components/workspace/diff-panel/Toolbar.tsx +77 -6
- package/src/client/components/workspace/diff-panel/UnifiedView.tsx +18 -5
- package/src/client/components/workspace/diff-panel/index.tsx +36 -7
- package/src/client/components/workspace/editor/Toolbar.tsx +11 -4
- package/src/client/components/workspace/git-panel/commit-modal/CommitDiffViewer.tsx +2 -1
- package/src/client/hooks/chat/omp/session-list.tsx +31 -1
- package/src/client/hooks/chat/omp/session-sidebar-controller.ts +7 -1
- package/src/client/hooks/chat/subagent/index.ts +37 -11
- package/src/client/hooks/editor/use-file-editor.ts +1 -1
- package/src/client/hooks/settings/master-detail.ts +33 -0
- package/src/client/hooks/workspace/expanded-sessions.ts +62 -0
- package/src/server/lib/db/client.ts +5 -1
- package/src/server/lib/fs/git-diff.ts +34 -10
- package/src/server/lib/fs/git-repos.ts +81 -0
- package/src/server/lib/lifecycle/flock.ts +199 -0
- package/src/server/lib/lifecycle/identity.ts +5 -1
- package/src/server/lib/lifecycle/port-guard.ts +18 -3
- package/src/server/lib/lifecycle/proc/darwin.ts +52 -17
- package/src/server/lib/lifecycle/proc/linux.ts +29 -7
- package/src/server/lib/lifecycle/proc/ps.ts +7 -0
- package/src/server/lib/lifecycle/proc/types.ts +17 -0
- package/src/server/lib/lifecycle/proc/win32.ts +5 -0
- package/src/server/lib/observer-ws.ts +150 -0
- package/src/server/lib/omp/rpc/constants.ts +9 -0
- package/src/server/lib/omp/rpc/subagent-liveness.ts +2 -12
- package/src/server/lib/omp/session/file-scan.ts +7 -1
- package/src/server/lib/omp/session/files.ts +95 -96
- package/src/server/lib/omp/session/locator.ts +18 -8
- package/src/server/lib/omp/session/messages.ts +7 -7
- package/src/server/lib/omp/session/scan.ts +108 -0
- package/src/server/lib/omp/session/stats.ts +50 -50
- package/src/server/lib/omp/session/telemetry/scan.ts +89 -7
- package/src/server/lib/omp/session/telemetry-raw.ts +24 -8
- package/src/server/lib/omp/session/usage/aggregate.ts +170 -55
- package/src/server/lib/terminal/runtime.server.ts +3 -0
- package/src/server/lib/terminal/session.server.ts +30 -3
- package/src/server/routes/agent/command.test.ts +69 -0
- package/src/server/routes/agent/command.ts +14 -0
- package/src/server/routes/agent/ws.test.ts +159 -0
- package/src/server/routes/agent/ws.ts +22 -97
- package/src/server/routes/btw/ws.ts +22 -95
- package/src/server/routes/fs/git.ts +61 -70
- package/src/server/routes/omp/session-stats.ts +1 -1
- package/src/server/routes/telemetry/tokens.ts +6 -4
- package/src/server/routes/terminal/run.ts +4 -8
- package/src/server/routes/terminal/ws.ts +24 -2
- package/src/shared/lib/chat/omp/agent-events.test.ts +33 -1
- package/src/shared/lib/chat/omp/agent-events.ts +10 -0
- package/src/shared/lib/code/language.ts +146 -0
- package/src/shared/lib/code/syntax-highlight.test.ts +2 -1
- package/src/shared/lib/code/syntax-highlight.ts +0 -122
- package/src/shared/lib/fs/diff-parser.test.ts +5 -5
- package/src/shared/lib/fs/diff-parser.ts +67 -42
- package/src/shared/lib/omp/subagent/parse.test.ts +88 -0
- package/src/shared/lib/omp/subagent/parse.ts +17 -0
- package/src/shared/lib/omp/subagent/transcript-client.test.ts +100 -0
- package/src/shared/lib/omp/subagent/transcript-client.ts +17 -1
- package/src/shared/types/omp/subagent.ts +6 -0
- package/dist/client/static/js/index-5yfre1kw.css +0 -1
|
@@ -84,7 +84,11 @@ export function SettingsModal({
|
|
|
84
84
|
if (isOpen) {
|
|
85
85
|
setActiveCategory(initialCategory);
|
|
86
86
|
setAutoOpenAdd(autoOpenAddProvider);
|
|
87
|
-
|
|
87
|
+
// `autoOpenAdd` asks for the provider dialog, which a category renders
|
|
88
|
+
// inside its own detail pane. On a phone the modal would otherwise open
|
|
89
|
+
// inside a `display: none` subtree and never be seen, so the request also
|
|
90
|
+
// lands the user on the category screen it belongs to.
|
|
91
|
+
setIsMobileDrilled(autoOpenAddProvider);
|
|
88
92
|
setSearchQuery('');
|
|
89
93
|
}
|
|
90
94
|
}, [isOpen, initialCategory, autoOpenAddProvider]);
|
|
@@ -206,8 +210,19 @@ export function SettingsModal({
|
|
|
206
210
|
|
|
207
211
|
{/* DESKTOP & MOBILE DETAIL VIEW */}
|
|
208
212
|
<div className={`flex-1 flex-col h-full bg-paper overflow-hidden ${isMobileDrilled ? 'flex' : 'hidden md:flex'}`}>
|
|
209
|
-
{/* Header Bar
|
|
210
|
-
|
|
213
|
+
{/* Header Bar. The height and padding carry the safe-area insets on a
|
|
214
|
+
phone: the modal is full-screen below `md`, so without them the
|
|
215
|
+
back button and the title sit under the status bar / notch, the
|
|
216
|
+
same way the mobile chrome (header, editor, diff) already avoids. */}
|
|
217
|
+
<div
|
|
218
|
+
className="px-6 border-b border-ink/10 flex items-center justify-between flex-shrink-0 bg-paper"
|
|
219
|
+
style={{
|
|
220
|
+
height: 'calc(4rem + env(safe-area-inset-top, 0px))',
|
|
221
|
+
paddingTop: 'env(safe-area-inset-top, 0px)',
|
|
222
|
+
paddingLeft: 'max(1.5rem, env(safe-area-inset-left, 0px))',
|
|
223
|
+
paddingRight: 'max(1.5rem, env(safe-area-inset-right, 0px))',
|
|
224
|
+
}}
|
|
225
|
+
>
|
|
211
226
|
<div className="flex items-center space-x-3 min-w-0">
|
|
212
227
|
{/* Mobile Back Button */}
|
|
213
228
|
<button
|
|
@@ -241,7 +256,10 @@ export function SettingsModal({
|
|
|
241
256
|
</div>
|
|
242
257
|
|
|
243
258
|
{/* Scrollable Settings Body - Full Width across all categories */}
|
|
244
|
-
<div
|
|
259
|
+
<div
|
|
260
|
+
className={`flex-1 scrollbar-overlay-container scrollbar-overlay-static w-full ${['projects', 'providers', 'agents', 'behavior', 'commands', 'mcp', 'skills', 'skills-catalog', 'usage'].includes(activeCategory) ? 'p-0 flex flex-col' : 'p-6 md:p-8 flex flex-col'}`}
|
|
261
|
+
style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}
|
|
262
|
+
>
|
|
245
263
|
<div className="w-full h-full flex-1 flex flex-col">
|
|
246
264
|
{renderCategoryContent()}
|
|
247
265
|
</div>
|
|
@@ -250,7 +268,15 @@ export function SettingsModal({
|
|
|
250
268
|
|
|
251
269
|
{/* MOBILE CATEGORY LIST VIEW (When not drilled into detail) */}
|
|
252
270
|
<div className={`flex-1 flex-col h-full bg-canvas md:hidden ${isMobileDrilled ? 'hidden' : 'flex'}`}>
|
|
253
|
-
<div
|
|
271
|
+
<div
|
|
272
|
+
className="px-4 border-b border-ink/10 flex items-center justify-between flex-shrink-0 bg-canvas"
|
|
273
|
+
style={{
|
|
274
|
+
height: 'calc(3.5rem + env(safe-area-inset-top, 0px))',
|
|
275
|
+
paddingTop: 'env(safe-area-inset-top, 0px)',
|
|
276
|
+
paddingLeft: 'max(1rem, env(safe-area-inset-left, 0px))',
|
|
277
|
+
paddingRight: 'max(1rem, env(safe-area-inset-right, 0px))',
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
254
280
|
<span className="font-bold text-sm tracking-tight text-ink">Settings</span>
|
|
255
281
|
<button
|
|
256
282
|
type="button"
|
|
@@ -262,7 +288,10 @@ export function SettingsModal({
|
|
|
262
288
|
</button>
|
|
263
289
|
</div>
|
|
264
290
|
|
|
265
|
-
<div
|
|
291
|
+
<div
|
|
292
|
+
className="flex-1 overflow-hidden"
|
|
293
|
+
style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}
|
|
294
|
+
>
|
|
266
295
|
<SettingsSidebar
|
|
267
296
|
activeCategory={activeCategory}
|
|
268
297
|
onSelectCategory={handleSelectCategory}
|
|
@@ -77,7 +77,7 @@ export const SettingsSidebarList: FunctionComponent<SettingsSidebarListProps> =
|
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
return (
|
|
80
|
-
<div className="w-
|
|
80
|
+
<div className="w-full md:w-64 md:border-r border-ink/10 h-full flex flex-col bg-paper/50 flex-shrink-0 select-none">
|
|
81
81
|
<ProjectSelectorDropdown
|
|
82
82
|
selectedProject={selectedProject}
|
|
83
83
|
onChangeProject={onChangeProject}
|
|
@@ -5,6 +5,8 @@ import { AgentSidebarList } from '@/client/components/settings/categories/agent-
|
|
|
5
5
|
import { AgentDetailPane } from '@/client/components/settings/categories/agent-settings/DetailPane';
|
|
6
6
|
import { LoadingState } from '@/client/components/settings/LoadingState';
|
|
7
7
|
import { useCrudList } from '@/client/hooks/settings/crud-list';
|
|
8
|
+
import { useSettingsMasterDetail } from '@/client/hooks/settings/master-detail';
|
|
9
|
+
import { SettingsMasterDetail } from '@/client/components/settings/master-detail';
|
|
8
10
|
|
|
9
11
|
interface AgentSettingsProps {
|
|
10
12
|
settings: SettingsState;
|
|
@@ -12,6 +14,7 @@ interface AgentSettingsProps {
|
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export const AgentSettings: FunctionComponent<AgentSettingsProps> = () => {
|
|
17
|
+
const masterDetail = useSettingsMasterDetail();
|
|
15
18
|
const [selectedProject, setSelectedProject] = useState('ompchamber');
|
|
16
19
|
const { items: agents, selectedId, selected: selectedAgent, isCreatingNew, isLoading, select, startCreate, save, remove } =
|
|
17
20
|
useCrudList<AgentItem>({
|
|
@@ -49,36 +52,50 @@ export const AgentSettings: FunctionComponent<AgentSettingsProps> = () => {
|
|
|
49
52
|
|
|
50
53
|
return (
|
|
51
54
|
<div className="flex h-full w-full overflow-hidden bg-paper">
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
<SettingsMasterDetail
|
|
56
|
+
pane={masterDetail.pane}
|
|
57
|
+
onBack={masterDetail.back}
|
|
58
|
+
listLabel="Agents"
|
|
59
|
+
list={
|
|
60
|
+
<AgentSidebarList
|
|
61
|
+
agents={agents}
|
|
62
|
+
selectedAgentId={isCreatingNew ? null : selectedId}
|
|
63
|
+
onSelectAgent={(id) => {
|
|
64
|
+
select(id);
|
|
65
|
+
masterDetail.openDetail();
|
|
66
|
+
}}
|
|
67
|
+
onAddNewAgent={() => {
|
|
68
|
+
startCreate();
|
|
69
|
+
masterDetail.openDetail();
|
|
70
|
+
}}
|
|
71
|
+
selectedProject={selectedProject}
|
|
72
|
+
onChangeProject={setSelectedProject}
|
|
67
73
|
/>
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
}
|
|
75
|
+
detail={
|
|
76
|
+
<div className="flex-1 h-full overflow-hidden flex flex-col">
|
|
77
|
+
{isCreatingNew ? (
|
|
78
|
+
<AgentDetailPane
|
|
79
|
+
agent={emptyAgentTemplate}
|
|
80
|
+
isNew={true}
|
|
81
|
+
onSave={save}
|
|
82
|
+
/>
|
|
83
|
+
) : selectedAgent ? (
|
|
84
|
+
<AgentDetailPane
|
|
85
|
+
key={selectedAgent.id}
|
|
86
|
+
agent={selectedAgent}
|
|
87
|
+
isNew={false}
|
|
88
|
+
onSave={save}
|
|
89
|
+
onDelete={remove}
|
|
90
|
+
/>
|
|
91
|
+
) : (
|
|
92
|
+
<div className="flex-1 flex items-center justify-center text-xs font-mono text-ink/40">
|
|
93
|
+
Select an agent or click + to create one
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
79
96
|
</div>
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
}
|
|
98
|
+
/>
|
|
82
99
|
</div>
|
|
83
100
|
);
|
|
84
101
|
};
|
|
@@ -5,6 +5,8 @@ import { CommandSidebarList } from '@/client/components/settings/categories/comm
|
|
|
5
5
|
import { CommandDetailPane } from '@/client/components/settings/categories/command-settings/DetailPane';
|
|
6
6
|
import { LoadingState } from '@/client/components/settings/LoadingState';
|
|
7
7
|
import { useCrudList } from '@/client/hooks/settings/crud-list';
|
|
8
|
+
import { useSettingsMasterDetail } from '@/client/hooks/settings/master-detail';
|
|
9
|
+
import { SettingsMasterDetail } from '@/client/components/settings/master-detail';
|
|
8
10
|
|
|
9
11
|
interface CommandSettingsProps {
|
|
10
12
|
settings: SettingsState;
|
|
@@ -12,6 +14,7 @@ interface CommandSettingsProps {
|
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export const CommandSettings: FunctionComponent<CommandSettingsProps> = () => {
|
|
17
|
+
const masterDetail = useSettingsMasterDetail();
|
|
15
18
|
const [selectedProject, setSelectedProject] = useState('ompchamber');
|
|
16
19
|
const { items: commands, selectedId, selected: selectedCommand, isCreatingNew, isLoading, select, startCreate, save, remove } =
|
|
17
20
|
useCrudList<CommandItem>({
|
|
@@ -46,36 +49,50 @@ export const CommandSettings: FunctionComponent<CommandSettingsProps> = () => {
|
|
|
46
49
|
|
|
47
50
|
return (
|
|
48
51
|
<div className="flex h-full w-full overflow-hidden bg-paper">
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
<SettingsMasterDetail
|
|
53
|
+
pane={masterDetail.pane}
|
|
54
|
+
onBack={masterDetail.back}
|
|
55
|
+
listLabel="Commands"
|
|
56
|
+
list={
|
|
57
|
+
<CommandSidebarList
|
|
58
|
+
commands={commands}
|
|
59
|
+
selectedCommandId={isCreatingNew ? null : selectedId}
|
|
60
|
+
onSelectCommand={(id) => {
|
|
61
|
+
select(id);
|
|
62
|
+
masterDetail.openDetail();
|
|
63
|
+
}}
|
|
64
|
+
onAddNewCommand={() => {
|
|
65
|
+
startCreate();
|
|
66
|
+
masterDetail.openDetail();
|
|
67
|
+
}}
|
|
68
|
+
selectedProject={selectedProject}
|
|
69
|
+
onChangeProject={setSelectedProject}
|
|
64
70
|
/>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
}
|
|
72
|
+
detail={
|
|
73
|
+
<div className="flex-1 h-full overflow-hidden flex flex-col">
|
|
74
|
+
{isCreatingNew ? (
|
|
75
|
+
<CommandDetailPane
|
|
76
|
+
command={emptyCommandTemplate}
|
|
77
|
+
isNew={true}
|
|
78
|
+
onSave={save}
|
|
79
|
+
/>
|
|
80
|
+
) : selectedCommand ? (
|
|
81
|
+
<CommandDetailPane
|
|
82
|
+
key={selectedCommand.id}
|
|
83
|
+
command={selectedCommand}
|
|
84
|
+
isNew={false}
|
|
85
|
+
onSave={save}
|
|
86
|
+
onDelete={remove}
|
|
87
|
+
/>
|
|
88
|
+
) : (
|
|
89
|
+
<div className="flex-1 flex items-center justify-center text-xs font-mono text-ink/40">
|
|
90
|
+
Select a command or click + to create one
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
76
93
|
</div>
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
}
|
|
95
|
+
/>
|
|
79
96
|
</div>
|
|
80
97
|
);
|
|
81
98
|
};
|
|
@@ -8,6 +8,8 @@ import { McpImportModal } from '@/client/components/settings/categories/mcp-sett
|
|
|
8
8
|
import { LoadingState } from '@/client/components/settings/LoadingState';
|
|
9
9
|
import { useSidebarData } from '@/client/hooks/chat/omp/session-list';
|
|
10
10
|
import { useCrudList } from '@/client/hooks/settings/crud-list';
|
|
11
|
+
import { useSettingsMasterDetail } from '@/client/hooks/settings/master-detail';
|
|
12
|
+
import { SettingsMasterDetail } from '@/client/components/settings/master-detail';
|
|
11
13
|
|
|
12
14
|
interface McpSettingsProps {
|
|
13
15
|
settings: SettingsState;
|
|
@@ -17,6 +19,7 @@ interface McpSettingsProps {
|
|
|
17
19
|
const GLOBAL_PROJECT: McpProjectOption = { id: 'global', name: 'Global (all projects)', path: '' };
|
|
18
20
|
|
|
19
21
|
export const McpSettings: FunctionComponent<McpSettingsProps> = () => {
|
|
22
|
+
const masterDetail = useSettingsMasterDetail();
|
|
20
23
|
const { folders } = useSidebarData();
|
|
21
24
|
const projects = useMemo<McpProjectOption[]>(() => {
|
|
22
25
|
const bound = folders.flatMap<McpProjectOption>((folder) =>
|
|
@@ -98,39 +101,53 @@ export const McpSettings: FunctionComponent<McpSettingsProps> = () => {
|
|
|
98
101
|
|
|
99
102
|
return (
|
|
100
103
|
<div className="flex h-full w-full overflow-hidden bg-paper">
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
<McpDetailPane
|
|
121
|
-
key={selectedServer.id}
|
|
122
|
-
server={selectedServer}
|
|
123
|
-
isNew={false}
|
|
124
|
-
onSave={save}
|
|
125
|
-
onDelete={remove}
|
|
126
|
-
onOpenImportModal={() => setIsImportModalOpen(true)}
|
|
104
|
+
<SettingsMasterDetail
|
|
105
|
+
pane={masterDetail.pane}
|
|
106
|
+
onBack={masterDetail.back}
|
|
107
|
+
listLabel="MCP Servers"
|
|
108
|
+
list={
|
|
109
|
+
<McpSidebarList
|
|
110
|
+
servers={servers}
|
|
111
|
+
projects={projects}
|
|
112
|
+
selectedServerId={isCreatingNew ? null : selectedId}
|
|
113
|
+
onSelectServer={(id) => {
|
|
114
|
+
select(id);
|
|
115
|
+
masterDetail.openDetail();
|
|
116
|
+
}}
|
|
117
|
+
onAddNewServer={() => {
|
|
118
|
+
startCreate();
|
|
119
|
+
masterDetail.openDetail();
|
|
120
|
+
}}
|
|
121
|
+
selectedProject={selectedProject}
|
|
122
|
+
onChangeProject={setSelectedProject}
|
|
127
123
|
/>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
}
|
|
125
|
+
detail={
|
|
126
|
+
<div className="flex-1 h-full overflow-hidden flex flex-col">
|
|
127
|
+
{isCreatingNew ? (
|
|
128
|
+
<McpDetailPane
|
|
129
|
+
server={emptyServerTemplate}
|
|
130
|
+
isNew={true}
|
|
131
|
+
onSave={save}
|
|
132
|
+
onOpenImportModal={() => setIsImportModalOpen(true)}
|
|
133
|
+
/>
|
|
134
|
+
) : selectedServer ? (
|
|
135
|
+
<McpDetailPane
|
|
136
|
+
key={selectedServer.id}
|
|
137
|
+
server={selectedServer}
|
|
138
|
+
isNew={false}
|
|
139
|
+
onSave={save}
|
|
140
|
+
onDelete={remove}
|
|
141
|
+
onOpenImportModal={() => setIsImportModalOpen(true)}
|
|
142
|
+
/>
|
|
143
|
+
) : (
|
|
144
|
+
<div className="flex-1 flex items-center justify-center text-xs font-mono text-ink/40">
|
|
145
|
+
Select an MCP server or click + to configure one
|
|
146
|
+
</div>
|
|
147
|
+
)}
|
|
131
148
|
</div>
|
|
132
|
-
|
|
133
|
-
|
|
149
|
+
}
|
|
150
|
+
/>
|
|
134
151
|
|
|
135
152
|
<McpImportModal
|
|
136
153
|
isOpen={isImportModalOpen}
|
|
@@ -4,8 +4,11 @@ import { ProjectSidebarList } from '@/client/components/settings/categories/proj
|
|
|
4
4
|
import { ProjectDetailsForm } from '@/client/components/settings/categories/project-settings/DetailsForm';
|
|
5
5
|
import { LoadingState } from '@/client/components/settings/LoadingState';
|
|
6
6
|
import { useChamberEvent } from '@/client/hooks/ui/window-event';
|
|
7
|
+
import { useSettingsMasterDetail } from '@/client/hooks/settings/master-detail';
|
|
8
|
+
import { SettingsMasterDetail } from '@/client/components/settings/master-detail';
|
|
7
9
|
|
|
8
10
|
export function ProjectSettings() {
|
|
11
|
+
const masterDetail = useSettingsMasterDetail();
|
|
9
12
|
const [projects, setProjects] = useState<ProjectConfigItem[]>([]);
|
|
10
13
|
const [availableModels, setAvailableModels] = useState<string[]>([]);
|
|
11
14
|
const [accentColorOptions, setAccentColorOptions] = useState<AccentColorOption[]>([]);
|
|
@@ -94,6 +97,9 @@ export function ProjectSettings() {
|
|
|
94
97
|
};
|
|
95
98
|
setProjects((current) => [...current, newProject]);
|
|
96
99
|
setSelectedProjectId(newProject.id);
|
|
100
|
+
// On a phone the new project's details are the point of adding it; the
|
|
101
|
+
// list pane would hide the row that was just created.
|
|
102
|
+
masterDetail.openDetail();
|
|
97
103
|
window.dispatchEvent(new CustomEvent('omp:workspace-updated', { detail: { folderId } }));
|
|
98
104
|
} catch (error) {
|
|
99
105
|
console.error('Failed to add project:', error);
|
|
@@ -151,26 +157,35 @@ export function ProjectSettings() {
|
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
return (
|
|
154
|
-
<div className="w-full h-full flex flex-col
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
<div className="w-full h-full flex flex-col overflow-hidden bg-paper text-ink">
|
|
161
|
+
<SettingsMasterDetail
|
|
162
|
+
pane={masterDetail.pane}
|
|
163
|
+
onBack={masterDetail.back}
|
|
164
|
+
listLabel="Projects"
|
|
165
|
+
list={
|
|
166
|
+
<ProjectSidebarList
|
|
167
|
+
projects={projects}
|
|
168
|
+
selectedProjectId={selectedProject?.id || ''}
|
|
169
|
+
onSelectProject={(id) => {
|
|
170
|
+
setSelectedProjectId(id);
|
|
171
|
+
masterDetail.openDetail();
|
|
172
|
+
}}
|
|
173
|
+
onAddProject={handleAddProject}
|
|
174
|
+
/>
|
|
175
|
+
}
|
|
176
|
+
detail={
|
|
177
|
+
selectedProject ? (
|
|
178
|
+
<ProjectDetailsForm
|
|
179
|
+
project={selectedProject}
|
|
180
|
+
canDelete={projects.length > 1}
|
|
181
|
+
onUpdateField={handleUpdateField}
|
|
182
|
+
onDeleteProject={handleDeleteProject}
|
|
183
|
+
availableModels={availableModels}
|
|
184
|
+
accentColorOptions={accentColorOptions}
|
|
185
|
+
/>
|
|
186
|
+
) : null
|
|
187
|
+
}
|
|
161
188
|
/>
|
|
162
|
-
|
|
163
|
-
{/* Right Column: Project details & controls */}
|
|
164
|
-
{selectedProject && (
|
|
165
|
-
<ProjectDetailsForm
|
|
166
|
-
project={selectedProject}
|
|
167
|
-
canDelete={projects.length > 1}
|
|
168
|
-
onUpdateField={handleUpdateField}
|
|
169
|
-
onDeleteProject={handleDeleteProject}
|
|
170
|
-
availableModels={availableModels}
|
|
171
|
-
accentColorOptions={accentColorOptions}
|
|
172
|
-
/>
|
|
173
|
-
)}
|
|
174
189
|
</div>
|
|
175
190
|
);
|
|
176
191
|
}
|
|
@@ -13,6 +13,8 @@ import { AddModelModal } from '@/client/components/settings/categories/provider-
|
|
|
13
13
|
import { DeleteProviderModal } from '@/client/components/settings/categories/provider-settings/DeleteProviderModal';
|
|
14
14
|
import { providerEmptyReason } from '@/shared/lib/models/provider/dialect';
|
|
15
15
|
import { configuredProviderSlugs } from '@/shared/lib/models/provider/presets';
|
|
16
|
+
import { useSettingsMasterDetail } from '@/client/hooks/settings/master-detail';
|
|
17
|
+
import { SettingsMasterDetail } from '@/client/components/settings/master-detail';
|
|
16
18
|
|
|
17
19
|
interface ProviderSettingsProps {
|
|
18
20
|
autoOpenAdd?: boolean;
|
|
@@ -23,6 +25,7 @@ export function ProviderSettings({
|
|
|
23
25
|
autoOpenAdd = false,
|
|
24
26
|
onAddModalClose,
|
|
25
27
|
}: ProviderSettingsProps) {
|
|
28
|
+
const masterDetail = useSettingsMasterDetail();
|
|
26
29
|
const {
|
|
27
30
|
providers,
|
|
28
31
|
connectedProviders,
|
|
@@ -66,61 +69,76 @@ export function ProviderSettings({
|
|
|
66
69
|
} = useProviderSettings({ autoOpenAdd, onAddModalClose });
|
|
67
70
|
|
|
68
71
|
return (
|
|
69
|
-
<div className="w-full h-full flex flex-col
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
<div className="w-full h-full flex flex-col overflow-hidden bg-paper text-ink">
|
|
73
|
+
<SettingsMasterDetail
|
|
74
|
+
// The "add provider" dialog lives inside the detail pane, so a hidden
|
|
75
|
+
// pane would hide the dialog with it — and `autoOpenAdd` (the model
|
|
76
|
+
// dropdown's "no provider configured" path) opens it without any row
|
|
77
|
+
// having been tapped. While that dialog is open the pane stays up.
|
|
78
|
+
pane={isAddModalOpen ? 'detail' : masterDetail.pane}
|
|
79
|
+
onBack={masterDetail.back}
|
|
80
|
+
listLabel="Providers"
|
|
81
|
+
list={
|
|
82
|
+
<ProviderSidebarList
|
|
83
|
+
providers={connectedProviders}
|
|
84
|
+
selectedProviderId={selectedProvider?.id || ''}
|
|
85
|
+
onSelectProvider={(id) => {
|
|
86
|
+
setSelectedProviderId(id);
|
|
87
|
+
masterDetail.openDetail();
|
|
88
|
+
}}
|
|
89
|
+
onOpenAddModal={() => setIsAddModalOpen(true)}
|
|
90
|
+
currentProject={currentProject}
|
|
91
|
+
onSelectProject={setCurrentProject}
|
|
92
|
+
/>
|
|
93
|
+
}
|
|
94
|
+
detail={
|
|
95
|
+
<div className="flex-1 scrollbar-overlay-container scrollbar-overlay-static p-6 md:p-8 space-y-6">
|
|
96
|
+
{selectedProvider ? (
|
|
97
|
+
<>
|
|
98
|
+
<ProviderHeader provider={selectedProvider} />
|
|
83
99
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
<ProviderAuthSection
|
|
101
|
+
provider={selectedProvider}
|
|
102
|
+
onOpenReconnectModal={() => {
|
|
103
|
+
// A provider omp signs in through `/login` needs the OAuth
|
|
104
|
+
// dialog; one whose endpoint and key live in models.yml (or the
|
|
105
|
+
// chamber overlay) is edited in place. Testing the provider ID
|
|
106
|
+
// for `omp-auth-` was wrong: a native models.yml entry shares
|
|
107
|
+
// that ID prefix once merged, and a keyless local server has no
|
|
108
|
+
// login flow to complete at all.
|
|
109
|
+
if (selectedProvider.credentialSource === 'omp-auth' && selectedProvider.auth !== 'none') {
|
|
110
|
+
setIsLoginModalOpen(true);
|
|
111
|
+
} else {
|
|
112
|
+
setIsReconnectModalOpen(true);
|
|
113
|
+
}
|
|
114
|
+
}}
|
|
115
|
+
onToggleDisconnect={handleToggleDisconnect}
|
|
116
|
+
onToggleDisabled={handleToggleProviderDisabled}
|
|
117
|
+
onDelete={() => setIsDeleteModalOpen(true)}
|
|
118
|
+
/>
|
|
103
119
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
<ProviderModelsList
|
|
121
|
+
models={selectedProvider.models}
|
|
122
|
+
emptyReason={providerEmptyReason(selectedProvider)}
|
|
123
|
+
onToggleModelVisibility={handleToggleModelVisibility}
|
|
124
|
+
onHideAll={handleHideAll}
|
|
125
|
+
onShowAll={handleShowAll}
|
|
126
|
+
onFetchModels={handleFetchModelsFromList}
|
|
127
|
+
onAddModel={() => setIsAddModelModalOpen(true)}
|
|
128
|
+
canFetchModels={Boolean(selectedProvider.baseUrl)}
|
|
129
|
+
isFetchingModels={isFetchingModels}
|
|
130
|
+
onOpenModelConfig={(model) => setConfigModel(model)}
|
|
131
|
+
onOpenModelCapabilities={(model) => setCapabilitiesModel(model)}
|
|
132
|
+
/>
|
|
133
|
+
</>
|
|
134
|
+
) : (
|
|
135
|
+
<div className="py-12 text-center text-xs text-ink/40">
|
|
136
|
+
Select a provider from the sidebar to view connection details
|
|
137
|
+
</div>
|
|
138
|
+
)}
|
|
121
139
|
</div>
|
|
122
|
-
|
|
123
|
-
|
|
140
|
+
}
|
|
141
|
+
/>
|
|
124
142
|
|
|
125
143
|
<ToastStack toasts={toasts} onDismiss={dismissToast} />
|
|
126
144
|
|
|
@@ -130,7 +148,13 @@ export function ProviderSettings({
|
|
|
130
148
|
setIsAddModalOpen(false);
|
|
131
149
|
onAddModalClose?.();
|
|
132
150
|
}}
|
|
133
|
-
onAddProvider={
|
|
151
|
+
onAddProvider={async (provider, options) => {
|
|
152
|
+
await handleAddProvider(provider, options);
|
|
153
|
+
// The row now exists in the list; on a phone the newly registered
|
|
154
|
+
// provider is what the user came to configure, so land on its detail
|
|
155
|
+
// rather than dropping them back on the list.
|
|
156
|
+
masterDetail.openDetail();
|
|
157
|
+
}}
|
|
134
158
|
presets={availablePresetProviders}
|
|
135
159
|
existingSlugs={configuredProviderSlugs(providers)}
|
|
136
160
|
/>
|