@burdenoff/microfe-vibecontrols 2026.521.3 → 2026.522.2
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/VibeControlsRoot.js +86 -78
- package/dist/VibeControlsRoot.js.map +1 -1
- package/dist/VibeControlsRoutes.js +168 -125
- package/dist/VibeControlsRoutes.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js +174 -0
- package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js.map +1 -0
- package/dist/components/agent-manager/AgentManagerChatPane.js +149 -94
- package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerMessageItem.js +52 -41
- package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerMessageList.js +22 -15
- package/dist/components/agent-manager/AgentManagerMessageList.js.map +1 -1
- package/dist/components/agents/OpenInEditorMenu.js +241 -0
- package/dist/components/agents/OpenInEditorMenu.js.map +1 -0
- package/dist/components/agents/VibeOpenInEditorMenu.js +20 -0
- package/dist/components/agents/VibeOpenInEditorMenu.js.map +1 -0
- package/dist/components/agents/idePresets.js +148 -0
- package/dist/components/agents/idePresets.js.map +1 -0
- package/dist/components/agents/index.js +3 -0
- package/dist/components/ai/AIPlanPanel.js +332 -0
- package/dist/components/ai/AIPlanPanel.js.map +1 -0
- package/dist/components/analytics/index.js +2 -2
- package/dist/components/assistant/AssistantMessageItem.js +55 -47
- package/dist/components/assistant/AssistantMessageItem.js.map +1 -1
- package/dist/components/audit/AuditExportButton.js +87 -41
- package/dist/components/audit/AuditExportButton.js.map +1 -1
- package/dist/components/gitops/CIDurationChart.js +11 -11
- package/dist/components/security/SecurityEmptyState.js +33 -0
- package/dist/components/security/SecurityEmptyState.js.map +1 -0
- package/dist/components/security/SecurityEvidenceList.js +42 -0
- package/dist/components/security/SecurityEvidenceList.js.map +1 -0
- package/dist/components/security/SecurityFindingsTable.js +201 -0
- package/dist/components/security/SecurityFindingsTable.js.map +1 -0
- package/dist/components/security/SecurityHeaderStrip.js +67 -0
- package/dist/components/security/SecurityHeaderStrip.js.map +1 -0
- package/dist/components/security/SecurityLifecycleStepper.js +66 -0
- package/dist/components/security/SecurityLifecycleStepper.js.map +1 -0
- package/dist/components/security/SecurityProviderDrawer.js +112 -0
- package/dist/components/security/SecurityProviderDrawer.js.map +1 -0
- package/dist/components/security/SecurityTabPanel.js +105 -0
- package/dist/components/security/SecurityTabPanel.js.map +1 -0
- package/dist/components/security/gql.js +128 -0
- package/dist/components/security/gql.js.map +1 -0
- package/dist/components/security/index.js +8 -0
- package/dist/components/security/types.js +80 -0
- package/dist/components/security/types.js.map +1 -0
- package/dist/components/settings/IdePreferencesSection.js +66 -0
- package/dist/components/settings/IdePreferencesSection.js.map +1 -0
- package/dist/components/shared/PageSkeleton.js +88 -0
- package/dist/components/shared/PageSkeleton.js.map +1 -0
- package/dist/constants/pluginCatalog.js +20 -8
- package/dist/constants/pluginCatalog.js.map +1 -1
- package/dist/generated/wspace-operations.js +1350 -1262
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/hooks/useToggleAgentBookmark.js +32 -0
- package/dist/hooks/useToggleAgentBookmark.js.map +1 -0
- package/dist/index.js +10 -10
- package/dist/pages/AgentDetailsPage.js +25 -25
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +326 -327
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/AnalyticsPage.js +425 -420
- package/dist/pages/AnalyticsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +258 -249
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/CatalogPage.js +22 -22
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +30 -30
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js +23 -23
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/LogsPage.js +107 -0
- package/dist/pages/LogsPage.js.map +1 -0
- package/dist/pages/NoteDetailsPage.js +12 -12
- package/dist/pages/NoteDetailsPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +12 -12
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +158 -136
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/SettingsPage.js +60 -52
- package/dist/pages/SettingsPage.js.map +1 -1
- package/dist/pages/TargetDetailsPage.js +18 -18
- package/dist/pages/TargetDetailsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +328 -331
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +17 -17
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/dist/pages/TemplatesPage.js +17 -17
- package/dist/pages/TemplatesPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +552 -535
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/VibesPage.js +321 -296
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/ai/AIBookmarksPage.js +184 -0
- package/dist/pages/ai/AIBookmarksPage.js.map +1 -0
- package/dist/pages/ai/AIPage.js +138 -109
- package/dist/pages/ai/AIPage.js.map +1 -1
- package/dist/services/planApi.js +18 -0
- package/dist/services/planApi.js.map +1 -0
- package/dist/store/vibeControlsStore.js +20 -2
- package/dist/store/vibeControlsStore.js.map +1 -1
- package/dist/translations/en.js +30 -1
- package/dist/translations/en.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { useDeleteAgentMessageBookmarkMutation as t, useMyAgentMessageBookmarksQuery as n, useUpdateAgentMessageBookmarkMutation as r } from "../../generated/wspace-operations.js";
|
|
3
|
+
import { relativeTime as i } from "../../utils/relativeTime.js";
|
|
4
|
+
import { useState as a } from "react";
|
|
5
|
+
import { Bookmark as o, Check as s, ChevronRight as c, ExternalLink as l, Pencil as u, Trash2 as d, X as f } from "lucide-react";
|
|
6
|
+
import { useNavigate as p } from "react-router-dom";
|
|
7
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/agent-manager/AgentManagerBookmarksDrawer.tsx
|
|
9
|
+
var g = 80;
|
|
10
|
+
function _(e) {
|
|
11
|
+
if (!e) return "";
|
|
12
|
+
let t = e.trim().replace(/\s+/g, " ");
|
|
13
|
+
return t.length > g ? `${t.slice(0, g)}…` : t;
|
|
14
|
+
}
|
|
15
|
+
var v = ({ open: g, onClose: v, sessionId: y, messages: b, onJump: x }) => {
|
|
16
|
+
let S = p(), C = e(), [w, T] = a(null), [E, D] = a(""), { data: O, loading: k } = n({
|
|
17
|
+
variables: { filter: { sessionId: y } },
|
|
18
|
+
skip: !g,
|
|
19
|
+
fetchPolicy: "cache-and-network"
|
|
20
|
+
}), [A] = r({ refetchQueries: ["MyAgentMessageBookmarks"] }), [j] = t({ refetchQueries: ["MyAgentMessageBookmarks"] }), M = O?.myAgentMessageBookmarks.items ?? [], N = new Map(b.map((e) => [e.id, e])), P = (e, t) => {
|
|
21
|
+
T(e), D(t ?? "");
|
|
22
|
+
}, F = async (e) => {
|
|
23
|
+
await A({ variables: {
|
|
24
|
+
id: e,
|
|
25
|
+
input: { label: E || null }
|
|
26
|
+
} }), T(null), D("");
|
|
27
|
+
}, I = async (e) => {
|
|
28
|
+
await j({ variables: { id: e } });
|
|
29
|
+
};
|
|
30
|
+
return g ? /* @__PURE__ */ h("div", {
|
|
31
|
+
className: "fixed inset-0 z-40 flex",
|
|
32
|
+
children: [/* @__PURE__ */ m("div", {
|
|
33
|
+
className: "flex-1 bg-bg-overlay/40",
|
|
34
|
+
onClick: v,
|
|
35
|
+
"aria-hidden": !0
|
|
36
|
+
}), /* @__PURE__ */ h("aside", {
|
|
37
|
+
className: "w-full max-w-md h-full bg-bg-surface border-l border-border-default shadow-xl flex flex-col",
|
|
38
|
+
role: "dialog",
|
|
39
|
+
"aria-label": C("vibecontrols.agentManager.bookmarksDrawer.title", "Bookmarks in this session"),
|
|
40
|
+
"data-testid": "agent-manager-bookmarks-drawer",
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ h("header", {
|
|
43
|
+
className: "flex items-center justify-between px-4 py-3 border-b border-border-default flex-shrink-0",
|
|
44
|
+
children: [/* @__PURE__ */ h("div", {
|
|
45
|
+
className: "flex items-center gap-2",
|
|
46
|
+
children: [/* @__PURE__ */ m(o, { className: "w-4 h-4 text-text-secondary" }), /* @__PURE__ */ m("h3", {
|
|
47
|
+
className: "text-sm font-semibold text-text-primary",
|
|
48
|
+
children: C("vibecontrols.agentManager.bookmarksDrawer.title", "Bookmarks in this session")
|
|
49
|
+
})]
|
|
50
|
+
}), /* @__PURE__ */ m("button", {
|
|
51
|
+
type: "button",
|
|
52
|
+
onClick: v,
|
|
53
|
+
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
54
|
+
"aria-label": C("vibecontrols.common.close", "Close"),
|
|
55
|
+
children: /* @__PURE__ */ m(f, { className: "w-4 h-4 text-text-secondary" })
|
|
56
|
+
})]
|
|
57
|
+
}),
|
|
58
|
+
/* @__PURE__ */ h("div", {
|
|
59
|
+
className: "flex-1 min-h-0 overflow-y-auto",
|
|
60
|
+
children: [
|
|
61
|
+
k && M.length === 0 && /* @__PURE__ */ m("div", {
|
|
62
|
+
className: "px-4 py-6 text-xs text-text-tertiary",
|
|
63
|
+
children: C("vibecontrols.common.loading", "Loading...")
|
|
64
|
+
}),
|
|
65
|
+
!k && M.length === 0 && /* @__PURE__ */ h("div", {
|
|
66
|
+
className: "px-4 py-10 flex flex-col items-center text-center gap-2",
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ m(o, { className: "w-8 h-8 text-text-muted" }),
|
|
69
|
+
/* @__PURE__ */ m("p", {
|
|
70
|
+
className: "text-sm font-medium text-text-primary",
|
|
71
|
+
children: C("vibecontrols.agentManager.bookmarksDrawer.empty", "No bookmarks in this session yet")
|
|
72
|
+
}),
|
|
73
|
+
/* @__PURE__ */ m("p", {
|
|
74
|
+
className: "text-xs text-text-secondary max-w-xs",
|
|
75
|
+
children: C("vibecontrols.agentManager.bookmarksDrawer.emptyHint", "Click the bookmark icon on any message to save it for later.")
|
|
76
|
+
})
|
|
77
|
+
]
|
|
78
|
+
}),
|
|
79
|
+
/* @__PURE__ */ m("ul", {
|
|
80
|
+
className: "divide-y divide-border-subtle",
|
|
81
|
+
children: M.map((e) => {
|
|
82
|
+
let t = N.get(e.messageId), n = !t, r = w === e.id;
|
|
83
|
+
return /* @__PURE__ */ m("li", {
|
|
84
|
+
className: "px-4 py-3 hover:bg-bg-elevated/40 transition-colors",
|
|
85
|
+
children: /* @__PURE__ */ h("div", {
|
|
86
|
+
className: "flex items-start gap-2",
|
|
87
|
+
children: [/* @__PURE__ */ h("div", {
|
|
88
|
+
className: "flex-1 min-w-0",
|
|
89
|
+
children: [
|
|
90
|
+
r ? /* @__PURE__ */ m("input", {
|
|
91
|
+
autoFocus: !0,
|
|
92
|
+
value: E,
|
|
93
|
+
onChange: (e) => D(e.target.value),
|
|
94
|
+
onKeyDown: (t) => {
|
|
95
|
+
t.key === "Enter" && F(e.id), t.key === "Escape" && (T(null), D(""));
|
|
96
|
+
},
|
|
97
|
+
placeholder: C("vibecontrols.agentManager.bookmarksDrawer.labelPlaceholder", "Add a label (optional)"),
|
|
98
|
+
className: "w-full bg-bg-sunken border border-border-subtle rounded px-2 py-1 text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-action-primary-bg"
|
|
99
|
+
}) : /* @__PURE__ */ m("p", {
|
|
100
|
+
className: "text-sm font-medium text-text-primary truncate",
|
|
101
|
+
children: e.label || (t?.role === "user" ? C("vibecontrols.agentManager.bookmarksDrawer.fromUser", "From you") : C("vibecontrols.agentManager.bookmarksDrawer.fromAssistant", "Assistant reply"))
|
|
102
|
+
}),
|
|
103
|
+
n ? /* @__PURE__ */ m("p", {
|
|
104
|
+
className: "text-xs text-text-tertiary italic mt-1",
|
|
105
|
+
children: C("vibecontrols.agentManager.bookmarksDrawer.stale", "Message no longer available")
|
|
106
|
+
}) : /* @__PURE__ */ m("p", {
|
|
107
|
+
className: "text-xs text-text-secondary mt-1 line-clamp-2",
|
|
108
|
+
children: _(t.content)
|
|
109
|
+
}),
|
|
110
|
+
/* @__PURE__ */ m("p", {
|
|
111
|
+
className: "text-[10px] text-text-tertiary mt-1",
|
|
112
|
+
children: i(e.createdAt)
|
|
113
|
+
})
|
|
114
|
+
]
|
|
115
|
+
}), /* @__PURE__ */ h("div", {
|
|
116
|
+
className: "flex items-center gap-0.5 shrink-0",
|
|
117
|
+
children: [
|
|
118
|
+
!n && !r && /* @__PURE__ */ m("button", {
|
|
119
|
+
type: "button",
|
|
120
|
+
onClick: () => {
|
|
121
|
+
x(e.messageId), v();
|
|
122
|
+
},
|
|
123
|
+
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
124
|
+
title: C("vibecontrols.agentManager.bookmarksDrawer.jump", "Jump to message"),
|
|
125
|
+
"aria-label": C("vibecontrols.agentManager.bookmarksDrawer.jump", "Jump to message"),
|
|
126
|
+
children: /* @__PURE__ */ m(c, { className: "w-3.5 h-3.5 text-text-secondary" })
|
|
127
|
+
}),
|
|
128
|
+
r ? /* @__PURE__ */ m("button", {
|
|
129
|
+
type: "button",
|
|
130
|
+
onClick: () => void F(e.id),
|
|
131
|
+
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
132
|
+
"aria-label": C("vibecontrols.common.save", "Save"),
|
|
133
|
+
children: /* @__PURE__ */ m(s, { className: "w-3.5 h-3.5 text-status-success-text" })
|
|
134
|
+
}) : /* @__PURE__ */ m("button", {
|
|
135
|
+
type: "button",
|
|
136
|
+
onClick: () => P(e.id, e.label),
|
|
137
|
+
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
138
|
+
"aria-label": C("vibecontrols.agentManager.bookmarksDrawer.editLabel", "Edit label"),
|
|
139
|
+
children: /* @__PURE__ */ m(u, { className: "w-3.5 h-3.5 text-text-secondary" })
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ m("button", {
|
|
142
|
+
type: "button",
|
|
143
|
+
onClick: () => void I(e.id),
|
|
144
|
+
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
145
|
+
"aria-label": C("vibecontrols.agentManager.messageItem.removeBookmark", "Remove bookmark"),
|
|
146
|
+
children: /* @__PURE__ */ m(d, { className: "w-3.5 h-3.5 text-status-danger-text" })
|
|
147
|
+
})
|
|
148
|
+
]
|
|
149
|
+
})]
|
|
150
|
+
})
|
|
151
|
+
}, e.id);
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
]
|
|
155
|
+
}),
|
|
156
|
+
/* @__PURE__ */ m("footer", {
|
|
157
|
+
className: "px-4 py-3 border-t border-border-default flex-shrink-0",
|
|
158
|
+
children: /* @__PURE__ */ h("button", {
|
|
159
|
+
type: "button",
|
|
160
|
+
onClick: () => {
|
|
161
|
+
v(), S("/vibecontrols/ai/bookmarks");
|
|
162
|
+
},
|
|
163
|
+
className: "inline-flex items-center gap-1.5 text-xs font-medium text-action-primary-text hover:underline",
|
|
164
|
+
children: [/* @__PURE__ */ m(l, { className: "w-3 h-3" }), C("vibecontrols.agentManager.bookmarksDrawer.seeAll", "See all bookmarks across sessions")]
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
]
|
|
168
|
+
})]
|
|
169
|
+
}) : null;
|
|
170
|
+
};
|
|
171
|
+
//#endregion
|
|
172
|
+
export { v as AgentManagerBookmarksDrawer };
|
|
173
|
+
|
|
174
|
+
//# sourceMappingURL=AgentManagerBookmarksDrawer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentManagerBookmarksDrawer.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerBookmarksDrawer.tsx"],"sourcesContent":["/**\n * Side drawer listing the current user's bookmarks for ONE chat session.\n *\n * Each row resolves its message body from the currently-loaded session\n * messages. When the bookmarked message is no longer in the session\n * (agent log pruning / rotation), the row degrades to a \"Message no\n * longer available\" affordance with a remove-only action.\n */\n\nimport { useState, type FC } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Bookmark, X, ChevronRight, Pencil, Trash2, Check, ExternalLink } from 'lucide-react';\nimport { relativeTime } from '@/utils/relativeTime';\nimport {\n useDeleteAgentMessageBookmarkMutation,\n useMyAgentMessageBookmarksQuery,\n useUpdateAgentMessageBookmarkMutation,\n type AgentMessageBookmarkFieldsFragment,\n} from '../../generated/wspace-operations';\nimport type { AgentManagerMessage } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerBookmarksDrawerProps {\n open: boolean;\n onClose: () => void;\n sessionId: string;\n messages: AgentManagerMessage[];\n onJump: (messageId: string) => void;\n}\n\nconst PREVIEW_LEN = 80;\n\nfunction snippet(content: string): string {\n if (!content) return '';\n const trimmed = content.trim().replace(/\\s+/g, ' ');\n return trimmed.length > PREVIEW_LEN ? `${trimmed.slice(0, PREVIEW_LEN)}…` : trimmed;\n}\n\nexport const AgentManagerBookmarksDrawer: FC<AgentManagerBookmarksDrawerProps> = ({\n open,\n onClose,\n sessionId,\n messages,\n onJump,\n}) => {\n const navigate = useNavigate();\n const tr = useTr();\n const [editingId, setEditingId] = useState<string | null>(null);\n const [labelDraft, setLabelDraft] = useState('');\n\n const { data, loading } = useMyAgentMessageBookmarksQuery({\n variables: { filter: { sessionId } },\n skip: !open,\n fetchPolicy: 'cache-and-network',\n });\n\n const [updateMutation] = useUpdateAgentMessageBookmarkMutation({\n refetchQueries: ['MyAgentMessageBookmarks'],\n });\n const [deleteMutation] = useDeleteAgentMessageBookmarkMutation({\n refetchQueries: ['MyAgentMessageBookmarks'],\n });\n\n const bookmarks = data?.myAgentMessageBookmarks.items ?? [];\n const messageById = new Map(messages.map((m) => [m.id, m]));\n\n const startEdit = (id: string, current: string | null | undefined) => {\n setEditingId(id);\n setLabelDraft(current ?? '');\n };\n\n const saveEdit = async (id: string) => {\n await updateMutation({ variables: { id, input: { label: labelDraft || null } } });\n setEditingId(null);\n setLabelDraft('');\n };\n\n const removeBookmark = async (id: string) => {\n await deleteMutation({ variables: { id } });\n };\n\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0 z-40 flex\">\n <div className=\"flex-1 bg-bg-overlay/40\" onClick={onClose} aria-hidden />\n <aside\n className=\"w-full max-w-md h-full bg-bg-surface border-l border-border-default shadow-xl flex flex-col\"\n role=\"dialog\"\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.title',\n 'Bookmarks in this session'\n )}\n data-testid=\"agent-manager-bookmarks-drawer\"\n >\n <header className=\"flex items-center justify-between px-4 py-3 border-b border-border-default flex-shrink-0\">\n <div className=\"flex items-center gap-2\">\n <Bookmark className=\"w-4 h-4 text-text-secondary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.bookmarksDrawer.title', 'Bookmarks in this session')}\n </h3>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.common.close', 'Close')}\n >\n <X className=\"w-4 h-4 text-text-secondary\" />\n </button>\n </header>\n\n <div className=\"flex-1 min-h-0 overflow-y-auto\">\n {loading && bookmarks.length === 0 && (\n <div className=\"px-4 py-6 text-xs text-text-tertiary\">\n {tr('vibecontrols.common.loading', 'Loading...')}\n </div>\n )}\n\n {!loading && bookmarks.length === 0 && (\n <div className=\"px-4 py-10 flex flex-col items-center text-center gap-2\">\n <Bookmark className=\"w-8 h-8 text-text-muted\" />\n <p className=\"text-sm font-medium text-text-primary\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.empty',\n 'No bookmarks in this session yet'\n )}\n </p>\n <p className=\"text-xs text-text-secondary max-w-xs\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.emptyHint',\n 'Click the bookmark icon on any message to save it for later.'\n )}\n </p>\n </div>\n )}\n\n <ul className=\"divide-y divide-border-subtle\">\n {bookmarks.map((bm: AgentMessageBookmarkFieldsFragment) => {\n const msg = messageById.get(bm.messageId);\n const isStale = !msg;\n const isEditing = editingId === bm.id;\n\n return (\n <li key={bm.id} className=\"px-4 py-3 hover:bg-bg-elevated/40 transition-colors\">\n <div className=\"flex items-start gap-2\">\n <div className=\"flex-1 min-w-0\">\n {isEditing ? (\n <input\n autoFocus\n value={labelDraft}\n onChange={(e) => setLabelDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') void saveEdit(bm.id);\n if (e.key === 'Escape') {\n setEditingId(null);\n setLabelDraft('');\n }\n }}\n placeholder={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.labelPlaceholder',\n 'Add a label (optional)'\n )}\n className=\"w-full bg-bg-sunken border border-border-subtle rounded px-2 py-1 text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-action-primary-bg\"\n />\n ) : (\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {bm.label ||\n (msg?.role === 'user'\n ? tr('vibecontrols.agentManager.bookmarksDrawer.fromUser', 'From you')\n : tr(\n 'vibecontrols.agentManager.bookmarksDrawer.fromAssistant',\n 'Assistant reply'\n ))}\n </p>\n )}\n\n {isStale ? (\n <p className=\"text-xs text-text-tertiary italic mt-1\">\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.stale',\n 'Message no longer available'\n )}\n </p>\n ) : (\n <p className=\"text-xs text-text-secondary mt-1 line-clamp-2\">\n {snippet(msg.content)}\n </p>\n )}\n\n <p className=\"text-[10px] text-text-tertiary mt-1\">\n {relativeTime(bm.createdAt)}\n </p>\n </div>\n\n <div className=\"flex items-center gap-0.5 shrink-0\">\n {!isStale && !isEditing && (\n <button\n type=\"button\"\n onClick={() => {\n onJump(bm.messageId);\n onClose();\n }}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n title={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.jump',\n 'Jump to message'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.jump',\n 'Jump to message'\n )}\n >\n <ChevronRight className=\"w-3.5 h-3.5 text-text-secondary\" />\n </button>\n )}\n\n {isEditing ? (\n <button\n type=\"button\"\n onClick={() => void saveEdit(bm.id)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.common.save', 'Save')}\n >\n <Check className=\"w-3.5 h-3.5 text-status-success-text\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => startEdit(bm.id, bm.label)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr(\n 'vibecontrols.agentManager.bookmarksDrawer.editLabel',\n 'Edit label'\n )}\n >\n <Pencil className=\"w-3.5 h-3.5 text-text-secondary\" />\n </button>\n )}\n\n <button\n type=\"button\"\n onClick={() => void removeBookmark(bm.id)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n aria-label={tr(\n 'vibecontrols.agentManager.messageItem.removeBookmark',\n 'Remove bookmark'\n )}\n >\n <Trash2 className=\"w-3.5 h-3.5 text-status-danger-text\" />\n </button>\n </div>\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n\n <footer className=\"px-4 py-3 border-t border-border-default flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => {\n onClose();\n navigate('/vibecontrols/ai/bookmarks');\n }}\n className=\"inline-flex items-center gap-1.5 text-xs font-medium text-action-primary-text hover:underline\"\n >\n <ExternalLink className=\"w-3 h-3\" />\n {tr(\n 'vibecontrols.agentManager.bookmarksDrawer.seeAll',\n 'See all bookmarks across sessions'\n )}\n </button>\n </footer>\n </aside>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -4,31 +4,52 @@ import { useActiveAgentProfile as n } from "../../hooks/useActiveAgentProfile.js
|
|
|
4
4
|
import { fetchSessionLogs as r } from "../../services/aiApi.js";
|
|
5
5
|
import { useAgentManagerStore as i } from "../../store/agentManagerStore.js";
|
|
6
6
|
import { useAgentManagerStreaming as a } from "../../hooks/useAgentManagerStreaming.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
7
|
+
import { useToggleAgentBookmark as o } from "../../hooks/useToggleAgentBookmark.js";
|
|
8
|
+
import { AgentManagerMessageList as s } from "./AgentManagerMessageList.js";
|
|
9
|
+
import { AgentManagerInputArea as c } from "./AgentManagerInputArea.js";
|
|
10
|
+
import { AgentManagerBookmarksDrawer as l } from "./AgentManagerBookmarksDrawer.js";
|
|
11
|
+
import { PluginInstallModal as ee } from "./PluginInstallModal.js";
|
|
12
|
+
import { AIProviderCredentialsCard as u } from "../ai/AIProviderCredentialsCard.js";
|
|
13
|
+
import { useCallback as d, useEffect as f, useMemo as p, useRef as m, useState as h } from "react";
|
|
14
|
+
import { Bookmark as g, PackagePlus as _, Search as v, X as y } from "lucide-react";
|
|
15
|
+
import { useSearchParams as b } from "react-router-dom";
|
|
16
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
14
17
|
//#region src/components/agent-manager/AgentManagerChatPane.tsx
|
|
15
|
-
var
|
|
16
|
-
let [
|
|
17
|
-
id:
|
|
18
|
-
profile:
|
|
19
|
-
}), [
|
|
20
|
-
agent:
|
|
21
|
-
sessionId:
|
|
22
|
-
}),
|
|
23
|
-
|
|
18
|
+
var C = ({ sessionId: u, agentId: C }) => {
|
|
19
|
+
let [T, E] = h(!1), [D, te] = h(!1), [O, k] = h(""), [A, j] = h(!1), [M, N] = h(!1), [P, F] = h(null), [I, L] = b(), R = I.get("messageId"), z = m(null), B = e(), { agents: V } = t(), H = p(() => V.find((e) => e.id === C) ?? null, [V, C]), U = i((e) => e.sessionStates[u]), W = i((e) => e.addMessage), G = p(() => U?.messages ?? [], [U?.messages]), { active: K } = n(H?.id ?? null), q = p(() => ({
|
|
20
|
+
id: C,
|
|
21
|
+
profile: K
|
|
22
|
+
}), [C, K]), { sendMessage: J, cancelStream: Y, isStreaming: X } = a({
|
|
23
|
+
agent: q,
|
|
24
|
+
sessionId: u
|
|
25
|
+
}), { bookmarkedIds: Z, toggle: Q } = o(u), $ = d((e) => {
|
|
26
|
+
let t = document.querySelector(`[data-message-id="${e}"]`);
|
|
27
|
+
t && (t.scrollIntoView({
|
|
28
|
+
behavior: "smooth",
|
|
29
|
+
block: "center"
|
|
30
|
+
}), F(e), window.setTimeout(() => {
|
|
31
|
+
F((t) => t === e ? null : t);
|
|
32
|
+
}, 1500));
|
|
33
|
+
}, []), ne = d((e) => {
|
|
34
|
+
Q({
|
|
35
|
+
agentId: C,
|
|
36
|
+
sessionId: u,
|
|
37
|
+
messageId: e
|
|
38
|
+
});
|
|
39
|
+
}, [
|
|
40
|
+
Q,
|
|
41
|
+
C,
|
|
42
|
+
u
|
|
43
|
+
]), re = d(() => {
|
|
44
|
+
te((e) => (e && k(""), !e));
|
|
24
45
|
}, []);
|
|
25
|
-
return
|
|
46
|
+
return f(() => {
|
|
26
47
|
let e = !1;
|
|
27
48
|
return (async () => {
|
|
28
|
-
if (!(
|
|
29
|
-
|
|
49
|
+
if (!(G.length > 0)) {
|
|
50
|
+
E(!0);
|
|
30
51
|
try {
|
|
31
|
-
let t = await r(
|
|
52
|
+
let t = await r(q, u, { types: ["input", "output"] });
|
|
32
53
|
if (e || t.length === 0) return;
|
|
33
54
|
let n = /* @__PURE__ */ new Set();
|
|
34
55
|
for (let e of t) {
|
|
@@ -38,7 +59,7 @@ var y = ({ sessionId: l, agentId: y }) => {
|
|
|
38
59
|
e.model ?? "",
|
|
39
60
|
i
|
|
40
61
|
].join("\0");
|
|
41
|
-
n.has(a) || (n.add(a),
|
|
62
|
+
n.has(a) || (n.add(a), W(u, {
|
|
42
63
|
id: e.id,
|
|
43
64
|
role: e.type === "input" ? "user" : "assistant",
|
|
44
65
|
content: t,
|
|
@@ -51,116 +72,150 @@ var y = ({ sessionId: l, agentId: y }) => {
|
|
|
51
72
|
}));
|
|
52
73
|
}
|
|
53
74
|
} catch {} finally {
|
|
54
|
-
e ||
|
|
75
|
+
e || E(!1);
|
|
55
76
|
}
|
|
56
77
|
}
|
|
57
78
|
})(), () => {
|
|
58
79
|
e = !0;
|
|
59
80
|
};
|
|
60
|
-
}, [
|
|
81
|
+
}, [C, u]), f(() => {
|
|
82
|
+
if (!R || T || z.current === R || !G.some((e) => e.id === R)) return;
|
|
83
|
+
z.current = R, $(R);
|
|
84
|
+
let e = new URLSearchParams(I);
|
|
85
|
+
e.delete("messageId"), L(e, { replace: !0 });
|
|
86
|
+
}, [
|
|
87
|
+
R,
|
|
88
|
+
T,
|
|
89
|
+
G,
|
|
90
|
+
$,
|
|
91
|
+
I,
|
|
92
|
+
L
|
|
93
|
+
]), /* @__PURE__ */ S("div", {
|
|
61
94
|
className: "flex flex-col h-full min-h-0",
|
|
62
95
|
"data-testid": "agent-manager-chat-pane",
|
|
63
96
|
children: [
|
|
64
|
-
/* @__PURE__ */
|
|
97
|
+
/* @__PURE__ */ S("div", {
|
|
65
98
|
className: "flex items-center gap-1 px-3 py-1 border-b border-border-default bg-bg-surface flex-shrink-0",
|
|
66
|
-
children: [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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", {
|
|
76
|
-
className: "flex items-center gap-1.5 flex-1 min-w-0",
|
|
77
|
-
children: [/* @__PURE__ */ _("input", {
|
|
78
|
-
type: "text",
|
|
79
|
-
value: T,
|
|
80
|
-
onChange: (e) => E(e.target.value),
|
|
81
|
-
placeholder: k("vibecontrols.agentManager.chatPane.searchPlaceholder", "Search in conversation..."),
|
|
82
|
-
autoFocus: !0,
|
|
83
|
-
className: "flex-1 min-w-0 bg-transparent text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none"
|
|
84
|
-
}), T && /* @__PURE__ */ _("button", {
|
|
99
|
+
children: [
|
|
100
|
+
/* @__PURE__ */ S("div", {
|
|
101
|
+
className: "flex items-center gap-1.5 min-w-0 flex-1",
|
|
102
|
+
children: [/* @__PURE__ */ x("button", {
|
|
85
103
|
type: "button",
|
|
86
|
-
onClick:
|
|
87
|
-
className:
|
|
88
|
-
|
|
104
|
+
onClick: re,
|
|
105
|
+
className: `p-1 rounded transition-colors shrink-0 ${D ? "bg-action-primary-bg/10 text-action-primary-text" : "text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated"}`,
|
|
106
|
+
title: B("vibecontrols.agentManager.chatPane.searchInSession", "Search in session (Ctrl+F)"),
|
|
107
|
+
"aria-label": B("vibecontrols.agentManager.chatPane.searchInSession", "Search in session"),
|
|
108
|
+
children: /* @__PURE__ */ x(v, { className: "w-3.5 h-3.5" })
|
|
109
|
+
}), D && /* @__PURE__ */ S("div", {
|
|
110
|
+
className: "flex items-center gap-1.5 flex-1 min-w-0",
|
|
111
|
+
children: [/* @__PURE__ */ x("input", {
|
|
112
|
+
type: "text",
|
|
113
|
+
value: O,
|
|
114
|
+
onChange: (e) => k(e.target.value),
|
|
115
|
+
placeholder: B("vibecontrols.agentManager.chatPane.searchPlaceholder", "Search in conversation..."),
|
|
116
|
+
autoFocus: !0,
|
|
117
|
+
className: "flex-1 min-w-0 bg-transparent text-xs text-text-primary placeholder:text-text-tertiary focus:outline-none"
|
|
118
|
+
}), O && /* @__PURE__ */ x("button", {
|
|
119
|
+
type: "button",
|
|
120
|
+
onClick: () => k(""),
|
|
121
|
+
className: "p-0.5 rounded hover:bg-bg-elevated transition-colors",
|
|
122
|
+
children: /* @__PURE__ */ x(y, { className: "w-3 h-3 text-text-tertiary" })
|
|
123
|
+
})]
|
|
89
124
|
})]
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
125
|
+
}),
|
|
126
|
+
/* @__PURE__ */ x("button", {
|
|
127
|
+
type: "button",
|
|
128
|
+
onClick: () => N(!0),
|
|
129
|
+
className: "shrink-0 p-1 rounded transition-colors text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated",
|
|
130
|
+
title: B("vibecontrols.agentManager.chatPane.bookmarksTooltip", "View bookmarked messages"),
|
|
131
|
+
"aria-label": B("vibecontrols.agentManager.chatPane.bookmarksLabel", "Bookmarks"),
|
|
132
|
+
"data-testid": "agent-manager-bookmarks-trigger",
|
|
133
|
+
children: /* @__PURE__ */ x(g, { className: "w-3.5 h-3.5" })
|
|
134
|
+
}),
|
|
135
|
+
/* @__PURE__ */ S("button", {
|
|
136
|
+
type: "button",
|
|
137
|
+
onClick: () => j(!0),
|
|
138
|
+
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",
|
|
139
|
+
title: B("vibecontrols.agentManager.chatPane.manageProvidersTooltip", "Install or remove AI providers (Claude, Codex, Gemini, OpenCode)"),
|
|
140
|
+
"aria-label": B("vibecontrols.agentManager.chatPane.manageProviders", "Manage AI providers"),
|
|
141
|
+
"data-testid": "agent-manager-manage-providers",
|
|
142
|
+
children: [/* @__PURE__ */ x(_, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ x("span", {
|
|
143
|
+
className: "hidden sm:inline",
|
|
144
|
+
children: B("vibecontrols.agentManager.chatPane.manageProviders", "Manage AI providers")
|
|
145
|
+
})]
|
|
146
|
+
})
|
|
147
|
+
]
|
|
103
148
|
}),
|
|
104
|
-
|
|
149
|
+
T ? /* @__PURE__ */ x("div", {
|
|
105
150
|
className: "flex-1 flex items-center justify-center",
|
|
106
|
-
children: /* @__PURE__ */
|
|
151
|
+
children: /* @__PURE__ */ x("p", {
|
|
107
152
|
className: "text-sm text-text-secondary",
|
|
108
|
-
children:
|
|
153
|
+
children: B("vibecontrols.agentManager.chatPane.loadingConversation", "Loading conversation...")
|
|
109
154
|
})
|
|
110
|
-
}) : /* @__PURE__ */
|
|
111
|
-
messages:
|
|
112
|
-
isStreaming:
|
|
113
|
-
onCancelStream:
|
|
114
|
-
searchQuery:
|
|
155
|
+
}) : /* @__PURE__ */ x(s, {
|
|
156
|
+
messages: G,
|
|
157
|
+
isStreaming: X,
|
|
158
|
+
onCancelStream: Y,
|
|
159
|
+
searchQuery: O,
|
|
160
|
+
bookmarkedIds: Z,
|
|
161
|
+
onToggleBookmark: ne,
|
|
162
|
+
flashMessageId: P
|
|
163
|
+
}),
|
|
164
|
+
/* @__PURE__ */ x(c, {
|
|
165
|
+
agentId: C,
|
|
166
|
+
sessionId: u,
|
|
167
|
+
onSend: (e) => void J(e),
|
|
168
|
+
onCancel: Y,
|
|
169
|
+
agentTunnelUrl: H?.tunnelUrl ?? void 0,
|
|
170
|
+
agentApiKey: H?.agentApiKey ?? void 0
|
|
115
171
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
agentApiKey: j?.agentApiKey ?? void 0
|
|
172
|
+
/* @__PURE__ */ x(l, {
|
|
173
|
+
open: M,
|
|
174
|
+
onClose: () => N(!1),
|
|
175
|
+
sessionId: u,
|
|
176
|
+
messages: G,
|
|
177
|
+
onJump: $
|
|
123
178
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
125
|
-
open:
|
|
126
|
-
agentId:
|
|
127
|
-
onClose: () =>
|
|
179
|
+
/* @__PURE__ */ x(ee, {
|
|
180
|
+
open: A,
|
|
181
|
+
agentId: C,
|
|
182
|
+
onClose: () => j(!1)
|
|
128
183
|
}),
|
|
129
|
-
/* @__PURE__ */
|
|
130
|
-
agentId:
|
|
131
|
-
agentRef:
|
|
132
|
-
initialProviderId:
|
|
184
|
+
/* @__PURE__ */ x(w, {
|
|
185
|
+
agentId: C,
|
|
186
|
+
agentRef: q,
|
|
187
|
+
initialProviderId: U?.sdk ?? void 0
|
|
133
188
|
})
|
|
134
189
|
]
|
|
135
190
|
});
|
|
136
|
-
},
|
|
191
|
+
}, w = ({ agentId: t, agentRef: n, initialProviderId: r }) => {
|
|
137
192
|
let a = i((e) => e.sdkAuthPrompt), o = i((e) => e.setSdkAuthPrompt), s = e();
|
|
138
|
-
return !a || a.agentId !== t ? null : /* @__PURE__ */
|
|
193
|
+
return !a || a.agentId !== t ? null : /* @__PURE__ */ S("div", {
|
|
139
194
|
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
140
|
-
children: [/* @__PURE__ */
|
|
195
|
+
children: [/* @__PURE__ */ x("div", {
|
|
141
196
|
className: "absolute inset-0 bg-bg-overlay",
|
|
142
197
|
onClick: () => o(null),
|
|
143
198
|
"aria-hidden": !0
|
|
144
|
-
}), /* @__PURE__ */
|
|
199
|
+
}), /* @__PURE__ */ S("div", {
|
|
145
200
|
className: "relative w-full max-w-xl max-h-[85vh] flex flex-col rounded-lg border border-border-default bg-bg-surface shadow-xl overflow-hidden",
|
|
146
|
-
children: [/* @__PURE__ */
|
|
201
|
+
children: [/* @__PURE__ */ S("div", {
|
|
147
202
|
className: "flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-surface flex-shrink-0",
|
|
148
|
-
children: [/* @__PURE__ */
|
|
203
|
+
children: [/* @__PURE__ */ x("h3", {
|
|
149
204
|
className: "text-base font-semibold text-text-primary truncate pr-2",
|
|
150
205
|
children: s("vibecontrols.ai.sdkAuth.title", "Configure AI provider credentials")
|
|
151
|
-
}), /* @__PURE__ */
|
|
206
|
+
}), /* @__PURE__ */ x("button", {
|
|
152
207
|
type: "button",
|
|
153
208
|
onClick: () => o(null),
|
|
154
209
|
className: "p-1 rounded hover:bg-bg-sunken transition-colors flex-shrink-0",
|
|
155
210
|
"aria-label": s("vibecontrols.common.close", "Close"),
|
|
156
|
-
children: /* @__PURE__ */
|
|
211
|
+
children: /* @__PURE__ */ x(y, { className: "w-5 h-5 text-text-secondary" })
|
|
157
212
|
})]
|
|
158
|
-
}), /* @__PURE__ */
|
|
213
|
+
}), /* @__PURE__ */ S("div", {
|
|
159
214
|
className: "flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-3",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
215
|
+
children: [/* @__PURE__ */ x("p", {
|
|
161
216
|
className: "text-sm text-text-secondary",
|
|
162
217
|
children: s("vibecontrols.ai.sdkAuth.body", "The agent does not have a credential configured for this provider. Pick a provider and save one to continue the conversation.")
|
|
163
|
-
}), /* @__PURE__ */
|
|
218
|
+
}), /* @__PURE__ */ x(u, {
|
|
164
219
|
agent: n,
|
|
165
220
|
initialProviderId: r
|
|
166
221
|
})]
|
|
@@ -169,6 +224,6 @@ var y = ({ sessionId: l, agentId: y }) => {
|
|
|
169
224
|
});
|
|
170
225
|
};
|
|
171
226
|
//#endregion
|
|
172
|
-
export {
|
|
227
|
+
export { C as AgentManagerChatPane };
|
|
173
228
|
|
|
174
229
|
//# 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 { 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 The session's currently-selected SDK is forwarded so the picker\n can preselect the right provider. */}\n <SdkAuthPromptModal\n agentId={agentId}\n agentRef={agentRef}\n initialProviderId={sessionState?.sdk ?? undefined}\n />\n </div>\n );\n};\n\ninterface SdkAuthPromptModalProps {\n agentId: string;\n agentRef: { id: string; profile: string };\n initialProviderId?: string;\n}\n\nconst SdkAuthPromptModal: FC<SdkAuthPromptModalProps> = ({\n agentId,\n agentRef,\n initialProviderId,\n}) => {\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 p-4\">\n <div\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={() => setSdkAuthPrompt(null)}\n aria-hidden\n />\n {/* Constrain height to the viewport and let the inner body scroll so the\n modal never escapes the screen no matter how many providers/fields\n render inside AIProviderCredentialsCard. The header stays sticky so\n the close button is always reachable. */}\n <div className=\"relative w-full max-w-xl max-h-[85vh] flex flex-col rounded-lg border border-border-default bg-bg-surface shadow-xl overflow-hidden\">\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <h3 className=\"text-base font-semibold text-text-primary truncate pr-2\">\n {tr('vibecontrols.ai.sdkAuth.title', 'Configure AI provider credentials')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setSdkAuthPrompt(null)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors flex-shrink-0\"\n aria-label={tr('vibecontrols.common.close', 'Close')}\n >\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.ai.sdkAuth.body',\n 'The agent does not have a credential configured for this provider. Pick a provider and save one to continue the conversation.'\n )}\n </p>\n <AIProviderCredentialsCard agent={agentRef} initialProviderId={initialProviderId} />\n </div>\n </div>\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, useRef, useState, useCallback, useMemo, type FC } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { Bookmark, 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 { useToggleAgentBookmark } from '@/hooks/useToggleAgentBookmark';\nimport { AgentManagerMessageList } from './AgentManagerMessageList';\nimport { AgentManagerInputArea } from './AgentManagerInputArea';\nimport { AgentManagerBookmarksDrawer } from './AgentManagerBookmarksDrawer';\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 [bookmarksDrawerOpen, setBookmarksDrawerOpen] = useState(false);\n const [flashMessageId, setFlashMessageId] = useState<string | null>(null);\n const [searchParams, setSearchParams] = useSearchParams();\n const pendingScrollMessageId = searchParams.get('messageId');\n const consumedScrollTargetRef = useRef<string | null>(null);\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 = useMemo(() => sessionState?.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 { bookmarkedIds, toggle: toggleBookmark } = useToggleAgentBookmark(sessionId);\n\n const jumpToMessage = useCallback((messageId: string) => {\n const el = document.querySelector(`[data-message-id=\"${messageId}\"]`);\n if (!el) return;\n el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n setFlashMessageId(messageId);\n window.setTimeout(() => {\n setFlashMessageId((current) => (current === messageId ? null : current));\n }, 1500);\n }, []);\n\n const handleToggleBookmark = useCallback(\n (messageId: string) => {\n void toggleBookmark({ agentId, sessionId, messageId });\n },\n [toggleBookmark, agentId, 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 // Auto-scroll to a message linked via ?messageId=... (from drawer / cross-session page).\n // Waits for logs to finish loading and the target to be in the DOM before scrolling.\n useEffect(() => {\n if (!pendingScrollMessageId || loadingLogs) return;\n if (consumedScrollTargetRef.current === pendingScrollMessageId) return;\n\n const exists = messages.some((m) => m.id === pendingScrollMessageId);\n if (!exists) return;\n\n consumedScrollTargetRef.current = pendingScrollMessageId;\n jumpToMessage(pendingScrollMessageId);\n\n // Strip the query param so a manual refresh doesn't re-trigger the flash.\n const next = new URLSearchParams(searchParams);\n next.delete('messageId');\n setSearchParams(next, { replace: true });\n }, [pendingScrollMessageId, loadingLogs, messages, jumpToMessage, searchParams, setSearchParams]);\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={() => setBookmarksDrawerOpen(true)}\n className=\"shrink-0 p-1 rounded transition-colors text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated\"\n title={tr(\n 'vibecontrols.agentManager.chatPane.bookmarksTooltip',\n 'View bookmarked messages'\n )}\n aria-label={tr('vibecontrols.agentManager.chatPane.bookmarksLabel', 'Bookmarks')}\n data-testid=\"agent-manager-bookmarks-trigger\"\n >\n <Bookmark className=\"w-3.5 h-3.5\" />\n </button>\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 bookmarkedIds={bookmarkedIds}\n onToggleBookmark={handleToggleBookmark}\n flashMessageId={flashMessageId}\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 {/* Per-session bookmarks drawer */}\n <AgentManagerBookmarksDrawer\n open={bookmarksDrawerOpen}\n onClose={() => setBookmarksDrawerOpen(false)}\n sessionId={sessionId}\n messages={messages}\n onJump={jumpToMessage}\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 The session's currently-selected SDK is forwarded so the picker\n can preselect the right provider. */}\n <SdkAuthPromptModal\n agentId={agentId}\n agentRef={agentRef}\n initialProviderId={sessionState?.sdk ?? undefined}\n />\n </div>\n );\n};\n\ninterface SdkAuthPromptModalProps {\n agentId: string;\n agentRef: { id: string; profile: string };\n initialProviderId?: string;\n}\n\nconst SdkAuthPromptModal: FC<SdkAuthPromptModalProps> = ({\n agentId,\n agentRef,\n initialProviderId,\n}) => {\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 p-4\">\n <div\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={() => setSdkAuthPrompt(null)}\n aria-hidden\n />\n {/* Constrain height to the viewport and let the inner body scroll so the\n modal never escapes the screen no matter how many providers/fields\n render inside AIProviderCredentialsCard. The header stays sticky so\n the close button is always reachable. */}\n <div className=\"relative w-full max-w-xl max-h-[85vh] flex flex-col rounded-lg border border-border-default bg-bg-surface shadow-xl overflow-hidden\">\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <h3 className=\"text-base font-semibold text-text-primary truncate pr-2\">\n {tr('vibecontrols.ai.sdkAuth.title', 'Configure AI provider credentials')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setSdkAuthPrompt(null)}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors flex-shrink-0\"\n aria-label={tr('vibecontrols.common.close', 'Close')}\n >\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.ai.sdkAuth.body',\n 'The agent does not have a credential configured for this provider. Pick a provider and save one to continue the conversation.'\n )}\n </p>\n <AIProviderCredentialsCard agent={agentRef} initialProviderId={initialProviderId} />\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AA8BA,IAAa,KAAuD,EAAE,cAAW,iBAAc;CAC7F,IAAM,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAY,MAAiB,EAAS,GAAM,EAC7C,CAAC,GAAa,KAAkB,EAAS,GAAG,EAG5C,CAAC,GAAkB,KAAuB,EAAS,GAAM,EACzD,CAAC,GAAqB,KAA0B,EAAS,GAAM,EAC/D,CAAC,GAAgB,KAAqB,EAAwB,KAAK,EACnE,CAAC,GAAc,KAAmB,GAAiB,EACnD,IAAyB,EAAa,IAAI,YAAY,EACtD,IAA0B,EAAsB,KAAK,EACrD,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,QAAc,GAAc,YAAY,EAAE,EAAE,CAAC,GAAc,SAAS,CAAC,EAEhF,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,EAAE,kBAAe,QAAQ,MAAmB,EAAuB,EAAU,EAE7E,IAAgB,GAAa,MAAsB;EACvD,IAAM,IAAK,SAAS,cAAc,qBAAqB,EAAU,IAAI;AAChE,QACL,EAAG,eAAe;GAAE,UAAU;GAAU,OAAO;GAAU,CAAC,EAC1D,EAAkB,EAAU,EAC5B,OAAO,iBAAiB;AACtB,MAAmB,MAAa,MAAY,IAAY,OAAO,EAAS;KACvE,KAAK;IACP,EAAE,CAAC,EAEA,KAAuB,GAC1B,MAAsB;AAChB,IAAe;GAAE;GAAS;GAAW;GAAW,CAAC;IAExD;EAAC;EAAgB;EAAS;EAAU,CACrC,EAEK,KAAe,QAAkB;AACrC,MAAe,OACT,KAAM,EAAe,GAAG,EACrB,CAAC,GACR;IACD,EAAE,CAAC;AA0EN,QAvEA,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,EAIxB,QAAgB;AAKd,MAJI,CAAC,KAA0B,KAC3B,EAAwB,YAAY,KAGpC,CADW,EAAS,MAAM,MAAM,EAAE,OAAO,EAAuB,CACvD;AAGb,EADA,EAAwB,UAAU,GAClC,EAAc,EAAuB;EAGrC,IAAM,IAAO,IAAI,gBAAgB,EAAa;AAE9C,EADA,EAAK,OAAO,YAAY,EACxB,EAAgB,GAAM,EAAE,SAAS,IAAM,CAAC;IACvC;EAAC;EAAwB;EAAa;EAAU;EAAe;EAAc;EAAgB,CAAC,EAG/F,kBAAC,OAAD;EAAK,WAAU;EAA+B,eAAY;YAA1D;GAKE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAW,0CACT,IACI,qDACA;OAEN,OAAO,EACL,sDACA,6BACD;OACD,cAAY,EACV,sDACA,oBACD;iBAED,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;OAC3B,CAAA,EACR,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QACE,MAAK;QACL,OAAO;QACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;QAC/C,aAAa,EACX,wDACA,4BACD;QACD,WAAA;QACA,WAAU;QACV,CAAA,EACD,KACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAe,GAAG;QACjC,WAAU;kBAEV,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;QACrC,CAAA,CAEP;SAEJ;;KACN,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAuB,GAAK;MAC3C,WAAU;MACV,OAAO,EACL,uDACA,2BACD;MACD,cAAY,EAAG,qDAAqD,YAAY;MAChF,eAAY;gBAEZ,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA;MAC7B,CAAA;KACT,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAoB,GAAK;MACxC,WAAU;MACV,OAAO,EACL,6DACA,mEACD;MACD,cAAY,EACV,sDACA,sBACD;MACD,eAAY;gBAZd,CAcE,kBAAC,GAAD,EAAa,WAAU,eAAgB,CAAA,EACvC,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAG,sDAAsD,sBAAsB;OAC3E,CAAA,CACA;;KACL;;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;IACE;IACf,kBAAkB;IACF;IAChB,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;GAGF,kBAAC,GAAD;IACE,MAAM;IACN,eAAe,EAAuB,GAAM;IACjC;IACD;IACV,QAAQ;IACR,CAAA;GAIF,kBAAC,IAAD;IACE,MAAM;IACG;IACT,eAAe,EAAoB,GAAM;IACzC,CAAA;GAOF,kBAAC,GAAD;IACW;IACC;IACV,mBAAmB,GAAc,OAAO,KAAA;IACxC,CAAA;GACE;;GAUJ,KAAmD,EACvD,YACA,aACA,2BACI;CACJ,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,EAKF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,iCAAiC,oCAAoC;KACtE,CAAA,EACL,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAiB,KAAK;KACrC,WAAU;KACV,cAAY,EAAG,6BAA6B,QAAQ;eAEpD,kBAAC,GAAD,EAAG,WAAU,+BAAgC,CAAA;KACtC,CAAA,CACL;OACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,gCACA,gIACD;KACC,CAAA,EACJ,kBAAC,GAAD;KAA2B,OAAO;KAA6B;KAAqB,CAAA,CAChF;MACF;KACF"}
|