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
|
@@ -4,6 +4,8 @@ import { SkillSidebarList } from '@/client/components/settings/categories/skill-
|
|
|
4
4
|
import { SkillDetailPane } from '@/client/components/settings/categories/skill-settings/DetailPane';
|
|
5
5
|
import { LoadingState } from '@/client/components/settings/LoadingState';
|
|
6
6
|
import { useCrudList } from '@/client/hooks/settings/crud-list';
|
|
7
|
+
import { useSettingsMasterDetail } from '@/client/hooks/settings/master-detail';
|
|
8
|
+
import { SettingsMasterDetail } from '@/client/components/settings/master-detail';
|
|
7
9
|
|
|
8
10
|
interface SkillSettingsProps {
|
|
9
11
|
settings?: SettingsState;
|
|
@@ -12,6 +14,7 @@ interface SkillSettingsProps {
|
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export function SkillSettings({ onNavigateToCatalog }: SkillSettingsProps) {
|
|
17
|
+
const masterDetail = useSettingsMasterDetail();
|
|
15
18
|
const [selectedProjectId, setSelectedProjectId] = useState('ompchamber');
|
|
16
19
|
const { items: skills, selectedId, selected: selectedSkill, isCreatingNew, isLoading, select, startCreate, save, remove } =
|
|
17
20
|
useCrudList<SkillItem, Partial<SkillItem>>({
|
|
@@ -46,23 +49,37 @@ export function SkillSettings({ onNavigateToCatalog }: SkillSettingsProps) {
|
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
return (
|
|
49
|
-
<div className="flex-1 flex flex-col
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
<div className="flex-1 flex flex-col h-full w-full overflow-hidden bg-paper">
|
|
53
|
+
<SettingsMasterDetail
|
|
54
|
+
pane={masterDetail.pane}
|
|
55
|
+
onBack={masterDetail.back}
|
|
56
|
+
listLabel="Skills"
|
|
57
|
+
list={
|
|
58
|
+
<SkillSidebarList
|
|
59
|
+
skills={skills}
|
|
60
|
+
selectedSkillId={selectedId}
|
|
61
|
+
isCreatingNew={isCreatingNew}
|
|
62
|
+
onSelectSkill={(id) => {
|
|
63
|
+
select(id);
|
|
64
|
+
masterDetail.openDetail();
|
|
65
|
+
}}
|
|
66
|
+
onAddNewSkill={() => {
|
|
67
|
+
startCreate();
|
|
68
|
+
masterDetail.openDetail();
|
|
69
|
+
}}
|
|
70
|
+
selectedProject={selectedProjectId}
|
|
71
|
+
onSelectProject={setSelectedProjectId}
|
|
72
|
+
/>
|
|
73
|
+
}
|
|
74
|
+
detail={
|
|
75
|
+
<SkillDetailPane
|
|
76
|
+
skill={selectedSkill ?? null}
|
|
77
|
+
isCreatingNew={isCreatingNew}
|
|
78
|
+
onSave={save}
|
|
79
|
+
onDelete={remove}
|
|
80
|
+
onOpenCatalog={onNavigateToCatalog}
|
|
81
|
+
/>
|
|
82
|
+
}
|
|
66
83
|
/>
|
|
67
84
|
</div>
|
|
68
85
|
);
|
|
@@ -5,9 +5,12 @@ import { buildProviders, type UsageProviderId } from '@/client/components/settin
|
|
|
5
5
|
import { ProviderDetail } from '@/client/components/settings/categories/usage-settings/ProviderDetail';
|
|
6
6
|
import { formatDateTime } from '@/client/components/settings/categories/usage-settings/format';
|
|
7
7
|
import { useUsageReport } from '@/client/hooks/settings/useUsageReport';
|
|
8
|
+
import { useSettingsMasterDetail } from '@/client/hooks/settings/master-detail';
|
|
9
|
+
import { SettingsMasterDetail } from '@/client/components/settings/master-detail';
|
|
8
10
|
|
|
9
11
|
/** Settings → Usage: provider quota, balances, and locally recorded burn. */
|
|
10
12
|
export function UsageSettings() {
|
|
13
|
+
const masterDetail = useSettingsMasterDetail();
|
|
11
14
|
const { report, isLoading, error, reload } = useUsageReport();
|
|
12
15
|
const [selectedProviderId, setSelectedProviderId] = useState<UsageProviderId>('');
|
|
13
16
|
|
|
@@ -46,47 +49,58 @@ export function UsageSettings() {
|
|
|
46
49
|
|
|
47
50
|
return (
|
|
48
51
|
<div className="flex h-full w-full overflow-hidden bg-paper text-xs text-ink">
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
52
|
+
<SettingsMasterDetail
|
|
53
|
+
pane={masterDetail.pane}
|
|
54
|
+
onBack={masterDetail.back}
|
|
55
|
+
listLabel="Usage"
|
|
56
|
+
list={
|
|
57
|
+
<UsageSidebarList
|
|
58
|
+
report={report}
|
|
59
|
+
selectedProviderId={selected?.id ?? ''}
|
|
60
|
+
onSelectProvider={(id) => {
|
|
61
|
+
setSelectedProviderId(id);
|
|
62
|
+
masterDetail.openDetail();
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
}
|
|
66
|
+
detail={
|
|
67
|
+
<div className="flex-1 h-full overflow-hidden flex flex-col">
|
|
68
|
+
<div className="px-3.5 py-2.5 border-b border-ink/10 flex items-center justify-between gap-3 flex-shrink-0">
|
|
69
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
70
|
+
<span className="text-xs font-semibold text-ink/75 truncate">
|
|
71
|
+
Updated {formatDateTime(report.generatedAt)}
|
|
72
|
+
</span>
|
|
73
|
+
{report.isMock && (
|
|
74
|
+
<span className="text-[10px] font-mono uppercase tracking-wider bg-ink/5 border border-ink/15 rounded px-1.5 py-0.5 text-ink/60">
|
|
75
|
+
Mock
|
|
76
|
+
</span>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
<button
|
|
80
|
+
type="button"
|
|
81
|
+
onClick={() => reload({ force: true })}
|
|
82
|
+
disabled={isLoading}
|
|
83
|
+
className="p-1.5 bg-paper border border-ink/15 rounded-md hover:bg-ink/5 text-ink/80 transition-colors disabled:opacity-50 flex-shrink-0"
|
|
84
|
+
title="Refresh usage"
|
|
85
|
+
>
|
|
86
|
+
<RefreshCw size={12} className={isLoading ? 'animate-spin text-ink' : ''} />
|
|
87
|
+
</button>
|
|
88
|
+
</div>
|
|
77
89
|
|
|
78
|
-
|
|
79
|
-
|
|
90
|
+
<div className="flex-1 overflow-y-auto scrollbar-overlay-container scrollbar-overlay-static p-3.5 space-y-4">
|
|
91
|
+
{error && <p className="text-error text-xs">{error}</p>}
|
|
80
92
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
{selected ? (
|
|
94
|
+
<ProviderDetail summary={selected.summary} />
|
|
95
|
+
) : (
|
|
96
|
+
<p className="text-[11px] text-ink/50 leading-relaxed">
|
|
97
|
+
No provider credentials detected. Add an API key in Settings → Providers and it will appear here.
|
|
98
|
+
</p>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
}
|
|
103
|
+
/>
|
|
90
104
|
</div>
|
|
91
105
|
);
|
|
92
106
|
}
|
|
@@ -105,24 +105,24 @@ export const BehaviorEditor: FunctionComponent<BehaviorEditorProps> = ({
|
|
|
105
105
|
return (
|
|
106
106
|
<div className="flex-1 flex flex-col h-full bg-paper text-ink p-6 md:p-8 overflow-hidden">
|
|
107
107
|
{/* Header Bar */}
|
|
108
|
-
<div className="flex items-center justify-between pb-4 border-b border-ink/10">
|
|
109
|
-
<div className="flex items-center gap-2">
|
|
108
|
+
<div className="flex flex-wrap items-center justify-between gap-x-3 gap-y-2 pb-4 border-b border-ink/10">
|
|
109
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
110
110
|
<h2 className="text-sm font-bold tracking-tight text-ink flex items-center gap-2">
|
|
111
|
-
<span>{copy.title}</span>
|
|
112
|
-
<span className="text-[10px] uppercase tracking-wider px-1.5 py-0.5 rounded border border-ink/15 text-ink/50 font-medium">
|
|
111
|
+
<span className="truncate">{copy.title}</span>
|
|
112
|
+
<span className="text-[10px] uppercase tracking-wider px-1.5 py-0.5 rounded border border-ink/15 text-ink/50 font-medium flex-shrink-0">
|
|
113
113
|
{copy.badge}
|
|
114
114
|
</span>
|
|
115
115
|
</h2>
|
|
116
|
-
<div className="group relative">
|
|
116
|
+
<div className="group relative flex-shrink-0">
|
|
117
117
|
<Info className="w-4 h-4 text-ink/40 cursor-help" />
|
|
118
|
-
<div className="absolute left-0 top-6 hidden group-hover:block w-80 p-2.5 rounded-lg bg-ink text-paper text-xs shadow-lg z-20 leading-relaxed pointer-events-none">
|
|
118
|
+
<div className="absolute left-0 top-6 hidden group-hover:block w-80 max-w-[80vw] p-2.5 rounded-lg bg-ink text-paper text-xs shadow-lg z-20 leading-relaxed pointer-events-none">
|
|
119
119
|
{copy.tooltip}
|
|
120
120
|
</div>
|
|
121
121
|
</div>
|
|
122
122
|
</div>
|
|
123
123
|
|
|
124
124
|
{/* Action Buttons Top */}
|
|
125
|
-
<div className="flex items-center gap-2">
|
|
125
|
+
<div className="flex items-center gap-2 flex-shrink-0">
|
|
126
126
|
<button
|
|
127
127
|
type="button"
|
|
128
128
|
onClick={handleCopy}
|
|
@@ -100,7 +100,7 @@ export const McpImportModal: FunctionComponent<McpImportModalProps> = ({
|
|
|
100
100
|
|
|
101
101
|
return (
|
|
102
102
|
<div className="fixed inset-0 bg-ink/40 backdrop-blur-[2px] z-50 flex items-center justify-center p-4 animate-in fade-in duration-150">
|
|
103
|
-
<div className="w-full max-w-lg rounded-lg border border-ink/20 bg-paper text-ink p-5 shadow-2xl space-y-4">
|
|
103
|
+
<div className="w-full max-w-lg rounded-lg border border-ink/20 bg-paper text-ink p-5 shadow-2xl space-y-4 max-h-full overflow-y-auto scrollbar-overlay-container scrollbar-overlay-static">
|
|
104
104
|
<div className="flex items-center justify-between pb-3 border-b border-ink/10">
|
|
105
105
|
<div className="flex items-center gap-2">
|
|
106
106
|
<Code className="w-4 h-4 text-ink/70" />
|
|
@@ -31,7 +31,7 @@ export const McpSidebarList: FunctionComponent<McpSidebarListProps> = ({
|
|
|
31
31
|
const activeProjectName = projects.find((proj) => proj.path === selectedProject)?.name ?? 'Global (all projects)';
|
|
32
32
|
|
|
33
33
|
return (
|
|
34
|
-
<div className="w-
|
|
34
|
+
<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">
|
|
35
35
|
<ProjectSelectorDropdown
|
|
36
36
|
selectedProject={selectedProject}
|
|
37
37
|
onChangeProject={onChangeProject}
|
|
@@ -39,7 +39,7 @@ export function ProjectSidebarList({
|
|
|
39
39
|
onAddProject,
|
|
40
40
|
}: ProjectSidebarListProps) {
|
|
41
41
|
return (
|
|
42
|
-
<div className="w-
|
|
42
|
+
<div className="w-full md:w-64 md:border-r border-ink/10 h-full flex flex-col bg-paper/50 flex-shrink-0">
|
|
43
43
|
{/* List Header */}
|
|
44
44
|
<div className="px-3.5 py-3 border-b border-ink/10 flex items-center justify-between">
|
|
45
45
|
<span className="text-xs font-semibold text-ink">
|
|
@@ -31,8 +31,13 @@ export function ProviderAuthSection({
|
|
|
31
31
|
Authentication
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
{/* Wraps instead of overflowing: this row is a status group plus three
|
|
35
|
+
action buttons, which needs ~330px, while the detail pane is 206px
|
|
36
|
+
at a 768px window (the modal's own two-pane split). Without the wrap
|
|
37
|
+
the buttons ran past the panel and were clipped by the modal's
|
|
38
|
+
`overflow-hidden`. */}
|
|
39
|
+
<div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-2">
|
|
40
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
36
41
|
{isDisabled ? (
|
|
37
42
|
<>
|
|
38
43
|
<Ban size={15} className="text-ink/50 flex-shrink-0" strokeWidth={2.4} />
|
|
@@ -68,7 +73,7 @@ export function ProviderAuthSection({
|
|
|
68
73
|
</div>
|
|
69
74
|
</div>
|
|
70
75
|
|
|
71
|
-
<div className="flex items-center gap-2">
|
|
76
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
72
77
|
<button
|
|
73
78
|
type="button"
|
|
74
79
|
onClick={onToggleDisabled}
|
|
@@ -57,7 +57,7 @@ export function DeleteProviderModal({
|
|
|
57
57
|
aria-label="Confirm provider deletion"
|
|
58
58
|
>
|
|
59
59
|
<div
|
|
60
|
-
className="bg-paper border border-ink/20 rounded-xl shadow-2xl w-[min(94vw,460px)] overflow-
|
|
60
|
+
className="bg-paper border border-ink/20 rounded-xl shadow-2xl w-[min(94vw,460px)] max-h-full overflow-y-auto scrollbar-overlay-container scrollbar-overlay-static text-ink"
|
|
61
61
|
onClick={(e) => e.stopPropagation()}
|
|
62
62
|
>
|
|
63
63
|
<div className="flex items-center gap-3 px-5 py-4 border-b border-ink/10">
|
|
@@ -42,12 +42,14 @@ export function ProviderModelsList({
|
|
|
42
42
|
return (
|
|
43
43
|
<div className="space-y-3.5">
|
|
44
44
|
{/* 1. Header with count & hide all / show all */}
|
|
45
|
-
<div className="flex items-center justify-between gap-4">
|
|
45
|
+
<div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-2">
|
|
46
46
|
<div className="text-xs font-semibold text-ink">
|
|
47
47
|
Available Models <span className="font-normal text-ink/60">({models.length})</span>
|
|
48
48
|
</div>
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
{/* Wraps: four action chips need ~330px, which the detail pane does not
|
|
51
|
+
have in a two-pane modal at a 768-900px window. */}
|
|
52
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
51
53
|
<button
|
|
52
54
|
type="button"
|
|
53
55
|
onClick={onFetchModels}
|
|
@@ -133,8 +135,11 @@ export function ProviderModelsList({
|
|
|
133
135
|
</span>
|
|
134
136
|
</div>
|
|
135
137
|
|
|
136
|
-
{/* Right: Price & Context Badge & Action Icons
|
|
137
|
-
|
|
138
|
+
{/* Right: Price & Context Badge & Action Icons. Wraps rather than
|
|
139
|
+
running past the pane: a long price plus a context badge plus
|
|
140
|
+
three icons exceeds the detail pane's width in a two-pane
|
|
141
|
+
modal on a ~768px window. */}
|
|
142
|
+
<div className="flex flex-wrap items-center justify-end gap-2">
|
|
138
143
|
{/* Pricing Badge (per 1M tokens) */}
|
|
139
144
|
{(model.priceInput !== undefined || model.priceOutput !== undefined) && (
|
|
140
145
|
<span
|
|
@@ -35,7 +35,7 @@ export function ProviderSidebarList({
|
|
|
35
35
|
useOnClickOutside(dropdownRef, () => setIsProjectDropdownOpen(false));
|
|
36
36
|
|
|
37
37
|
return (
|
|
38
|
-
<div className="w-
|
|
38
|
+
<div className="w-full md:w-64 md:border-r border-ink/10 h-full flex flex-col bg-paper/50 flex-shrink-0">
|
|
39
39
|
{/* 1. Project Selector Dropdown */}
|
|
40
40
|
<div className="p-3 border-b border-ink/10 relative" ref={dropdownRef}>
|
|
41
41
|
<button
|
|
@@ -50,7 +50,7 @@ export function AddSourceModal({ isOpen, onClose, onAddSource }: AddSourceModalP
|
|
|
50
50
|
onClick={onClose}
|
|
51
51
|
>
|
|
52
52
|
<div
|
|
53
|
-
className="bg-paper border border-ink/20 rounded-xl max-w-md w-full p-6 shadow-2xl space-y-4 text-ink animate-in fade-in zoom-in-95 duration-150"
|
|
53
|
+
className="bg-paper border border-ink/20 rounded-xl max-w-md w-full p-6 shadow-2xl space-y-4 text-ink animate-in fade-in zoom-in-95 duration-150 max-h-full overflow-y-auto scrollbar-overlay-container scrollbar-overlay-static"
|
|
54
54
|
onClick={(e) => e.stopPropagation()}
|
|
55
55
|
>
|
|
56
56
|
<div className="flex items-center justify-between pb-3 border-b border-ink/10">
|
|
@@ -49,7 +49,7 @@ export function SkillSidebarList({
|
|
|
49
49
|
const totalCount = skills.length;
|
|
50
50
|
|
|
51
51
|
return (
|
|
52
|
-
<div className="w-
|
|
52
|
+
<div className="w-full md:w-64 md:border-r border-ink/10 h-full flex flex-col bg-paper/50 flex-shrink-0">
|
|
53
53
|
{/* Project Selector Dropdown */}
|
|
54
54
|
<ProjectSelectorDropdown
|
|
55
55
|
variant="compact"
|
|
@@ -24,7 +24,7 @@ export function UsageSidebarList({
|
|
|
24
24
|
const providers = buildProviders(report);
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
|
-
<div className="w-
|
|
27
|
+
<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">
|
|
28
28
|
<div className="px-3.5 py-2.5 border-b border-ink/10 flex items-center justify-between">
|
|
29
29
|
<span className="text-xs font-semibold text-ink">
|
|
30
30
|
{providers.length} provider{providers.length === 1 ? '' : 's'} with keys
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ComponentChildren } from 'preact';
|
|
7
|
+
import { ChevronLeft } from 'lucide-preact';
|
|
8
|
+
import type { SettingsPane } from '@/client/hooks/settings/master-detail';
|
|
9
|
+
|
|
10
|
+
interface SettingsMasterDetailProps {
|
|
11
|
+
/** Which pane a phone is showing; desktop shows both. */
|
|
12
|
+
pane: SettingsPane;
|
|
13
|
+
onBack: () => void;
|
|
14
|
+
/** The list pane (a settings sidebar). Owns its own `w-full md:w-64` width. */
|
|
15
|
+
list: ComponentChildren;
|
|
16
|
+
detail: ComponentChildren;
|
|
17
|
+
/** Name of the list, shown on the phone's back bar. */
|
|
18
|
+
listLabel: string;
|
|
19
|
+
/** Extra classes for the root (e.g. a category-wide `text-xs`). */
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Master-detail shell for the settings categories that are two panes on a
|
|
25
|
+
* desktop (providers, projects, agents, commands, MCP, skills, usage).
|
|
26
|
+
*
|
|
27
|
+
* Both panes stay mounted at every width, so a pane's state — a half-typed
|
|
28
|
+
* form, a scroll position — survives the drill and the back. Only the phone
|
|
29
|
+
* needs the extra bar: on `md` the two panes sit side by side exactly as
|
|
30
|
+
* before and the bar is not rendered at all.
|
|
31
|
+
*/
|
|
32
|
+
export function SettingsMasterDetail({
|
|
33
|
+
pane,
|
|
34
|
+
onBack,
|
|
35
|
+
list,
|
|
36
|
+
detail,
|
|
37
|
+
listLabel,
|
|
38
|
+
className = '',
|
|
39
|
+
}: SettingsMasterDetailProps) {
|
|
40
|
+
return (
|
|
41
|
+
<div className={`flex-1 flex flex-col md:flex-row h-full w-full min-w-0 overflow-hidden bg-paper text-ink ${className}`}>
|
|
42
|
+
{/* `min-w-0` on both panes: a flex item's default `min-width: auto` is its
|
|
43
|
+
content's min-content width, so the detail pane refused to shrink below
|
|
44
|
+
its widest row (a provider's name + action strip) and ran 207px past the
|
|
45
|
+
modal at a 768-900px window — clipped, since the modal is
|
|
46
|
+
`overflow-hidden`. The list pane carries it for the same reason. */}
|
|
47
|
+
<div className={`${pane === 'list' ? 'flex' : 'hidden'} md:flex flex-1 md:flex-none md:w-auto min-h-0 min-w-0 flex-col`}>
|
|
48
|
+
{list}
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div className={`${pane === 'detail' ? 'flex' : 'hidden'} md:flex flex-1 min-h-0 min-w-0 flex-col`}>
|
|
52
|
+
<div className="md:hidden flex items-center px-2 py-1 border-b border-ink/10 bg-canvas flex-shrink-0">
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
onClick={onBack}
|
|
56
|
+
aria-label={`Back to ${listLabel}`}
|
|
57
|
+
className="flex items-center gap-1 -ml-0.5 pl-0.5 pr-2 py-1 rounded-lg text-ink hover:bg-ink/5 active:bg-ink/10 transition-colors cursor-pointer"
|
|
58
|
+
>
|
|
59
|
+
<ChevronLeft size={18} />
|
|
60
|
+
<span className="text-xs font-semibold truncate">{listLabel}</span>
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
{detail}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -9,7 +9,8 @@ import { useSessionState } from '@/client/hooks/workspace/session-state';
|
|
|
9
9
|
import { useRepoList, useRepoScope } from '@/client/hooks/workspace/repo-scope';
|
|
10
10
|
import { useSearchStream } from '@/client/hooks/workspace/search-stream';
|
|
11
11
|
import { useOnClickOutside } from '@/client/hooks/ui/on-click-outside';
|
|
12
|
-
import { getLanguageFromPath
|
|
12
|
+
import { getLanguageFromPath } from '@/shared/lib/code/language';
|
|
13
|
+
import { highlightCode } from '@/shared/lib/code/syntax-highlight';
|
|
13
14
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
14
15
|
|
|
15
16
|
export function SearchPanel({ className = '', enabled = true, rootPath }: { className?: string, enabled?: boolean, rootPath?: string }) {
|
|
@@ -18,6 +18,8 @@ interface SubagentViewProps {
|
|
|
18
18
|
onBack: () => void;
|
|
19
19
|
provider?: string;
|
|
20
20
|
providerNames?: Record<string, string>;
|
|
21
|
+
/** Mobile renders the chat's compact footer variant (three-dot menu). */
|
|
22
|
+
isMobile?: boolean;
|
|
21
23
|
className?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -30,7 +32,7 @@ const STATUS_LABEL: Record<SubagentInfo['status'], string> = {
|
|
|
30
32
|
|
|
31
33
|
/** Read-only subagent transcript: compact banner, live-growing message list,
|
|
32
34
|
* and a notice row where the main timeline's composer would be. */
|
|
33
|
-
export function SubagentView({ sessionId, subagent, onBack, provider, providerNames, className = '' }: SubagentViewProps) {
|
|
35
|
+
export function SubagentView({ sessionId, subagent, onBack, provider, providerNames, isMobile = false, className = '' }: SubagentViewProps) {
|
|
34
36
|
const { messages, isLoading, status, isActive } = useSubagentTranscript(sessionId, subagent, onBack);
|
|
35
37
|
const modelNames = useModelNames();
|
|
36
38
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
@@ -133,6 +135,7 @@ export function SubagentView({ sessionId, subagent, onBack, provider, providerNa
|
|
|
133
135
|
msg={msg}
|
|
134
136
|
isStreaming={isLoading}
|
|
135
137
|
isPrevAssistant={isPrevAssistant}
|
|
138
|
+
userActions={false}
|
|
136
139
|
className={isNoticeRow(msg) ? 'mt-3 mb-1' : isPrevAssistant ? 'mt-1' : 'mt-3'}
|
|
137
140
|
/>
|
|
138
141
|
{footer && (
|
|
@@ -142,7 +145,10 @@ export function SubagentView({ sessionId, subagent, onBack, provider, providerNa
|
|
|
142
145
|
providerNames={providerNames}
|
|
143
146
|
modelName={modelName}
|
|
144
147
|
modelNames={modelNames}
|
|
148
|
+
thinkingLevel={progress?.resolvedThinkingLevel}
|
|
145
149
|
durationMs={footer.durationMs}
|
|
150
|
+
isMobile={isMobile}
|
|
151
|
+
showActions={false}
|
|
146
152
|
/>
|
|
147
153
|
)}
|
|
148
154
|
</Fragment>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo, useState } from 'preact/hooks';
|
|
2
2
|
import { AlertCircle, Bell, Bot, CheckCircle2, ChevronDown, Clock, Info, Layers } from 'lucide-preact';
|
|
3
|
-
import {
|
|
3
|
+
import { isCodeLike } from '@/shared/lib/code/language';
|
|
4
|
+
import { highlightCode } from '@/shared/lib/code/syntax-highlight';
|
|
4
5
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
5
6
|
import { parseTaskNotice } from '@/shared/lib/chat/task-result-parser';
|
|
6
7
|
import { TaskResultContent } from '@/client/components/workspace/chat-timeline/TaskResultContent';
|
|
@@ -3,7 +3,8 @@ import { AlignLeft, Code, Eye, FileCode, FileText, Layers, Terminal } from 'luci
|
|
|
3
3
|
import type { ParsedTaskNotice } from '@/shared/lib/chat/task-result-parser';
|
|
4
4
|
import { MarkdownRenderer } from '@/client/components/common/MarkdownRenderer';
|
|
5
5
|
import { CopyButton } from '@/client/components/common/CopyButton';
|
|
6
|
-
import {
|
|
6
|
+
import { isCodeLike } from '@/shared/lib/code/language';
|
|
7
|
+
import { highlightCode } from '@/shared/lib/code/syntax-highlight';
|
|
7
8
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
8
9
|
|
|
9
10
|
interface TaskResultContentProps {
|
package/src/client/components/workspace/chat-timeline/tool-renderers/hashline-patch/index.tsx
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
import { useMemo } from 'preact/hooks';
|
|
14
14
|
import type { HashlineSection, HashlineVerb } from '@/shared/lib/omp/session/hashline-patch';
|
|
15
|
-
import { getLanguageFromPath
|
|
15
|
+
import { getLanguageFromPath } from '@/shared/lib/code/language';
|
|
16
|
+
import { highlightLines } from '@/shared/lib/code/syntax-highlight';
|
|
16
17
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
17
18
|
|
|
18
19
|
/** A hashline `CUT` removes lines the model already saw, so it reads as the
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useMemo } from 'preact/hooks';
|
|
2
2
|
import { ArrowRight, CheckCircle2, Clock, Wand2 } from 'lucide-preact';
|
|
3
3
|
import type { ToolCallData } from '@/shared/types';
|
|
4
|
-
import { getLanguageFromPath
|
|
4
|
+
import { getLanguageFromPath } from '@/shared/lib/code/language';
|
|
5
|
+
import { highlightLines } from '@/shared/lib/code/syntax-highlight';
|
|
5
6
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
6
7
|
|
|
7
8
|
interface AstEditItem {
|
|
@@ -3,7 +3,8 @@ import { Check, Clock } from 'lucide-preact';
|
|
|
3
3
|
import type { ToolCallData } from '@/shared/types';
|
|
4
4
|
import { CopyButton } from '@/client/components/common/CopyButton';
|
|
5
5
|
import { stripAnsiCodes } from '@/shared/lib/code/ansi';
|
|
6
|
-
import {
|
|
6
|
+
import { isCodeLike } from '@/shared/lib/code/language';
|
|
7
|
+
import { highlightCode, tryParseJson } from '@/shared/lib/code/syntax-highlight';
|
|
7
8
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
8
9
|
import { JsonCodeBlock } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/JsonCodeBlock';
|
|
9
10
|
import { MAX_OUTPUT_LINES, truncateTailLines } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/truncate';
|
|
@@ -6,7 +6,8 @@ import { DiffView } from '@/client/components/workspace/chat-timeline/tool-rende
|
|
|
6
6
|
import { HashlinePatch } from '@/client/components/workspace/chat-timeline/tool-renderers/hashline-patch';
|
|
7
7
|
import { MarkdownRenderer } from '@/client/components/common/MarkdownRenderer';
|
|
8
8
|
import { EnvelopeHeader } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/EnvelopeHeader';
|
|
9
|
-
import { getLanguageFromPath
|
|
9
|
+
import { getLanguageFromPath } from '@/shared/lib/code/language';
|
|
10
|
+
import { highlightCode } from '@/shared/lib/code/syntax-highlight';
|
|
10
11
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
11
12
|
import { MAX_OUTPUT_LINES, truncateTailLines } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/truncate';
|
|
12
13
|
import { outputMarkdown, readToolOutput } from '@/shared/lib/chat/tool-output';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from 'preact/hooks';
|
|
2
2
|
import { FileCode, FileImage, FileText, Folder, FolderOpen, Info, Loader2 } from 'lucide-preact';
|
|
3
3
|
import { CopyButton } from '@/client/components/common/CopyButton';
|
|
4
|
-
import { getLanguageFromPath
|
|
4
|
+
import { getLanguageFromPath } from '@/shared/lib/code/language';
|
|
5
|
+
import { highlightCode } from '@/shared/lib/code/syntax-highlight';
|
|
5
6
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
6
7
|
import { parseDirListing, parseNumberedCode } from '@/shared/lib/code/parser';
|
|
7
8
|
import { getImageMimeType } from '@/shared/lib/fs/file-kind';
|
|
@@ -2,7 +2,8 @@ import { useMemo, useState } from 'preact/hooks';
|
|
|
2
2
|
import { ChevronRight, FileSearch, FileText, FolderSearch } from 'lucide-preact';
|
|
3
3
|
import type { ToolCallData } from '@/shared/types';
|
|
4
4
|
import { CopyButton } from '@/client/components/common/CopyButton';
|
|
5
|
-
import { getLanguageFromPath
|
|
5
|
+
import { getLanguageFromPath } from '@/shared/lib/code/language';
|
|
6
|
+
import { highlightLines } from '@/shared/lib/code/syntax-highlight';
|
|
6
7
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
7
8
|
import { MAX_OUTPUT_LINES, truncateTailLines } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/truncate';
|
|
8
9
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'preact/hooks';
|
|
2
|
-
import { getLanguageFromPath
|
|
2
|
+
import { getLanguageFromPath } from '@/shared/lib/code/language';
|
|
3
|
+
import { highlightLines } from '@/shared/lib/code/syntax-highlight';
|
|
3
4
|
import { useSyntaxReady } from '@/client/hooks/ui/syntax-ready';
|
|
4
5
|
|
|
5
6
|
interface DiffLine {
|