ompchamber 0.7.0 → 1.0.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/static/css/index.e880517fcd.css +2 -0
- package/dist/client/static/js/async/{3y.4c2596f64c.js → 3y.71f2ccf3b6.js} +2 -2
- package/dist/client/static/js/async/{xw.ae67ba06fd.js → 5b.0d6ae50fe8.js} +10 -10
- package/dist/client/static/js/async/5bh.8b2363424f.js +1 -0
- package/dist/client/static/js/async/{vy.bbe114c4e0.js → bi.09688ba75c.js} +1 -2
- package/dist/client/static/js/async/c.797d933882.js +1 -0
- package/dist/client/static/js/async/{dc.6dc4b0a04b.js → dc.856fda6560.js} +2 -2
- package/dist/client/static/js/async/g.a06ca3b3e9.js +2 -0
- package/dist/client/static/js/async/g.a06ca3b3e9.js.LICENSE.txt +11 -0
- package/dist/client/static/js/async/{m.4b29d34f93.js → m.c038c71b9f.js} +2 -2
- package/dist/client/static/js/async/ma.2c261b04dd.js +2 -0
- package/dist/client/static/js/async/ms.98960af4eb.js +67 -0
- package/dist/client/static/js/async/nam.b5663e919f.js +10 -0
- package/dist/client/static/js/async/nam.b5663e919f.js.LICENSE.txt +11 -0
- package/dist/client/static/js/async/ot.84c71b308f.js +2 -0
- package/dist/client/static/js/async/ot.84c71b308f.js.LICENSE.txt +11 -0
- package/dist/client/static/js/async/{q.542c184fec.js → q.23af2a358f.js} +3 -3
- package/dist/client/static/js/async/{s.413c2a4683.js → s.222ee974f6.js} +2 -2
- package/dist/client/static/js/async/t.4dca0e2ba8.js +1 -0
- package/dist/client/static/js/async/uf.46ad6209fa.js +2 -0
- package/dist/client/static/js/async/xn.6360379f71.js +2 -0
- package/dist/client/static/js/async/xnl.feb0021e50.js +2 -0
- package/dist/client/static/js/async/xnl.feb0021e50.js.LICENSE.txt +12 -0
- package/dist/client/static/js/index.0de15c5e17.js +33 -0
- package/dist/client/static/js/{uy.2e8929ed0c.js → jp.50817896f3.js} +6 -6
- package/package.json +2 -1
- package/src/client/components/common/MarkdownRenderer.tsx +69 -27
- package/src/client/components/common/code-editor/history.test.ts +118 -0
- package/src/client/components/common/code-editor/history.ts +101 -0
- package/src/client/components/common/code-editor/index.tsx +56 -66
- package/src/client/components/common/code-surface/index.tsx +84 -11
- package/src/client/components/common/code-surface/measure.ts +66 -17
- package/src/client/components/common/diagram-viewer/index.tsx +258 -0
- package/src/client/components/common/diagram-viewer/read-block.ts +53 -0
- package/src/client/components/layout/session-sidebar/CategoryItem.tsx +2 -1
- package/src/client/components/layout/session-sidebar/SessionItem.tsx +11 -4
- package/src/client/components/layout/session-sidebar/SessionList.tsx +1 -1
- package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +1 -1
- package/src/client/components/mobile/mobile-session-sidebar/List.tsx +1 -1
- package/src/client/components/mobile/mobile-session-sidebar/SessionRow.tsx +2 -2
- package/src/client/components/settings/LazyModal.tsx +1 -1
- package/src/client/components/settings/Modal.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/ComposerDock.tsx +11 -4
- package/src/client/components/workspace/chat-timeline/QueueList.tsx +18 -14
- package/src/client/components/workspace/chat-timeline/ToolCallCard.tsx +8 -1
- package/src/client/components/workspace/chat-timeline/chat-input/index.tsx +11 -52
- package/src/client/components/workspace/chat-timeline/chat-input/selection.ts +57 -0
- package/src/client/components/workspace/chat-timeline/index.tsx +124 -108
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/FrameControl.tsx +172 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/QuestionBlock.tsx +155 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/drafts.ts +113 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/index.tsx +92 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Footer.tsx +3 -3
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Header.tsx +3 -3
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Lazy.tsx +8 -8
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/SelectBody.tsx +3 -3
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/index.tsx +16 -10
- package/src/client/components/workspace/chat-timeline/tool-renderers/index.tsx +2 -2
- package/src/client/components/workspace/file-explorer/index.tsx +18 -5
- package/src/client/components/workspace/git-panel/BranchToolbar.tsx +2 -2
- package/src/client/components/workspace/git-panel/index.tsx +5 -1
- package/src/client/components/workspace/terminal-panel/Header.tsx +51 -19
- package/src/client/components/workspace/terminal-panel/RealtimeXtermView.tsx +86 -282
- package/src/client/components/workspace/terminal-panel/index.tsx +76 -50
- package/src/client/components/workspace/terminal-panel/mount.ts +275 -0
- package/src/client/data/theme/terminal.ts +27 -29
- package/src/client/hooks/chat/omp/session-list.tsx +23 -2
- package/src/client/hooks/chat/omp/session-sidebar-controller.ts +1 -1
- package/src/client/hooks/chat/timeline/actions.ts +23 -34
- package/src/client/hooks/chat/timeline/ask-frames.ts +102 -0
- package/src/client/hooks/chat/timeline/extension-dialog.ts +12 -9
- package/src/client/hooks/chat/timeline/index.ts +18 -28
- package/src/client/hooks/chat/timeline/prompt-text.ts +35 -0
- package/src/client/hooks/chat/timeline/queue.ts +110 -46
- package/src/client/hooks/chat/timeline/send.ts +13 -27
- package/src/client/hooks/chat/timeline/session-load.ts +48 -106
- package/src/client/hooks/chat/timeline/session-pagination.ts +144 -0
- package/src/client/hooks/editor/use-code-window.ts +118 -0
- package/src/client/hooks/ui/input-required-alert.ts +43 -0
- package/src/client/hooks/ui/notification-sound.ts +65 -0
- package/src/client/hooks/ui/theme.ts +22 -6
- package/src/client/hooks/workspace/terminal/index.ts +217 -0
- package/src/client/hooks/workspace/terminal/socket.ts +227 -0
- package/src/client/tailwind.css +38 -12
- package/src/server/db.server.ts +19 -0
- package/src/server/lib/fs/git-sync.ts +191 -0
- package/src/server/lib/omp/rpc/manager.ts +72 -6
- package/src/server/lib/omp/rpc/session-commands.ts +6 -0
- package/src/server/lib/omp/rpc/session-registry.ts +18 -0
- package/src/server/lib/omp/session/sidebar-data.server.ts +10 -4
- package/src/server/lib/queue/delivery.server.ts +114 -0
- package/src/server/lib/queue/schema-migration.server.ts +50 -0
- package/src/server/lib/queue/store.server.ts +185 -0
- package/src/server/lib/terminal/foreground.ts +73 -0
- package/src/server/lib/terminal/history.test.ts +118 -0
- package/src/server/lib/terminal/history.ts +205 -0
- package/src/server/lib/terminal/runtime.server.ts +338 -0
- package/src/server/lib/terminal/session.server.ts +215 -0
- package/src/server/lib/terminal/shell.test.ts +97 -0
- package/src/server/lib/terminal/shell.ts +103 -0
- package/src/server/routes/fs/git.ts +22 -12
- package/src/server/routes/index.ts +2 -1
- package/src/server/routes/sessions/index.ts +11 -3
- package/src/server/routes/sessions/queue.ts +121 -0
- package/src/server/routes/sessions/session.ts +1 -132
- package/src/server/routes/settings/commands.ts +1 -1
- package/src/server/routes/terminal/index.ts +2 -2
- package/src/server/routes/terminal/run.ts +8 -100
- package/src/server/routes/terminal/sessions.ts +24 -0
- package/src/server/routes/terminal/ws.ts +149 -0
- package/src/shared/lib/chat/ask-questions.test.ts +169 -0
- package/src/shared/lib/chat/ask-questions.ts +264 -0
- package/src/shared/lib/chat/omp/agent-events.test.ts +37 -1
- package/src/shared/lib/chat/omp/agent-events.ts +30 -0
- package/src/shared/lib/chat/timeline/omp-callbacks.test.ts +9 -1
- package/src/shared/lib/chat/timeline/omp-callbacks.ts +45 -8
- package/src/shared/lib/code/lazy-window.test.ts +161 -0
- package/src/shared/lib/code/lazy-window.ts +156 -0
- package/src/shared/lib/code/syntax-highlight.test.ts +50 -1
- package/src/shared/lib/code/syntax-highlight.ts +14 -1
- package/src/shared/lib/code/windowed-highlight.ts +63 -0
- package/src/shared/lib/markdown/mermaid.ts +8 -6
- package/src/shared/lib/omp/session/stream-state.server.ts +2 -3
- package/src/shared/lib/workspace/session-state/store.ts +1 -4
- package/src/shared/lib/workspace/terminal/protocol.ts +222 -0
- package/src/shared/types/omp/agent.ts +14 -2
- package/src/shared/types/terminal.ts +17 -15
- package/src/shared/types/workspace.ts +6 -2
- package/dist/client/static/css/index.51f68210a5.css +0 -2
- package/dist/client/static/js/async/5b.d88304bc13.js +0 -1
- package/dist/client/static/js/async/c.cd45b0ac45.js +0 -1
- package/dist/client/static/js/async/fw.6a51998a8e.js +0 -6
- package/dist/client/static/js/async/g.86e2256a70.js +0 -2
- package/dist/client/static/js/async/kc.bbdb35b771.js +0 -1
- package/dist/client/static/js/async/mw.ca1512563c.js +0 -10
- package/dist/client/static/js/async/rf.d626afc367.js +0 -2
- package/dist/client/static/js/async/t.cda4891526.js +0 -1
- package/dist/client/static/js/async/uf.24ca3d49c3.js +0 -2
- package/dist/client/static/js/async/uf.24ca3d49c3.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/v0.394ed37c6b.js +0 -2
- package/dist/client/static/js/async/v0.394ed37c6b.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/vy.bbe114c4e0.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/xn.afecebbbd9.js +0 -2
- package/dist/client/static/js/index.eccad0b70a.js +0 -33
- package/dist/client/static/js/index.eccad0b70a.js.LICENSE.txt +0 -4
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Ask.tsx +0 -205
- package/src/client/components/workspace/terminal-panel/InputBar.tsx +0 -82
- package/src/client/components/workspace/terminal-panel/LogEntry.tsx +0 -70
- package/src/client/hooks/workspace/terminal-output.ts +0 -66
- package/src/client/hooks/workspace/terminal.ts +0 -283
- package/src/server/lib/fs/terminal-cwd.ts +0 -49
- package/src/server/routes/terminal/stream.ts +0 -100
- /package/dist/client/static/js/async/{3y.4c2596f64c.js.LICENSE.txt → 3y.71f2ccf3b6.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{g.86e2256a70.js.LICENSE.txt → 5b.0d6ae50fe8.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{dc.6dc4b0a04b.js.LICENSE.txt → dc.856fda6560.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{fw.6a51998a8e.js.LICENSE.txt → m.c038c71b9f.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{m.4b29d34f93.js.LICENSE.txt → ma.2c261b04dd.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{xn.afecebbbd9.js.LICENSE.txt → ms.98960af4eb.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{mw.ca1512563c.js.LICENSE.txt → q.23af2a358f.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{q.542c184fec.js.LICENSE.txt → s.222ee974f6.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{rf.d626afc367.js.LICENSE.txt → uf.46ad6209fa.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{s.413c2a4683.js.LICENSE.txt → xn.6360379f71.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/{async/xw.ae67ba06fd.js.LICENSE.txt → index.0de15c5e17.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/{uy.2e8929ed0c.js.LICENSE.txt → jp.50817896f3.js.LICENSE.txt} +0 -0
|
@@ -7,7 +7,7 @@ interface MobileSessionListProps {
|
|
|
7
7
|
activeSessionId: number | string | null;
|
|
8
8
|
expandedFolders: Record<number, boolean>;
|
|
9
9
|
showArchived: boolean;
|
|
10
|
-
sessionStatus: Record<string, 'stream' | 'finish' | 'abort'
|
|
10
|
+
sessionStatus: Record<string, 'stream' | 'finish' | 'abort'>;
|
|
11
11
|
isScrolling: boolean;
|
|
12
12
|
onScroll: () => void;
|
|
13
13
|
onSelectSession: (id: number | string) => void;
|
|
@@ -5,7 +5,7 @@ import { useInlineRename } from '@/client/hooks/ui/inline-rename';
|
|
|
5
5
|
export interface MobileSessionRowProps {
|
|
6
6
|
session: SessionItemData;
|
|
7
7
|
isActive: boolean;
|
|
8
|
-
status?: 'stream' | 'finish' | 'abort'
|
|
8
|
+
status?: 'stream' | 'finish' | 'abort';
|
|
9
9
|
/** Relative age of the session, or null when no usable timestamp exists. */
|
|
10
10
|
timeAgo: string | null;
|
|
11
11
|
showTreeGlyph?: boolean;
|
|
@@ -64,7 +64,7 @@ export function MobileSessionRow({
|
|
|
64
64
|
<div className="flex items-center space-x-1.5 min-w-0 pr-2">
|
|
65
65
|
<span className="w-4 flex-shrink-0 flex items-center justify-center">
|
|
66
66
|
{status === 'stream' && <Loader2 size={13} className="text-ink/50 animate-spin" />}
|
|
67
|
-
{(status === 'finish' || status === 'abort'
|
|
67
|
+
{(status === 'finish' || status === 'abort') && <Check size={13} className="text-ink/50" />}
|
|
68
68
|
</span>
|
|
69
69
|
{showTreeGlyph && <span className="text-ink/40 text-xs flex-shrink-0 font-mono">></span>}
|
|
70
70
|
<span className="text-xs truncate leading-snug">
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* the stale bootstrap payload.
|
|
25
25
|
*
|
|
26
26
|
* `fallback={null}` matches the other lazy dialog boundary
|
|
27
|
-
* (`tool-renderers/
|
|
27
|
+
* (`tool-renderers/extension-dialog/Lazy.tsx`): the modal is a full-screen overlay
|
|
28
28
|
* that appears on user intent, so a brief absence reads as the click landing
|
|
29
29
|
* rather than as a broken surface.
|
|
30
30
|
*/
|
|
@@ -3,6 +3,7 @@ import { Check, ChevronLeft, X } from 'lucide-preact';
|
|
|
3
3
|
import type { SettingsCategoryId, SettingsState } from '@/shared/types';
|
|
4
4
|
import { mergeChamberSettings } from '@/shared/lib/settings/client';
|
|
5
5
|
import { useChamberSettingsWriter } from '@/client/hooks/settings/use-chamber-setting';
|
|
6
|
+
import { applyDocumentTheme } from '@/client/hooks/ui/theme';
|
|
6
7
|
import { useChamberEvent } from '@/client/hooks/ui/window-event';
|
|
7
8
|
import { SETTINGS_CATEGORIES, SettingsSidebar } from '@/client/components/settings/Sidebar';
|
|
8
9
|
import { AppearanceSettings } from '@/client/components/settings/categories/AppearanceSettings';
|
|
@@ -100,7 +101,7 @@ export function SettingsModal({
|
|
|
100
101
|
setSettings(prev => {
|
|
101
102
|
const next = typeof updater === 'function' ? updater(prev) : { ...prev, ...updater };
|
|
102
103
|
if (typeof document !== 'undefined' && next.theme) {
|
|
103
|
-
|
|
104
|
+
applyDocumentTheme(next.theme);
|
|
104
105
|
}
|
|
105
106
|
writeChamberSettings(next);
|
|
106
107
|
return next;
|
|
@@ -13,7 +13,8 @@ interface ComposerDockProps {
|
|
|
13
13
|
provider?: string;
|
|
14
14
|
providerNames?: Record<string, string>;
|
|
15
15
|
messageQueue: QueuedMessage[];
|
|
16
|
-
|
|
16
|
+
onRemoveQueueItem: (id: string) => void;
|
|
17
|
+
onReorderQueue: (orderedIds: string[]) => void;
|
|
17
18
|
onEditQueueItem: (item: QueuedMessage) => void;
|
|
18
19
|
onSendNowQueueItem: (item: QueuedMessage) => void;
|
|
19
20
|
steeringQueue: QueuedMessage[];
|
|
@@ -49,7 +50,8 @@ export function ComposerDock({
|
|
|
49
50
|
provider,
|
|
50
51
|
providerNames,
|
|
51
52
|
messageQueue,
|
|
52
|
-
|
|
53
|
+
onRemoveQueueItem,
|
|
54
|
+
onReorderQueue,
|
|
53
55
|
onEditQueueItem,
|
|
54
56
|
onSendNowQueueItem,
|
|
55
57
|
steeringQueue,
|
|
@@ -87,13 +89,18 @@ export function ComposerDock({
|
|
|
87
89
|
)}
|
|
88
90
|
<QueueList
|
|
89
91
|
queue={messageQueue}
|
|
90
|
-
|
|
92
|
+
onRemove={onRemoveQueueItem}
|
|
93
|
+
onReorder={onReorderQueue}
|
|
91
94
|
onEdit={onEditQueueItem}
|
|
92
95
|
onSendNow={onSendNowQueueItem}
|
|
93
96
|
/>
|
|
94
97
|
<QueueList
|
|
95
98
|
queue={steeringQueue}
|
|
96
|
-
|
|
99
|
+
onRemove={(id) => setSteeringQueue((q) => q.filter((i) => i.id !== id))}
|
|
100
|
+
onReorder={(orderedIds) => setSteeringQueue((prev) => {
|
|
101
|
+
const byId = new Map(prev.map((i) => [i.id, i]));
|
|
102
|
+
return orderedIds.map((id) => byId.get(id)).filter((i): i is QueuedMessage => Boolean(i));
|
|
103
|
+
})}
|
|
97
104
|
isSteering
|
|
98
105
|
/>
|
|
99
106
|
<ChatInput
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Dispatch, SetStateAction } from 'preact/compat';
|
|
2
1
|
import { useRef } from 'preact/hooks';
|
|
3
2
|
import { GripVertical, Pencil, Send, X } from 'lucide-preact';
|
|
4
3
|
import type { QueuedMessage } from '@/shared/types';
|
|
@@ -7,7 +6,10 @@ export type { QueuedMessage };
|
|
|
7
6
|
|
|
8
7
|
interface QueueListProps {
|
|
9
8
|
queue: QueuedMessage[];
|
|
10
|
-
|
|
9
|
+
/** Reorder handler (drag-and-drop): receives the new id order. */
|
|
10
|
+
onReorder?: (orderedIds: string[]) => void;
|
|
11
|
+
/** Remove one row by id. */
|
|
12
|
+
onRemove?: (id: string) => void;
|
|
11
13
|
/** True when every row in this panel is a steering delivery. */
|
|
12
14
|
isSteering?: boolean;
|
|
13
15
|
onEdit?: (item: QueuedMessage) => void;
|
|
@@ -20,18 +22,18 @@ const KIND_LABEL: Record<string, string> = {
|
|
|
20
22
|
followup: 'follow-up',
|
|
21
23
|
};
|
|
22
24
|
|
|
23
|
-
export function QueueList({ queue,
|
|
25
|
+
export function QueueList({ queue, onReorder, onRemove, isSteering = false, onEdit, onSendNow }: QueueListProps) {
|
|
24
26
|
const dragItem = useRef<number | null>(null);
|
|
25
27
|
const dragOverItem = useRef<number | null>(null);
|
|
26
28
|
|
|
27
29
|
const handleSort = () => {
|
|
28
30
|
if (dragItem.current === null || dragOverItem.current === null) return;
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
31
|
+
const ids = queue.map((i) => i.id);
|
|
32
|
+
const draggedId = ids.splice(dragItem.current, 1)[0];
|
|
33
|
+
ids.splice(dragOverItem.current, 0, draggedId);
|
|
32
34
|
dragItem.current = null;
|
|
33
35
|
dragOverItem.current = null;
|
|
34
|
-
|
|
36
|
+
onReorder?.(ids);
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
if (queue.length === 0) return null;
|
|
@@ -80,13 +82,15 @@ export function QueueList({ queue, setQueue, isSteering = false, onEdit, onSendN
|
|
|
80
82
|
<Pencil size={12} />
|
|
81
83
|
</button>
|
|
82
84
|
)}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
{onRemove && (
|
|
86
|
+
<button
|
|
87
|
+
onClick={() => onRemove(item.id)}
|
|
88
|
+
className="text-ink/40 hover:text-error opacity-0 group-hover:opacity-100 transition-opacity"
|
|
89
|
+
title="Remove"
|
|
90
|
+
>
|
|
91
|
+
<X size={14} />
|
|
92
|
+
</button>
|
|
93
|
+
)}
|
|
90
94
|
</div>
|
|
91
95
|
))}
|
|
92
96
|
</div>
|
|
@@ -13,6 +13,7 @@ import { FallbackOutput } from '@/client/components/workspace/chat-timeline/tool
|
|
|
13
13
|
import { tryParseJson } from '@/shared/lib/code/syntax-highlight';
|
|
14
14
|
import { JsonCodeBlock } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/JsonCodeBlock';
|
|
15
15
|
import { getTodoSummary } from '@/shared/lib/chat/todo-parser';
|
|
16
|
+
import { parseAskQuestions } from '@/shared/lib/chat/ask-questions';
|
|
16
17
|
|
|
17
18
|
interface ToolCallCardProps {
|
|
18
19
|
tool: ToolCallData;
|
|
@@ -199,6 +200,12 @@ export const ToolCallCard = memo(function ToolCallCard({ tool, isOpen, onToggle,
|
|
|
199
200
|
.join(' · ');
|
|
200
201
|
displaySubtitle = cleaned || sub;
|
|
201
202
|
}
|
|
203
|
+
} else if (toolKey === 'ask') {
|
|
204
|
+
// The question itself is the card's body; the header says how many were
|
|
205
|
+
// asked, matching the summary the same card shows once they are answered.
|
|
206
|
+
displayTitle = 'Question';
|
|
207
|
+
const asked = parseAskQuestions(tool).length;
|
|
208
|
+
if (asked > 0) displaySubtitle = `Asked ${asked} question${asked === 1 ? '' : 's'}`;
|
|
202
209
|
} else if (mcpToolNameOf(tool)) {
|
|
203
210
|
// MCP call: `write xd://mcp__<tool>` (or a direct `mcp__<tool>` name) —
|
|
204
211
|
// the MCP tool names the action, never the transport `write`.
|
|
@@ -258,7 +265,7 @@ export const ToolCallCard = memo(function ToolCallCard({ tool, isOpen, onToggle,
|
|
|
258
265
|
isOpen={isOpen}
|
|
259
266
|
onToggle={onToggle ? handleToggle : undefined}
|
|
260
267
|
defaultExpanded={defaultExpanded}
|
|
261
|
-
alwaysExpanded={toolKey === 'yield'
|
|
268
|
+
alwaysExpanded={hasPanel && (toolKey === 'yield' || toolKey === 'ask')}
|
|
262
269
|
>
|
|
263
270
|
<ToolDetailsPanel tool={tool} />
|
|
264
271
|
|
|
@@ -7,6 +7,7 @@ import { ComposerTextarea } from '@/client/components/common/ComposerTextarea';
|
|
|
7
7
|
import { AttachmentToolbar } from '@/client/components/workspace/chat-timeline/chat-input/AttachmentToolbar';
|
|
8
8
|
import { selectableThinkingLevels } from '@/shared/lib/models/thinking-levels';
|
|
9
9
|
import { fetchModelsData, subscribeModelsUpdated } from '@/shared/lib/models/client';
|
|
10
|
+
import { resolveThinkingLevel, selectionFor } from '@/client/components/workspace/chat-timeline/chat-input/selection';
|
|
10
11
|
|
|
11
12
|
export function ChatInput({
|
|
12
13
|
value,
|
|
@@ -96,21 +97,11 @@ export function ChatInput({
|
|
|
96
97
|
const sessionModelRef = useRef<{ provider: string; modelId: string } | null>(sessionModel ?? null);
|
|
97
98
|
sessionModelRef.current = sessionModel ?? null;
|
|
98
99
|
|
|
99
|
-
// Preserve the session's last-used thinking level when applying a model
|
|
100
|
-
// picked from the catalog: the session level is authoritative and an async
|
|
101
|
-
// fetch resolving later must not reset it to the ladder default.
|
|
102
|
-
const resolveSessionLevel = (ladder: readonly string[] | undefined, fallback: string): string => {
|
|
103
|
-
const sessionLevel = sessionThinkingLevelRef.current;
|
|
104
|
-
if (!sessionLevel) return fallback;
|
|
105
|
-
const selectable = selectableThinkingLevels(ladder ?? []);
|
|
106
|
-
return selectable.length === 0 || selectable.includes(sessionLevel) ? sessionLevel : fallback;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
100
|
// Adopt the active session's last-used model as the selected model. The
|
|
110
|
-
// thinking level must fall back to the CURRENT one, never the ladder
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
101
|
+
// thinking level must fall back to the CURRENT one, never the ladder default:
|
|
102
|
+
// this effect re-runs right after a spawn (sessionModel changes while the
|
|
103
|
+
// session's own thinking_level_change entry is not readable yet), and any
|
|
104
|
+
// concrete fallback here claims a level the run never used.
|
|
114
105
|
useEffect(() => {
|
|
115
106
|
if (!sessionModel?.provider || !sessionModel.modelId) return;
|
|
116
107
|
let active = true;
|
|
@@ -121,14 +112,7 @@ export function ChatInput({
|
|
|
121
112
|
(m) => m.id === sessionModel.modelId && m.provider === sessionModel.provider
|
|
122
113
|
);
|
|
123
114
|
if (match) {
|
|
124
|
-
setSelectedModel(prev => (
|
|
125
|
-
id: match.id,
|
|
126
|
-
name: match.name,
|
|
127
|
-
provider: match.provider,
|
|
128
|
-
contextWindow: match.contextWindow,
|
|
129
|
-
thinkingLevels: match.thinkingLevels,
|
|
130
|
-
thinkingLevel: resolveSessionLevel(match.thinkingLevels, prev?.thinkingLevel ?? match.thinkingLevels?.[0] ?? 'off'),
|
|
131
|
-
}));
|
|
115
|
+
setSelectedModel(prev => selectionFor(match, sessionThinkingLevelRef.current, prev));
|
|
132
116
|
}
|
|
133
117
|
})
|
|
134
118
|
.catch(() => {});
|
|
@@ -143,14 +127,7 @@ export function ChatInput({
|
|
|
143
127
|
if (!sessionThinkingLevel) return;
|
|
144
128
|
setSelectedModel(prev => {
|
|
145
129
|
if (!prev) return prev;
|
|
146
|
-
const
|
|
147
|
-
// The session level is authoritative (omp recorded it for this model).
|
|
148
|
-
// Only reject it when the ladder is KNOWN and explicitly excludes it;
|
|
149
|
-
// an empty ladder (catalog not resolved / model exposes none) must not
|
|
150
|
-
// fall back to 'off' and erase the session's actual level.
|
|
151
|
-
const level = selectable.length === 0 || selectable.includes(sessionThinkingLevel)
|
|
152
|
-
? sessionThinkingLevel
|
|
153
|
-
: prev.thinkingLevel;
|
|
130
|
+
const level = resolveThinkingLevel(prev.thinkingLevels, sessionThinkingLevel, prev.thinkingLevel);
|
|
154
131
|
return level === prev.thinkingLevel ? prev : { ...prev, thinkingLevel: level };
|
|
155
132
|
});
|
|
156
133
|
}, [sessionThinkingLevel]);
|
|
@@ -169,13 +146,7 @@ export function ChatInput({
|
|
|
169
146
|
if (sm?.provider && sm.modelId) {
|
|
170
147
|
const match = data.modelList.find((m: ModelEntry) => m.id === sm.modelId && m.provider === sm.provider);
|
|
171
148
|
if (match) {
|
|
172
|
-
setSelectedModel(prev => (
|
|
173
|
-
id: match.id,
|
|
174
|
-
name: match.name,
|
|
175
|
-
provider: match.provider,
|
|
176
|
-
thinkingLevels: match.thinkingLevels,
|
|
177
|
-
thinkingLevel: resolveSessionLevel(match.thinkingLevels, prev?.thinkingLevel ?? match.thinkingLevels?.[0] ?? 'off'),
|
|
178
|
-
}));
|
|
149
|
+
setSelectedModel(prev => selectionFor(match, sessionThinkingLevelRef.current, prev));
|
|
179
150
|
}
|
|
180
151
|
return;
|
|
181
152
|
}
|
|
@@ -184,13 +155,7 @@ export function ChatInput({
|
|
|
184
155
|
if (persisted?.provider && persisted.id) {
|
|
185
156
|
const match = data.modelList.find((m: ModelEntry) => m.id === persisted.id && m.provider === persisted.provider);
|
|
186
157
|
if (match) {
|
|
187
|
-
setSelectedModel(prev => (
|
|
188
|
-
id: match.id,
|
|
189
|
-
name: match.name,
|
|
190
|
-
provider: match.provider,
|
|
191
|
-
thinkingLevels: match.thinkingLevels,
|
|
192
|
-
thinkingLevel: resolveSessionLevel(match.thinkingLevels, prev?.thinkingLevel ?? match.thinkingLevels?.[0] ?? 'off'),
|
|
193
|
-
}));
|
|
158
|
+
setSelectedModel(prev => selectionFor(match, sessionThinkingLevelRef.current, prev));
|
|
194
159
|
return;
|
|
195
160
|
}
|
|
196
161
|
}
|
|
@@ -199,20 +164,14 @@ export function ChatInput({
|
|
|
199
164
|
if (defaultModel) {
|
|
200
165
|
const match = data.modelList.find((m: ModelEntry) => m.id === defaultModel.modelId && m.provider === defaultModel.provider);
|
|
201
166
|
if (match) {
|
|
202
|
-
setSelectedModel(prev => (
|
|
203
|
-
id: match.id,
|
|
204
|
-
name: match.name,
|
|
205
|
-
provider: match.provider,
|
|
206
|
-
thinkingLevels: match.thinkingLevels,
|
|
207
|
-
thinkingLevel: resolveSessionLevel(match.thinkingLevels, prev?.thinkingLevel ?? match.thinkingLevels?.[0] ?? 'off'),
|
|
208
|
-
}));
|
|
167
|
+
setSelectedModel(prev => selectionFor(match, sessionThinkingLevelRef.current, prev));
|
|
209
168
|
}
|
|
210
169
|
}
|
|
211
170
|
} else if (data.selectedModel) {
|
|
212
171
|
const selected = data.selectedModel;
|
|
213
172
|
setSelectedModel(prev => ({
|
|
214
173
|
...selected,
|
|
215
|
-
thinkingLevel:
|
|
174
|
+
thinkingLevel: resolveThinkingLevel(selected.thinkingLevels, sessionThinkingLevelRef.current, prev?.thinkingLevel ?? selected.thinkingLevel),
|
|
216
175
|
}));
|
|
217
176
|
}
|
|
218
177
|
} catch {}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Catalog → composer mapping for the model/thinking pickers: one definition of
|
|
8
|
+
* how a catalog entry becomes the selected model, and of which thinking level
|
|
9
|
+
* that selection is allowed to claim. Shared by every resolution site in
|
|
10
|
+
* ChatInput so the level rule cannot drift between them.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { AIModelOption, ModelEntry } from '@/shared/types';
|
|
14
|
+
import { selectableThinkingLevels } from '@/shared/lib/models/thinking-levels';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Level reported while none is known. A catalog ladder's first entry is NOT a
|
|
18
|
+
* fact about the session — every model's ladder starts at `off` — and the send
|
|
19
|
+
* path pushes any non-`auto` value onto the live session (queue replay), so an
|
|
20
|
+
* unresolved level must stay indistinguishable from "leave omp alone".
|
|
21
|
+
*/
|
|
22
|
+
export const UNKNOWN_THINKING_LEVEL = 'auto';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Level the composer may select for a model. The session's last-used level is
|
|
26
|
+
* authoritative whenever the model offers it — an async fetch resolving later
|
|
27
|
+
* must not reset it to a ladder default. Only a ladder that is KNOWN and
|
|
28
|
+
* excludes the session level rejects it; an empty ladder (catalog not resolved
|
|
29
|
+
* / model exposes none) must not erase the session's actual level. With no
|
|
30
|
+
* session level to go on, the current selection is kept, and a composer that
|
|
31
|
+
* has none yet reports {@link UNKNOWN_THINKING_LEVEL}.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveThinkingLevel(
|
|
34
|
+
ladder: readonly string[] | null | undefined,
|
|
35
|
+
sessionLevel: string | null | undefined,
|
|
36
|
+
currentLevel: string | null | undefined,
|
|
37
|
+
): string {
|
|
38
|
+
const selectable = selectableThinkingLevels(ladder ?? []);
|
|
39
|
+
if (sessionLevel && (selectable.length === 0 || selectable.includes(sessionLevel))) return sessionLevel;
|
|
40
|
+
return currentLevel ?? UNKNOWN_THINKING_LEVEL;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The composer's selection for a catalog model, carrying its thinking level. */
|
|
44
|
+
export function selectionFor(
|
|
45
|
+
match: ModelEntry,
|
|
46
|
+
sessionLevel: string | null | undefined,
|
|
47
|
+
current: AIModelOption | null,
|
|
48
|
+
): AIModelOption {
|
|
49
|
+
return {
|
|
50
|
+
id: match.id,
|
|
51
|
+
name: match.name,
|
|
52
|
+
provider: match.provider,
|
|
53
|
+
contextWindow: match.contextWindow,
|
|
54
|
+
thinkingLevels: match.thinkingLevels,
|
|
55
|
+
thinkingLevel: resolveThinkingLevel(match.thinkingLevels, sessionLevel, current?.thinkingLevel),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState } from 'preact/hooks';
|
|
2
2
|
import { useSearchParams } from '@/client/lib/router/search-params';
|
|
3
|
-
import {
|
|
3
|
+
import { ExtensionDialog } from '@/client/components/workspace/chat-timeline/tool-renderers/extension-dialog/Lazy';
|
|
4
|
+
import { AskFramesContext, splitAskFrames, type AskFramesHandle } from '@/client/hooks/chat/timeline/ask-frames';
|
|
5
|
+
import type { ExtensionUiDialogRequest } from '@/shared/types/omp/agent';
|
|
6
|
+
import type { ExtensionDialogResponse } from '@/client/components/workspace/chat-timeline/tool-renderers/extension-dialog/Lazy';
|
|
4
7
|
import { EmptyWorkspacePrompt } from '@/client/components/workspace/chat-timeline/EmptyWorkspacePrompt';
|
|
5
8
|
import { SessionSkeleton } from '@/client/components/workspace/chat-timeline/SessionSkeleton';
|
|
6
9
|
import { UndoConfirmModal } from '@/client/components/workspace/chat-timeline/UndoConfirmModal';
|
|
@@ -46,7 +49,8 @@ export function ChatTimeline({ className = '', appSettings = {}, onSessionTitle,
|
|
|
46
49
|
loadOlder,
|
|
47
50
|
generatingVerb,
|
|
48
51
|
messageQueue,
|
|
49
|
-
|
|
52
|
+
removeMessage,
|
|
53
|
+
reorderMessages,
|
|
50
54
|
steeringQueue,
|
|
51
55
|
setSteeringQueue,
|
|
52
56
|
inputValue,
|
|
@@ -71,12 +75,27 @@ export function ChatTimeline({ className = '', appSettings = {}, onSessionTitle,
|
|
|
71
75
|
accessMode,
|
|
72
76
|
handleAccessModeChange,
|
|
73
77
|
composerModelRef,
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
extensionDialogs,
|
|
79
|
+
resolveExtensionDialog,
|
|
76
80
|
respondToExtensionUi,
|
|
77
81
|
} = useChatTimeline({ folders, appSettings });
|
|
78
82
|
const { toasts, pushToast, dismissToast } = useToasts();
|
|
79
83
|
|
|
84
|
+
// Ask dialogs render on their own tool card; anything else omp is blocked on
|
|
85
|
+
// (an approval gate, an extension picker) has no card and keeps the modal.
|
|
86
|
+
const { framesByTool, modalRequest } = useMemo(
|
|
87
|
+
() => splitAskFrames(localMessages, extensionDialogs),
|
|
88
|
+
[localMessages, extensionDialogs],
|
|
89
|
+
);
|
|
90
|
+
const respondToFrame = useCallback((request: ExtensionUiDialogRequest, response: ExtensionDialogResponse) => {
|
|
91
|
+
void respondToExtensionUi(request, response);
|
|
92
|
+
resolveExtensionDialog(request.id);
|
|
93
|
+
}, [respondToExtensionUi, resolveExtensionDialog]);
|
|
94
|
+
const askFrames = useMemo<AskFramesHandle>(
|
|
95
|
+
() => ({ framesByTool, respond: respondToFrame }),
|
|
96
|
+
[framesByTool, respondToFrame],
|
|
97
|
+
);
|
|
98
|
+
|
|
80
99
|
const [newChatInitialContent, setNewChatInitialContent] = useState<string | null>(null);
|
|
81
100
|
const { pendingUndo, undoing, requestUndo: handleRequestUndo, closeUndoConfirm, confirmUndo } = useUndoConfirmation(handleUndo);
|
|
82
101
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
@@ -170,118 +189,115 @@ export function ChatTimeline({ className = '', appSettings = {}, onSessionTitle,
|
|
|
170
189
|
}
|
|
171
190
|
|
|
172
191
|
return (
|
|
173
|
-
<
|
|
174
|
-
{
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
192
|
+
<AskFramesContext.Provider value={askFrames}>
|
|
193
|
+
<div className={`flex flex-col h-full min-h-0 overflow-hidden bg-canvas relative ${className}`}>
|
|
194
|
+
{showFullSkeleton ? (
|
|
195
|
+
<SessionSkeleton />
|
|
196
|
+
) : (
|
|
197
|
+
<>
|
|
198
|
+
{/* Main chat container wrapper */}
|
|
199
|
+
<div className="relative flex-1 min-h-0 flex flex-col">
|
|
200
|
+
{activeSubagent ? (
|
|
201
|
+
<SubagentView
|
|
202
|
+
sessionId={sessionId}
|
|
203
|
+
subagent={activeSubagent}
|
|
204
|
+
onBack={handleSubagentBack}
|
|
205
|
+
provider={sessionProvider}
|
|
206
|
+
providerNames={providerNames}
|
|
207
|
+
/>
|
|
208
|
+
) : (
|
|
209
|
+
<TimelineBody
|
|
210
|
+
isMobile={isMobile}
|
|
211
|
+
userMessages={userMessages}
|
|
212
|
+
onScrollTo={handleScrollTo}
|
|
213
|
+
scrollRef={scrollRef}
|
|
214
|
+
contentRef={contentRef}
|
|
215
|
+
handleScroll={handleScroll}
|
|
216
|
+
isScrolling={isScrolling}
|
|
217
|
+
loadingOlder={loadingOlder}
|
|
218
|
+
hasMore={hasMore}
|
|
219
|
+
loadOlderError={loadOlderError}
|
|
220
|
+
loadOlder={loadOlder}
|
|
221
|
+
messages={localMessages}
|
|
222
|
+
isGenerating={isGenerating}
|
|
223
|
+
provider={sessionProvider}
|
|
224
|
+
providerNames={providerNames}
|
|
225
|
+
modelName={sessionModelName}
|
|
226
|
+
modelNames={modelNames}
|
|
227
|
+
thinkingLevel={sessionData?.thinkingLevel}
|
|
228
|
+
onUndo={handleRequestUndo}
|
|
229
|
+
onRetry={handleRetry}
|
|
230
|
+
onNewChat={handleNewChat}
|
|
231
|
+
showScrollBottom={showScrollBottom}
|
|
232
|
+
jumpToBottom={jumpToBottom}
|
|
233
|
+
/>
|
|
234
|
+
)}
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
{/* Input Area Footer with Docked Generating Indicator (Seamless & Transparent) */}
|
|
238
|
+
{!activeSubagent && (
|
|
239
|
+
<ComposerDock
|
|
190
240
|
isMobile={isMobile}
|
|
191
|
-
userMessages={userMessages}
|
|
192
|
-
onScrollTo={handleScrollTo}
|
|
193
|
-
scrollRef={scrollRef}
|
|
194
|
-
contentRef={contentRef}
|
|
195
|
-
handleScroll={handleScroll}
|
|
196
|
-
isScrolling={isScrolling}
|
|
197
|
-
loadingOlder={loadingOlder}
|
|
198
|
-
hasMore={hasMore}
|
|
199
|
-
loadOlderError={loadOlderError}
|
|
200
|
-
loadOlder={loadOlder}
|
|
201
|
-
messages={localMessages}
|
|
202
241
|
isGenerating={isGenerating}
|
|
242
|
+
modelName={sessionModelName}
|
|
243
|
+
generatingVerb={generatingVerb}
|
|
203
244
|
provider={sessionProvider}
|
|
204
245
|
providerNames={providerNames}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
246
|
+
messageQueue={messageQueue}
|
|
247
|
+
onRemoveQueueItem={removeMessage}
|
|
248
|
+
onReorderQueue={reorderMessages}
|
|
249
|
+
onEditQueueItem={handleEditQueueItem}
|
|
250
|
+
onSendNowQueueItem={handleSendNowQueueItem}
|
|
251
|
+
steeringQueue={steeringQueue}
|
|
252
|
+
setSteeringQueue={setSteeringQueue}
|
|
253
|
+
inputValue={inputValue}
|
|
254
|
+
setInputValue={setInputValue}
|
|
255
|
+
rootPath={composerRoot}
|
|
256
|
+
attachments={inputAttachments}
|
|
257
|
+
setAttachments={setInputAttachments}
|
|
258
|
+
onSend={handleSend}
|
|
259
|
+
onStop={handleStop}
|
|
260
|
+
appSettings={appSettings}
|
|
261
|
+
onThinkingLevelChange={handleThinkingLevelChange}
|
|
262
|
+
onModelChange={handleModelChange}
|
|
263
|
+
accessMode={accessMode}
|
|
264
|
+
onAccessModeChange={handleAccessModeChange}
|
|
265
|
+
composerModelRef={composerModelRef}
|
|
266
|
+
sessionModel={typeof sessionData?.model === 'object' ? sessionData.model : null}
|
|
267
|
+
sessionThinkingLevel={sessionData?.thinkingLevel}
|
|
268
|
+
variant={variant}
|
|
213
269
|
/>
|
|
214
270
|
)}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
{/* Input Area Footer with Docked Generating Indicator (Seamless & Transparent) */}
|
|
218
|
-
{!activeSubagent && (
|
|
219
|
-
<ComposerDock
|
|
220
|
-
isMobile={isMobile}
|
|
221
|
-
isGenerating={isGenerating}
|
|
222
|
-
modelName={sessionModelName}
|
|
223
|
-
generatingVerb={generatingVerb}
|
|
224
|
-
provider={sessionProvider}
|
|
225
|
-
providerNames={providerNames}
|
|
226
|
-
messageQueue={messageQueue}
|
|
227
|
-
setMessageQueue={setMessageQueue}
|
|
228
|
-
onEditQueueItem={handleEditQueueItem}
|
|
229
|
-
onSendNowQueueItem={handleSendNowQueueItem}
|
|
230
|
-
steeringQueue={steeringQueue}
|
|
231
|
-
setSteeringQueue={setSteeringQueue}
|
|
232
|
-
inputValue={inputValue}
|
|
233
|
-
setInputValue={setInputValue}
|
|
234
|
-
rootPath={composerRoot}
|
|
235
|
-
attachments={inputAttachments}
|
|
236
|
-
setAttachments={setInputAttachments}
|
|
237
|
-
onSend={handleSend}
|
|
238
|
-
onStop={handleStop}
|
|
239
|
-
appSettings={appSettings}
|
|
240
|
-
onThinkingLevelChange={handleThinkingLevelChange}
|
|
241
|
-
onModelChange={handleModelChange}
|
|
242
|
-
accessMode={accessMode}
|
|
243
|
-
onAccessModeChange={handleAccessModeChange}
|
|
244
|
-
composerModelRef={composerModelRef}
|
|
245
|
-
sessionModel={typeof sessionData?.model === 'object' ? sessionData.model : null}
|
|
246
|
-
sessionThinkingLevel={sessionData?.thinkingLevel}
|
|
247
|
-
variant={variant}
|
|
248
|
-
/>
|
|
249
|
-
)}
|
|
250
|
-
</>
|
|
251
|
-
)}
|
|
271
|
+
</>
|
|
272
|
+
)}
|
|
252
273
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
274
|
+
{pendingUndo && (
|
|
275
|
+
<UndoConfirmModal
|
|
276
|
+
isOmpSession={Boolean(sessionId) && !sessionId.startsWith('new-') && Number.isNaN(Number(sessionId))}
|
|
277
|
+
content={pendingUndo.content}
|
|
278
|
+
undoing={undoing}
|
|
279
|
+
onClose={closeUndoConfirm}
|
|
280
|
+
onConfirm={confirmUndo}
|
|
281
|
+
/>
|
|
282
|
+
)}
|
|
283
|
+
{newChatInitialContent !== null && (
|
|
284
|
+
<NewChatModal
|
|
285
|
+
initialContent={newChatInitialContent}
|
|
286
|
+
onClose={() => setNewChatInitialContent(null)}
|
|
287
|
+
onSend={submitNewChat}
|
|
288
|
+
appSettings={appSettings}
|
|
289
|
+
accessMode={accessMode}
|
|
290
|
+
onAccessModeChange={handleAccessModeChange}
|
|
291
|
+
composerModelRef={composerModelRef}
|
|
292
|
+
/>
|
|
293
|
+
)}
|
|
273
294
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
onRespond={(request, response) => {
|
|
278
|
-
void respondToExtensionUi(request, response);
|
|
279
|
-
closeExtensionDialog();
|
|
280
|
-
}}
|
|
281
|
-
/>
|
|
282
|
-
)}
|
|
295
|
+
{modalRequest && (
|
|
296
|
+
<ExtensionDialog request={modalRequest} onRespond={respondToFrame} />
|
|
297
|
+
)}
|
|
283
298
|
|
|
284
|
-
|
|
285
|
-
|
|
299
|
+
<ToastStack toasts={toasts} onDismiss={dismissToast} />
|
|
300
|
+
</div>
|
|
301
|
+
</AskFramesContext.Provider>
|
|
286
302
|
);
|
|
287
303
|
}
|