@burdenoff/microfe-vibecontrols 2026.514.2 → 2026.514.4
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/components/agent-manager/AgentManagerAdminBar.js +16 -16
- package/dist/components/agent-manager/AgentManagerCenter.js +100 -62
- package/dist/components/agent-manager/AgentManagerCenter.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerChatPane.js +95 -58
- package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerSessionList.js +12 -6
- package/dist/components/agent-manager/AgentManagerSessionList.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTabBar.js +118 -100
- package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
- package/dist/components/assistant/AssistantChatArea.js +1 -1
- package/dist/components/assistant/AssistantChatArea.js.map +1 -1
- package/dist/hooks/useAgentManagerStreaming.js +6 -2
- package/dist/hooks/useAgentManagerStreaming.js.map +1 -1
- package/dist/hooks/useAssistantKeyboardShortcuts.js +3 -3
- package/dist/hooks/useAssistantKeyboardShortcuts.js.map +1 -1
- package/dist/store/agentManagerStore.js +4 -0
- package/dist/store/agentManagerStore.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
2
|
import { useVibeNavigate as t } from "../../providers/VibeControlsProvider.js";
|
|
3
3
|
import { useAgentManagerStore as n } from "../../store/agentManagerStore.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
4
|
+
import { AIProviderCredentialsCard as r } from "../ai/AIProviderCredentialsCard.js";
|
|
5
|
+
import { AITemplatesTab as i } from "../ai/AITemplatesTab.js";
|
|
6
|
+
import { AIContextsTab as a } from "../ai/AIContextsTab.js";
|
|
7
|
+
import { AIPromptsTab as o } from "../ai/AIPromptsTab.js";
|
|
8
|
+
import { AITasksTab as s } from "../ai/AITasksTab.js";
|
|
9
|
+
import { AIStatsTab as c } from "../ai/AIStatsTab.js";
|
|
10
|
+
import { AIPlaygroundTab as l } from "../ai/AIPlaygroundTab.js";
|
|
11
|
+
import { BridleConfigPanel as u } from "./BridleConfigPanel.js";
|
|
12
12
|
import { useState as d } from "react";
|
|
13
13
|
import { BarChart3 as f, BookOpen as p, ExternalLink as m, FileText as h, ListChecks as g, MessageSquare as _, Play as v, Settings as y, X as b } from "lucide-react";
|
|
14
14
|
import { AnimatePresence as x, motion as S, useReducedMotion as C } from "framer-motion";
|
|
@@ -63,15 +63,15 @@ function O({ agent: f }) {
|
|
|
63
63
|
}
|
|
64
64
|
function A() {
|
|
65
65
|
switch (p) {
|
|
66
|
-
case "templates": return /* @__PURE__ */ T(
|
|
67
|
-
case "contexts": return /* @__PURE__ */ T(
|
|
68
|
-
case "prompts": return /* @__PURE__ */ T(
|
|
69
|
-
case "tasks": return /* @__PURE__ */ T(
|
|
70
|
-
case "stats": return /* @__PURE__ */ T(
|
|
71
|
-
case "playground": return /* @__PURE__ */ T(
|
|
66
|
+
case "templates": return /* @__PURE__ */ T(i, { agentId: f.id });
|
|
67
|
+
case "contexts": return /* @__PURE__ */ T(a, { agent: f });
|
|
68
|
+
case "prompts": return /* @__PURE__ */ T(o, { agent: f });
|
|
69
|
+
case "tasks": return /* @__PURE__ */ T(s, {});
|
|
70
|
+
case "stats": return /* @__PURE__ */ T(c, { agent: f });
|
|
71
|
+
case "playground": return /* @__PURE__ */ T(l, { agent: f });
|
|
72
72
|
case "aiKeys":
|
|
73
|
-
case "config": return /* @__PURE__ */ T(
|
|
74
|
-
case "bridle": return /* @__PURE__ */ T(
|
|
73
|
+
case "config": return /* @__PURE__ */ T(r, { agent: f });
|
|
74
|
+
case "bridle": return /* @__PURE__ */ T(u, { agent: f });
|
|
75
75
|
default: return null;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
2
|
import { EmptyState as t } from "../shared/EmptyState.js";
|
|
3
3
|
import "../shared/index.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
4
|
+
import { createSession as n } from "../../services/aiApi.js";
|
|
5
|
+
import { useAgentManagerStore as r } from "../../store/agentManagerStore.js";
|
|
6
|
+
import { AgentManagerTabBar as i } from "./AgentManagerTabBar.js";
|
|
7
|
+
import { AgentManagerChatPane as a } from "./AgentManagerChatPane.js";
|
|
8
|
+
import { AgentManagerAdminBar as o } from "./AgentManagerAdminBar.js";
|
|
9
|
+
import { SessionSearchDialog as s } from "./dialogs/SessionSearchDialog.js";
|
|
10
|
+
import { useCallback as c, useEffect as l, useRef as u, useState as d } from "react";
|
|
11
|
+
import { MessageSquarePlus as f, X as p } from "lucide-react";
|
|
12
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
12
13
|
//#region src/components/agent-manager/AgentManagerCenter.tsx
|
|
13
|
-
var
|
|
14
|
+
var g = [
|
|
14
15
|
{
|
|
15
|
-
keys: "Ctrl+Shift+N",
|
|
16
|
+
keys: "Ctrl/Cmd+Shift+N",
|
|
16
17
|
description: "New session"
|
|
17
18
|
},
|
|
18
19
|
{
|
|
19
|
-
keys: "Ctrl+K",
|
|
20
|
+
keys: "Ctrl/Cmd+Shift+K",
|
|
20
21
|
description: "Search sessions"
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
|
-
keys: "Ctrl+F",
|
|
24
|
+
keys: "Ctrl/Cmd+Shift+F",
|
|
24
25
|
description: "In-session search"
|
|
25
26
|
},
|
|
26
27
|
{
|
|
27
28
|
keys: "Escape",
|
|
28
|
-
description: "Cancel"
|
|
29
|
+
description: "Cancel / close modal"
|
|
29
30
|
},
|
|
30
31
|
{
|
|
31
32
|
keys: "Enter",
|
|
@@ -35,92 +36,129 @@ var m = [
|
|
|
35
36
|
keys: "Shift+Enter",
|
|
36
37
|
description: "New line"
|
|
37
38
|
}
|
|
38
|
-
],
|
|
39
|
-
let
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
], _ = ({ agent: _ }) => {
|
|
40
|
+
let v = r((e) => e.tabs), y = r((e) => e.activeTabSessionId), b = r((e) => e.openSession), x = r((e) => e.setActiveTab), S = e(), [C, w] = d(!1), [T, E] = d(!1), D = u(null), O = r((e) => e.bumpSessionsRefresh), k = c(async () => {
|
|
41
|
+
try {
|
|
42
|
+
let e = await n(_, {
|
|
43
|
+
name: `Session ${v.length + 1}`,
|
|
44
|
+
agentType: "claude",
|
|
45
|
+
config: {
|
|
46
|
+
model: "default",
|
|
47
|
+
mode: "cli",
|
|
48
|
+
tags: []
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
b(e.id, e.name), O();
|
|
52
|
+
} catch (e) {
|
|
53
|
+
console.error("[AgentManagerCenter] createSession failed:", e);
|
|
54
|
+
}
|
|
44
55
|
}, [
|
|
45
56
|
_,
|
|
57
|
+
v.length,
|
|
58
|
+
b,
|
|
59
|
+
O
|
|
60
|
+
]), A = c(() => w(!0), []);
|
|
61
|
+
return l(() => {
|
|
62
|
+
y && v.some((e) => e.sessionId === y) && x(y);
|
|
63
|
+
}, [
|
|
46
64
|
y,
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
x,
|
|
66
|
+
v
|
|
67
|
+
]), l(() => {
|
|
68
|
+
let e = D.current;
|
|
69
|
+
if (!e) return;
|
|
70
|
+
let t = (t) => {
|
|
71
|
+
if (!e.contains(document.activeElement) && document.activeElement !== e) return;
|
|
72
|
+
let n = t.ctrlKey || t.metaKey;
|
|
73
|
+
if (n && t.shiftKey && (t.key === "N" || t.key === "n")) {
|
|
74
|
+
t.preventDefault(), k();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (n && t.shiftKey && (t.key === "K" || t.key === "k")) {
|
|
78
|
+
t.preventDefault(), A();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
n && t.shiftKey && (t.key === "F" || t.key === "f") && (t.preventDefault(), e.querySelector("[data-testid=\"agent-manager-in-session-search\"]")?.click());
|
|
82
|
+
};
|
|
83
|
+
return document.addEventListener("keydown", t), () => document.removeEventListener("keydown", t);
|
|
84
|
+
}, [k, A]), /* @__PURE__ */ h("div", {
|
|
85
|
+
ref: D,
|
|
49
86
|
className: "flex flex-col h-full min-h-0",
|
|
87
|
+
tabIndex: -1,
|
|
50
88
|
children: [
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
-
agent:
|
|
53
|
-
onSearch:
|
|
54
|
-
onShowShortcuts: () =>
|
|
89
|
+
/* @__PURE__ */ m(i, {
|
|
90
|
+
agent: _,
|
|
91
|
+
onSearch: A,
|
|
92
|
+
onShowShortcuts: () => E(!0)
|
|
55
93
|
}),
|
|
56
|
-
/* @__PURE__ */
|
|
94
|
+
/* @__PURE__ */ m("div", {
|
|
57
95
|
className: "flex-1 min-h-0 overflow-hidden",
|
|
58
|
-
children:
|
|
59
|
-
sessionId:
|
|
60
|
-
agentId:
|
|
61
|
-
},
|
|
96
|
+
children: y && v.some((e) => e.sessionId === y) ? /* @__PURE__ */ m(a, {
|
|
97
|
+
sessionId: y,
|
|
98
|
+
agentId: _.id
|
|
99
|
+
}, y) : /* @__PURE__ */ m("div", {
|
|
62
100
|
className: "flex-1 flex items-center justify-center h-full",
|
|
63
|
-
children: /* @__PURE__ */
|
|
64
|
-
icon: /* @__PURE__ */ f
|
|
65
|
-
title:
|
|
66
|
-
description:
|
|
67
|
-
action: /* @__PURE__ */
|
|
68
|
-
onClick:
|
|
101
|
+
children: /* @__PURE__ */ m(t, {
|
|
102
|
+
icon: /* @__PURE__ */ m(f, { className: "w-8 h-8 text-text-secondary" }),
|
|
103
|
+
title: S("vibecontrols.agentManager.center.noActiveSession", "No active session"),
|
|
104
|
+
description: S("vibecontrols.agentManager.center.createSessionDesc", "Create a new session to start interacting with the AI agent."),
|
|
105
|
+
action: /* @__PURE__ */ m("button", {
|
|
106
|
+
onClick: k,
|
|
69
107
|
className: "px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
70
|
-
children:
|
|
108
|
+
children: S("vibecontrols.agentManager.center.createNewSession", "Create a new session")
|
|
71
109
|
})
|
|
72
110
|
})
|
|
73
111
|
})
|
|
74
112
|
}),
|
|
75
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ m("div", {
|
|
76
114
|
className: "flex-shrink-0",
|
|
77
|
-
children: /* @__PURE__ */
|
|
115
|
+
children: /* @__PURE__ */ m(o, { agent: _ })
|
|
78
116
|
}),
|
|
79
|
-
/* @__PURE__ */
|
|
80
|
-
isOpen:
|
|
81
|
-
agent:
|
|
82
|
-
onClose: () =>
|
|
117
|
+
/* @__PURE__ */ m(s, {
|
|
118
|
+
isOpen: C,
|
|
119
|
+
agent: _,
|
|
120
|
+
onClose: () => w(!1)
|
|
83
121
|
}),
|
|
84
|
-
|
|
122
|
+
T && /* @__PURE__ */ h("div", {
|
|
85
123
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
86
|
-
children: [/* @__PURE__ */
|
|
124
|
+
children: [/* @__PURE__ */ m("div", {
|
|
87
125
|
className: "absolute inset-0 bg-bg-overlay",
|
|
88
|
-
onClick: () =>
|
|
89
|
-
}), /* @__PURE__ */
|
|
126
|
+
onClick: () => E(!1)
|
|
127
|
+
}), /* @__PURE__ */ h("div", {
|
|
90
128
|
className: "relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl",
|
|
91
129
|
children: [
|
|
92
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ h("div", {
|
|
93
131
|
className: "flex items-center justify-between px-6 py-4 border-b border-border-default",
|
|
94
|
-
children: [/* @__PURE__ */
|
|
132
|
+
children: [/* @__PURE__ */ m("h3", {
|
|
95
133
|
className: "text-lg font-semibold text-text-primary",
|
|
96
|
-
children:
|
|
97
|
-
}), /* @__PURE__ */
|
|
134
|
+
children: S("vibecontrols.agentManager.center.keyboardShortcuts", "Keyboard Shortcuts")
|
|
135
|
+
}), /* @__PURE__ */ m("button", {
|
|
98
136
|
type: "button",
|
|
99
|
-
onClick: () =>
|
|
137
|
+
onClick: () => E(!1),
|
|
100
138
|
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
101
|
-
children: /* @__PURE__ */
|
|
139
|
+
children: /* @__PURE__ */ m(p, { className: "w-5 h-5 text-text-secondary" })
|
|
102
140
|
})]
|
|
103
141
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ m("div", {
|
|
105
143
|
className: "px-6 py-4 space-y-2",
|
|
106
|
-
children:
|
|
144
|
+
children: g.map((e) => /* @__PURE__ */ h("div", {
|
|
107
145
|
className: "flex items-center justify-between py-1",
|
|
108
|
-
children: [/* @__PURE__ */
|
|
146
|
+
children: [/* @__PURE__ */ m("span", {
|
|
109
147
|
className: "text-sm text-text-secondary",
|
|
110
148
|
children: e.description
|
|
111
|
-
}), /* @__PURE__ */
|
|
149
|
+
}), /* @__PURE__ */ m("kbd", {
|
|
112
150
|
className: "px-2 py-0.5 text-xs font-mono rounded bg-bg-elevated border border-border-default text-text-primary",
|
|
113
151
|
children: e.keys
|
|
114
152
|
})]
|
|
115
153
|
}, e.keys))
|
|
116
154
|
}),
|
|
117
|
-
/* @__PURE__ */
|
|
155
|
+
/* @__PURE__ */ m("div", {
|
|
118
156
|
className: "px-6 py-3 border-t border-border-default bg-bg-sunken rounded-b-lg",
|
|
119
|
-
children: /* @__PURE__ */
|
|
157
|
+
children: /* @__PURE__ */ m("button", {
|
|
120
158
|
type: "button",
|
|
121
|
-
onClick: () =>
|
|
159
|
+
onClick: () => E(!1),
|
|
122
160
|
className: "w-full px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors",
|
|
123
|
-
children:
|
|
161
|
+
children: S("vibecontrols.agentManager.center.close", "Close")
|
|
124
162
|
})
|
|
125
163
|
})
|
|
126
164
|
]
|
|
@@ -130,6 +168,6 @@ var m = [
|
|
|
130
168
|
});
|
|
131
169
|
};
|
|
132
170
|
//#endregion
|
|
133
|
-
export {
|
|
171
|
+
export { _ as AgentManagerCenter };
|
|
134
172
|
|
|
135
173
|
//# sourceMappingURL=AgentManagerCenter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentManagerCenter.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerCenter.tsx"],"sourcesContent":["/**\n * Center container for the Agent Manager.\n *\n * Renders the tab bar at top and the active session's chat pane below.\n * Shows a welcome/empty state when no tabs are open.\n */\n\nimport { useState, useCallback, useEffect, type FC } from 'react';\nimport { MessageSquarePlus, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { EmptyState } from '@/components/shared';\nimport { AgentManagerTabBar } from './AgentManagerTabBar';\nimport { AgentManagerChatPane } from './AgentManagerChatPane';\nimport { AgentManagerAdminBar } from './AgentManagerAdminBar';\nimport { SessionSearchDialog } from './dialogs/SessionSearchDialog';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type { AgentRef } from '@/services/aiApi';\n\n/* ────────────────────────────────────────────────────────────────────── */\n/* Keyboard shortcuts reference */\n/* ────────────────────────────────────────────────────────────────────── */\n\nconst SHORTCUTS: Array<{ keys: string; description: string }> = [\n { keys: 'Ctrl+Shift+N', description: 'New session' },\n { keys: 'Ctrl+K', description: 'Search sessions' },\n { keys: 'Ctrl+F', description: 'In-session search' },\n { keys: 'Escape', description: 'Cancel' },\n { keys: 'Enter', description: 'Send message' },\n { keys: 'Shift+Enter', description: 'New line' },\n];\n\ninterface AgentManagerCenterProps {\n agent: AgentRef;\n}\n\nexport const AgentManagerCenter: FC<AgentManagerCenterProps> = ({ agent }) => {\n const tabs = useAgentManagerStore((s) => s.tabs);\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const openSession = useAgentManagerStore((s) => s.openSession);\n const setActiveTab = useAgentManagerStore((s) => s.setActiveTab);\n const tr = useTr();\n\n const [searchOpen, setSearchOpen] = useState(false);\n const [shortcutsOpen, setShortcutsOpen] = useState(false);\n\n const handleCreateSession = () => {\n const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;\n openSession(sessionId);\n };\n\n const handleOpenSearch = useCallback(() => setSearchOpen(true), []);\n\n useEffect(() => {\n if (activeTabSessionId && tabs.some((tab) => tab.sessionId === activeTabSessionId)) {\n setActiveTab(activeTabSessionId);\n }\n }, [activeTabSessionId, setActiveTab, tabs]);\n\n return (\n <div className=\"flex flex-col h-full min-h-0\">\n {/* Tab bar — occupies full row width, search/help live inside the tabBar area */}\n <AgentManagerTabBar\n agent={agent}\n onSearch={handleOpenSearch}\n onShowShortcuts={() => setShortcutsOpen(true)}\n />\n\n {/* Active session or empty state */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n {activeTabSessionId && tabs.some((t) => t.sessionId === activeTabSessionId) ? (\n <AgentManagerChatPane\n key={activeTabSessionId}\n sessionId={activeTabSessionId}\n agentId={agent.id}\n />\n ) : (\n <div className=\"flex-1 flex items-center justify-center h-full\">\n <EmptyState\n icon={<MessageSquarePlus className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('vibecontrols.agentManager.center.noActiveSession', 'No active session')}\n description={tr(\n 'vibecontrols.agentManager.center.createSessionDesc',\n 'Create a new session to start interacting with the AI agent.'\n )}\n action={\n <button\n onClick={handleCreateSession}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.createNewSession', 'Create a new session')}\n </button>\n }\n />\n </div>\n )}\n </div>\n\n {/* Admin bar at bottom */}\n <div className=\"flex-shrink-0\">\n <AgentManagerAdminBar agent={agent} />\n </div>\n\n {/* Session search dialog */}\n <SessionSearchDialog isOpen={searchOpen} agent={agent} onClose={() => setSearchOpen(false)} />\n\n {/* Keyboard shortcuts modal */}\n {shortcutsOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-bg-overlay\" onClick={() => setShortcutsOpen(false)} />\n <div className=\"relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.center.keyboardShortcuts', 'Keyboard Shortcuts')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"px-6 py-4 space-y-2\">\n {SHORTCUTS.map((s) => (\n <div key={s.keys} className=\"flex items-center justify-between py-1\">\n <span className=\"text-sm text-text-secondary\">{s.description}</span>\n <kbd className=\"px-2 py-0.5 text-xs font-mono rounded bg-bg-elevated border border-border-default text-text-primary\">\n {s.keys}\n </kbd>\n </div>\n ))}\n </div>\n <div className=\"px-6 py-3 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"w-full px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentManagerCenter.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerCenter.tsx"],"sourcesContent":["/**\n * Center container for the Agent Manager.\n *\n * Renders the tab bar at top and the active session's chat pane below.\n * Shows a welcome/empty state when no tabs are open.\n */\n\nimport { useState, useCallback, useEffect, useRef, type FC } from 'react';\nimport { MessageSquarePlus, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { EmptyState } from '@/components/shared';\nimport { AgentManagerTabBar } from './AgentManagerTabBar';\nimport { AgentManagerChatPane } from './AgentManagerChatPane';\nimport { AgentManagerAdminBar } from './AgentManagerAdminBar';\nimport { SessionSearchDialog } from './dialogs/SessionSearchDialog';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { createSession, type AgentRef } from '@/services/aiApi';\n\n/* ────────────────────────────────────────────────────────────────────── */\n/* Keyboard shortcuts reference */\n/* ────────────────────────────────────────────────────────────────────── */\n\nconst SHORTCUTS: Array<{ keys: string; description: string }> = [\n { keys: 'Ctrl/Cmd+Shift+N', description: 'New session' },\n { keys: 'Ctrl/Cmd+Shift+K', description: 'Search sessions' },\n { keys: 'Ctrl/Cmd+Shift+F', description: 'In-session search' },\n { keys: 'Escape', description: 'Cancel / close modal' },\n { keys: 'Enter', description: 'Send message' },\n { keys: 'Shift+Enter', description: 'New line' },\n];\n\ninterface AgentManagerCenterProps {\n agent: AgentRef;\n}\n\nexport const AgentManagerCenter: FC<AgentManagerCenterProps> = ({ agent }) => {\n const tabs = useAgentManagerStore((s) => s.tabs);\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const openSession = useAgentManagerStore((s) => s.openSession);\n const setActiveTab = useAgentManagerStore((s) => s.setActiveTab);\n const tr = useTr();\n\n const [searchOpen, setSearchOpen] = useState(false);\n const [shortcutsOpen, setShortcutsOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n const bumpSessionsRefresh = useAgentManagerStore((s) => s.bumpSessionsRefresh);\n const handleCreateSession = useCallback(async () => {\n try {\n const session = await createSession(agent, {\n name: `Session ${tabs.length + 1}`,\n agentType: 'claude',\n config: { model: 'default', mode: 'cli', tags: [] },\n });\n openSession(session.id, session.name);\n bumpSessionsRefresh();\n } catch (err) {\n console.error('[AgentManagerCenter] createSession failed:', err);\n }\n }, [agent, tabs.length, openSession, bumpSessionsRefresh]);\n\n const handleOpenSearch = useCallback(() => setSearchOpen(true), []);\n\n useEffect(() => {\n if (activeTabSessionId && tabs.some((tab) => tab.sessionId === activeTabSessionId)) {\n setActiveTab(activeTabSessionId);\n }\n }, [activeTabSessionId, setActiveTab, tabs]);\n\n // Wire the shortcuts advertised in the SHORTCUTS modal. Scoped to the\n // Agent Manager container so they don't clash with shortcuts on other\n // pages. Each shortcut uses Shift to avoid colliding with built-in\n // browser shortcuts (Ctrl+N opens a window, Ctrl+F opens find).\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const handler = (e: KeyboardEvent) => {\n if (!root.contains(document.activeElement) && document.activeElement !== root) return;\n const mod = e.ctrlKey || e.metaKey;\n if (mod && e.shiftKey && (e.key === 'N' || e.key === 'n')) {\n e.preventDefault();\n void handleCreateSession();\n return;\n }\n if (mod && e.shiftKey && (e.key === 'K' || e.key === 'k')) {\n e.preventDefault();\n handleOpenSearch();\n return;\n }\n if (mod && e.shiftKey && (e.key === 'F' || e.key === 'f')) {\n e.preventDefault();\n const inSession = root.querySelector(\n '[data-testid=\"agent-manager-in-session-search\"]'\n ) as HTMLButtonElement | null;\n inSession?.click();\n }\n };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [handleCreateSession, handleOpenSearch]);\n\n return (\n <div ref={rootRef} className=\"flex flex-col h-full min-h-0\" tabIndex={-1}>\n {/* Tab bar — occupies full row width, search/help live inside the tabBar area */}\n <AgentManagerTabBar\n agent={agent}\n onSearch={handleOpenSearch}\n onShowShortcuts={() => setShortcutsOpen(true)}\n />\n\n {/* Active session or empty state */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n {activeTabSessionId && tabs.some((t) => t.sessionId === activeTabSessionId) ? (\n <AgentManagerChatPane\n key={activeTabSessionId}\n sessionId={activeTabSessionId}\n agentId={agent.id}\n />\n ) : (\n <div className=\"flex-1 flex items-center justify-center h-full\">\n <EmptyState\n icon={<MessageSquarePlus className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('vibecontrols.agentManager.center.noActiveSession', 'No active session')}\n description={tr(\n 'vibecontrols.agentManager.center.createSessionDesc',\n 'Create a new session to start interacting with the AI agent.'\n )}\n action={\n <button\n onClick={handleCreateSession}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.createNewSession', 'Create a new session')}\n </button>\n }\n />\n </div>\n )}\n </div>\n\n {/* Admin bar at bottom */}\n <div className=\"flex-shrink-0\">\n <AgentManagerAdminBar agent={agent} />\n </div>\n\n {/* Session search dialog */}\n <SessionSearchDialog isOpen={searchOpen} agent={agent} onClose={() => setSearchOpen(false)} />\n\n {/* Keyboard shortcuts modal */}\n {shortcutsOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-bg-overlay\" onClick={() => setShortcutsOpen(false)} />\n <div className=\"relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.center.keyboardShortcuts', 'Keyboard Shortcuts')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"px-6 py-4 space-y-2\">\n {SHORTCUTS.map((s) => (\n <div key={s.keys} className=\"flex items-center justify-between py-1\">\n <span className=\"text-sm text-text-secondary\">{s.description}</span>\n <kbd className=\"px-2 py-0.5 text-xs font-mono rounded bg-bg-elevated border border-border-default text-text-primary\">\n {s.keys}\n </kbd>\n </div>\n ))}\n </div>\n <div className=\"px-6 py-3 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={() => setShortcutsOpen(false)}\n className=\"w-full px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('vibecontrols.agentManager.center.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -7,27 +7,28 @@ import { useAgentManagerStreaming as a } from "../../hooks/useAgentManagerStream
|
|
|
7
7
|
import { AgentManagerMessageList as o } from "./AgentManagerMessageList.js";
|
|
8
8
|
import { AgentManagerInputArea as s } from "./AgentManagerInputArea.js";
|
|
9
9
|
import { PluginInstallModal as c } from "./PluginInstallModal.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { AIProviderCredentialsCard as l } from "../ai/AIProviderCredentialsCard.js";
|
|
11
|
+
import { useCallback as u, useEffect as d, useMemo as f, useState as p } from "react";
|
|
12
|
+
import { PackagePlus as m, Search as h, X as g } from "lucide-react";
|
|
13
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
13
14
|
//#region src/components/agent-manager/AgentManagerChatPane.tsx
|
|
14
|
-
var
|
|
15
|
-
let [
|
|
15
|
+
var y = ({ sessionId: l, agentId: y }) => {
|
|
16
|
+
let [x, S] = p(!1), [C, w] = p(!1), [T, E] = p(""), [D, O] = p(!1), k = e(), { agents: A } = t(), j = f(() => A.find((e) => e.id === y) ?? null, [A, y]), M = i((e) => e.sessionStates[l]), N = i((e) => e.addMessage), P = M?.messages ?? [], { active: F } = n(j?.id ?? null), I = f(() => ({
|
|
16
17
|
id: y,
|
|
17
|
-
profile:
|
|
18
|
-
}), [y,
|
|
19
|
-
agent:
|
|
20
|
-
sessionId:
|
|
21
|
-
}),
|
|
22
|
-
|
|
18
|
+
profile: F
|
|
19
|
+
}), [y, F]), { sendMessage: L, cancelStream: R, isStreaming: z } = a({
|
|
20
|
+
agent: I,
|
|
21
|
+
sessionId: l
|
|
22
|
+
}), B = u(() => {
|
|
23
|
+
w((e) => (e && E(""), !e));
|
|
23
24
|
}, []);
|
|
24
|
-
return
|
|
25
|
+
return d(() => {
|
|
25
26
|
let e = !1;
|
|
26
27
|
return (async () => {
|
|
27
|
-
if (!(
|
|
28
|
-
|
|
28
|
+
if (!(P.length > 0)) {
|
|
29
|
+
S(!0);
|
|
29
30
|
try {
|
|
30
|
-
let t = await r(
|
|
31
|
+
let t = await r(I, l, { types: ["input", "output"] });
|
|
31
32
|
if (e || t.length === 0) return;
|
|
32
33
|
let n = /* @__PURE__ */ new Set();
|
|
33
34
|
for (let e of t) {
|
|
@@ -37,7 +38,7 @@ var v = ({ sessionId: v, agentId: y }) => {
|
|
|
37
38
|
e.model ?? "",
|
|
38
39
|
i
|
|
39
40
|
].join("\0");
|
|
40
|
-
n.has(a) || (n.add(a),
|
|
41
|
+
n.has(a) || (n.add(a), N(l, {
|
|
41
42
|
id: e.id,
|
|
42
43
|
role: e.type === "input" ? "user" : "assistant",
|
|
43
44
|
content: t,
|
|
@@ -50,85 +51,121 @@ var v = ({ sessionId: v, agentId: y }) => {
|
|
|
50
51
|
}));
|
|
51
52
|
}
|
|
52
53
|
} catch {} finally {
|
|
53
|
-
e ||
|
|
54
|
+
e || S(!1);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
})(), () => {
|
|
57
58
|
e = !0;
|
|
58
59
|
};
|
|
59
|
-
}, [y,
|
|
60
|
+
}, [y, l]), /* @__PURE__ */ v("div", {
|
|
60
61
|
className: "flex flex-col h-full min-h-0",
|
|
61
62
|
"data-testid": "agent-manager-chat-pane",
|
|
62
63
|
children: [
|
|
63
|
-
/* @__PURE__ */
|
|
64
|
+
/* @__PURE__ */ v("div", {
|
|
64
65
|
className: "flex items-center gap-1 px-3 py-1 border-b border-border-default bg-bg-surface flex-shrink-0",
|
|
65
|
-
children: [/* @__PURE__ */
|
|
66
|
+
children: [/* @__PURE__ */ v("div", {
|
|
66
67
|
className: "flex items-center gap-1.5 min-w-0 flex-1",
|
|
67
|
-
children: [/* @__PURE__ */
|
|
68
|
+
children: [/* @__PURE__ */ _("button", {
|
|
68
69
|
type: "button",
|
|
69
|
-
onClick:
|
|
70
|
-
className: `p-1 rounded transition-colors shrink-0 ${
|
|
71
|
-
title:
|
|
72
|
-
"aria-label":
|
|
73
|
-
children: /* @__PURE__ */
|
|
74
|
-
}),
|
|
70
|
+
onClick: B,
|
|
71
|
+
className: `p-1 rounded transition-colors shrink-0 ${C ? "bg-action-primary-bg/10 text-action-primary-text" : "text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated"}`,
|
|
72
|
+
title: k("vibecontrols.agentManager.chatPane.searchInSession", "Search in session (Ctrl+F)"),
|
|
73
|
+
"aria-label": k("vibecontrols.agentManager.chatPane.searchInSession", "Search in session"),
|
|
74
|
+
children: /* @__PURE__ */ _(h, { className: "w-3.5 h-3.5" })
|
|
75
|
+
}), C && /* @__PURE__ */ v("div", {
|
|
75
76
|
className: "flex items-center gap-1.5 flex-1 min-w-0",
|
|
76
|
-
children: [/* @__PURE__ */
|
|
77
|
+
children: [/* @__PURE__ */ _("input", {
|
|
77
78
|
type: "text",
|
|
78
|
-
value:
|
|
79
|
-
onChange: (e) =>
|
|
80
|
-
placeholder:
|
|
79
|
+
value: T,
|
|
80
|
+
onChange: (e) => E(e.target.value),
|
|
81
|
+
placeholder: k("vibecontrols.agentManager.chatPane.searchPlaceholder", "Search in conversation..."),
|
|
81
82
|
autoFocus: !0,
|
|
82
83
|
className: "flex-1 min-w-0 bg-transparent text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none"
|
|
83
|
-
}),
|
|
84
|
+
}), T && /* @__PURE__ */ _("button", {
|
|
84
85
|
type: "button",
|
|
85
|
-
onClick: () =>
|
|
86
|
+
onClick: () => E(""),
|
|
86
87
|
className: "p-0.5 rounded hover:bg-bg-elevated transition-colors",
|
|
87
|
-
children: /* @__PURE__ */ g
|
|
88
|
+
children: /* @__PURE__ */ _(g, { className: "w-3 h-3 text-text-tertiary" })
|
|
88
89
|
})]
|
|
89
90
|
})]
|
|
90
|
-
}), /* @__PURE__ */
|
|
91
|
+
}), /* @__PURE__ */ v("button", {
|
|
91
92
|
type: "button",
|
|
92
|
-
onClick: () =>
|
|
93
|
+
onClick: () => O(!0),
|
|
93
94
|
className: "shrink-0 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-elevated px-2 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken hover:border-action-primary-bg/40",
|
|
94
|
-
title:
|
|
95
|
-
"aria-label":
|
|
95
|
+
title: k("vibecontrols.agentManager.chatPane.manageProvidersTooltip", "Install or remove AI providers (Claude, Codex, Gemini, OpenCode)"),
|
|
96
|
+
"aria-label": k("vibecontrols.agentManager.chatPane.manageProviders", "Manage AI providers"),
|
|
96
97
|
"data-testid": "agent-manager-manage-providers",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
98
|
+
children: [/* @__PURE__ */ _(m, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ _("span", {
|
|
98
99
|
className: "hidden sm:inline",
|
|
99
|
-
children:
|
|
100
|
+
children: k("vibecontrols.agentManager.chatPane.manageProviders", "Manage AI providers")
|
|
100
101
|
})]
|
|
101
102
|
})]
|
|
102
103
|
}),
|
|
103
|
-
|
|
104
|
+
x ? /* @__PURE__ */ _("div", {
|
|
104
105
|
className: "flex-1 flex items-center justify-center",
|
|
105
|
-
children: /* @__PURE__ */
|
|
106
|
+
children: /* @__PURE__ */ _("p", {
|
|
106
107
|
className: "text-sm text-text-secondary",
|
|
107
|
-
children:
|
|
108
|
+
children: k("vibecontrols.agentManager.chatPane.loadingConversation", "Loading conversation...")
|
|
108
109
|
})
|
|
109
|
-
}) : /* @__PURE__ */
|
|
110
|
-
messages:
|
|
111
|
-
isStreaming:
|
|
112
|
-
onCancelStream:
|
|
113
|
-
searchQuery:
|
|
110
|
+
}) : /* @__PURE__ */ _(o, {
|
|
111
|
+
messages: P,
|
|
112
|
+
isStreaming: z,
|
|
113
|
+
onCancelStream: R,
|
|
114
|
+
searchQuery: T
|
|
114
115
|
}),
|
|
115
|
-
/* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ _(s, {
|
|
116
117
|
agentId: y,
|
|
117
|
-
sessionId:
|
|
118
|
-
onSend: (e) => void
|
|
119
|
-
onCancel:
|
|
120
|
-
agentTunnelUrl:
|
|
121
|
-
agentApiKey:
|
|
118
|
+
sessionId: l,
|
|
119
|
+
onSend: (e) => void L(e),
|
|
120
|
+
onCancel: R,
|
|
121
|
+
agentTunnelUrl: j?.tunnelUrl ?? void 0,
|
|
122
|
+
agentApiKey: j?.agentApiKey ?? void 0
|
|
122
123
|
}),
|
|
123
|
-
/* @__PURE__ */
|
|
124
|
-
open:
|
|
124
|
+
/* @__PURE__ */ _(c, {
|
|
125
|
+
open: D,
|
|
125
126
|
agentId: y,
|
|
126
|
-
onClose: () =>
|
|
127
|
+
onClose: () => O(!1)
|
|
128
|
+
}),
|
|
129
|
+
/* @__PURE__ */ _(b, {
|
|
130
|
+
agentId: y,
|
|
131
|
+
agentRef: I
|
|
127
132
|
})
|
|
128
133
|
]
|
|
129
134
|
});
|
|
135
|
+
}, b = ({ agentId: t, agentRef: n }) => {
|
|
136
|
+
let r = i((e) => e.sdkAuthPrompt), a = i((e) => e.setSdkAuthPrompt), o = e();
|
|
137
|
+
return !r || r.agentId !== t ? null : /* @__PURE__ */ v("div", {
|
|
138
|
+
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
139
|
+
children: [/* @__PURE__ */ _("div", {
|
|
140
|
+
className: "absolute inset-0 bg-bg-overlay",
|
|
141
|
+
onClick: () => a(null),
|
|
142
|
+
"aria-hidden": !0
|
|
143
|
+
}), /* @__PURE__ */ v("div", {
|
|
144
|
+
className: "relative w-full max-w-xl mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl p-4 space-y-3",
|
|
145
|
+
children: [
|
|
146
|
+
/* @__PURE__ */ v("div", {
|
|
147
|
+
className: "flex items-center justify-between",
|
|
148
|
+
children: [/* @__PURE__ */ _("h3", {
|
|
149
|
+
className: "text-lg font-semibold text-text-primary",
|
|
150
|
+
children: o("vibecontrols.ai.sdkAuth.title", "Configure AI provider credentials")
|
|
151
|
+
}), /* @__PURE__ */ _("button", {
|
|
152
|
+
type: "button",
|
|
153
|
+
onClick: () => a(null),
|
|
154
|
+
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
155
|
+
"aria-label": o("vibecontrols.common.close", "Close"),
|
|
156
|
+
children: /* @__PURE__ */ _(g, { className: "w-5 h-5 text-text-secondary" })
|
|
157
|
+
})]
|
|
158
|
+
}),
|
|
159
|
+
/* @__PURE__ */ _("p", {
|
|
160
|
+
className: "text-sm text-text-secondary",
|
|
161
|
+
children: o("vibecontrols.ai.sdkAuth.body", "The agent does not have an Anthropic credential configured. Save one below to continue this conversation.")
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ _(l, { agent: n })
|
|
164
|
+
]
|
|
165
|
+
})]
|
|
166
|
+
});
|
|
130
167
|
};
|
|
131
168
|
//#endregion
|
|
132
|
-
export {
|
|
169
|
+
export { y as AgentManagerChatPane };
|
|
133
170
|
|
|
134
171
|
//# sourceMappingURL=AgentManagerChatPane.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentManagerChatPane.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerChatPane.tsx"],"sourcesContent":["/**\n * Main chat interface for a single Agent Manager session.\n *\n * Renders message list + input area, manages the send/receive flow,\n * fetches existing conversation logs on mount, and provides\n * auto-scroll, cancel, and in-session search support.\n */\n\nimport { useEffect, useState, useCallback, useMemo, type FC } from 'react';\nimport { PackagePlus, Search, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { useAgentManagerStreaming } from '@/hooks/useAgentManagerStreaming';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { fetchSessionLogs } from '@/services/aiApi';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { AgentManagerMessageList } from './AgentManagerMessageList';\nimport { AgentManagerInputArea } from './AgentManagerInputArea';\nimport { PluginInstallModal } from './PluginInstallModal';\nimport type { AgentManagerMessage } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerChatPaneProps {\n sessionId: string;\n agentId: string;\n}\n\nexport const AgentManagerChatPane: FC<AgentManagerChatPaneProps> = ({ sessionId, agentId }) => {\n const [loadingLogs, setLoadingLogs] = useState(false);\n const [searchOpen, setSearchOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n // Provider-install modal triggered from the header. Lets users add a\n // claude/codex/gemini/opencode plugin without leaving the conversation.\n const [installModalOpen, setInstallModalOpen] = useState(false);\n const tr = useTr();\n\n const { agents } = useCachedInitialData();\n const activeAgent = useMemo(\n () => agents.find((a) => a.id === agentId) ?? null,\n [agents, agentId]\n );\n\n const sessionState = useAgentManagerStore((s) => s.sessionStates[sessionId]);\n const addMessage = useAgentManagerStore((s) => s.addMessage);\n\n const messages = sessionState?.messages ?? [];\n\n const { active: activeProfile } = useActiveAgentProfile(activeAgent?.id ?? null);\n const agentRef = useMemo(\n () => ({ id: agentId, profile: activeProfile }),\n [agentId, activeProfile]\n );\n\n const { sendMessage, cancelStream, isStreaming } = useAgentManagerStreaming({\n agent: agentRef,\n sessionId,\n });\n\n const toggleSearch = useCallback(() => {\n setSearchOpen((prev) => {\n if (prev) setSearchQuery('');\n return !prev;\n });\n }, []);\n\n // Fetch existing conversation logs on mount\n useEffect(() => {\n let cancelled = false;\n\n const loadExistingLogs = async () => {\n // Only load if session has no messages yet\n if (messages.length > 0) return;\n\n setLoadingLogs(true);\n try {\n const logs = await fetchSessionLogs(agentRef, sessionId, {\n types: ['input', 'output'],\n });\n\n if (cancelled || logs.length === 0) return;\n\n const seenLogKeys = new Set<string>();\n\n // Convert session logs to AgentManagerMessage format. The core AI\n // route owns logging, but older provider builds also wrote the same\n // input/output records; collapse same-second duplicates on restore.\n for (const log of logs) {\n const content = log.message || log.content || '';\n const timestamp = log.timestamp || log.createdAt;\n const timestampSecond = timestamp ? Math.floor(new Date(timestamp).getTime() / 1000) : 0;\n const logKey = [log.type, content, log.model ?? '', timestampSecond].join('\\u0000');\n if (seenLogKeys.has(logKey)) continue;\n seenLogKeys.add(logKey);\n\n const msg: AgentManagerMessage = {\n id: log.id,\n role: log.type === 'input' ? 'user' : 'assistant',\n content,\n timestamp,\n tokens: log.tokenCount ? { input: 0, output: log.tokenCount } : undefined,\n model: log.model,\n };\n addMessage(sessionId, msg);\n }\n } catch {\n // Silent fail -- session may not have logs yet\n } finally {\n if (!cancelled) setLoadingLogs(false);\n }\n };\n\n void loadExistingLogs();\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- only run on mount\n }, [agentId, sessionId]);\n\n return (\n <div className=\"flex flex-col h-full min-h-0\" data-testid=\"agent-manager-chat-pane\">\n {/* In-session search bar.\n Layout: [search controls (flex-1)] ... [Manage AI providers].\n The search input expands inside the LEFT cluster only, so the\n Manage button stays anchored on the right and never shifts. */}\n <div className=\"flex items-center gap-1 px-3 py-1 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <div className=\"flex items-center gap-1.5 min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={toggleSearch}\n className={`p-1 rounded transition-colors shrink-0 ${\n searchOpen\n ? 'bg-action-primary-bg/10 text-action-primary-text'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated'\n }`}\n title={tr(\n 'vibecontrols.agentManager.chatPane.searchInSession',\n 'Search in session (Ctrl+F)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.chatPane.searchInSession',\n 'Search in session'\n )}\n >\n <Search className=\"w-3.5 h-3.5\" />\n </button>\n {searchOpen && (\n <div className=\"flex items-center gap-1.5 flex-1 min-w-0\">\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={tr(\n 'vibecontrols.agentManager.chatPane.searchPlaceholder',\n 'Search in conversation...'\n )}\n autoFocus\n className=\"flex-1 min-w-0 bg-transparent text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n className=\"p-0.5 rounded hover:bg-bg-elevated transition-colors\"\n >\n <X className=\"w-3 h-3 text-text-tertiary\" />\n </button>\n )}\n </div>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => setInstallModalOpen(true)}\n className=\"shrink-0 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-elevated px-2 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken hover:border-action-primary-bg/40\"\n title={tr(\n 'vibecontrols.agentManager.chatPane.manageProvidersTooltip',\n 'Install or remove AI providers (Claude, Codex, Gemini, OpenCode)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.chatPane.manageProviders',\n 'Manage AI providers'\n )}\n data-testid=\"agent-manager-manage-providers\"\n >\n <PackagePlus className=\"w-3.5 h-3.5\" />\n <span className=\"hidden sm:inline\">\n {tr('vibecontrols.agentManager.chatPane.manageProviders', 'Manage AI providers')}\n </span>\n </button>\n </div>\n\n {/* Message list */}\n {loadingLogs ? (\n <div className=\"flex-1 flex items-center justify-center\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.chatPane.loadingConversation',\n 'Loading conversation...'\n )}\n </p>\n </div>\n ) : (\n <AgentManagerMessageList\n messages={messages}\n isStreaming={isStreaming}\n onCancelStream={cancelStream}\n searchQuery={searchQuery}\n />\n )}\n\n {/* Rich input area with controls */}\n <AgentManagerInputArea\n agentId={agentId}\n sessionId={sessionId}\n onSend={(text) => void sendMessage(text)}\n onCancel={cancelStream}\n agentTunnelUrl={activeAgent?.tunnelUrl ?? undefined}\n agentApiKey={activeAgent?.agentApiKey ?? undefined}\n />\n\n {/* Header-triggered provider install/remove. Sits below the chat\n tree so it can render full-screen without disturbing layout. */}\n <PluginInstallModal\n open={installModalOpen}\n agentId={agentId}\n onClose={() => setInstallModalOpen(false)}\n />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentManagerChatPane.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerChatPane.tsx"],"sourcesContent":["/**\n * Main chat interface for a single Agent Manager session.\n *\n * Renders message list + input area, manages the send/receive flow,\n * fetches existing conversation logs on mount, and provides\n * auto-scroll, cancel, and in-session search support.\n */\n\nimport { useEffect, useState, useCallback, useMemo, type FC } from 'react';\nimport { PackagePlus, Search, X } from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { useAgentManagerStreaming } from '@/hooks/useAgentManagerStreaming';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { fetchSessionLogs } from '@/services/aiApi';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { AgentManagerMessageList } from './AgentManagerMessageList';\nimport { AgentManagerInputArea } from './AgentManagerInputArea';\nimport { PluginInstallModal } from './PluginInstallModal';\nimport { AIProviderCredentialsCard } from '@/components/ai/AIProviderCredentialsCard';\nimport type { AgentManagerMessage } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerChatPaneProps {\n sessionId: string;\n agentId: string;\n}\n\nexport const AgentManagerChatPane: FC<AgentManagerChatPaneProps> = ({ sessionId, agentId }) => {\n const [loadingLogs, setLoadingLogs] = useState(false);\n const [searchOpen, setSearchOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n // Provider-install modal triggered from the header. Lets users add a\n // claude/codex/gemini/opencode plugin without leaving the conversation.\n const [installModalOpen, setInstallModalOpen] = useState(false);\n const tr = useTr();\n\n const { agents } = useCachedInitialData();\n const activeAgent = useMemo(\n () => agents.find((a) => a.id === agentId) ?? null,\n [agents, agentId]\n );\n\n const sessionState = useAgentManagerStore((s) => s.sessionStates[sessionId]);\n const addMessage = useAgentManagerStore((s) => s.addMessage);\n\n const messages = sessionState?.messages ?? [];\n\n const { active: activeProfile } = useActiveAgentProfile(activeAgent?.id ?? null);\n const agentRef = useMemo(\n () => ({ id: agentId, profile: activeProfile }),\n [agentId, activeProfile]\n );\n\n const { sendMessage, cancelStream, isStreaming } = useAgentManagerStreaming({\n agent: agentRef,\n sessionId,\n });\n\n const toggleSearch = useCallback(() => {\n setSearchOpen((prev) => {\n if (prev) setSearchQuery('');\n return !prev;\n });\n }, []);\n\n // Fetch existing conversation logs on mount\n useEffect(() => {\n let cancelled = false;\n\n const loadExistingLogs = async () => {\n // Only load if session has no messages yet\n if (messages.length > 0) return;\n\n setLoadingLogs(true);\n try {\n const logs = await fetchSessionLogs(agentRef, sessionId, {\n types: ['input', 'output'],\n });\n\n if (cancelled || logs.length === 0) return;\n\n const seenLogKeys = new Set<string>();\n\n // Convert session logs to AgentManagerMessage format. The core AI\n // route owns logging, but older provider builds also wrote the same\n // input/output records; collapse same-second duplicates on restore.\n for (const log of logs) {\n const content = log.message || log.content || '';\n const timestamp = log.timestamp || log.createdAt;\n const timestampSecond = timestamp ? Math.floor(new Date(timestamp).getTime() / 1000) : 0;\n const logKey = [log.type, content, log.model ?? '', timestampSecond].join('\\u0000');\n if (seenLogKeys.has(logKey)) continue;\n seenLogKeys.add(logKey);\n\n const msg: AgentManagerMessage = {\n id: log.id,\n role: log.type === 'input' ? 'user' : 'assistant',\n content,\n timestamp,\n tokens: log.tokenCount ? { input: 0, output: log.tokenCount } : undefined,\n model: log.model,\n };\n addMessage(sessionId, msg);\n }\n } catch {\n // Silent fail -- session may not have logs yet\n } finally {\n if (!cancelled) setLoadingLogs(false);\n }\n };\n\n void loadExistingLogs();\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- only run on mount\n }, [agentId, sessionId]);\n\n return (\n <div className=\"flex flex-col h-full min-h-0\" data-testid=\"agent-manager-chat-pane\">\n {/* In-session search bar.\n Layout: [search controls (flex-1)] ... [Manage AI providers].\n The search input expands inside the LEFT cluster only, so the\n Manage button stays anchored on the right and never shifts. */}\n <div className=\"flex items-center gap-1 px-3 py-1 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <div className=\"flex items-center gap-1.5 min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={toggleSearch}\n className={`p-1 rounded transition-colors shrink-0 ${\n searchOpen\n ? 'bg-action-primary-bg/10 text-action-primary-text'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated'\n }`}\n title={tr(\n 'vibecontrols.agentManager.chatPane.searchInSession',\n 'Search in session (Ctrl+F)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.chatPane.searchInSession',\n 'Search in session'\n )}\n >\n <Search className=\"w-3.5 h-3.5\" />\n </button>\n {searchOpen && (\n <div className=\"flex items-center gap-1.5 flex-1 min-w-0\">\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={tr(\n 'vibecontrols.agentManager.chatPane.searchPlaceholder',\n 'Search in conversation...'\n )}\n autoFocus\n className=\"flex-1 min-w-0 bg-transparent text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n className=\"p-0.5 rounded hover:bg-bg-elevated transition-colors\"\n >\n <X className=\"w-3 h-3 text-text-tertiary\" />\n </button>\n )}\n </div>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => setInstallModalOpen(true)}\n className=\"shrink-0 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-elevated px-2 py-1 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken hover:border-action-primary-bg/40\"\n title={tr(\n 'vibecontrols.agentManager.chatPane.manageProvidersTooltip',\n 'Install or remove AI providers (Claude, Codex, Gemini, OpenCode)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.chatPane.manageProviders',\n 'Manage AI providers'\n )}\n data-testid=\"agent-manager-manage-providers\"\n >\n <PackagePlus className=\"w-3.5 h-3.5\" />\n <span className=\"hidden sm:inline\">\n {tr('vibecontrols.agentManager.chatPane.manageProviders', 'Manage AI providers')}\n </span>\n </button>\n </div>\n\n {/* Message list */}\n {loadingLogs ? (\n <div className=\"flex-1 flex items-center justify-center\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.chatPane.loadingConversation',\n 'Loading conversation...'\n )}\n </p>\n </div>\n ) : (\n <AgentManagerMessageList\n messages={messages}\n isStreaming={isStreaming}\n onCancelStream={cancelStream}\n searchQuery={searchQuery}\n />\n )}\n\n {/* Rich input area with controls */}\n <AgentManagerInputArea\n agentId={agentId}\n sessionId={sessionId}\n onSend={(text) => void sendMessage(text)}\n onCancel={cancelStream}\n agentTunnelUrl={activeAgent?.tunnelUrl ?? undefined}\n agentApiKey={activeAgent?.agentApiKey ?? undefined}\n />\n\n {/* Header-triggered provider install/remove. Sits below the chat\n tree so it can render full-screen without disturbing layout. */}\n <PluginInstallModal\n open={installModalOpen}\n agentId={agentId}\n onClose={() => setInstallModalOpen(false)}\n />\n\n {/* SDK auth prompt: opens when the streaming hook detects a missing\n ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN, so the user can save the\n credential inline instead of being told to go look elsewhere. */}\n <SdkAuthPromptModal agentId={agentId} agentRef={agentRef} />\n </div>\n );\n};\n\ninterface SdkAuthPromptModalProps {\n agentId: string;\n agentRef: { id: string; profile: string };\n}\n\nconst SdkAuthPromptModal: FC<SdkAuthPromptModalProps> = ({ agentId, agentRef }) => {\n const sdkAuthPrompt = useAgentManagerStore((s) => s.sdkAuthPrompt);\n const setSdkAuthPrompt = useAgentManagerStore((s) => s.setSdkAuthPrompt);\n const tr = useTr();\n\n if (!sdkAuthPrompt || sdkAuthPrompt.agentId !== agentId) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={() => setSdkAuthPrompt(null)}\n aria-hidden\n />\n <div className=\"relative w-full max-w-xl mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl p-4 space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr(\n 'vibecontrols.ai.sdkAuth.title',\n 'Configure AI provider credentials'\n )}\n </h3>\n <button\n type=\"button\"\n onClick={() => setSdkAuthPrompt(null)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.common.close', 'Close')}\n >\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.ai.sdkAuth.body',\n 'The agent does not have an Anthropic credential configured. Save one below to continue this conversation.'\n )}\n </p>\n <AIProviderCredentialsCard agent={agentRef} />\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AA2BA,IAAa,KAAuD,EAAE,cAAW,iBAAc;CAC7F,IAAM,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAa,KAAkB,EAAS,GAAG,EAG5C,CAAC,GAAkB,KAAuB,EAAS,GAAM,EACzD,IAAK,GAAO,EAEZ,EAAE,cAAW,GAAsB,EACnC,IAAc,QACZ,EAAO,MAAM,MAAM,EAAE,OAAO,EAAQ,IAAI,MAC9C,CAAC,GAAQ,EAAQ,CAClB,EAEK,IAAe,GAAsB,MAAM,EAAE,cAAc,GAAW,EACtE,IAAa,GAAsB,MAAM,EAAE,WAAW,EAEtD,IAAW,GAAc,YAAY,EAAE,EAEvC,EAAE,QAAQ,MAAkB,EAAsB,GAAa,MAAM,KAAK,EAC1E,IAAW,SACR;EAAE,IAAI;EAAS,SAAS;EAAe,GAC9C,CAAC,GAAS,EAAc,CACzB,EAEK,EAAE,gBAAa,iBAAc,mBAAgB,EAAyB;EAC1E,OAAO;EACP;EACD,CAAC,EAEI,IAAe,QAAkB;AACrC,KAAe,OACT,KAAM,EAAe,GAAG,EACrB,CAAC,GACR;IACD,EAAE,CAAC;AAwDN,QArDA,QAAgB;EACd,IAAI,IAAY;AA8ChB,UA5CyB,YAAY;AAE/B,WAAS,SAAS,IAEtB;MAAe,GAAK;AACpB,QAAI;KACF,IAAM,IAAO,MAAM,EAAiB,GAAU,GAAW,EACvD,OAAO,CAAC,SAAS,SAAS,EAC3B,CAAC;AAEF,SAAI,KAAa,EAAK,WAAW,EAAG;KAEpC,IAAM,oBAAc,IAAI,KAAa;AAKrC,UAAK,IAAM,KAAO,GAAM;MACtB,IAAM,IAAU,EAAI,WAAW,EAAI,WAAW,IACxC,IAAY,EAAI,aAAa,EAAI,WACjC,IAAkB,IAAY,KAAK,MAAM,IAAI,KAAK,EAAU,CAAC,SAAS,GAAG,IAAK,GAAG,GACjF,IAAS;OAAC,EAAI;OAAM;OAAS,EAAI,SAAS;OAAI;OAAgB,CAAC,KAAK,KAAS;AAC/E,QAAY,IAAI,EAAO,KAC3B,EAAY,IAAI,EAAO,EAUvB,EAAW,GARsB;OAC/B,IAAI,EAAI;OACR,MAAM,EAAI,SAAS,UAAU,SAAS;OACtC;OACA;OACA,QAAQ,EAAI,aAAa;QAAE,OAAO;QAAG,QAAQ,EAAI;QAAY,GAAG,KAAA;OAChE,OAAO,EAAI;OACZ,CACyB;;YAEtB,WAEE;AACR,KAAK,KAAW,EAAe,GAAM;;;MAIlB,QAEV;AACX,OAAY;;IAGb,CAAC,GAAS,EAAU,CAAC,EAGtB,kBAAC,OAAD;EAAK,WAAU;EAA+B,eAAY;YAA1D;GAKE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAW,0CACT,IACI,qDACA;MAEN,OAAO,EACL,sDACA,6BACD;MACD,cAAY,EACV,sDACA,oBACD;gBAED,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;MAC3B,CAAA,EACR,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OACE,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;OAC/C,aAAa,EACX,wDACA,4BACD;OACD,WAAA;OACA,WAAU;OACV,CAAA,EACD,KACC,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAe,GAAG;OACjC,WAAU;iBAEV,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;OACrC,CAAA,CAEP;QAEJ;QACN,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAoB,GAAK;KACxC,WAAU;KACV,OAAO,EACL,6DACA,mEACD;KACD,cAAY,EACV,sDACA,sBACD;KACD,eAAY;eAZd,CAcE,kBAAC,GAAD,EAAa,WAAU,eAAgB,CAAA,EACvC,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,sDAAsD,sBAAsB;MAC3E,CAAA,CACA;OACL;;GAGL,IACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,0DACA,0BACD;KACC,CAAA;IACA,CAAA,GAEN,kBAAC,GAAD;IACY;IACG;IACb,gBAAgB;IACH;IACb,CAAA;GAIJ,kBAAC,GAAD;IACW;IACE;IACX,SAAS,MAAS,KAAK,EAAY,EAAK;IACxC,UAAU;IACV,gBAAgB,GAAa,aAAa,KAAA;IAC1C,aAAa,GAAa,eAAe,KAAA;IACzC,CAAA;GAIF,kBAAC,GAAD;IACE,MAAM;IACG;IACT,eAAe,EAAoB,GAAM;IACzC,CAAA;GAKF,kBAAC,GAAD;IAA6B;IAAmB;IAAY,CAAA;GACxD;;GASJ,KAAmD,EAAE,YAAS,kBAAe;CACjF,IAAM,IAAgB,GAAsB,MAAM,EAAE,cAAc,EAC5D,IAAmB,GAAsB,MAAM,EAAE,iBAAiB,EAClE,IAAK,GAAO;AAIlB,QAFI,CAAC,KAAiB,EAAc,YAAY,IAAgB,OAG9D,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GACE,WAAU;GACV,eAAe,EAAiB,KAAK;GACrC,eAAA;GACA,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EACC,iCACA,oCACD;MACE,CAAA,EACL,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAiB,KAAK;MACrC,WAAU;MACV,cAAY,EAAG,6BAA6B,QAAQ;gBAEpD,kBAAC,GAAD,EAAG,WAAU,+BAAgC,CAAA;MACtC,CAAA,CACL;;IACN,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,gCACA,4GACD;KACC,CAAA;IACJ,kBAAC,GAAD,EAA2B,OAAO,GAAY,CAAA;IAC1C;KACF"}
|