@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
|
@@ -75,13 +75,19 @@ function b({ agent: b, onDeleteSession: x, filterTags: S, dateFrom: C, dateTo: w
|
|
|
75
75
|
j,
|
|
76
76
|
F,
|
|
77
77
|
W
|
|
78
|
-
]);
|
|
78
|
+
]), Y = i((e) => e.sessionsRefreshTrigger);
|
|
79
79
|
s(() => {
|
|
80
80
|
let e = setTimeout(() => {
|
|
81
81
|
q(W);
|
|
82
82
|
}, 300);
|
|
83
83
|
return () => clearTimeout(e);
|
|
84
84
|
}, [W, q]), s(() => {
|
|
85
|
+
Y !== 0 && q(W);
|
|
86
|
+
}, [
|
|
87
|
+
Y,
|
|
88
|
+
q,
|
|
89
|
+
W
|
|
90
|
+
]), s(() => {
|
|
85
91
|
let e = new Set(V.map((e) => e.sessionId));
|
|
86
92
|
for (let t of T) (t.id === B || e.has(t.id)) && H(t.id, t);
|
|
87
93
|
}, [
|
|
@@ -97,7 +103,7 @@ function b({ agent: b, onDeleteSession: x, filterTags: S, dateFrom: C, dateTo: w
|
|
|
97
103
|
}, { threshold: .1 });
|
|
98
104
|
return t.observe(e), () => t.disconnect();
|
|
99
105
|
}, [J]);
|
|
100
|
-
let
|
|
106
|
+
let X = c(() => {
|
|
101
107
|
let e = T;
|
|
102
108
|
if (S && S.length > 0 && (e = e.filter((e) => {
|
|
103
109
|
let t = e.config?.tags;
|
|
@@ -116,7 +122,7 @@ function b({ agent: b, onDeleteSession: x, filterTags: S, dateFrom: C, dateTo: w
|
|
|
116
122
|
S,
|
|
117
123
|
C,
|
|
118
124
|
w
|
|
119
|
-
]),
|
|
125
|
+
]), Z = c(() => y(X, G), [X, G]), Q = (e) => {
|
|
120
126
|
R((t) => {
|
|
121
127
|
let n = new Set(t);
|
|
122
128
|
return n.has(e) ? n.delete(e) : n.add(e), n;
|
|
@@ -141,7 +147,7 @@ function b({ agent: b, onDeleteSession: x, filterTags: S, dateFrom: C, dateTo: w
|
|
|
141
147
|
className: "text-xs text-action-primary-bg hover:underline",
|
|
142
148
|
children: K("vibecontrols.agentManager.sessionList.retry", "Retry")
|
|
143
149
|
})]
|
|
144
|
-
}) :
|
|
150
|
+
}) : X.length === 0 ? /* @__PURE__ */ g("div", {
|
|
145
151
|
className: "p-4",
|
|
146
152
|
children: /* @__PURE__ */ g(n, {
|
|
147
153
|
title: W ? K("vibecontrols.agentManager.sessionList.noMatchingSessions", "No matching sessions") : K("vibecontrols.agentManager.sessionList.noSessionsYet", "No sessions yet"),
|
|
@@ -149,10 +155,10 @@ function b({ agent: b, onDeleteSession: x, filterTags: S, dateFrom: C, dateTo: w
|
|
|
149
155
|
})
|
|
150
156
|
}) : /* @__PURE__ */ _("div", {
|
|
151
157
|
className: "flex flex-col",
|
|
152
|
-
children: [
|
|
158
|
+
children: [Z.map(([e, t]) => {
|
|
153
159
|
let n = L.has(e);
|
|
154
160
|
return /* @__PURE__ */ _("div", { children: [G !== "none" && e !== "" && /* @__PURE__ */ _("button", {
|
|
155
|
-
onClick: () =>
|
|
161
|
+
onClick: () => Q(e),
|
|
156
162
|
className: "flex items-center gap-1 w-full px-3 py-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary hover:text-text-secondary transition-colors",
|
|
157
163
|
children: [
|
|
158
164
|
g(n ? f : d, { className: "w-3 h-3" }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentManagerSessionList.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerSessionList.tsx"],"sourcesContent":["/**\n * Renders the list of sessions in the sidebar.\n *\n * Fetches sessions via aiApi, groups them based on the sidebar groupBy\n * setting, and renders each as an AgentManagerSessionItem.\n */\n\nimport { useState, useEffect, useRef, useMemo, useCallback } from 'react';\nimport { AnimatePresence } from 'framer-motion';\nimport { ChevronDown, ChevronRight, Loader2 } from 'lucide-react';\nimport { LoadingState, EmptyState } from '@/components/shared';\nimport { fetchSessionsPaginated } from '@/services/aiApi';\nimport type { AISession, AgentRef } from '@/services/aiApi';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { AgentManagerSessionItem } from './AgentManagerSessionItem';\nimport type { SidebarGroupBy } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\nconst PAGE_SIZE = 10;\n\ninterface AgentManagerSessionListProps {\n agent: AgentRef;\n onDeleteSession: (sessionId: string) => void;\n filterTags?: string[];\n dateFrom?: string;\n dateTo?: string;\n}\n\n/** Group sessions by the given key. Returns array of [groupLabel, sessions] pairs. */\nfunction groupSessions(\n sessions: AISession[],\n groupBy: SidebarGroupBy\n): Array<[string, AISession[]]> {\n if (groupBy === 'none') {\n return [['', sessions]];\n }\n\n const groups = new Map<string, AISession[]>();\n\n for (const session of sessions) {\n let key: string;\n switch (groupBy) {\n case 'date': {\n if (!session.createdAt) {\n key = 'Unknown';\n } else {\n const d = new Date(session.createdAt);\n const now = new Date();\n const diffMs = now.getTime() - d.getTime();\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n if (diffDays === 0) key = 'Today';\n else if (diffDays === 1) key = 'Yesterday';\n else if (diffDays < 7) key = 'This Week';\n else if (diffDays < 30) key = 'This Month';\n else key = 'Older';\n }\n break;\n }\n case 'model':\n key = session.agentType || 'Unknown';\n break;\n case 'status':\n key = session.status.charAt(0).toUpperCase() + session.status.slice(1);\n break;\n case 'sdk':\n key = session.providerPlugin ?? session.agentType ?? 'Unknown';\n break;\n default:\n key = 'All';\n }\n\n const existing = groups.get(key);\n if (existing) {\n existing.push(session);\n } else {\n groups.set(key, [session]);\n }\n }\n\n return Array.from(groups.entries());\n}\n\nexport function AgentManagerSessionList({\n agent,\n onDeleteSession,\n filterTags,\n dateFrom,\n dateTo,\n}: AgentManagerSessionListProps) {\n const [sessions, setSessions] = useState<AISession[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [hasMore, setHasMore] = useState(false);\n const [isLoadingMore, setIsLoadingMore] = useState(false);\n const [offset, setOffset] = useState(0);\n const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set());\n const sentinelRef = useRef<HTMLDivElement>(null);\n\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const tabs = useAgentManagerStore((s) => s.tabs);\n const hydrateSessionConfig = useAgentManagerStore((s) => s.hydrateSessionConfig);\n const selectedSessionIds = useAgentManagerStore((s) => s.selectedSessionIds);\n const sidebarSearchQuery = useAgentManagerStore((s) => s.sidebarSearchQuery);\n const sidebarGroupBy = useAgentManagerStore((s) => s.sidebarGroupBy);\n const tr = useTr();\n\n const loadSessions = useCallback(\n async (search: string) => {\n setLoading(true);\n setError(null);\n setOffset(0);\n try {\n const data = await fetchSessionsPaginated(agent, {\n limit: PAGE_SIZE,\n offset: 0,\n search: search || undefined,\n });\n setSessions(data.items);\n setHasMore(data.hasMore);\n setOffset(PAGE_SIZE);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load sessions');\n } finally {\n setLoading(false);\n }\n },\n [agent]\n );\n\n const loadMore = useCallback(async () => {\n if (isLoadingMore || !hasMore) return;\n setIsLoadingMore(true);\n try {\n const data = await fetchSessionsPaginated(agent, {\n limit: PAGE_SIZE,\n offset,\n search: sidebarSearchQuery || undefined,\n });\n setSessions((prev) => [...prev, ...data.items]);\n setHasMore(data.hasMore);\n setOffset((prev) => prev + PAGE_SIZE);\n } catch {\n // Silently fail on load-more — user can scroll up to retry\n } finally {\n setIsLoadingMore(false);\n }\n }, [agent, isLoadingMore, hasMore, offset, sidebarSearchQuery]);\n\n // Debounced reload when search query changes\n useEffect(() => {\n const timer = setTimeout(() => {\n void loadSessions(sidebarSearchQuery);\n }, 300);\n return () => clearTimeout(timer);\n }, [sidebarSearchQuery, loadSessions]);\n\n useEffect(() => {\n const openSessionIds = new Set(tabs.map((tab) => tab.sessionId));\n for (const session of sessions) {\n if (session.id === activeTabSessionId || openSessionIds.has(session.id)) {\n hydrateSessionConfig(session.id, session);\n }\n }\n }, [activeTabSessionId, hydrateSessionConfig, sessions, tabs]);\n\n // IntersectionObserver to auto-trigger loadMore when sentinel is visible\n useEffect(() => {\n const sentinel = sentinelRef.current;\n if (!sentinel) return;\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting) {\n void loadMore();\n }\n },\n { threshold: 0.1 }\n );\n observer.observe(sentinel);\n return () => observer.disconnect();\n }, [loadMore]);\n\n /** Filter sessions by tags and date range (client-side; search is backend-driven) */\n const filteredSessions = useMemo(() => {\n let result = sessions;\n\n // Tag filter\n if (filterTags && filterTags.length > 0) {\n result = result.filter((s) => {\n const sessionTags = (s.config as Record<string, unknown>)?.tags as string[] | undefined;\n if (!sessionTags || !Array.isArray(sessionTags)) return false;\n return filterTags.some((ft) =>\n sessionTags.some((st) => st.toLowerCase().includes(ft.toLowerCase()))\n );\n });\n }\n\n // Date range filter\n if (dateFrom) {\n const from = new Date(dateFrom);\n result = result.filter((s) => {\n if (!s.createdAt) return false;\n return new Date(s.createdAt) >= from;\n });\n }\n if (dateTo) {\n const to = new Date(dateTo);\n to.setHours(23, 59, 59, 999);\n result = result.filter((s) => {\n if (!s.createdAt) return false;\n return new Date(s.createdAt) <= to;\n });\n }\n\n return result;\n }, [sessions, filterTags, dateFrom, dateTo]);\n\n /** Grouped sessions */\n const grouped = useMemo(\n () => groupSessions(filteredSessions, sidebarGroupBy),\n [filteredSessions, sidebarGroupBy]\n );\n\n const toggleGroup = (groupLabel: string) => {\n setCollapsedGroups((prev) => {\n const next = new Set(prev);\n if (next.has(groupLabel)) {\n next.delete(groupLabel);\n } else {\n next.add(groupLabel);\n }\n return next;\n });\n };\n\n if (loading) {\n return (\n <div className=\"p-4\">\n <LoadingState\n message={tr(\n 'vibecontrols.agentManager.sessionList.loadingSessions',\n 'Loading sessions...'\n )}\n />\n </div>\n );\n }\n\n if (error) {\n const isSchemaError = error.includes('Cannot query field') || error.includes('agentAiGet');\n // NOT_FOUND in the sessions fetch means the AI plugin's route isn't\n // mounted yet (usually transient — plugin loads a moment after the\n // agent reports ready). Plugin install is already gated by\n // <PluginGate> above this component, so showing a CLI-install hint\n // here is both misleading (the plugin may already be installed) and\n // redundant. Just offer a retry.\n return (\n <div className=\"p-4 space-y-2\">\n {isSchemaError ? (\n <>\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.sessionList.proxyNotAvailable',\n 'Agent AI proxy not available yet. The backend schema may need to be published to GraphQL Hive.'\n )}\n </p>\n <p className=\"text-xs text-text-tertiary\">\n {tr(\n 'vibecontrols.agentManager.sessionList.sessionsWillLoad',\n 'Sessions will load once the gateway schema is updated.'\n )}\n </p>\n </>\n ) : (\n <p className=\"text-xs text-status-error-text\">{error}</p>\n )}\n <button\n onClick={() => void loadSessions(sidebarSearchQuery)}\n className=\"text-xs text-action-primary-bg hover:underline\"\n >\n {tr('vibecontrols.agentManager.sessionList.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n if (filteredSessions.length === 0) {\n return (\n <div className=\"p-4\">\n <EmptyState\n title={\n sidebarSearchQuery\n ? tr(\n 'vibecontrols.agentManager.sessionList.noMatchingSessions',\n 'No matching sessions'\n )\n : tr('vibecontrols.agentManager.sessionList.noSessionsYet', 'No sessions yet')\n }\n description={\n sidebarSearchQuery\n ? tr(\n 'vibecontrols.agentManager.sessionList.tryDifferentSearch',\n 'Try a different search query'\n )\n : tr(\n 'vibecontrols.agentManager.sessionList.createToGetStarted',\n 'Create a new session to get started'\n )\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col\">\n {grouped.map(([groupLabel, groupSessions]) => {\n const isCollapsed = collapsedGroups.has(groupLabel);\n const showHeader = sidebarGroupBy !== 'none' && groupLabel !== '';\n\n return (\n <div key={groupLabel || '__ungrouped'}>\n {showHeader && (\n <button\n onClick={() => toggleGroup(groupLabel)}\n className=\"flex items-center gap-1 w-full px-3 py-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary hover:text-text-secondary transition-colors\"\n >\n {isCollapsed ? (\n <ChevronRight className=\"w-3 h-3\" />\n ) : (\n <ChevronDown className=\"w-3 h-3\" />\n )}\n {groupLabel}\n <span className=\"ml-auto text-text-tertiary font-normal\">\n {groupSessions.length}\n </span>\n </button>\n )}\n\n {!isCollapsed && (\n <AnimatePresence mode=\"popLayout\">\n {groupSessions.map((session) => (\n <AgentManagerSessionItem\n key={session.id}\n session={session}\n agent={agent}\n isActive={activeTabSessionId === session.id}\n isSelected={selectedSessionIds.includes(session.id)}\n onDelete={onDeleteSession}\n />\n ))}\n </AnimatePresence>\n )}\n </div>\n );\n })}\n\n {/* Sentinel for infinite scroll */}\n <div ref={sentinelRef} className=\"py-1\">\n {isLoadingMore && (\n <div className=\"flex items-center justify-center py-2\">\n <Loader2 className=\"w-4 h-4 animate-spin text-text-tertiary\" />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentManagerSessionList.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerSessionList.tsx"],"sourcesContent":["/**\n * Renders the list of sessions in the sidebar.\n *\n * Fetches sessions via aiApi, groups them based on the sidebar groupBy\n * setting, and renders each as an AgentManagerSessionItem.\n */\n\nimport { useState, useEffect, useRef, useMemo, useCallback } from 'react';\nimport { AnimatePresence } from 'framer-motion';\nimport { ChevronDown, ChevronRight, Loader2 } from 'lucide-react';\nimport { LoadingState, EmptyState } from '@/components/shared';\nimport { fetchSessionsPaginated } from '@/services/aiApi';\nimport type { AISession, AgentRef } from '@/services/aiApi';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { AgentManagerSessionItem } from './AgentManagerSessionItem';\nimport type { SidebarGroupBy } from '@/types/agentManager';\nimport { useTr } from '../../shared/hooks/useTr';\n\nconst PAGE_SIZE = 10;\n\ninterface AgentManagerSessionListProps {\n agent: AgentRef;\n onDeleteSession: (sessionId: string) => void;\n filterTags?: string[];\n dateFrom?: string;\n dateTo?: string;\n}\n\n/** Group sessions by the given key. Returns array of [groupLabel, sessions] pairs. */\nfunction groupSessions(\n sessions: AISession[],\n groupBy: SidebarGroupBy\n): Array<[string, AISession[]]> {\n if (groupBy === 'none') {\n return [['', sessions]];\n }\n\n const groups = new Map<string, AISession[]>();\n\n for (const session of sessions) {\n let key: string;\n switch (groupBy) {\n case 'date': {\n if (!session.createdAt) {\n key = 'Unknown';\n } else {\n const d = new Date(session.createdAt);\n const now = new Date();\n const diffMs = now.getTime() - d.getTime();\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n if (diffDays === 0) key = 'Today';\n else if (diffDays === 1) key = 'Yesterday';\n else if (diffDays < 7) key = 'This Week';\n else if (diffDays < 30) key = 'This Month';\n else key = 'Older';\n }\n break;\n }\n case 'model':\n key = session.agentType || 'Unknown';\n break;\n case 'status':\n key = session.status.charAt(0).toUpperCase() + session.status.slice(1);\n break;\n case 'sdk':\n key = session.providerPlugin ?? session.agentType ?? 'Unknown';\n break;\n default:\n key = 'All';\n }\n\n const existing = groups.get(key);\n if (existing) {\n existing.push(session);\n } else {\n groups.set(key, [session]);\n }\n }\n\n return Array.from(groups.entries());\n}\n\nexport function AgentManagerSessionList({\n agent,\n onDeleteSession,\n filterTags,\n dateFrom,\n dateTo,\n}: AgentManagerSessionListProps) {\n const [sessions, setSessions] = useState<AISession[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [hasMore, setHasMore] = useState(false);\n const [isLoadingMore, setIsLoadingMore] = useState(false);\n const [offset, setOffset] = useState(0);\n const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set());\n const sentinelRef = useRef<HTMLDivElement>(null);\n\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const tabs = useAgentManagerStore((s) => s.tabs);\n const hydrateSessionConfig = useAgentManagerStore((s) => s.hydrateSessionConfig);\n const selectedSessionIds = useAgentManagerStore((s) => s.selectedSessionIds);\n const sidebarSearchQuery = useAgentManagerStore((s) => s.sidebarSearchQuery);\n const sidebarGroupBy = useAgentManagerStore((s) => s.sidebarGroupBy);\n const tr = useTr();\n\n const loadSessions = useCallback(\n async (search: string) => {\n setLoading(true);\n setError(null);\n setOffset(0);\n try {\n const data = await fetchSessionsPaginated(agent, {\n limit: PAGE_SIZE,\n offset: 0,\n search: search || undefined,\n });\n setSessions(data.items);\n setHasMore(data.hasMore);\n setOffset(PAGE_SIZE);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load sessions');\n } finally {\n setLoading(false);\n }\n },\n [agent]\n );\n\n const loadMore = useCallback(async () => {\n if (isLoadingMore || !hasMore) return;\n setIsLoadingMore(true);\n try {\n const data = await fetchSessionsPaginated(agent, {\n limit: PAGE_SIZE,\n offset,\n search: sidebarSearchQuery || undefined,\n });\n setSessions((prev) => [...prev, ...data.items]);\n setHasMore(data.hasMore);\n setOffset((prev) => prev + PAGE_SIZE);\n } catch {\n // Silently fail on load-more — user can scroll up to retry\n } finally {\n setIsLoadingMore(false);\n }\n }, [agent, isLoadingMore, hasMore, offset, sidebarSearchQuery]);\n\n const sessionsRefreshTrigger = useAgentManagerStore((s) => s.sessionsRefreshTrigger);\n\n // Debounced reload when search query changes\n useEffect(() => {\n const timer = setTimeout(() => {\n void loadSessions(sidebarSearchQuery);\n }, 300);\n return () => clearTimeout(timer);\n }, [sidebarSearchQuery, loadSessions]);\n\n // Reload whenever a peer component bumps the refresh trigger\n // (e.g. after createSession from the tab bar or empty-state CTA).\n useEffect(() => {\n if (sessionsRefreshTrigger === 0) return;\n void loadSessions(sidebarSearchQuery);\n }, [sessionsRefreshTrigger, loadSessions, sidebarSearchQuery]);\n\n useEffect(() => {\n const openSessionIds = new Set(tabs.map((tab) => tab.sessionId));\n for (const session of sessions) {\n if (session.id === activeTabSessionId || openSessionIds.has(session.id)) {\n hydrateSessionConfig(session.id, session);\n }\n }\n }, [activeTabSessionId, hydrateSessionConfig, sessions, tabs]);\n\n // IntersectionObserver to auto-trigger loadMore when sentinel is visible\n useEffect(() => {\n const sentinel = sentinelRef.current;\n if (!sentinel) return;\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting) {\n void loadMore();\n }\n },\n { threshold: 0.1 }\n );\n observer.observe(sentinel);\n return () => observer.disconnect();\n }, [loadMore]);\n\n /** Filter sessions by tags and date range (client-side; search is backend-driven) */\n const filteredSessions = useMemo(() => {\n let result = sessions;\n\n // Tag filter\n if (filterTags && filterTags.length > 0) {\n result = result.filter((s) => {\n const sessionTags = (s.config as Record<string, unknown>)?.tags as string[] | undefined;\n if (!sessionTags || !Array.isArray(sessionTags)) return false;\n return filterTags.some((ft) =>\n sessionTags.some((st) => st.toLowerCase().includes(ft.toLowerCase()))\n );\n });\n }\n\n // Date range filter\n if (dateFrom) {\n const from = new Date(dateFrom);\n result = result.filter((s) => {\n if (!s.createdAt) return false;\n return new Date(s.createdAt) >= from;\n });\n }\n if (dateTo) {\n const to = new Date(dateTo);\n to.setHours(23, 59, 59, 999);\n result = result.filter((s) => {\n if (!s.createdAt) return false;\n return new Date(s.createdAt) <= to;\n });\n }\n\n return result;\n }, [sessions, filterTags, dateFrom, dateTo]);\n\n /** Grouped sessions */\n const grouped = useMemo(\n () => groupSessions(filteredSessions, sidebarGroupBy),\n [filteredSessions, sidebarGroupBy]\n );\n\n const toggleGroup = (groupLabel: string) => {\n setCollapsedGroups((prev) => {\n const next = new Set(prev);\n if (next.has(groupLabel)) {\n next.delete(groupLabel);\n } else {\n next.add(groupLabel);\n }\n return next;\n });\n };\n\n if (loading) {\n return (\n <div className=\"p-4\">\n <LoadingState\n message={tr(\n 'vibecontrols.agentManager.sessionList.loadingSessions',\n 'Loading sessions...'\n )}\n />\n </div>\n );\n }\n\n if (error) {\n const isSchemaError = error.includes('Cannot query field') || error.includes('agentAiGet');\n // NOT_FOUND in the sessions fetch means the AI plugin's route isn't\n // mounted yet (usually transient — plugin loads a moment after the\n // agent reports ready). Plugin install is already gated by\n // <PluginGate> above this component, so showing a CLI-install hint\n // here is both misleading (the plugin may already be installed) and\n // redundant. Just offer a retry.\n return (\n <div className=\"p-4 space-y-2\">\n {isSchemaError ? (\n <>\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.sessionList.proxyNotAvailable',\n 'Agent AI proxy not available yet. The backend schema may need to be published to GraphQL Hive.'\n )}\n </p>\n <p className=\"text-xs text-text-tertiary\">\n {tr(\n 'vibecontrols.agentManager.sessionList.sessionsWillLoad',\n 'Sessions will load once the gateway schema is updated.'\n )}\n </p>\n </>\n ) : (\n <p className=\"text-xs text-status-error-text\">{error}</p>\n )}\n <button\n onClick={() => void loadSessions(sidebarSearchQuery)}\n className=\"text-xs text-action-primary-bg hover:underline\"\n >\n {tr('vibecontrols.agentManager.sessionList.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n if (filteredSessions.length === 0) {\n return (\n <div className=\"p-4\">\n <EmptyState\n title={\n sidebarSearchQuery\n ? tr(\n 'vibecontrols.agentManager.sessionList.noMatchingSessions',\n 'No matching sessions'\n )\n : tr('vibecontrols.agentManager.sessionList.noSessionsYet', 'No sessions yet')\n }\n description={\n sidebarSearchQuery\n ? tr(\n 'vibecontrols.agentManager.sessionList.tryDifferentSearch',\n 'Try a different search query'\n )\n : tr(\n 'vibecontrols.agentManager.sessionList.createToGetStarted',\n 'Create a new session to get started'\n )\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col\">\n {grouped.map(([groupLabel, groupSessions]) => {\n const isCollapsed = collapsedGroups.has(groupLabel);\n const showHeader = sidebarGroupBy !== 'none' && groupLabel !== '';\n\n return (\n <div key={groupLabel || '__ungrouped'}>\n {showHeader && (\n <button\n onClick={() => toggleGroup(groupLabel)}\n className=\"flex items-center gap-1 w-full px-3 py-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary hover:text-text-secondary transition-colors\"\n >\n {isCollapsed ? (\n <ChevronRight className=\"w-3 h-3\" />\n ) : (\n <ChevronDown className=\"w-3 h-3\" />\n )}\n {groupLabel}\n <span className=\"ml-auto text-text-tertiary font-normal\">\n {groupSessions.length}\n </span>\n </button>\n )}\n\n {!isCollapsed && (\n <AnimatePresence mode=\"popLayout\">\n {groupSessions.map((session) => (\n <AgentManagerSessionItem\n key={session.id}\n session={session}\n agent={agent}\n isActive={activeTabSessionId === session.id}\n isSelected={selectedSessionIds.includes(session.id)}\n onDelete={onDeleteSession}\n />\n ))}\n </AnimatePresence>\n )}\n </div>\n );\n })}\n\n {/* Sentinel for infinite scroll */}\n <div ref={sentinelRef} className=\"py-1\">\n {isLoadingMore && (\n <div className=\"flex items-center justify-center py-2\">\n <Loader2 className=\"w-4 h-4 animate-spin text-text-tertiary\" />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,152 +1,170 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
-
import {
|
|
3
|
-
import { useAgentManagerStore as
|
|
4
|
-
import { AgentManagerTab as
|
|
5
|
-
import { RenameSessionDialog as
|
|
6
|
-
import { ExportSessionDialog as
|
|
7
|
-
import { useCallback as
|
|
8
|
-
import { ChevronLeft as
|
|
9
|
-
import { jsx as
|
|
2
|
+
import { createSession as t, renameSession as n } from "../../services/aiApi.js";
|
|
3
|
+
import { useAgentManagerStore as r } from "../../store/agentManagerStore.js";
|
|
4
|
+
import { AgentManagerTab as i } from "./AgentManagerTab.js";
|
|
5
|
+
import { RenameSessionDialog as a } from "./dialogs/RenameSessionDialog.js";
|
|
6
|
+
import { ExportSessionDialog as o } from "./dialogs/ExportSessionDialog.js";
|
|
7
|
+
import { useCallback as s, useEffect as c, useRef as l, useState as u } from "react";
|
|
8
|
+
import { ChevronLeft as d, ChevronRight as f, Download as p, HelpCircle as m, Maximize2 as h, Minimize2 as g, Plus as _, Search as v } from "lucide-react";
|
|
9
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
10
10
|
//#region src/components/agent-manager/AgentManagerTabBar.tsx
|
|
11
|
-
var
|
|
12
|
-
let
|
|
13
|
-
let e =
|
|
14
|
-
e && (
|
|
15
|
-
}, []),
|
|
16
|
-
let t =
|
|
11
|
+
var x = ({ agent: x, onSearch: S, onShowShortcuts: C }) => {
|
|
12
|
+
let w = r((e) => e.tabs), T = r((e) => e.activeTabSessionId), E = r((e) => e.sessionStates), D = r((e) => e.setActiveTab), O = r((e) => e.closeTab), k = r((e) => e.reorderTabs), A = r((e) => e.openSession), j = r((e) => e.renameTab), M = r((e) => e.isFullscreen), N = r((e) => e.toggleFullscreen), P = l(null), F = e(), [I, L] = u(null), [R, z] = u(null), [B, V] = u(!1), [H, U] = u(!1), [W, G] = u(!1), K = s(() => {
|
|
13
|
+
let e = P.current;
|
|
14
|
+
e && (U(e.scrollLeft > 0), G(e.scrollLeft + e.clientWidth < e.scrollWidth - 1));
|
|
15
|
+
}, []), q = s((e) => {
|
|
16
|
+
let t = P.current;
|
|
17
17
|
t && (t.scrollBy({
|
|
18
18
|
left: e === "left" ? -200 : 200,
|
|
19
19
|
behavior: "smooth"
|
|
20
|
-
}), setTimeout(
|
|
21
|
-
}, [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}, [
|
|
25
|
-
let e =
|
|
20
|
+
}), setTimeout(K, 300));
|
|
21
|
+
}, [K]);
|
|
22
|
+
c(() => {
|
|
23
|
+
K();
|
|
24
|
+
}, [w.length, K]), c(() => {
|
|
25
|
+
let e = P.current;
|
|
26
26
|
if (!e) return;
|
|
27
|
-
let t = new ResizeObserver(() =>
|
|
27
|
+
let t = new ResizeObserver(() => K());
|
|
28
28
|
return t.observe(e), () => t.disconnect();
|
|
29
|
-
}, [
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
}, [K]);
|
|
30
|
+
let J = r((e) => e.bumpSessionsRefresh), Y = s(async () => {
|
|
31
|
+
try {
|
|
32
|
+
let e = await t(x, {
|
|
33
|
+
name: `Session ${w.length + 1}`,
|
|
34
|
+
agentType: "claude",
|
|
35
|
+
config: {
|
|
36
|
+
model: "default",
|
|
37
|
+
mode: "cli",
|
|
38
|
+
tags: []
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
A(e.id, e.name), J();
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.error("[AgentManagerTabBar] createSession failed:", e);
|
|
44
|
+
}
|
|
45
|
+
}, [
|
|
46
|
+
x,
|
|
47
|
+
w.length,
|
|
48
|
+
A,
|
|
49
|
+
J
|
|
50
|
+
]), X = (e, t) => {
|
|
51
|
+
L(t), e.dataTransfer.effectAllowed = "move", e.dataTransfer.setData("text/plain", String(t));
|
|
52
|
+
}, Z = (e) => {
|
|
35
53
|
e.preventDefault(), e.dataTransfer.dropEffect = "move";
|
|
36
|
-
},
|
|
37
|
-
if (e.preventDefault(),
|
|
38
|
-
let n = [...
|
|
39
|
-
n.splice(t, 0, r),
|
|
54
|
+
}, Q = (e, t) => {
|
|
55
|
+
if (e.preventDefault(), I === null || I === t) return;
|
|
56
|
+
let n = [...w], [r] = n.splice(I, 1);
|
|
57
|
+
n.splice(t, 0, r), k(n.map((e, t) => ({
|
|
40
58
|
...e,
|
|
41
59
|
order: t
|
|
42
|
-
}))),
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
|
|
60
|
+
}))), L(null);
|
|
61
|
+
}, $ = () => {
|
|
62
|
+
L(null);
|
|
63
|
+
}, ee = s(async (e, t) => {
|
|
64
|
+
j(e, t);
|
|
47
65
|
try {
|
|
48
|
-
await
|
|
66
|
+
await n(x, e, t);
|
|
49
67
|
} catch {}
|
|
50
|
-
}, [
|
|
51
|
-
return /* @__PURE__ */
|
|
68
|
+
}, [x, j]), te = R ? w.find((e) => e.sessionId === R) : null;
|
|
69
|
+
return /* @__PURE__ */ b("div", {
|
|
52
70
|
className: "flex items-stretch bg-bg-primary border-b border-border-default w-full",
|
|
53
71
|
children: [
|
|
54
|
-
|
|
72
|
+
H && /* @__PURE__ */ y("button", {
|
|
55
73
|
type: "button",
|
|
56
|
-
onClick: () =>
|
|
74
|
+
onClick: () => q("left"),
|
|
57
75
|
className: "flex-shrink-0 px-1 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-r border-border-default",
|
|
58
76
|
"aria-label": "Scroll tabs left",
|
|
59
|
-
children: /* @__PURE__ */
|
|
77
|
+
children: /* @__PURE__ */ y(d, { className: "w-3.5 h-3.5" })
|
|
60
78
|
}),
|
|
61
|
-
/* @__PURE__ */
|
|
79
|
+
/* @__PURE__ */ y("div", {
|
|
62
80
|
className: "flex-1 min-w-0 overflow-hidden",
|
|
63
|
-
children: /* @__PURE__ */
|
|
64
|
-
ref:
|
|
65
|
-
onScroll:
|
|
81
|
+
children: /* @__PURE__ */ y("div", {
|
|
82
|
+
ref: P,
|
|
83
|
+
onScroll: K,
|
|
66
84
|
className: "flex items-stretch overflow-x-auto h-full",
|
|
67
85
|
style: { scrollbarWidth: "none" },
|
|
68
|
-
children:
|
|
69
|
-
let n =
|
|
70
|
-
return /* @__PURE__ */
|
|
86
|
+
children: w.map((e, t) => {
|
|
87
|
+
let n = E[e.sessionId], r = n?.status === "streaming" || n?.status === "processing";
|
|
88
|
+
return /* @__PURE__ */ y("div", {
|
|
71
89
|
draggable: !0,
|
|
72
|
-
onDragStart: (e) =>
|
|
73
|
-
onDragOver:
|
|
74
|
-
onDrop: (e) =>
|
|
75
|
-
onDragEnd:
|
|
76
|
-
className: `flex-shrink-0${
|
|
77
|
-
children: /* @__PURE__ */
|
|
90
|
+
onDragStart: (e) => X(e, t),
|
|
91
|
+
onDragOver: Z,
|
|
92
|
+
onDrop: (e) => Q(e, t),
|
|
93
|
+
onDragEnd: $,
|
|
94
|
+
className: `flex-shrink-0${I === t ? " opacity-50" : ""}`,
|
|
95
|
+
children: /* @__PURE__ */ y(i, {
|
|
78
96
|
sessionId: e.sessionId,
|
|
79
97
|
name: e.name,
|
|
80
|
-
isActive: e.sessionId ===
|
|
98
|
+
isActive: e.sessionId === T,
|
|
81
99
|
isPinned: e.pinned,
|
|
82
|
-
isProcessing:
|
|
83
|
-
onActivate: () =>
|
|
84
|
-
onClose: () =>
|
|
85
|
-
onRename:
|
|
100
|
+
isProcessing: r,
|
|
101
|
+
onActivate: () => D(e.sessionId),
|
|
102
|
+
onClose: () => O(e.sessionId),
|
|
103
|
+
onRename: z
|
|
86
104
|
})
|
|
87
105
|
}, e.sessionId);
|
|
88
106
|
})
|
|
89
107
|
})
|
|
90
108
|
}),
|
|
91
|
-
|
|
109
|
+
W && /* @__PURE__ */ y("button", {
|
|
92
110
|
type: "button",
|
|
93
|
-
onClick: () =>
|
|
111
|
+
onClick: () => q("right"),
|
|
94
112
|
className: "flex-shrink-0 px-1 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default",
|
|
95
113
|
"aria-label": "Scroll tabs right",
|
|
96
|
-
children: /* @__PURE__ */
|
|
114
|
+
children: /* @__PURE__ */ y(f, { className: "w-3.5 h-3.5" })
|
|
97
115
|
}),
|
|
98
|
-
|
|
99
|
-
onClick: () =>
|
|
116
|
+
T && /* @__PURE__ */ y("button", {
|
|
117
|
+
onClick: () => V(!0),
|
|
100
118
|
className: "flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default",
|
|
101
|
-
title:
|
|
102
|
-
"aria-label":
|
|
103
|
-
children: /* @__PURE__ */
|
|
119
|
+
title: F("vibecontrols.agentManager.tabBar.exportSession", "Export session"),
|
|
120
|
+
"aria-label": F("vibecontrols.agentManager.tabBar.exportSession", "Export session"),
|
|
121
|
+
children: /* @__PURE__ */ y(p, { className: "w-4 h-4" })
|
|
104
122
|
}),
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
onClick:
|
|
123
|
+
/* @__PURE__ */ y("button", {
|
|
124
|
+
onClick: Y,
|
|
107
125
|
className: "flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default",
|
|
108
|
-
title:
|
|
109
|
-
"aria-label":
|
|
110
|
-
children: /* @__PURE__ */
|
|
126
|
+
title: F("vibecontrols.agentManager.tabBar.newSession", "New session (Ctrl/Cmd+Shift+N)"),
|
|
127
|
+
"aria-label": F("vibecontrols.agentManager.tabBar.createNewSession", "Create new session"),
|
|
128
|
+
children: /* @__PURE__ */ y(_, { className: "w-4 h-4" })
|
|
111
129
|
}),
|
|
112
|
-
|
|
113
|
-
onClick:
|
|
130
|
+
S && /* @__PURE__ */ y("button", {
|
|
131
|
+
onClick: S,
|
|
114
132
|
className: "flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default",
|
|
115
|
-
title:
|
|
116
|
-
"aria-label":
|
|
117
|
-
children: /* @__PURE__ */ v
|
|
133
|
+
title: F("vibecontrols.agentManager.tabBar.searchSessions", "Search sessions (Ctrl/Cmd+Shift+K)"),
|
|
134
|
+
"aria-label": F("vibecontrols.agentManager.tabBar.searchSessions", "Search sessions"),
|
|
135
|
+
children: /* @__PURE__ */ y(v, { className: "w-4 h-4" })
|
|
118
136
|
}),
|
|
119
|
-
|
|
120
|
-
onClick:
|
|
137
|
+
C && /* @__PURE__ */ y("button", {
|
|
138
|
+
onClick: C,
|
|
121
139
|
className: "flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default",
|
|
122
|
-
title:
|
|
123
|
-
"aria-label":
|
|
124
|
-
children: /* @__PURE__ */
|
|
140
|
+
title: F("vibecontrols.agentManager.tabBar.keyboardShortcuts", "Keyboard shortcuts"),
|
|
141
|
+
"aria-label": F("vibecontrols.agentManager.tabBar.keyboardShortcuts", "Keyboard shortcuts"),
|
|
142
|
+
children: /* @__PURE__ */ y(m, { className: "w-4 h-4" })
|
|
125
143
|
}),
|
|
126
|
-
/* @__PURE__ */
|
|
127
|
-
onClick:
|
|
144
|
+
/* @__PURE__ */ y("button", {
|
|
145
|
+
onClick: N,
|
|
128
146
|
className: "flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default",
|
|
129
|
-
title:
|
|
130
|
-
"aria-label":
|
|
131
|
-
children:
|
|
147
|
+
title: M ? F("vibecontrols.agentManager.tabBar.exitFullscreen", "Exit fullscreen") : F("vibecontrols.agentManager.tabBar.enterFullscreen", "Enter fullscreen"),
|
|
148
|
+
"aria-label": M ? F("vibecontrols.agentManager.tabBar.exitFullscreen", "Exit fullscreen") : F("vibecontrols.agentManager.tabBar.enterFullscreen", "Enter fullscreen"),
|
|
149
|
+
children: y(M ? g : h, { className: "w-4 h-4" })
|
|
132
150
|
}),
|
|
133
|
-
/* @__PURE__ */
|
|
134
|
-
isOpen:
|
|
135
|
-
onClose: () =>
|
|
136
|
-
sessionId:
|
|
137
|
-
agent:
|
|
151
|
+
/* @__PURE__ */ y(o, {
|
|
152
|
+
isOpen: B,
|
|
153
|
+
onClose: () => V(!1),
|
|
154
|
+
sessionId: T ?? "",
|
|
155
|
+
agent: x
|
|
138
156
|
}),
|
|
139
|
-
/* @__PURE__ */
|
|
140
|
-
isOpen:
|
|
141
|
-
sessionId:
|
|
142
|
-
currentName:
|
|
143
|
-
onClose: () =>
|
|
144
|
-
onRename: (e, t) => void
|
|
157
|
+
/* @__PURE__ */ y(a, {
|
|
158
|
+
isOpen: R !== null,
|
|
159
|
+
sessionId: R ?? "",
|
|
160
|
+
currentName: te?.name ?? "",
|
|
161
|
+
onClose: () => z(null),
|
|
162
|
+
onRename: (e, t) => void ee(e, t)
|
|
145
163
|
})
|
|
146
164
|
]
|
|
147
165
|
});
|
|
148
166
|
};
|
|
149
167
|
//#endregion
|
|
150
|
-
export {
|
|
168
|
+
export { x as AgentManagerTabBar };
|
|
151
169
|
|
|
152
170
|
//# sourceMappingURL=AgentManagerTabBar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentManagerTabBar.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerTabBar.tsx"],"sourcesContent":["/**\n * Browser-style tab bar for the Agent Manager.\n *\n * Horizontal scrolling tabs with a \"+\" button to create new sessions.\n * Supports drag-to-reorder via simple index swap.\n */\n\nimport { useRef, useState, useCallback, useEffect, type FC, type DragEvent } from 'react';\nimport {\n Plus,\n Download,\n ChevronLeft,\n ChevronRight,\n Search,\n HelpCircle,\n Maximize2,\n Minimize2,\n} from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { renameSession, type AgentRef } from '@/services/aiApi';\nimport { AgentManagerTab } from './AgentManagerTab';\nimport { RenameSessionDialog } from './dialogs/RenameSessionDialog';\nimport { ExportSessionDialog } from './dialogs/ExportSessionDialog';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerTabBarProps {\n agent: AgentRef;\n onSearch?: () => void;\n onShowShortcuts?: () => void;\n}\n\nexport const AgentManagerTabBar: FC<AgentManagerTabBarProps> = ({\n agent,\n onSearch,\n onShowShortcuts,\n}) => {\n const tabs = useAgentManagerStore((s) => s.tabs);\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const sessionStates = useAgentManagerStore((s) => s.sessionStates);\n const setActiveTab = useAgentManagerStore((s) => s.setActiveTab);\n const closeTab = useAgentManagerStore((s) => s.closeTab);\n const reorderTabs = useAgentManagerStore((s) => s.reorderTabs);\n const openSession = useAgentManagerStore((s) => s.openSession);\n const renameTab = useAgentManagerStore((s) => s.renameTab);\n const isFullscreen = useAgentManagerStore((s) => s.isFullscreen);\n const toggleFullscreen = useAgentManagerStore((s) => s.toggleFullscreen);\n const scrollRef = useRef<HTMLDivElement>(null);\n const tr = useTr();\n const [dragIndex, setDragIndex] = useState<number | null>(null);\n const [renamingSessionId, setRenamingSessionId] = useState<string | null>(null);\n const [exportOpen, setExportOpen] = useState(false);\n const [showLeftArrow, setShowLeftArrow] = useState(false);\n const [showRightArrow, setShowRightArrow] = useState(false);\n\n const checkScroll = useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n setShowLeftArrow(el.scrollLeft > 0);\n setShowRightArrow(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);\n }, []);\n\n const scrollTabs = useCallback(\n (direction: 'left' | 'right') => {\n const el = scrollRef.current;\n if (!el) return;\n el.scrollBy({ left: direction === 'left' ? -200 : 200, behavior: 'smooth' });\n // Update arrow visibility after scroll animation\n setTimeout(checkScroll, 300);\n },\n [checkScroll]\n );\n\n useEffect(() => {\n checkScroll();\n }, [tabs.length, checkScroll]);\n\n // Re-check scroll whenever the container is resized\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n const ro = new ResizeObserver(() => checkScroll());\n ro.observe(el);\n return () => ro.disconnect();\n }, [checkScroll]);\n\n const handleNewSession = () => {\n const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;\n openSession(sessionId);\n };\n\n const handleDragStart = (e: DragEvent, index: number) => {\n setDragIndex(index);\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', String(index));\n };\n\n const handleDragOver = (e: DragEvent) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n };\n\n const handleDrop = (e: DragEvent, dropIndex: number) => {\n e.preventDefault();\n if (dragIndex === null || dragIndex === dropIndex) return;\n\n const reordered = [...tabs];\n const [moved] = reordered.splice(dragIndex, 1);\n reordered.splice(dropIndex, 0, moved);\n\n // Update order values\n const updated = reordered.map((tab, i) => ({ ...tab, order: i }));\n reorderTabs(updated);\n setDragIndex(null);\n };\n\n const handleDragEnd = () => {\n setDragIndex(null);\n };\n\n const handleRename = useCallback(\n async (sessionId: string, newName: string) => {\n renameTab(sessionId, newName);\n try {\n await renameSession(agent, sessionId, newName);\n } catch {\n // Best-effort API sync -- local rename already applied\n }\n },\n [agent, renameTab]\n );\n\n const renamingTab = renamingSessionId\n ? tabs.find((t) => t.sessionId === renamingSessionId)\n : null;\n\n return (\n <div className=\"flex items-stretch bg-bg-primary border-b border-border-default w-full\">\n {showLeftArrow && (\n <button\n type=\"button\"\n onClick={() => scrollTabs('left')}\n className=\"flex-shrink-0 px-1 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-r border-border-default\"\n aria-label=\"Scroll tabs left\"\n >\n <ChevronLeft className=\"w-3.5 h-3.5\" />\n </button>\n )}\n {/* Clip wrapper hides the browser scrollbar while keeping scroll functional */}\n <div className=\"flex-1 min-w-0 overflow-hidden\">\n <div\n ref={scrollRef}\n onScroll={checkScroll}\n className=\"flex items-stretch overflow-x-auto h-full\"\n style={{ scrollbarWidth: 'none' }}\n >\n {tabs.map((tab, index) => {\n const sessionState = sessionStates[tab.sessionId];\n const isProcessing =\n sessionState?.status === 'streaming' || sessionState?.status === 'processing';\n\n return (\n <div\n key={tab.sessionId}\n draggable\n onDragStart={(e) => handleDragStart(e, index)}\n onDragOver={handleDragOver}\n onDrop={(e) => handleDrop(e, index)}\n onDragEnd={handleDragEnd}\n className={`flex-shrink-0${dragIndex === index ? ' opacity-50' : ''}`}\n >\n <AgentManagerTab\n sessionId={tab.sessionId}\n name={tab.name}\n isActive={tab.sessionId === activeTabSessionId}\n isPinned={tab.pinned}\n isProcessing={isProcessing}\n onActivate={() => setActiveTab(tab.sessionId)}\n onClose={() => closeTab(tab.sessionId)}\n onRename={setRenamingSessionId}\n />\n </div>\n );\n })}\n </div>\n </div>\n {showRightArrow && (\n <button\n type=\"button\"\n onClick={() => scrollTabs('right')}\n className=\"flex-shrink-0 px-1 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n aria-label=\"Scroll tabs right\"\n >\n <ChevronRight className=\"w-3.5 h-3.5\" />\n </button>\n )}\n\n {/* Export session button */}\n {activeTabSessionId && (\n <button\n onClick={() => setExportOpen(true)}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={tr('vibecontrols.agentManager.tabBar.exportSession', 'Export session')}\n aria-label={tr('vibecontrols.agentManager.tabBar.exportSession', 'Export session')}\n >\n <Download className=\"w-4 h-4\" />\n </button>\n )}\n\n {/* New session button */}\n <button\n onClick={handleNewSession}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={tr('vibecontrols.agentManager.tabBar.newSession', 'New session (Ctrl+N)')}\n aria-label={tr('vibecontrols.agentManager.tabBar.createNewSession', 'Create new session')}\n >\n <Plus className=\"w-4 h-4\" />\n </button>\n\n {/* Search sessions */}\n {onSearch && (\n <button\n onClick={onSearch}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={tr('vibecontrols.agentManager.tabBar.searchSessions', 'Search sessions (Ctrl+K)')}\n aria-label={tr('vibecontrols.agentManager.tabBar.searchSessions', 'Search sessions')}\n >\n <Search className=\"w-4 h-4\" />\n </button>\n )}\n\n {/* Keyboard shortcuts */}\n {onShowShortcuts && (\n <button\n onClick={onShowShortcuts}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={tr('vibecontrols.agentManager.tabBar.keyboardShortcuts', 'Keyboard shortcuts')}\n aria-label={tr(\n 'vibecontrols.agentManager.tabBar.keyboardShortcuts',\n 'Keyboard shortcuts'\n )}\n >\n <HelpCircle className=\"w-4 h-4\" />\n </button>\n )}\n\n {/* Fullscreen toggle */}\n <button\n onClick={toggleFullscreen}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={\n isFullscreen\n ? tr('vibecontrols.agentManager.tabBar.exitFullscreen', 'Exit fullscreen')\n : tr('vibecontrols.agentManager.tabBar.enterFullscreen', 'Enter fullscreen')\n }\n aria-label={\n isFullscreen\n ? tr('vibecontrols.agentManager.tabBar.exitFullscreen', 'Exit fullscreen')\n : tr('vibecontrols.agentManager.tabBar.enterFullscreen', 'Enter fullscreen')\n }\n >\n {isFullscreen ? <Minimize2 className=\"w-4 h-4\" /> : <Maximize2 className=\"w-4 h-4\" />}\n </button>\n\n {/* Export session dialog */}\n <ExportSessionDialog\n isOpen={exportOpen}\n onClose={() => setExportOpen(false)}\n sessionId={activeTabSessionId ?? ''}\n agent={agent}\n />\n\n {/* Rename session dialog */}\n <RenameSessionDialog\n isOpen={renamingSessionId !== null}\n sessionId={renamingSessionId ?? ''}\n currentName={renamingTab?.name ?? ''}\n onClose={() => setRenamingSessionId(null)}\n onRename={(sessionId, newName) => void handleRename(sessionId, newName)}\n />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentManagerTabBar.js","names":[],"sources":["../../../src/components/agent-manager/AgentManagerTabBar.tsx"],"sourcesContent":["/**\n * Browser-style tab bar for the Agent Manager.\n *\n * Horizontal scrolling tabs with a \"+\" button to create new sessions.\n * Supports drag-to-reorder via simple index swap.\n */\n\nimport { useRef, useState, useCallback, useEffect, type FC, type DragEvent } from 'react';\nimport {\n Plus,\n Download,\n ChevronLeft,\n ChevronRight,\n Search,\n HelpCircle,\n Maximize2,\n Minimize2,\n} from 'lucide-react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { renameSession, createSession, type AgentRef } from '@/services/aiApi';\nimport { AgentManagerTab } from './AgentManagerTab';\nimport { RenameSessionDialog } from './dialogs/RenameSessionDialog';\nimport { ExportSessionDialog } from './dialogs/ExportSessionDialog';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AgentManagerTabBarProps {\n agent: AgentRef;\n onSearch?: () => void;\n onShowShortcuts?: () => void;\n}\n\nexport const AgentManagerTabBar: FC<AgentManagerTabBarProps> = ({\n agent,\n onSearch,\n onShowShortcuts,\n}) => {\n const tabs = useAgentManagerStore((s) => s.tabs);\n const activeTabSessionId = useAgentManagerStore((s) => s.activeTabSessionId);\n const sessionStates = useAgentManagerStore((s) => s.sessionStates);\n const setActiveTab = useAgentManagerStore((s) => s.setActiveTab);\n const closeTab = useAgentManagerStore((s) => s.closeTab);\n const reorderTabs = useAgentManagerStore((s) => s.reorderTabs);\n const openSession = useAgentManagerStore((s) => s.openSession);\n const renameTab = useAgentManagerStore((s) => s.renameTab);\n const isFullscreen = useAgentManagerStore((s) => s.isFullscreen);\n const toggleFullscreen = useAgentManagerStore((s) => s.toggleFullscreen);\n const scrollRef = useRef<HTMLDivElement>(null);\n const tr = useTr();\n const [dragIndex, setDragIndex] = useState<number | null>(null);\n const [renamingSessionId, setRenamingSessionId] = useState<string | null>(null);\n const [exportOpen, setExportOpen] = useState(false);\n const [showLeftArrow, setShowLeftArrow] = useState(false);\n const [showRightArrow, setShowRightArrow] = useState(false);\n\n const checkScroll = useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n setShowLeftArrow(el.scrollLeft > 0);\n setShowRightArrow(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);\n }, []);\n\n const scrollTabs = useCallback(\n (direction: 'left' | 'right') => {\n const el = scrollRef.current;\n if (!el) return;\n el.scrollBy({ left: direction === 'left' ? -200 : 200, behavior: 'smooth' });\n // Update arrow visibility after scroll animation\n setTimeout(checkScroll, 300);\n },\n [checkScroll]\n );\n\n useEffect(() => {\n checkScroll();\n }, [tabs.length, checkScroll]);\n\n // Re-check scroll whenever the container is resized\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n const ro = new ResizeObserver(() => checkScroll());\n ro.observe(el);\n return () => ro.disconnect();\n }, [checkScroll]);\n\n const bumpSessionsRefresh = useAgentManagerStore((s) => s.bumpSessionsRefresh);\n const handleNewSession = 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('[AgentManagerTabBar] createSession failed:', err);\n }\n }, [agent, tabs.length, openSession, bumpSessionsRefresh]);\n\n const handleDragStart = (e: DragEvent, index: number) => {\n setDragIndex(index);\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', String(index));\n };\n\n const handleDragOver = (e: DragEvent) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n };\n\n const handleDrop = (e: DragEvent, dropIndex: number) => {\n e.preventDefault();\n if (dragIndex === null || dragIndex === dropIndex) return;\n\n const reordered = [...tabs];\n const [moved] = reordered.splice(dragIndex, 1);\n reordered.splice(dropIndex, 0, moved);\n\n // Update order values\n const updated = reordered.map((tab, i) => ({ ...tab, order: i }));\n reorderTabs(updated);\n setDragIndex(null);\n };\n\n const handleDragEnd = () => {\n setDragIndex(null);\n };\n\n const handleRename = useCallback(\n async (sessionId: string, newName: string) => {\n renameTab(sessionId, newName);\n try {\n await renameSession(agent, sessionId, newName);\n } catch {\n // Best-effort API sync -- local rename already applied\n }\n },\n [agent, renameTab]\n );\n\n const renamingTab = renamingSessionId\n ? tabs.find((t) => t.sessionId === renamingSessionId)\n : null;\n\n return (\n <div className=\"flex items-stretch bg-bg-primary border-b border-border-default w-full\">\n {showLeftArrow && (\n <button\n type=\"button\"\n onClick={() => scrollTabs('left')}\n className=\"flex-shrink-0 px-1 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-r border-border-default\"\n aria-label=\"Scroll tabs left\"\n >\n <ChevronLeft className=\"w-3.5 h-3.5\" />\n </button>\n )}\n {/* Clip wrapper hides the browser scrollbar while keeping scroll functional */}\n <div className=\"flex-1 min-w-0 overflow-hidden\">\n <div\n ref={scrollRef}\n onScroll={checkScroll}\n className=\"flex items-stretch overflow-x-auto h-full\"\n style={{ scrollbarWidth: 'none' }}\n >\n {tabs.map((tab, index) => {\n const sessionState = sessionStates[tab.sessionId];\n const isProcessing =\n sessionState?.status === 'streaming' || sessionState?.status === 'processing';\n\n return (\n <div\n key={tab.sessionId}\n draggable\n onDragStart={(e) => handleDragStart(e, index)}\n onDragOver={handleDragOver}\n onDrop={(e) => handleDrop(e, index)}\n onDragEnd={handleDragEnd}\n className={`flex-shrink-0${dragIndex === index ? ' opacity-50' : ''}`}\n >\n <AgentManagerTab\n sessionId={tab.sessionId}\n name={tab.name}\n isActive={tab.sessionId === activeTabSessionId}\n isPinned={tab.pinned}\n isProcessing={isProcessing}\n onActivate={() => setActiveTab(tab.sessionId)}\n onClose={() => closeTab(tab.sessionId)}\n onRename={setRenamingSessionId}\n />\n </div>\n );\n })}\n </div>\n </div>\n {showRightArrow && (\n <button\n type=\"button\"\n onClick={() => scrollTabs('right')}\n className=\"flex-shrink-0 px-1 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n aria-label=\"Scroll tabs right\"\n >\n <ChevronRight className=\"w-3.5 h-3.5\" />\n </button>\n )}\n\n {/* Export session button */}\n {activeTabSessionId && (\n <button\n onClick={() => setExportOpen(true)}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={tr('vibecontrols.agentManager.tabBar.exportSession', 'Export session')}\n aria-label={tr('vibecontrols.agentManager.tabBar.exportSession', 'Export session')}\n >\n <Download className=\"w-4 h-4\" />\n </button>\n )}\n\n {/* New session button */}\n <button\n onClick={handleNewSession}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={tr(\n 'vibecontrols.agentManager.tabBar.newSession',\n 'New session (Ctrl/Cmd+Shift+N)'\n )}\n aria-label={tr('vibecontrols.agentManager.tabBar.createNewSession', 'Create new session')}\n >\n <Plus className=\"w-4 h-4\" />\n </button>\n\n {/* Search sessions */}\n {onSearch && (\n <button\n onClick={onSearch}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={tr(\n 'vibecontrols.agentManager.tabBar.searchSessions',\n 'Search sessions (Ctrl/Cmd+Shift+K)'\n )}\n aria-label={tr('vibecontrols.agentManager.tabBar.searchSessions', 'Search sessions')}\n >\n <Search className=\"w-4 h-4\" />\n </button>\n )}\n\n {/* Keyboard shortcuts */}\n {onShowShortcuts && (\n <button\n onClick={onShowShortcuts}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={tr('vibecontrols.agentManager.tabBar.keyboardShortcuts', 'Keyboard shortcuts')}\n aria-label={tr(\n 'vibecontrols.agentManager.tabBar.keyboardShortcuts',\n 'Keyboard shortcuts'\n )}\n >\n <HelpCircle className=\"w-4 h-4\" />\n </button>\n )}\n\n {/* Fullscreen toggle */}\n <button\n onClick={toggleFullscreen}\n className=\"flex-shrink-0 px-2 py-1.5 text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors border-l border-border-default\"\n title={\n isFullscreen\n ? tr('vibecontrols.agentManager.tabBar.exitFullscreen', 'Exit fullscreen')\n : tr('vibecontrols.agentManager.tabBar.enterFullscreen', 'Enter fullscreen')\n }\n aria-label={\n isFullscreen\n ? tr('vibecontrols.agentManager.tabBar.exitFullscreen', 'Exit fullscreen')\n : tr('vibecontrols.agentManager.tabBar.enterFullscreen', 'Enter fullscreen')\n }\n >\n {isFullscreen ? <Minimize2 className=\"w-4 h-4\" /> : <Maximize2 className=\"w-4 h-4\" />}\n </button>\n\n {/* Export session dialog */}\n <ExportSessionDialog\n isOpen={exportOpen}\n onClose={() => setExportOpen(false)}\n sessionId={activeTabSessionId ?? ''}\n agent={agent}\n />\n\n {/* Rename session dialog */}\n <RenameSessionDialog\n isOpen={renamingSessionId !== null}\n sessionId={renamingSessionId ?? ''}\n currentName={renamingTab?.name ?? ''}\n onClose={() => setRenamingSessionId(null)}\n onRename={(sessionId, newName) => void handleRename(sessionId, newName)}\n />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -320,7 +320,7 @@ var j = () => {
|
|
|
320
320
|
F
|
|
321
321
|
]);
|
|
322
322
|
return /* @__PURE__ */ y("div", {
|
|
323
|
-
className: "flex flex-col flex-1 overflow-hidden",
|
|
323
|
+
className: "flex flex-col flex-1 overflow-hidden pt-3",
|
|
324
324
|
children: [q.length === 0 ? /* @__PURE__ */ v(h, {
|
|
325
325
|
mode: N,
|
|
326
326
|
onSuggestionClick: ge
|