@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.
@@ -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 Y = c(() => {
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
- ]), X = c(() => y(Y, G), [Y, G]), Z = (e) => {
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
- }) : Y.length === 0 ? /* @__PURE__ */ g("div", {
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: [X.map(([e, t]) => {
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: () => Z(e),
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 { renameSession as t } from "../../services/aiApi.js";
3
- import { useAgentManagerStore as n } from "../../store/agentManagerStore.js";
4
- import { AgentManagerTab as r } from "./AgentManagerTab.js";
5
- import { RenameSessionDialog as i } from "./dialogs/RenameSessionDialog.js";
6
- import { ExportSessionDialog as a } from "./dialogs/ExportSessionDialog.js";
7
- import { useCallback as o, useEffect as s, useRef as c, useState as l } from "react";
8
- import { ChevronLeft as u, ChevronRight as d, Download as f, HelpCircle as p, Maximize2 as m, Minimize2 as h, Plus as g, Search as _ } from "lucide-react";
9
- import { jsx as v, jsxs as y } from "react/jsx-runtime";
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 b = ({ agent: b, onSearch: x, onShowShortcuts: S }) => {
12
- let C = n((e) => e.tabs), w = n((e) => e.activeTabSessionId), T = n((e) => e.sessionStates), E = n((e) => e.setActiveTab), D = n((e) => e.closeTab), O = n((e) => e.reorderTabs), k = n((e) => e.openSession), A = n((e) => e.renameTab), j = n((e) => e.isFullscreen), M = n((e) => e.toggleFullscreen), N = c(null), P = e(), [F, I] = l(null), [L, R] = l(null), [z, B] = l(!1), [V, H] = l(!1), [U, W] = l(!1), G = o(() => {
13
- let e = N.current;
14
- e && (H(e.scrollLeft > 0), W(e.scrollLeft + e.clientWidth < e.scrollWidth - 1));
15
- }, []), K = o((e) => {
16
- let t = N.current;
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(G, 300));
21
- }, [G]);
22
- s(() => {
23
- G();
24
- }, [C.length, G]), s(() => {
25
- let e = N.current;
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(() => G());
27
+ let t = new ResizeObserver(() => K());
28
28
  return t.observe(e), () => t.disconnect();
29
- }, [G]);
30
- let q = () => {
31
- k(`session-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`);
32
- }, J = (e, t) => {
33
- I(t), e.dataTransfer.effectAllowed = "move", e.dataTransfer.setData("text/plain", String(t));
34
- }, Y = (e) => {
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
- }, X = (e, t) => {
37
- if (e.preventDefault(), F === null || F === t) return;
38
- let n = [...C], [r] = n.splice(F, 1);
39
- n.splice(t, 0, r), O(n.map((e, t) => ({
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
- }))), I(null);
43
- }, Z = () => {
44
- I(null);
45
- }, Q = o(async (e, n) => {
46
- A(e, n);
60
+ }))), L(null);
61
+ }, $ = () => {
62
+ L(null);
63
+ }, ee = s(async (e, t) => {
64
+ j(e, t);
47
65
  try {
48
- await t(b, e, n);
66
+ await n(x, e, t);
49
67
  } catch {}
50
- }, [b, A]), $ = L ? C.find((e) => e.sessionId === L) : null;
51
- return /* @__PURE__ */ y("div", {
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
- V && /* @__PURE__ */ v("button", {
72
+ H && /* @__PURE__ */ y("button", {
55
73
  type: "button",
56
- onClick: () => K("left"),
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__ */ v(u, { className: "w-3.5 h-3.5" })
77
+ children: /* @__PURE__ */ y(d, { className: "w-3.5 h-3.5" })
60
78
  }),
61
- /* @__PURE__ */ v("div", {
79
+ /* @__PURE__ */ y("div", {
62
80
  className: "flex-1 min-w-0 overflow-hidden",
63
- children: /* @__PURE__ */ v("div", {
64
- ref: N,
65
- onScroll: G,
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: C.map((e, t) => {
69
- let n = T[e.sessionId], i = n?.status === "streaming" || n?.status === "processing";
70
- return /* @__PURE__ */ v("div", {
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) => J(e, t),
73
- onDragOver: Y,
74
- onDrop: (e) => X(e, t),
75
- onDragEnd: Z,
76
- className: `flex-shrink-0${F === t ? " opacity-50" : ""}`,
77
- children: /* @__PURE__ */ v(r, {
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 === w,
98
+ isActive: e.sessionId === T,
81
99
  isPinned: e.pinned,
82
- isProcessing: i,
83
- onActivate: () => E(e.sessionId),
84
- onClose: () => D(e.sessionId),
85
- onRename: R
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
- U && /* @__PURE__ */ v("button", {
109
+ W && /* @__PURE__ */ y("button", {
92
110
  type: "button",
93
- onClick: () => K("right"),
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__ */ v(d, { className: "w-3.5 h-3.5" })
114
+ children: /* @__PURE__ */ y(f, { className: "w-3.5 h-3.5" })
97
115
  }),
98
- w && /* @__PURE__ */ v("button", {
99
- onClick: () => B(!0),
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: P("vibecontrols.agentManager.tabBar.exportSession", "Export session"),
102
- "aria-label": P("vibecontrols.agentManager.tabBar.exportSession", "Export session"),
103
- children: /* @__PURE__ */ v(f, { className: "w-4 h-4" })
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__ */ v("button", {
106
- onClick: q,
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: P("vibecontrols.agentManager.tabBar.newSession", "New session (Ctrl+N)"),
109
- "aria-label": P("vibecontrols.agentManager.tabBar.createNewSession", "Create new session"),
110
- children: /* @__PURE__ */ v(g, { className: "w-4 h-4" })
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
- x && /* @__PURE__ */ v("button", {
113
- onClick: x,
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: P("vibecontrols.agentManager.tabBar.searchSessions", "Search sessions (Ctrl+K)"),
116
- "aria-label": P("vibecontrols.agentManager.tabBar.searchSessions", "Search sessions"),
117
- children: /* @__PURE__ */ v(_, { className: "w-4 h-4" })
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
- S && /* @__PURE__ */ v("button", {
120
- onClick: S,
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: P("vibecontrols.agentManager.tabBar.keyboardShortcuts", "Keyboard shortcuts"),
123
- "aria-label": P("vibecontrols.agentManager.tabBar.keyboardShortcuts", "Keyboard shortcuts"),
124
- children: /* @__PURE__ */ v(p, { className: "w-4 h-4" })
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__ */ v("button", {
127
- onClick: M,
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: j ? P("vibecontrols.agentManager.tabBar.exitFullscreen", "Exit fullscreen") : P("vibecontrols.agentManager.tabBar.enterFullscreen", "Enter fullscreen"),
130
- "aria-label": j ? P("vibecontrols.agentManager.tabBar.exitFullscreen", "Exit fullscreen") : P("vibecontrols.agentManager.tabBar.enterFullscreen", "Enter fullscreen"),
131
- children: v(j ? h : m, { className: "w-4 h-4" })
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__ */ v(a, {
134
- isOpen: z,
135
- onClose: () => B(!1),
136
- sessionId: w ?? "",
137
- agent: b
151
+ /* @__PURE__ */ y(o, {
152
+ isOpen: B,
153
+ onClose: () => V(!1),
154
+ sessionId: T ?? "",
155
+ agent: x
138
156
  }),
139
- /* @__PURE__ */ v(i, {
140
- isOpen: L !== null,
141
- sessionId: L ?? "",
142
- currentName: $?.name ?? "",
143
- onClose: () => R(null),
144
- onRename: (e, t) => void Q(e, t)
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 { b as AgentManagerTabBar };
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