@athenaintel/react 0.36.1 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -623,8 +623,11 @@ workbench the layout opens no asset column of its own: assets the agent or the
623
623
  person opens show as the workbench's **Open assets** tab (closing the tab
624
624
  closes the asset panel). Maximize is offered only inside `AthenaLayout`, which
625
625
  owns the column's width; collapsing a maximized workspace brings the chat back
626
- beside the rail. `SessionWorkbench` also works on its own anywhere under
627
- `<AthenaProvider>`; it then keeps its collapsed state itself.
626
+ beside the rail. Each session keeps its own open or collapsed workspace while
627
+ the layout stays mounted, so `defaultWorkbenchCollapsed` is how a session the
628
+ person has not opened it in starts. `SessionWorkbench` also works on its own
629
+ anywhere under `<AthenaProvider>`; it then keeps its collapsed state itself,
630
+ per session too.
628
631
 
629
632
  Built-in panes and what each reads:
630
633
 
@@ -774,6 +777,9 @@ Both use the same bearer / API key as session creation. The full recipe
774
777
  template skill `voice-session-guide.md`. `createAthenaVoiceSession` is also
775
778
  exported if you hold the bridge state yourself.
776
779
 
780
+ ## What's new in 0.37.0
781
+
782
+ **Each session keeps its own open or collapsed workspace.** `SessionWorkbench` already kept each session's tabs; it now keeps whether its workspace is open too. A session the person has not opened or collapsed it in, a new one or one switched to, shows `AthenaLayout`'s `defaultWorkbenchCollapsed`. With `defaultWorkbenchCollapsed`, every session starts with the workspace on its rail, and opening it in one session no longer opens it in the next. An asset the agent or the person opens still brings it out, for that session. Without the prop nothing changes for a session that has not collapsed it, but collapsing one no longer collapses the others.
777
783
  ## What's new in 0.36.1
778
784
 
779
785
  **Opening a long session no longer jumps the transcript around.** As older history pages in above what you are reading, the reading position now stays put, instantly. Before, the chat added its own scroll correction on top of the browser's scroll anchoring (a jump by a page's height), let it play through the viewport's smooth scrolling (a fast scroll through the transcript), and missed a page that folds into the first loaded turn, which moved the text under the reader. Cut tool outputs are now read only for messages near the viewport, at most two at a time, instead of every loaded step at once: opening a long agent run sent dozens of reads, each re-rendering the transcript and all queued ahead of your next message. Nothing to change in your app.
@@ -96,8 +96,15 @@ const WorkbenchSplitLayout = ({
96
96
  )
97
97
  });
98
98
  const layoutState = react.useMemo(
99
- () => ({ collapsed, setCollapsed, maximized, setMaximized, hostsAssetPanel: true }),
100
- [collapsed, maximized, setCollapsed, setMaximized]
99
+ () => ({
100
+ collapsed,
101
+ setCollapsed,
102
+ defaultCollapsed: defaultWorkbenchCollapsed,
103
+ maximized,
104
+ setMaximized,
105
+ hostsAssetPanel: true
106
+ }),
107
+ [collapsed, defaultWorkbenchCollapsed, maximized, setCollapsed, setMaximized]
101
108
  );
102
109
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: "flex h-full w-full", children: [
103
110
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"AthenaLayout.cjs","sources":["../../src/assets/AthenaLayout.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type FC,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport {\n WorkbenchLayoutContext,\n type WorkbenchLayoutState,\n} from \"../workbench/workbench-layout-context\";\nimport { useAssetPanelStore } from \"./asset-panel-store\";\nimport { AssetPanel } from \"./AssetPanel\";\n\nexport interface AthenaLayoutProps {\n children: ReactNode;\n /** Default width (%) of the chat panel when assets are open. Defaults to 50. */\n defaultChatPercent?: number;\n /** Minimum width (%) of each panel. Defaults to 25. */\n minPercent?: number;\n /**\n * A right-hand workspace column beside the chat — typically\n * `<SessionWorkbench />`. It is always shown (collapsible to a rail,\n * maximizable over the chat, and resizable by its edge), and the assets the agent or the person opens\n * render inside it as its \"Open assets\" tab instead of in a column of their\n * own.\n */\n workbench?: ReactNode;\n /** Default width (%) of the workbench column. Defaults to 40. */\n defaultWorkbenchPercent?: number;\n /** Start with the workbench collapsed to its rail. Defaults to `false`. */\n defaultWorkbenchCollapsed?: boolean;\n}\n\n/** Drag-to-resize for a split: the pointer's x as a share of the container. */\nfunction useSplitDrag({\n onPercent,\n}: {\n /** Called with the pointer's x position as a percentage of the container width. */\n onPercent: (percentFromLeft: number) => void;\n}) {\n const containerRef = useRef<HTMLDivElement>(null);\n const dragging = useRef(false);\n const onPointerDown = useCallback((e: React.PointerEvent) => {\n e.preventDefault();\n dragging.current = true;\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n }, []);\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n if (!dragging.current || !containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n onPercent(((e.clientX - rect.left) / rect.width) * 100);\n },\n [onPercent],\n );\n const onPointerUp = useCallback(() => {\n dragging.current = false;\n }, []);\n return { containerRef, handlers: { onPointerDown, onPointerMove, onPointerUp } };\n}\n\nconst RESIZE_HANDLE_CLASS =\n \"athena-sdk-chrome flex h-full w-1 shrink-0 cursor-col-resize items-center bg-border/40 transition-colors hover:bg-border active:bg-primary/30\";\n\n/**\n * Layout that places the chat on the left and the asset panel on the right.\n * When no assets are open, the chat takes full width. Includes a draggable resize handle.\n * With `workbench`, the right-hand column is the workbench instead (see the prop).\n */\nexport const AthenaLayout: FC<AthenaLayoutProps> = ({ workbench, ...props }) => {\n const registerAssetPanelHost = useAssetPanelStore((s) => s.registerAssetPanelHost);\n const unregisterAssetPanelHost = useAssetPanelStore((s) => s.unregisterAssetPanelHost);\n\n useEffect(() => {\n registerAssetPanelHost();\n return () => {\n unregisterAssetPanelHost();\n };\n }, [registerAssetPanelHost, unregisterAssetPanelHost]);\n\n return workbench === undefined || workbench === null ? (\n <AssetSplitLayout {...props} />\n ) : (\n <WorkbenchSplitLayout {...props} workbench={workbench} />\n );\n};\n\nconst AssetSplitLayout: FC<Omit<AthenaLayoutProps, \"workbench\">> = ({\n children,\n defaultChatPercent = 50,\n minPercent = 25,\n}) => {\n const isOpen = useAssetPanelStore((s) => s.isOpen);\n const [chatPercent, setChatPercent] = useState(defaultChatPercent);\n const { containerRef, handlers } = useSplitDrag({\n onPercent: useCallback(\n (pct: number) => setChatPercent(Math.max(minPercent, Math.min(100 - minPercent, pct))),\n [minPercent],\n ),\n });\n\n if (!isOpen) {\n return <div className=\"flex h-full w-full flex-col\">{children}</div>;\n }\n\n return (\n <div ref={containerRef} className=\"flex h-full w-full\">\n <div\n className=\"flex h-full flex-col overflow-hidden\"\n style={{ width: `${chatPercent}%` }}\n >\n {children}\n </div>\n <div className={RESIZE_HANDLE_CLASS} {...handlers} />\n <div className=\"athena-sdk-chrome flex h-full flex-1 flex-col overflow-hidden\">\n <AssetPanel manageAssetPanelHostRegistration={false} />\n </div>\n </div>\n );\n};\n\nconst WorkbenchSplitLayout: FC<\n Omit<AthenaLayoutProps, \"workbench\" | \"defaultChatPercent\"> & { workbench: ReactNode }\n> = ({\n children,\n workbench,\n minPercent = 25,\n defaultWorkbenchPercent = 40,\n defaultWorkbenchCollapsed = false,\n}) => {\n const [workbenchPercent, setWorkbenchPercent] = useState(defaultWorkbenchPercent);\n const [collapsed, setCollapsedState] = useState(defaultWorkbenchCollapsed);\n const [maximizedState, setMaximizedState] = useState(false);\n // A rail is never maximized: collapsing restores the chat beside it.\n const maximized = maximizedState && !collapsed;\n const setCollapsed = useCallback((next: boolean) => {\n setCollapsedState(next);\n if (next) setMaximizedState(false);\n }, []);\n const setMaximized = useCallback((next: boolean) => {\n setMaximizedState(next);\n if (next) setCollapsedState(false);\n }, []);\n const { containerRef, handlers } = useSplitDrag({\n onPercent: useCallback(\n (pct: number) =>\n setWorkbenchPercent(Math.max(minPercent, Math.min(100 - minPercent, 100 - pct))),\n [minPercent],\n ),\n });\n const layoutState = useMemo<WorkbenchLayoutState>(\n () => ({ collapsed, setCollapsed, maximized, setMaximized, hostsAssetPanel: true }),\n [collapsed, maximized, setCollapsed, setMaximized],\n );\n\n return (\n <div ref={containerRef} className=\"flex h-full w-full\">\n {/* Hidden, not unmounted, while the workbench is maximized: the chat keeps its thread and draft. */}\n <div\n className={cn(\"flex h-full min-w-0 flex-1 flex-col overflow-hidden\", maximized && \"hidden\")}\n >\n {children}\n </div>\n {collapsed || maximized ? null : (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize workspace\"\n className={RESIZE_HANDLE_CLASS}\n {...handlers}\n />\n )}\n <div\n className={cn(\n \"flex h-full shrink-0 flex-col overflow-hidden\",\n collapsed && \"w-10\",\n maximized && \"min-w-0 flex-1\",\n )}\n // The dragged width is a runtime percentage — no static utility expresses it.\n style={collapsed || maximized ? undefined : { width: `${workbenchPercent}%` }}\n >\n <WorkbenchLayoutContext.Provider value={layoutState}>\n {workbench}\n </WorkbenchLayoutContext.Provider>\n </div>\n </div>\n );\n};\n"],"names":["useRef","useCallback","useAssetPanelStore","useEffect","jsx","useState","jsxs","AssetPanel","useMemo","cn","WorkbenchLayoutContext"],"mappings":"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"}
1
+ {"version":3,"file":"AthenaLayout.cjs","sources":["../../src/assets/AthenaLayout.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type FC,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport {\n WorkbenchLayoutContext,\n type WorkbenchLayoutState,\n} from \"../workbench/workbench-layout-context\";\nimport { useAssetPanelStore } from \"./asset-panel-store\";\nimport { AssetPanel } from \"./AssetPanel\";\n\nexport interface AthenaLayoutProps {\n children: ReactNode;\n /** Default width (%) of the chat panel when assets are open. Defaults to 50. */\n defaultChatPercent?: number;\n /** Minimum width (%) of each panel. Defaults to 25. */\n minPercent?: number;\n /**\n * A right-hand workspace column beside the chat — typically\n * `<SessionWorkbench />`. It is always shown (collapsible to a rail,\n * maximizable over the chat, and resizable by its edge), and the assets the agent or the person opens\n * render inside it as its \"Open assets\" tab instead of in a column of their\n * own.\n */\n workbench?: ReactNode;\n /** Default width (%) of the workbench column. Defaults to 40. */\n defaultWorkbenchPercent?: number;\n /**\n * Whether a session's workbench starts collapsed to its rail. Each session\n * keeps its own open or collapsed workbench while the layout stays mounted,\n * as it keeps its own tabs, so this is how every session the person has not\n * opened it in shows it — a new one, or one switched to. Defaults to `false`.\n */\n defaultWorkbenchCollapsed?: boolean;\n}\n\n/** Drag-to-resize for a split: the pointer's x as a share of the container. */\nfunction useSplitDrag({\n onPercent,\n}: {\n /** Called with the pointer's x position as a percentage of the container width. */\n onPercent: (percentFromLeft: number) => void;\n}) {\n const containerRef = useRef<HTMLDivElement>(null);\n const dragging = useRef(false);\n const onPointerDown = useCallback((e: React.PointerEvent) => {\n e.preventDefault();\n dragging.current = true;\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n }, []);\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n if (!dragging.current || !containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n onPercent(((e.clientX - rect.left) / rect.width) * 100);\n },\n [onPercent],\n );\n const onPointerUp = useCallback(() => {\n dragging.current = false;\n }, []);\n return { containerRef, handlers: { onPointerDown, onPointerMove, onPointerUp } };\n}\n\nconst RESIZE_HANDLE_CLASS =\n \"athena-sdk-chrome flex h-full w-1 shrink-0 cursor-col-resize items-center bg-border/40 transition-colors hover:bg-border active:bg-primary/30\";\n\n/**\n * Layout that places the chat on the left and the asset panel on the right.\n * When no assets are open, the chat takes full width. Includes a draggable resize handle.\n * With `workbench`, the right-hand column is the workbench instead (see the prop).\n */\nexport const AthenaLayout: FC<AthenaLayoutProps> = ({ workbench, ...props }) => {\n const registerAssetPanelHost = useAssetPanelStore((s) => s.registerAssetPanelHost);\n const unregisterAssetPanelHost = useAssetPanelStore((s) => s.unregisterAssetPanelHost);\n\n useEffect(() => {\n registerAssetPanelHost();\n return () => {\n unregisterAssetPanelHost();\n };\n }, [registerAssetPanelHost, unregisterAssetPanelHost]);\n\n return workbench === undefined || workbench === null ? (\n <AssetSplitLayout {...props} />\n ) : (\n <WorkbenchSplitLayout {...props} workbench={workbench} />\n );\n};\n\nconst AssetSplitLayout: FC<Omit<AthenaLayoutProps, \"workbench\">> = ({\n children,\n defaultChatPercent = 50,\n minPercent = 25,\n}) => {\n const isOpen = useAssetPanelStore((s) => s.isOpen);\n const [chatPercent, setChatPercent] = useState(defaultChatPercent);\n const { containerRef, handlers } = useSplitDrag({\n onPercent: useCallback(\n (pct: number) => setChatPercent(Math.max(minPercent, Math.min(100 - minPercent, pct))),\n [minPercent],\n ),\n });\n\n if (!isOpen) {\n return <div className=\"flex h-full w-full flex-col\">{children}</div>;\n }\n\n return (\n <div ref={containerRef} className=\"flex h-full w-full\">\n <div\n className=\"flex h-full flex-col overflow-hidden\"\n style={{ width: `${chatPercent}%` }}\n >\n {children}\n </div>\n <div className={RESIZE_HANDLE_CLASS} {...handlers} />\n <div className=\"athena-sdk-chrome flex h-full flex-1 flex-col overflow-hidden\">\n <AssetPanel manageAssetPanelHostRegistration={false} />\n </div>\n </div>\n );\n};\n\nconst WorkbenchSplitLayout: FC<\n Omit<AthenaLayoutProps, \"workbench\" | \"defaultChatPercent\"> & { workbench: ReactNode }\n> = ({\n children,\n workbench,\n minPercent = 25,\n defaultWorkbenchPercent = 40,\n defaultWorkbenchCollapsed = false,\n}) => {\n const [workbenchPercent, setWorkbenchPercent] = useState(defaultWorkbenchPercent);\n const [collapsed, setCollapsedState] = useState(defaultWorkbenchCollapsed);\n const [maximizedState, setMaximizedState] = useState(false);\n // A rail is never maximized: collapsing restores the chat beside it.\n const maximized = maximizedState && !collapsed;\n const setCollapsed = useCallback((next: boolean) => {\n setCollapsedState(next);\n if (next) setMaximizedState(false);\n }, []);\n const setMaximized = useCallback((next: boolean) => {\n setMaximizedState(next);\n if (next) setCollapsedState(false);\n }, []);\n const { containerRef, handlers } = useSplitDrag({\n onPercent: useCallback(\n (pct: number) =>\n setWorkbenchPercent(Math.max(minPercent, Math.min(100 - minPercent, 100 - pct))),\n [minPercent],\n ),\n });\n const layoutState = useMemo<WorkbenchLayoutState>(\n () => ({\n collapsed,\n setCollapsed,\n defaultCollapsed: defaultWorkbenchCollapsed,\n maximized,\n setMaximized,\n hostsAssetPanel: true,\n }),\n [collapsed, defaultWorkbenchCollapsed, maximized, setCollapsed, setMaximized],\n );\n\n return (\n <div ref={containerRef} className=\"flex h-full w-full\">\n {/* Hidden, not unmounted, while the workbench is maximized: the chat keeps its thread and draft. */}\n <div\n className={cn(\"flex h-full min-w-0 flex-1 flex-col overflow-hidden\", maximized && \"hidden\")}\n >\n {children}\n </div>\n {collapsed || maximized ? null : (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize workspace\"\n className={RESIZE_HANDLE_CLASS}\n {...handlers}\n />\n )}\n <div\n className={cn(\n \"flex h-full shrink-0 flex-col overflow-hidden\",\n collapsed && \"w-10\",\n maximized && \"min-w-0 flex-1\",\n )}\n // The dragged width is a runtime percentage — no static utility expresses it.\n style={collapsed || maximized ? undefined : { width: `${workbenchPercent}%` }}\n >\n <WorkbenchLayoutContext.Provider value={layoutState}>\n {workbench}\n </WorkbenchLayoutContext.Provider>\n </div>\n </div>\n );\n};\n"],"names":["useRef","useCallback","useAssetPanelStore","useEffect","jsx","useState","jsxs","AssetPanel","useMemo","cn","WorkbenchLayoutContext"],"mappings":"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"}
@@ -15,7 +15,12 @@ export interface AthenaLayoutProps {
15
15
  workbench?: ReactNode;
16
16
  /** Default width (%) of the workbench column. Defaults to 40. */
17
17
  defaultWorkbenchPercent?: number;
18
- /** Start with the workbench collapsed to its rail. Defaults to `false`. */
18
+ /**
19
+ * Whether a session's workbench starts collapsed to its rail. Each session
20
+ * keeps its own open or collapsed workbench while the layout stays mounted,
21
+ * as it keeps its own tabs, so this is how every session the person has not
22
+ * opened it in shows it — a new one, or one switched to. Defaults to `false`.
23
+ */
19
24
  defaultWorkbenchCollapsed?: boolean;
20
25
  }
21
26
  /**
@@ -94,8 +94,15 @@ const WorkbenchSplitLayout = ({
94
94
  )
95
95
  });
96
96
  const layoutState = useMemo(
97
- () => ({ collapsed, setCollapsed, maximized, setMaximized, hostsAssetPanel: true }),
98
- [collapsed, maximized, setCollapsed, setMaximized]
97
+ () => ({
98
+ collapsed,
99
+ setCollapsed,
100
+ defaultCollapsed: defaultWorkbenchCollapsed,
101
+ maximized,
102
+ setMaximized,
103
+ hostsAssetPanel: true
104
+ }),
105
+ [collapsed, defaultWorkbenchCollapsed, maximized, setCollapsed, setMaximized]
99
106
  );
100
107
  return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "flex h-full w-full", children: [
101
108
  /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"AthenaLayout.js","sources":["../../src/assets/AthenaLayout.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type FC,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport {\n WorkbenchLayoutContext,\n type WorkbenchLayoutState,\n} from \"../workbench/workbench-layout-context\";\nimport { useAssetPanelStore } from \"./asset-panel-store\";\nimport { AssetPanel } from \"./AssetPanel\";\n\nexport interface AthenaLayoutProps {\n children: ReactNode;\n /** Default width (%) of the chat panel when assets are open. Defaults to 50. */\n defaultChatPercent?: number;\n /** Minimum width (%) of each panel. Defaults to 25. */\n minPercent?: number;\n /**\n * A right-hand workspace column beside the chat — typically\n * `<SessionWorkbench />`. It is always shown (collapsible to a rail,\n * maximizable over the chat, and resizable by its edge), and the assets the agent or the person opens\n * render inside it as its \"Open assets\" tab instead of in a column of their\n * own.\n */\n workbench?: ReactNode;\n /** Default width (%) of the workbench column. Defaults to 40. */\n defaultWorkbenchPercent?: number;\n /** Start with the workbench collapsed to its rail. Defaults to `false`. */\n defaultWorkbenchCollapsed?: boolean;\n}\n\n/** Drag-to-resize for a split: the pointer's x as a share of the container. */\nfunction useSplitDrag({\n onPercent,\n}: {\n /** Called with the pointer's x position as a percentage of the container width. */\n onPercent: (percentFromLeft: number) => void;\n}) {\n const containerRef = useRef<HTMLDivElement>(null);\n const dragging = useRef(false);\n const onPointerDown = useCallback((e: React.PointerEvent) => {\n e.preventDefault();\n dragging.current = true;\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n }, []);\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n if (!dragging.current || !containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n onPercent(((e.clientX - rect.left) / rect.width) * 100);\n },\n [onPercent],\n );\n const onPointerUp = useCallback(() => {\n dragging.current = false;\n }, []);\n return { containerRef, handlers: { onPointerDown, onPointerMove, onPointerUp } };\n}\n\nconst RESIZE_HANDLE_CLASS =\n \"athena-sdk-chrome flex h-full w-1 shrink-0 cursor-col-resize items-center bg-border/40 transition-colors hover:bg-border active:bg-primary/30\";\n\n/**\n * Layout that places the chat on the left and the asset panel on the right.\n * When no assets are open, the chat takes full width. Includes a draggable resize handle.\n * With `workbench`, the right-hand column is the workbench instead (see the prop).\n */\nexport const AthenaLayout: FC<AthenaLayoutProps> = ({ workbench, ...props }) => {\n const registerAssetPanelHost = useAssetPanelStore((s) => s.registerAssetPanelHost);\n const unregisterAssetPanelHost = useAssetPanelStore((s) => s.unregisterAssetPanelHost);\n\n useEffect(() => {\n registerAssetPanelHost();\n return () => {\n unregisterAssetPanelHost();\n };\n }, [registerAssetPanelHost, unregisterAssetPanelHost]);\n\n return workbench === undefined || workbench === null ? (\n <AssetSplitLayout {...props} />\n ) : (\n <WorkbenchSplitLayout {...props} workbench={workbench} />\n );\n};\n\nconst AssetSplitLayout: FC<Omit<AthenaLayoutProps, \"workbench\">> = ({\n children,\n defaultChatPercent = 50,\n minPercent = 25,\n}) => {\n const isOpen = useAssetPanelStore((s) => s.isOpen);\n const [chatPercent, setChatPercent] = useState(defaultChatPercent);\n const { containerRef, handlers } = useSplitDrag({\n onPercent: useCallback(\n (pct: number) => setChatPercent(Math.max(minPercent, Math.min(100 - minPercent, pct))),\n [minPercent],\n ),\n });\n\n if (!isOpen) {\n return <div className=\"flex h-full w-full flex-col\">{children}</div>;\n }\n\n return (\n <div ref={containerRef} className=\"flex h-full w-full\">\n <div\n className=\"flex h-full flex-col overflow-hidden\"\n style={{ width: `${chatPercent}%` }}\n >\n {children}\n </div>\n <div className={RESIZE_HANDLE_CLASS} {...handlers} />\n <div className=\"athena-sdk-chrome flex h-full flex-1 flex-col overflow-hidden\">\n <AssetPanel manageAssetPanelHostRegistration={false} />\n </div>\n </div>\n );\n};\n\nconst WorkbenchSplitLayout: FC<\n Omit<AthenaLayoutProps, \"workbench\" | \"defaultChatPercent\"> & { workbench: ReactNode }\n> = ({\n children,\n workbench,\n minPercent = 25,\n defaultWorkbenchPercent = 40,\n defaultWorkbenchCollapsed = false,\n}) => {\n const [workbenchPercent, setWorkbenchPercent] = useState(defaultWorkbenchPercent);\n const [collapsed, setCollapsedState] = useState(defaultWorkbenchCollapsed);\n const [maximizedState, setMaximizedState] = useState(false);\n // A rail is never maximized: collapsing restores the chat beside it.\n const maximized = maximizedState && !collapsed;\n const setCollapsed = useCallback((next: boolean) => {\n setCollapsedState(next);\n if (next) setMaximizedState(false);\n }, []);\n const setMaximized = useCallback((next: boolean) => {\n setMaximizedState(next);\n if (next) setCollapsedState(false);\n }, []);\n const { containerRef, handlers } = useSplitDrag({\n onPercent: useCallback(\n (pct: number) =>\n setWorkbenchPercent(Math.max(minPercent, Math.min(100 - minPercent, 100 - pct))),\n [minPercent],\n ),\n });\n const layoutState = useMemo<WorkbenchLayoutState>(\n () => ({ collapsed, setCollapsed, maximized, setMaximized, hostsAssetPanel: true }),\n [collapsed, maximized, setCollapsed, setMaximized],\n );\n\n return (\n <div ref={containerRef} className=\"flex h-full w-full\">\n {/* Hidden, not unmounted, while the workbench is maximized: the chat keeps its thread and draft. */}\n <div\n className={cn(\"flex h-full min-w-0 flex-1 flex-col overflow-hidden\", maximized && \"hidden\")}\n >\n {children}\n </div>\n {collapsed || maximized ? null : (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize workspace\"\n className={RESIZE_HANDLE_CLASS}\n {...handlers}\n />\n )}\n <div\n className={cn(\n \"flex h-full shrink-0 flex-col overflow-hidden\",\n collapsed && \"w-10\",\n maximized && \"min-w-0 flex-1\",\n )}\n // The dragged width is a runtime percentage — no static utility expresses it.\n style={collapsed || maximized ? undefined : { width: `${workbenchPercent}%` }}\n >\n <WorkbenchLayoutContext.Provider value={layoutState}>\n {workbench}\n </WorkbenchLayoutContext.Provider>\n </div>\n </div>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AthenaLayout.js","sources":["../../src/assets/AthenaLayout.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type FC,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport {\n WorkbenchLayoutContext,\n type WorkbenchLayoutState,\n} from \"../workbench/workbench-layout-context\";\nimport { useAssetPanelStore } from \"./asset-panel-store\";\nimport { AssetPanel } from \"./AssetPanel\";\n\nexport interface AthenaLayoutProps {\n children: ReactNode;\n /** Default width (%) of the chat panel when assets are open. Defaults to 50. */\n defaultChatPercent?: number;\n /** Minimum width (%) of each panel. Defaults to 25. */\n minPercent?: number;\n /**\n * A right-hand workspace column beside the chat — typically\n * `<SessionWorkbench />`. It is always shown (collapsible to a rail,\n * maximizable over the chat, and resizable by its edge), and the assets the agent or the person opens\n * render inside it as its \"Open assets\" tab instead of in a column of their\n * own.\n */\n workbench?: ReactNode;\n /** Default width (%) of the workbench column. Defaults to 40. */\n defaultWorkbenchPercent?: number;\n /**\n * Whether a session's workbench starts collapsed to its rail. Each session\n * keeps its own open or collapsed workbench while the layout stays mounted,\n * as it keeps its own tabs, so this is how every session the person has not\n * opened it in shows it — a new one, or one switched to. Defaults to `false`.\n */\n defaultWorkbenchCollapsed?: boolean;\n}\n\n/** Drag-to-resize for a split: the pointer's x as a share of the container. */\nfunction useSplitDrag({\n onPercent,\n}: {\n /** Called with the pointer's x position as a percentage of the container width. */\n onPercent: (percentFromLeft: number) => void;\n}) {\n const containerRef = useRef<HTMLDivElement>(null);\n const dragging = useRef(false);\n const onPointerDown = useCallback((e: React.PointerEvent) => {\n e.preventDefault();\n dragging.current = true;\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n }, []);\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n if (!dragging.current || !containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n onPercent(((e.clientX - rect.left) / rect.width) * 100);\n },\n [onPercent],\n );\n const onPointerUp = useCallback(() => {\n dragging.current = false;\n }, []);\n return { containerRef, handlers: { onPointerDown, onPointerMove, onPointerUp } };\n}\n\nconst RESIZE_HANDLE_CLASS =\n \"athena-sdk-chrome flex h-full w-1 shrink-0 cursor-col-resize items-center bg-border/40 transition-colors hover:bg-border active:bg-primary/30\";\n\n/**\n * Layout that places the chat on the left and the asset panel on the right.\n * When no assets are open, the chat takes full width. Includes a draggable resize handle.\n * With `workbench`, the right-hand column is the workbench instead (see the prop).\n */\nexport const AthenaLayout: FC<AthenaLayoutProps> = ({ workbench, ...props }) => {\n const registerAssetPanelHost = useAssetPanelStore((s) => s.registerAssetPanelHost);\n const unregisterAssetPanelHost = useAssetPanelStore((s) => s.unregisterAssetPanelHost);\n\n useEffect(() => {\n registerAssetPanelHost();\n return () => {\n unregisterAssetPanelHost();\n };\n }, [registerAssetPanelHost, unregisterAssetPanelHost]);\n\n return workbench === undefined || workbench === null ? (\n <AssetSplitLayout {...props} />\n ) : (\n <WorkbenchSplitLayout {...props} workbench={workbench} />\n );\n};\n\nconst AssetSplitLayout: FC<Omit<AthenaLayoutProps, \"workbench\">> = ({\n children,\n defaultChatPercent = 50,\n minPercent = 25,\n}) => {\n const isOpen = useAssetPanelStore((s) => s.isOpen);\n const [chatPercent, setChatPercent] = useState(defaultChatPercent);\n const { containerRef, handlers } = useSplitDrag({\n onPercent: useCallback(\n (pct: number) => setChatPercent(Math.max(minPercent, Math.min(100 - minPercent, pct))),\n [minPercent],\n ),\n });\n\n if (!isOpen) {\n return <div className=\"flex h-full w-full flex-col\">{children}</div>;\n }\n\n return (\n <div ref={containerRef} className=\"flex h-full w-full\">\n <div\n className=\"flex h-full flex-col overflow-hidden\"\n style={{ width: `${chatPercent}%` }}\n >\n {children}\n </div>\n <div className={RESIZE_HANDLE_CLASS} {...handlers} />\n <div className=\"athena-sdk-chrome flex h-full flex-1 flex-col overflow-hidden\">\n <AssetPanel manageAssetPanelHostRegistration={false} />\n </div>\n </div>\n );\n};\n\nconst WorkbenchSplitLayout: FC<\n Omit<AthenaLayoutProps, \"workbench\" | \"defaultChatPercent\"> & { workbench: ReactNode }\n> = ({\n children,\n workbench,\n minPercent = 25,\n defaultWorkbenchPercent = 40,\n defaultWorkbenchCollapsed = false,\n}) => {\n const [workbenchPercent, setWorkbenchPercent] = useState(defaultWorkbenchPercent);\n const [collapsed, setCollapsedState] = useState(defaultWorkbenchCollapsed);\n const [maximizedState, setMaximizedState] = useState(false);\n // A rail is never maximized: collapsing restores the chat beside it.\n const maximized = maximizedState && !collapsed;\n const setCollapsed = useCallback((next: boolean) => {\n setCollapsedState(next);\n if (next) setMaximizedState(false);\n }, []);\n const setMaximized = useCallback((next: boolean) => {\n setMaximizedState(next);\n if (next) setCollapsedState(false);\n }, []);\n const { containerRef, handlers } = useSplitDrag({\n onPercent: useCallback(\n (pct: number) =>\n setWorkbenchPercent(Math.max(minPercent, Math.min(100 - minPercent, 100 - pct))),\n [minPercent],\n ),\n });\n const layoutState = useMemo<WorkbenchLayoutState>(\n () => ({\n collapsed,\n setCollapsed,\n defaultCollapsed: defaultWorkbenchCollapsed,\n maximized,\n setMaximized,\n hostsAssetPanel: true,\n }),\n [collapsed, defaultWorkbenchCollapsed, maximized, setCollapsed, setMaximized],\n );\n\n return (\n <div ref={containerRef} className=\"flex h-full w-full\">\n {/* Hidden, not unmounted, while the workbench is maximized: the chat keeps its thread and draft. */}\n <div\n className={cn(\"flex h-full min-w-0 flex-1 flex-col overflow-hidden\", maximized && \"hidden\")}\n >\n {children}\n </div>\n {collapsed || maximized ? null : (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize workspace\"\n className={RESIZE_HANDLE_CLASS}\n {...handlers}\n />\n )}\n <div\n className={cn(\n \"flex h-full shrink-0 flex-col overflow-hidden\",\n collapsed && \"w-10\",\n maximized && \"min-w-0 flex-1\",\n )}\n // The dragged width is a runtime percentage — no static utility expresses it.\n style={collapsed || maximized ? undefined : { width: `${workbenchPercent}%` }}\n >\n <WorkbenchLayoutContext.Provider value={layoutState}>\n {workbench}\n </WorkbenchLayoutContext.Provider>\n </div>\n </div>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const version = "0.36.1";
3
+ const version = "0.37.0";
4
4
  const packageJson = {
5
5
  version
6
6
  };
@@ -1,4 +1,4 @@
1
- const version = "0.36.1";
1
+ const version = "0.37.0";
2
2
  const packageJson = {
3
3
  version
4
4
  };
@@ -85,6 +85,15 @@ const SessionWorkbenchInner = ({
85
85
  }, [defaultOpenPaneIds, panes]);
86
86
  const [tabsState, dispatchTabs] = react.useReducer(sessionWorkbenchModel.workbenchTabsReducer, {});
87
87
  const threadKey = threadId ?? "";
88
+ const defaultCollapsed = (layout == null ? void 0 : layout.defaultCollapsed) ?? false;
89
+ const collapsedByThread = react.useRef(/* @__PURE__ */ new Map());
90
+ const setThreadCollapsed = (next) => {
91
+ collapsedByThread.current.set(threadKey, next);
92
+ setCollapsed(next);
93
+ };
94
+ react.useLayoutEffect(() => {
95
+ setCollapsed(collapsedByThread.current.get(threadKey) ?? defaultCollapsed);
96
+ }, [threadKey, defaultCollapsed, setCollapsed]);
88
97
  let tabs = sessionWorkbenchModel.visibleThreadTabs(sessionWorkbenchModel.threadTabs(tabsState, threadKey, defaults), paneIds);
89
98
  if (paneIds.has(SESSION_WORKBENCH_ASSETS_PANE_ID) && !tabs.open.includes(SESSION_WORKBENCH_ASSETS_PANE_ID)) {
90
99
  tabs = {
@@ -105,6 +114,7 @@ const SessionWorkbenchInner = ({
105
114
  paneId: SESSION_WORKBENCH_ASSETS_PANE_ID,
106
115
  defaults: latest.current.defaults
107
116
  });
117
+ collapsedByThread.current.set(latest.current.threadKey, false);
108
118
  latest.current.setCollapsed(false);
109
119
  });
110
120
  }, [hostsAssetPanel]);
@@ -147,7 +157,7 @@ const SessionWorkbenchInner = ({
147
157
  type: "button",
148
158
  "aria-label": "Expand workspace",
149
159
  title: "Expand workspace",
150
- onClick: () => setCollapsed(false),
160
+ onClick: () => setThreadCollapsed(false),
151
161
  className: ICON_BUTTON_CLASS,
152
162
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelRightOpenIcon, { className: "size-4" })
153
163
  }
@@ -164,7 +174,7 @@ const SessionWorkbenchInner = ({
164
174
  title: pane.title,
165
175
  onClick: () => {
166
176
  activatePane(paneId);
167
- setCollapsed(false);
177
+ setThreadCollapsed(false);
168
178
  },
169
179
  className: cnfast.cn(
170
180
  ICON_BUTTON_CLASS,
@@ -255,7 +265,7 @@ const SessionWorkbenchInner = ({
255
265
  type: "button",
256
266
  "aria-label": "Collapse workspace",
257
267
  title: "Collapse workspace",
258
- onClick: () => setCollapsed(true),
268
+ onClick: () => setThreadCollapsed(true),
259
269
  className: ICON_BUTTON_CLASS,
260
270
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelRightCloseIcon, { className: "size-4" })
261
271
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SessionWorkbench.cjs","sources":["../../src/workbench/SessionWorkbench.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuiState } from '@assistant-ui/react';\nimport { readDeepAgentStreamValues } from '@athenaintel/agent-runtime';\nimport { QueryClientProvider, useQueryClient } from '@tanstack/react-query';\nimport {\n LayersIcon,\n Maximize2Icon,\n Minimize2Icon,\n PanelRightCloseIcon,\n PanelRightOpenIcon,\n PlusIcon,\n SearchIcon,\n XIcon,\n} from 'lucide-react';\nimport {\n type FC,\n type KeyboardEvent,\n useContext,\n useEffect,\n useId,\n useMemo,\n useReducer,\n useRef,\n useState,\n} from 'react';\nimport { useAssetPanelStore } from '../assets/asset-panel-store';\nimport { AssetPanel } from '../assets/AssetPanel';\nimport { useAthenaQueryClient } from '../lib/use-athena-rest-query';\nimport { cn } from '../lib/utils';\nimport { useAthenaStatewireThreadList } from '../threads/statewire-thread-list';\nimport { AthenaStatewireThreadIdContext } from '../threads/thread-id-context';\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';\nimport {\n type SessionWorkbenchPane,\n type SessionWorkbenchPaneContext,\n SessionWorkbenchContext,\n} from './session-workbench-context';\nimport {\n filterWorkbenchPanes,\n threadTabs,\n visibleThreadTabs,\n type WorkbenchThreadTabs,\n workbenchTabsReducer,\n} from './session-workbench-model';\nimport {\n DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS,\n DEFAULT_SESSION_WORKBENCH_PANES,\n} from './session-workbench-panes';\nimport { SESSION_WORKSPACE_QUERY_KEY } from './use-session-workspace';\nimport { useWorkbenchLayout } from './workbench-layout-context';\n\n/** The tab `AthenaLayout`'s workbench shows the open assets in. */\nexport const SESSION_WORKBENCH_ASSETS_PANE_ID = 'open-assets';\n\nconst OPEN_ASSETS_PANE: SessionWorkbenchPane = {\n id: SESSION_WORKBENCH_ASSETS_PANE_ID,\n title: 'Open assets',\n description: 'The documents, sheets and decks open in this session',\n icon: LayersIcon,\n render: () => <AssetPanel manageAssetPanelHostRegistration={false} />,\n};\n\nexport interface SessionWorkbenchProps {\n /**\n * The panes the \"+\" menu offers, in order. Defaults to the built-in Tasks,\n * Shell, Computer, Pull requests and Attachments\n * (`DEFAULT_SESSION_WORKBENCH_PANES`); spread it to add your own.\n */\n panes?: readonly SessionWorkbenchPane[];\n /**\n * The panes a thread opens with until the person changes its tabs (ids from\n * `panes`; unknown ids are ignored). Defaults to Tasks, Shell and Computer.\n */\n defaultOpenPaneIds?: readonly string[];\n className?: string;\n}\n\nconst ICON_BUTTON_CLASS =\n 'inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring/60';\n\n/**\n * A Devin-style workspace pane for the active session: a tab strip of panes\n * (Tasks, Shell, Computer, Pull requests, Attachments — and any the host\n * adds), a \"+\" menu to open more, closable tabs, and a collapse to a rail.\n *\n * Mount it inside `<AthenaProvider>` — as `AthenaLayout`'s `workbench` for a\n * resizable right-hand column, or anywhere in your own layout. Each thread\n * keeps its own open tabs while the workbench stays mounted (in memory only);\n * a thread it has not seen opens `defaultOpenPaneIds`. In `AthenaLayout`, the\n * assets the agent or the person opens show here too, as an \"Open assets\" tab.\n */\nexport const SessionWorkbench: FC<SessionWorkbenchProps> = (props) => {\n // One client for every pane, so the workspace read several panes share is\n // fetched once even when the host provides no QueryClient.\n const queryClient = useAthenaQueryClient();\n return (\n <QueryClientProvider client={queryClient}>\n <SessionWorkbenchInner {...props} />\n </QueryClientProvider>\n );\n};\n\nconst SessionWorkbenchInner: FC<SessionWorkbenchProps> = ({\n panes = DEFAULT_SESSION_WORKBENCH_PANES,\n defaultOpenPaneIds = DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS,\n className,\n}) => {\n const layout = useWorkbenchLayout();\n const [ownCollapsed, setOwnCollapsed] = useState(false);\n const collapsed = layout ? layout.collapsed : ownCollapsed;\n const setCollapsed = layout ? layout.setCollapsed : setOwnCollapsed;\n // Only a hosting layout can give the workbench the chat's width.\n const maximized = layout?.maximized ?? false;\n const setMaximized = layout?.setMaximized;\n const hostsAssetPanel = layout?.hostsAssetPanel ?? false;\n\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const threadList = useAthenaStatewireThreadList();\n const hasMessages = useAuiState((state) => state.thread.messages.length > 0);\n // The attach snapshot says whether the backend minted the thread's session\n // (`sessionPersisted`) — true for an existing thread before any message.\n const sessionPersisted = useAuiState(\n (state) =>\n readDeepAgentStreamValues((state.thread.extras as { state?: unknown } | undefined)?.state)\n .sessionPersisted,\n );\n const isRunning = useAuiState(\n (state) => (state.thread.extras as { status?: unknown } | undefined)?.status === 'running',\n );\n // In thread-list mode the list knows whether the backend has the thread; a\n // single-thread provider's thread is known once the host says its session\n // exists, or once it has history (a host that predates the flag).\n const isPersisted = threadList\n ? !threadList.isNewChat\n : sessionPersisted === true || hasMessages;\n const paneContext = useMemo<SessionWorkbenchPaneContext>(\n () => ({ threadId, isPersisted, isRunning }),\n [threadId, isPersisted, isRunning],\n );\n\n // Polling stops with the run; read the workspace once more as it settles so\n // what the run's last steps created shows without waiting for a refocus.\n const queryClient = useQueryClient();\n const wasRunning = useRef(isRunning);\n useEffect(() => {\n if (wasRunning.current && !isRunning) {\n void queryClient.invalidateQueries({ queryKey: SESSION_WORKSPACE_QUERY_KEY });\n }\n wasRunning.current = isRunning;\n }, [isRunning, queryClient]);\n\n const assetPanelOpen = useAssetPanelStore((state) => state.isOpen);\n const closeAssetPanel = useAssetPanelStore((state) => state.closePanel);\n const allPanes = useMemo<readonly SessionWorkbenchPane[]>(\n () => (hostsAssetPanel && assetPanelOpen ? [...panes, OPEN_ASSETS_PANE] : panes),\n [assetPanelOpen, hostsAssetPanel, panes],\n );\n const paneById = useMemo(() => new Map(allPanes.map((pane) => [pane.id, pane])), [allPanes]);\n const paneIds = useMemo(() => new Set(paneById.keys()), [paneById]);\n\n const defaults = useMemo<WorkbenchThreadTabs>(() => {\n const open = defaultOpenPaneIds.filter((id) => panes.some((pane) => pane.id === id));\n return { open, active: open[0] ?? null };\n }, [defaultOpenPaneIds, panes]);\n const [tabsState, dispatchTabs] = useReducer(workbenchTabsReducer, {});\n const threadKey = threadId ?? '';\n let tabs = visibleThreadTabs(threadTabs(tabsState, threadKey, defaults), paneIds);\n // Assets already open when the workbench mounts (or when a thread with open\n // assets comes back) show their tab without an opening to react to.\n if (\n paneIds.has(SESSION_WORKBENCH_ASSETS_PANE_ID) &&\n !tabs.open.includes(SESSION_WORKBENCH_ASSETS_PANE_ID)\n ) {\n tabs = {\n open: [...tabs.open, SESSION_WORKBENCH_ASSETS_PANE_ID],\n active: tabs.active ?? SESSION_WORKBENCH_ASSETS_PANE_ID,\n };\n }\n\n // An asset opening (a tool card, a citation, the Attachments pane) brings\n // the \"Open assets\" tab forward and the workbench out of its rail. A thread\n // switch restoring that thread's open assets (`setCurrentThread`) is not an\n // opening: the thread keeps the pane it had selected. Read through refs:\n // the store notifies outside React's render.\n const latest = useRef({ threadKey, defaults, setCollapsed });\n latest.current = { threadKey, defaults, setCollapsed };\n useEffect(() => {\n if (!hostsAssetPanel) return undefined;\n return useAssetPanelStore.subscribe((state, previous) => {\n if (state.currentThreadId !== previous.currentThreadId) return;\n // Every open request, a repeat click on the asset already showing too.\n if (!state.isOpen || state.openRequestSeq === previous.openRequestSeq) return;\n dispatchTabs({\n type: 'open',\n threadKey: latest.current.threadKey,\n paneId: SESSION_WORKBENCH_ASSETS_PANE_ID,\n defaults: latest.current.defaults,\n });\n latest.current.setCollapsed(false);\n });\n }, [hostsAssetPanel]);\n\n // Showing a tab the strip renders is an `open`: it activates a stored tab\n // and records one the strip only derived (the \"Open assets\" tab above).\n const openPane = (paneId: string) =>\n dispatchTabs({ type: 'open', threadKey, paneId, defaults });\n const activatePane = openPane;\n const closePane = (paneId: string) => {\n if (paneId === SESSION_WORKBENCH_ASSETS_PANE_ID) closeAssetPanel();\n dispatchTabs({ type: 'close', threadKey, paneId, defaults });\n };\n\n const baseId = useId();\n const tabId = (paneId: string) => `${baseId}-tab-${paneId}`;\n const panelId = `${baseId}-panel`;\n\n const onTabKeyDown = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0;\n if (step === 0 || tabs.open.length === 0) return;\n event.preventDefault();\n const nextId = tabs.open[(index + step + tabs.open.length) % tabs.open.length];\n if (!nextId) return;\n activatePane(nextId);\n event.currentTarget.ownerDocument.getElementById(tabId(nextId))?.focus();\n };\n\n const activePane = tabs.active ? paneById.get(tabs.active) : undefined;\n const assetsPane = paneById.get(SESSION_WORKBENCH_ASSETS_PANE_ID);\n const assetsActive = activePane?.id === SESSION_WORKBENCH_ASSETS_PANE_ID;\n\n // One tree for both states: the panel stays mounted (hidden) while the\n // workbench is a rail, and the open assets stay mounted (hidden) while\n // another pane shows — their iframes keep their editing session, as the\n // asset panel's own tabs do.\n return (\n <SessionWorkbenchContext.Provider value={paneContext}>\n <aside\n aria-label=\"Session workspace\"\n className={cn(\n 'athena-sdk-chrome flex h-full flex-col border-border border-l bg-background',\n collapsed ? 'w-10 shrink-0 items-center gap-1 py-2' : 'min-h-0 w-full min-w-0',\n className,\n )}\n >\n {collapsed ? (\n <>\n <button\n type=\"button\"\n aria-label=\"Expand workspace\"\n title=\"Expand workspace\"\n onClick={() => setCollapsed(false)}\n className={ICON_BUTTON_CLASS}\n >\n <PanelRightOpenIcon className=\"size-4\" />\n </button>\n {tabs.open.map((paneId) => {\n const pane = paneById.get(paneId);\n if (!pane) return null;\n const Icon = pane.icon ?? LayersIcon;\n return (\n <button\n key={paneId}\n type=\"button\"\n aria-label={`Show ${pane.title}`}\n title={pane.title}\n onClick={() => {\n activatePane(paneId);\n setCollapsed(false);\n }}\n className={cn(\n ICON_BUTTON_CLASS,\n paneId === tabs.active && 'bg-muted text-foreground',\n )}\n >\n <Icon className=\"size-4\" />\n </button>\n );\n })}\n </>\n ) : (\n <div className=\"flex h-11 w-full shrink-0 items-center gap-1 border-border border-b pr-1.5 pl-2\">\n <div\n role=\"tablist\"\n aria-label=\"Workspace panes\"\n className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none]\"\n >\n {tabs.open.map((paneId, index) => {\n const pane = paneById.get(paneId);\n if (!pane) return null;\n const Icon = pane.icon;\n const selected = paneId === tabs.active;\n return (\n <div\n key={paneId}\n data-selected={selected ? '' : undefined}\n className={cn(\n 'group/tab flex h-7 shrink-0 items-center rounded-md text-[13px] transition-colors',\n selected\n ? 'bg-foreground/[0.06] text-foreground'\n : 'text-muted-foreground hover:bg-foreground/[0.04] hover:text-foreground',\n )}\n >\n <button\n type=\"button\"\n role=\"tab\"\n id={tabId(paneId)}\n aria-selected={selected}\n aria-controls={panelId}\n tabIndex={selected ? 0 : -1}\n onClick={() => activatePane(paneId)}\n onKeyDown={(event) => onTabKeyDown(event, index)}\n className=\"flex h-full items-center gap-1.5 rounded-md pr-1 pl-2 outline-none focus-visible:ring-2 focus-visible:ring-ring/60\"\n >\n {Icon ? <Icon className=\"size-3.5\" /> : null}\n {pane.title}\n </button>\n {/* Quiet until the tab is hovered, focused or selected, as in Devin's strip. */}\n <button\n type=\"button\"\n aria-label={`Close ${pane.title}`}\n title={`Close ${pane.title}`}\n onClick={() => closePane(paneId)}\n className={cn(\n 'mr-1 rounded p-0.5 transition-opacity hover:bg-background hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60',\n selected ? 'opacity-60' : 'opacity-0 group-hover/tab:opacity-60',\n )}\n >\n <XIcon className=\"size-3\" />\n </button>\n </div>\n );\n })}\n </div>\n <AddPaneMenu panes={allPanes} openIds={tabs.open} onOpen={openPane} />\n <div className=\"min-w-0 flex-1\" />\n {setMaximized ? (\n <button\n type=\"button\"\n aria-label={maximized ? 'Restore workspace' : 'Maximize workspace'}\n title={maximized ? 'Restore workspace' : 'Maximize workspace'}\n aria-pressed={maximized}\n onClick={() => setMaximized(!maximized)}\n className={ICON_BUTTON_CLASS}\n >\n {maximized ? <Minimize2Icon className=\"size-4\" /> : <Maximize2Icon className=\"size-4\" />}\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label=\"Collapse workspace\"\n title=\"Collapse workspace\"\n onClick={() => setCollapsed(true)}\n className={ICON_BUTTON_CLASS}\n >\n <PanelRightCloseIcon className=\"size-4\" />\n </button>\n </div>\n )}\n <div\n role=\"tabpanel\"\n id={panelId}\n aria-labelledby={activePane ? tabId(activePane.id) : undefined}\n className={cn(\n 'min-h-0 w-full flex-1',\n assetsActive ? 'flex flex-col' : 'overflow-y-auto',\n collapsed && 'hidden',\n )}\n >\n {assetsActive ? null : activePane ? (\n activePane.render(paneContext)\n ) : (\n <NoOpenPane panes={allPanes} onOpen={openPane} />\n )}\n {assetsPane ? (\n <div\n key={SESSION_WORKBENCH_ASSETS_PANE_ID}\n data-workbench-assets=\"\"\n className={cn(assetsActive ? 'flex min-h-0 flex-1 flex-col' : 'hidden')}\n >\n {assetsPane.render(paneContext)}\n </div>\n ) : null}\n </div>\n </aside>\n </SessionWorkbenchContext.Provider>\n );\n};\n\nconst PaneMenuItems: FC<{\n panes: readonly SessionWorkbenchPane[];\n openIds: readonly string[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, openIds, onOpen }) => (\n <ul className=\"flex flex-col\">\n {panes.map((pane) => {\n const Icon = pane.icon ?? LayersIcon;\n const isOpen = openIds.includes(pane.id);\n return (\n <li key={pane.id}>\n <button\n type=\"button\"\n title={pane.description}\n onClick={() => onOpen(pane.id)}\n className=\"flex h-[30px] w-full items-center gap-2 rounded-md px-2 text-left text-[13px] transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none\"\n >\n <Icon className=\"size-4 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate\">{pane.title}</span>\n {isOpen ? <span className=\"text-[11px] text-muted-foreground\">Open</span> : null}\n </button>\n </li>\n );\n })}\n </ul>\n);\n\nconst AddPaneMenu: FC<{\n panes: readonly SessionWorkbenchPane[];\n openIds: readonly string[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, openIds, onOpen }) => {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const shown = filterWorkbenchPanes(panes, query);\n const choose = (paneId: string) => {\n onOpen(paneId);\n setOpen(false);\n setQuery('');\n };\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setQuery('');\n }}\n >\n <PopoverTrigger asChild>\n <button type=\"button\" aria-label=\"Open a pane\" title=\"Open a pane\" className={ICON_BUTTON_CLASS}>\n <PlusIcon className=\"size-4\" />\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-56 p-1\" aria-label=\"Panes\">\n <div className=\"flex items-center gap-2 border-border border-b px-2 pb-1.5 pt-1\">\n <SearchIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <input\n // biome-ignore lint/a11y/noAutofocus: the menu opens to type into, as a command menu does.\n autoFocus\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={(event) => {\n const first = shown[0];\n if (event.key === 'Enter' && first) {\n event.preventDefault();\n choose(first.id);\n }\n }}\n placeholder=\"Search\"\n aria-label=\"Search panes\"\n className=\"h-7 min-w-0 flex-1 bg-transparent text-[13px] outline-none placeholder:text-muted-foreground\"\n />\n </div>\n <div className=\"pt-1\">\n {shown.length > 0 ? (\n <PaneMenuItems panes={shown} openIds={openIds} onOpen={choose} />\n ) : (\n <p className=\"px-2 py-1.5 text-[13px] text-muted-foreground\">No pane matches.</p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n};\n\n/** Nothing open: every pane the \"+\" menu offers, with what each shows, centred in the column. */\nconst NoOpenPane: FC<{\n panes: readonly SessionWorkbenchPane[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, onOpen }) => (\n <div className=\"flex min-h-full items-center justify-center p-6\">\n <ul aria-label=\"Available panes\" className=\"flex w-full max-w-xs flex-col gap-1\">\n {panes.map((pane) => {\n const Icon = pane.icon ?? LayersIcon;\n return (\n <li key={pane.id}>\n <button\n type=\"button\"\n onClick={() => onOpen(pane.id)}\n className=\"flex w-full items-start gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none\"\n >\n <Icon className=\"mt-0.5 size-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"text-[13px] text-foreground\">{pane.title}</span>\n {pane.description ? (\n <span className=\"text-muted-foreground text-xs\">{pane.description}</span>\n ) : null}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n);\n"],"names":["LayersIcon","jsx","AssetPanel","useAthenaQueryClient","QueryClientProvider","DEFAULT_SESSION_WORKBENCH_PANES","DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS","useWorkbenchLayout","useState","useContext","AthenaStatewireThreadIdContext","useAthenaStatewireThreadList","useAuiState","readDeepAgentStreamValues","useMemo","useQueryClient","useRef","useEffect","SESSION_WORKSPACE_QUERY_KEY","useAssetPanelStore","useReducer","workbenchTabsReducer","visibleThreadTabs","threadTabs","useId","SessionWorkbenchContext","jsxs","cn","Fragment","PanelRightOpenIcon","XIcon","Minimize2Icon","Maximize2Icon","PanelRightCloseIcon","filterWorkbenchPanes","Popover","PopoverTrigger","PlusIcon","PopoverContent","SearchIcon"],"mappings":"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"}
1
+ {"version":3,"file":"SessionWorkbench.cjs","sources":["../../src/workbench/SessionWorkbench.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuiState } from '@assistant-ui/react';\nimport { readDeepAgentStreamValues } from '@athenaintel/agent-runtime';\nimport { QueryClientProvider, useQueryClient } from '@tanstack/react-query';\nimport {\n LayersIcon,\n Maximize2Icon,\n Minimize2Icon,\n PanelRightCloseIcon,\n PanelRightOpenIcon,\n PlusIcon,\n SearchIcon,\n XIcon,\n} from 'lucide-react';\nimport {\n type FC,\n type KeyboardEvent,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n} from 'react';\nimport { useAssetPanelStore } from '../assets/asset-panel-store';\nimport { AssetPanel } from '../assets/AssetPanel';\nimport { useAthenaQueryClient } from '../lib/use-athena-rest-query';\nimport { cn } from '../lib/utils';\nimport { useAthenaStatewireThreadList } from '../threads/statewire-thread-list';\nimport { AthenaStatewireThreadIdContext } from '../threads/thread-id-context';\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';\nimport {\n type SessionWorkbenchPane,\n type SessionWorkbenchPaneContext,\n SessionWorkbenchContext,\n} from './session-workbench-context';\nimport {\n filterWorkbenchPanes,\n threadTabs,\n visibleThreadTabs,\n type WorkbenchThreadTabs,\n workbenchTabsReducer,\n} from './session-workbench-model';\nimport {\n DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS,\n DEFAULT_SESSION_WORKBENCH_PANES,\n} from './session-workbench-panes';\nimport { SESSION_WORKSPACE_QUERY_KEY } from './use-session-workspace';\nimport { useWorkbenchLayout } from './workbench-layout-context';\n\n/** The tab `AthenaLayout`'s workbench shows the open assets in. */\nexport const SESSION_WORKBENCH_ASSETS_PANE_ID = 'open-assets';\n\nconst OPEN_ASSETS_PANE: SessionWorkbenchPane = {\n id: SESSION_WORKBENCH_ASSETS_PANE_ID,\n title: 'Open assets',\n description: 'The documents, sheets and decks open in this session',\n icon: LayersIcon,\n render: () => <AssetPanel manageAssetPanelHostRegistration={false} />,\n};\n\nexport interface SessionWorkbenchProps {\n /**\n * The panes the \"+\" menu offers, in order. Defaults to the built-in Tasks,\n * Shell, Computer, Pull requests and Attachments\n * (`DEFAULT_SESSION_WORKBENCH_PANES`); spread it to add your own.\n */\n panes?: readonly SessionWorkbenchPane[];\n /**\n * The panes a thread opens with until the person changes its tabs (ids from\n * `panes`; unknown ids are ignored). Defaults to Tasks, Shell and Computer.\n */\n defaultOpenPaneIds?: readonly string[];\n className?: string;\n}\n\nconst ICON_BUTTON_CLASS =\n 'inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring/60';\n\n/**\n * A Devin-style workspace pane for the active session: a tab strip of panes\n * (Tasks, Shell, Computer, Pull requests, Attachments — and any the host\n * adds), a \"+\" menu to open more, closable tabs, and a collapse to a rail.\n *\n * Mount it inside `<AthenaProvider>` — as `AthenaLayout`'s `workbench` for a\n * resizable right-hand column, or anywhere in your own layout. Each thread\n * keeps its own open tabs, and whether the workspace is open or collapsed,\n * while the workbench stays mounted (in memory only); a thread it has not seen\n * opens `defaultOpenPaneIds`, collapsed when the layout's\n * `defaultWorkbenchCollapsed` says so. In `AthenaLayout`, the assets the agent\n * or the person opens show here too, as an \"Open assets\" tab.\n */\nexport const SessionWorkbench: FC<SessionWorkbenchProps> = (props) => {\n // One client for every pane, so the workspace read several panes share is\n // fetched once even when the host provides no QueryClient.\n const queryClient = useAthenaQueryClient();\n return (\n <QueryClientProvider client={queryClient}>\n <SessionWorkbenchInner {...props} />\n </QueryClientProvider>\n );\n};\n\nconst SessionWorkbenchInner: FC<SessionWorkbenchProps> = ({\n panes = DEFAULT_SESSION_WORKBENCH_PANES,\n defaultOpenPaneIds = DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS,\n className,\n}) => {\n const layout = useWorkbenchLayout();\n const [ownCollapsed, setOwnCollapsed] = useState(false);\n const collapsed = layout ? layout.collapsed : ownCollapsed;\n const setCollapsed = layout ? layout.setCollapsed : setOwnCollapsed;\n // Only a hosting layout can give the workbench the chat's width.\n const maximized = layout?.maximized ?? false;\n const setMaximized = layout?.setMaximized;\n const hostsAssetPanel = layout?.hostsAssetPanel ?? false;\n\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const threadList = useAthenaStatewireThreadList();\n const hasMessages = useAuiState((state) => state.thread.messages.length > 0);\n // The attach snapshot says whether the backend minted the thread's session\n // (`sessionPersisted`) — true for an existing thread before any message.\n const sessionPersisted = useAuiState(\n (state) =>\n readDeepAgentStreamValues((state.thread.extras as { state?: unknown } | undefined)?.state)\n .sessionPersisted,\n );\n const isRunning = useAuiState(\n (state) => (state.thread.extras as { status?: unknown } | undefined)?.status === 'running',\n );\n // In thread-list mode the list knows whether the backend has the thread; a\n // single-thread provider's thread is known once the host says its session\n // exists, or once it has history (a host that predates the flag).\n const isPersisted = threadList\n ? !threadList.isNewChat\n : sessionPersisted === true || hasMessages;\n const paneContext = useMemo<SessionWorkbenchPaneContext>(\n () => ({ threadId, isPersisted, isRunning }),\n [threadId, isPersisted, isRunning],\n );\n\n // Polling stops with the run; read the workspace once more as it settles so\n // what the run's last steps created shows without waiting for a refocus.\n const queryClient = useQueryClient();\n const wasRunning = useRef(isRunning);\n useEffect(() => {\n if (wasRunning.current && !isRunning) {\n void queryClient.invalidateQueries({ queryKey: SESSION_WORKSPACE_QUERY_KEY });\n }\n wasRunning.current = isRunning;\n }, [isRunning, queryClient]);\n\n const assetPanelOpen = useAssetPanelStore((state) => state.isOpen);\n const closeAssetPanel = useAssetPanelStore((state) => state.closePanel);\n const allPanes = useMemo<readonly SessionWorkbenchPane[]>(\n () => (hostsAssetPanel && assetPanelOpen ? [...panes, OPEN_ASSETS_PANE] : panes),\n [assetPanelOpen, hostsAssetPanel, panes],\n );\n const paneById = useMemo(() => new Map(allPanes.map((pane) => [pane.id, pane])), [allPanes]);\n const paneIds = useMemo(() => new Set(paneById.keys()), [paneById]);\n\n const defaults = useMemo<WorkbenchThreadTabs>(() => {\n const open = defaultOpenPaneIds.filter((id) => panes.some((pane) => pane.id === id));\n return { open, active: open[0] ?? null };\n }, [defaultOpenPaneIds, panes]);\n const [tabsState, dispatchTabs] = useReducer(workbenchTabsReducer, {});\n const threadKey = threadId ?? '';\n\n // Whether the workspace is open is kept per thread, like its tabs: a thread\n // the person has not opened or collapsed it in shows the layout's default (a\n // new session, or one switched to), so one session's open workspace never\n // follows the person into the next. Applied before paint, so a switch never\n // flashes the previous thread's state.\n const defaultCollapsed = layout?.defaultCollapsed ?? false;\n const collapsedByThread = useRef(new Map<string, boolean>());\n const setThreadCollapsed = (next: boolean) => {\n collapsedByThread.current.set(threadKey, next);\n setCollapsed(next);\n };\n useLayoutEffect(() => {\n setCollapsed(collapsedByThread.current.get(threadKey) ?? defaultCollapsed);\n }, [threadKey, defaultCollapsed, setCollapsed]);\n let tabs = visibleThreadTabs(threadTabs(tabsState, threadKey, defaults), paneIds);\n // Assets already open when the workbench mounts (or when a thread with open\n // assets comes back) show their tab without an opening to react to.\n if (\n paneIds.has(SESSION_WORKBENCH_ASSETS_PANE_ID) &&\n !tabs.open.includes(SESSION_WORKBENCH_ASSETS_PANE_ID)\n ) {\n tabs = {\n open: [...tabs.open, SESSION_WORKBENCH_ASSETS_PANE_ID],\n active: tabs.active ?? SESSION_WORKBENCH_ASSETS_PANE_ID,\n };\n }\n\n // An asset opening (a tool card, a citation, the Attachments pane) brings\n // the \"Open assets\" tab forward and the workbench out of its rail. A thread\n // switch restoring that thread's open assets (`setCurrentThread`) is not an\n // opening: the thread keeps the pane it had selected. Read through refs:\n // the store notifies outside React's render.\n const latest = useRef({ threadKey, defaults, setCollapsed });\n latest.current = { threadKey, defaults, setCollapsed };\n useEffect(() => {\n if (!hostsAssetPanel) return undefined;\n return useAssetPanelStore.subscribe((state, previous) => {\n if (state.currentThreadId !== previous.currentThreadId) return;\n // Every open request, a repeat click on the asset already showing too.\n if (!state.isOpen || state.openRequestSeq === previous.openRequestSeq) return;\n dispatchTabs({\n type: 'open',\n threadKey: latest.current.threadKey,\n paneId: SESSION_WORKBENCH_ASSETS_PANE_ID,\n defaults: latest.current.defaults,\n });\n collapsedByThread.current.set(latest.current.threadKey, false);\n latest.current.setCollapsed(false);\n });\n }, [hostsAssetPanel]);\n\n // Showing a tab the strip renders is an `open`: it activates a stored tab\n // and records one the strip only derived (the \"Open assets\" tab above).\n const openPane = (paneId: string) =>\n dispatchTabs({ type: 'open', threadKey, paneId, defaults });\n const activatePane = openPane;\n const closePane = (paneId: string) => {\n if (paneId === SESSION_WORKBENCH_ASSETS_PANE_ID) closeAssetPanel();\n dispatchTabs({ type: 'close', threadKey, paneId, defaults });\n };\n\n const baseId = useId();\n const tabId = (paneId: string) => `${baseId}-tab-${paneId}`;\n const panelId = `${baseId}-panel`;\n\n const onTabKeyDown = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0;\n if (step === 0 || tabs.open.length === 0) return;\n event.preventDefault();\n const nextId = tabs.open[(index + step + tabs.open.length) % tabs.open.length];\n if (!nextId) return;\n activatePane(nextId);\n event.currentTarget.ownerDocument.getElementById(tabId(nextId))?.focus();\n };\n\n const activePane = tabs.active ? paneById.get(tabs.active) : undefined;\n const assetsPane = paneById.get(SESSION_WORKBENCH_ASSETS_PANE_ID);\n const assetsActive = activePane?.id === SESSION_WORKBENCH_ASSETS_PANE_ID;\n\n // One tree for both states: the panel stays mounted (hidden) while the\n // workbench is a rail, and the open assets stay mounted (hidden) while\n // another pane shows — their iframes keep their editing session, as the\n // asset panel's own tabs do.\n return (\n <SessionWorkbenchContext.Provider value={paneContext}>\n <aside\n aria-label=\"Session workspace\"\n className={cn(\n 'athena-sdk-chrome flex h-full flex-col border-border border-l bg-background',\n collapsed ? 'w-10 shrink-0 items-center gap-1 py-2' : 'min-h-0 w-full min-w-0',\n className,\n )}\n >\n {collapsed ? (\n <>\n <button\n type=\"button\"\n aria-label=\"Expand workspace\"\n title=\"Expand workspace\"\n onClick={() => setThreadCollapsed(false)}\n className={ICON_BUTTON_CLASS}\n >\n <PanelRightOpenIcon className=\"size-4\" />\n </button>\n {tabs.open.map((paneId) => {\n const pane = paneById.get(paneId);\n if (!pane) return null;\n const Icon = pane.icon ?? LayersIcon;\n return (\n <button\n key={paneId}\n type=\"button\"\n aria-label={`Show ${pane.title}`}\n title={pane.title}\n onClick={() => {\n activatePane(paneId);\n setThreadCollapsed(false);\n }}\n className={cn(\n ICON_BUTTON_CLASS,\n paneId === tabs.active && 'bg-muted text-foreground',\n )}\n >\n <Icon className=\"size-4\" />\n </button>\n );\n })}\n </>\n ) : (\n <div className=\"flex h-11 w-full shrink-0 items-center gap-1 border-border border-b pr-1.5 pl-2\">\n <div\n role=\"tablist\"\n aria-label=\"Workspace panes\"\n className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none]\"\n >\n {tabs.open.map((paneId, index) => {\n const pane = paneById.get(paneId);\n if (!pane) return null;\n const Icon = pane.icon;\n const selected = paneId === tabs.active;\n return (\n <div\n key={paneId}\n data-selected={selected ? '' : undefined}\n className={cn(\n 'group/tab flex h-7 shrink-0 items-center rounded-md text-[13px] transition-colors',\n selected\n ? 'bg-foreground/[0.06] text-foreground'\n : 'text-muted-foreground hover:bg-foreground/[0.04] hover:text-foreground',\n )}\n >\n <button\n type=\"button\"\n role=\"tab\"\n id={tabId(paneId)}\n aria-selected={selected}\n aria-controls={panelId}\n tabIndex={selected ? 0 : -1}\n onClick={() => activatePane(paneId)}\n onKeyDown={(event) => onTabKeyDown(event, index)}\n className=\"flex h-full items-center gap-1.5 rounded-md pr-1 pl-2 outline-none focus-visible:ring-2 focus-visible:ring-ring/60\"\n >\n {Icon ? <Icon className=\"size-3.5\" /> : null}\n {pane.title}\n </button>\n {/* Quiet until the tab is hovered, focused or selected, as in Devin's strip. */}\n <button\n type=\"button\"\n aria-label={`Close ${pane.title}`}\n title={`Close ${pane.title}`}\n onClick={() => closePane(paneId)}\n className={cn(\n 'mr-1 rounded p-0.5 transition-opacity hover:bg-background hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60',\n selected ? 'opacity-60' : 'opacity-0 group-hover/tab:opacity-60',\n )}\n >\n <XIcon className=\"size-3\" />\n </button>\n </div>\n );\n })}\n </div>\n <AddPaneMenu panes={allPanes} openIds={tabs.open} onOpen={openPane} />\n <div className=\"min-w-0 flex-1\" />\n {setMaximized ? (\n <button\n type=\"button\"\n aria-label={maximized ? 'Restore workspace' : 'Maximize workspace'}\n title={maximized ? 'Restore workspace' : 'Maximize workspace'}\n aria-pressed={maximized}\n onClick={() => setMaximized(!maximized)}\n className={ICON_BUTTON_CLASS}\n >\n {maximized ? <Minimize2Icon className=\"size-4\" /> : <Maximize2Icon className=\"size-4\" />}\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label=\"Collapse workspace\"\n title=\"Collapse workspace\"\n onClick={() => setThreadCollapsed(true)}\n className={ICON_BUTTON_CLASS}\n >\n <PanelRightCloseIcon className=\"size-4\" />\n </button>\n </div>\n )}\n <div\n role=\"tabpanel\"\n id={panelId}\n aria-labelledby={activePane ? tabId(activePane.id) : undefined}\n className={cn(\n 'min-h-0 w-full flex-1',\n assetsActive ? 'flex flex-col' : 'overflow-y-auto',\n collapsed && 'hidden',\n )}\n >\n {assetsActive ? null : activePane ? (\n activePane.render(paneContext)\n ) : (\n <NoOpenPane panes={allPanes} onOpen={openPane} />\n )}\n {assetsPane ? (\n <div\n key={SESSION_WORKBENCH_ASSETS_PANE_ID}\n data-workbench-assets=\"\"\n className={cn(assetsActive ? 'flex min-h-0 flex-1 flex-col' : 'hidden')}\n >\n {assetsPane.render(paneContext)}\n </div>\n ) : null}\n </div>\n </aside>\n </SessionWorkbenchContext.Provider>\n );\n};\n\nconst PaneMenuItems: FC<{\n panes: readonly SessionWorkbenchPane[];\n openIds: readonly string[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, openIds, onOpen }) => (\n <ul className=\"flex flex-col\">\n {panes.map((pane) => {\n const Icon = pane.icon ?? LayersIcon;\n const isOpen = openIds.includes(pane.id);\n return (\n <li key={pane.id}>\n <button\n type=\"button\"\n title={pane.description}\n onClick={() => onOpen(pane.id)}\n className=\"flex h-[30px] w-full items-center gap-2 rounded-md px-2 text-left text-[13px] transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none\"\n >\n <Icon className=\"size-4 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate\">{pane.title}</span>\n {isOpen ? <span className=\"text-[11px] text-muted-foreground\">Open</span> : null}\n </button>\n </li>\n );\n })}\n </ul>\n);\n\nconst AddPaneMenu: FC<{\n panes: readonly SessionWorkbenchPane[];\n openIds: readonly string[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, openIds, onOpen }) => {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const shown = filterWorkbenchPanes(panes, query);\n const choose = (paneId: string) => {\n onOpen(paneId);\n setOpen(false);\n setQuery('');\n };\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setQuery('');\n }}\n >\n <PopoverTrigger asChild>\n <button type=\"button\" aria-label=\"Open a pane\" title=\"Open a pane\" className={ICON_BUTTON_CLASS}>\n <PlusIcon className=\"size-4\" />\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-56 p-1\" aria-label=\"Panes\">\n <div className=\"flex items-center gap-2 border-border border-b px-2 pb-1.5 pt-1\">\n <SearchIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <input\n // biome-ignore lint/a11y/noAutofocus: the menu opens to type into, as a command menu does.\n autoFocus\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={(event) => {\n const first = shown[0];\n if (event.key === 'Enter' && first) {\n event.preventDefault();\n choose(first.id);\n }\n }}\n placeholder=\"Search\"\n aria-label=\"Search panes\"\n className=\"h-7 min-w-0 flex-1 bg-transparent text-[13px] outline-none placeholder:text-muted-foreground\"\n />\n </div>\n <div className=\"pt-1\">\n {shown.length > 0 ? (\n <PaneMenuItems panes={shown} openIds={openIds} onOpen={choose} />\n ) : (\n <p className=\"px-2 py-1.5 text-[13px] text-muted-foreground\">No pane matches.</p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n};\n\n/** Nothing open: every pane the \"+\" menu offers, with what each shows, centred in the column. */\nconst NoOpenPane: FC<{\n panes: readonly SessionWorkbenchPane[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, onOpen }) => (\n <div className=\"flex min-h-full items-center justify-center p-6\">\n <ul aria-label=\"Available panes\" className=\"flex w-full max-w-xs flex-col gap-1\">\n {panes.map((pane) => {\n const Icon = pane.icon ?? LayersIcon;\n return (\n <li key={pane.id}>\n <button\n type=\"button\"\n onClick={() => onOpen(pane.id)}\n className=\"flex w-full items-start gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none\"\n >\n <Icon className=\"mt-0.5 size-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"text-[13px] text-foreground\">{pane.title}</span>\n {pane.description ? (\n <span className=\"text-muted-foreground text-xs\">{pane.description}</span>\n ) : null}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n);\n"],"names":["LayersIcon","jsx","AssetPanel","useAthenaQueryClient","QueryClientProvider","DEFAULT_SESSION_WORKBENCH_PANES","DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS","useWorkbenchLayout","useState","useContext","AthenaStatewireThreadIdContext","useAthenaStatewireThreadList","useAuiState","readDeepAgentStreamValues","useMemo","useQueryClient","useRef","useEffect","SESSION_WORKSPACE_QUERY_KEY","useAssetPanelStore","useReducer","workbenchTabsReducer","useLayoutEffect","visibleThreadTabs","threadTabs","useId","SessionWorkbenchContext","jsxs","cn","Fragment","PanelRightOpenIcon","XIcon","Minimize2Icon","Maximize2Icon","PanelRightCloseIcon","filterWorkbenchPanes","Popover","PopoverTrigger","PlusIcon","PopoverContent","SearchIcon"],"mappings":"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"}
@@ -23,8 +23,10 @@ export interface SessionWorkbenchProps {
23
23
  *
24
24
  * Mount it inside `<AthenaProvider>` — as `AthenaLayout`'s `workbench` for a
25
25
  * resizable right-hand column, or anywhere in your own layout. Each thread
26
- * keeps its own open tabs while the workbench stays mounted (in memory only);
27
- * a thread it has not seen opens `defaultOpenPaneIds`. In `AthenaLayout`, the
28
- * assets the agent or the person opens show here too, as an "Open assets" tab.
26
+ * keeps its own open tabs, and whether the workspace is open or collapsed,
27
+ * while the workbench stays mounted (in memory only); a thread it has not seen
28
+ * opens `defaultOpenPaneIds`, collapsed when the layout's
29
+ * `defaultWorkbenchCollapsed` says so. In `AthenaLayout`, the assets the agent
30
+ * or the person opens show here too, as an "Open assets" tab.
29
31
  */
30
32
  export declare const SessionWorkbench: FC<SessionWorkbenchProps>;
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { useAuiState } from "@assistant-ui/react";
3
3
  import { QueryClientProvider, useQueryClient } from "@tanstack/react-query";
4
4
  import { LayersIcon, PanelRightOpenIcon, XIcon, Minimize2Icon, Maximize2Icon, PanelRightCloseIcon, PlusIcon, SearchIcon } from "lucide-react";
5
- import { useState, useContext, useMemo, useRef, useEffect, useReducer, useId } from "react";
5
+ import { useState, useContext, useMemo, useRef, useEffect, useReducer, useLayoutEffect, useId } from "react";
6
6
  import { useAssetPanelStore } from "../assets/asset-panel-store.js";
7
7
  import { AssetPanel } from "../assets/AssetPanel.js";
8
8
  import { useAthenaQueryClient } from "../lib/use-athena-rest-query.js";
@@ -83,6 +83,15 @@ const SessionWorkbenchInner = ({
83
83
  }, [defaultOpenPaneIds, panes]);
84
84
  const [tabsState, dispatchTabs] = useReducer(workbenchTabsReducer, {});
85
85
  const threadKey = threadId ?? "";
86
+ const defaultCollapsed = (layout == null ? void 0 : layout.defaultCollapsed) ?? false;
87
+ const collapsedByThread = useRef(/* @__PURE__ */ new Map());
88
+ const setThreadCollapsed = (next) => {
89
+ collapsedByThread.current.set(threadKey, next);
90
+ setCollapsed(next);
91
+ };
92
+ useLayoutEffect(() => {
93
+ setCollapsed(collapsedByThread.current.get(threadKey) ?? defaultCollapsed);
94
+ }, [threadKey, defaultCollapsed, setCollapsed]);
86
95
  let tabs = visibleThreadTabs(threadTabs(tabsState, threadKey, defaults), paneIds);
87
96
  if (paneIds.has(SESSION_WORKBENCH_ASSETS_PANE_ID) && !tabs.open.includes(SESSION_WORKBENCH_ASSETS_PANE_ID)) {
88
97
  tabs = {
@@ -103,6 +112,7 @@ const SessionWorkbenchInner = ({
103
112
  paneId: SESSION_WORKBENCH_ASSETS_PANE_ID,
104
113
  defaults: latest.current.defaults
105
114
  });
115
+ collapsedByThread.current.set(latest.current.threadKey, false);
106
116
  latest.current.setCollapsed(false);
107
117
  });
108
118
  }, [hostsAssetPanel]);
@@ -145,7 +155,7 @@ const SessionWorkbenchInner = ({
145
155
  type: "button",
146
156
  "aria-label": "Expand workspace",
147
157
  title: "Expand workspace",
148
- onClick: () => setCollapsed(false),
158
+ onClick: () => setThreadCollapsed(false),
149
159
  className: ICON_BUTTON_CLASS,
150
160
  children: /* @__PURE__ */ jsx(PanelRightOpenIcon, { className: "size-4" })
151
161
  }
@@ -162,7 +172,7 @@ const SessionWorkbenchInner = ({
162
172
  title: pane.title,
163
173
  onClick: () => {
164
174
  activatePane(paneId);
165
- setCollapsed(false);
175
+ setThreadCollapsed(false);
166
176
  },
167
177
  className: cn(
168
178
  ICON_BUTTON_CLASS,
@@ -253,7 +263,7 @@ const SessionWorkbenchInner = ({
253
263
  type: "button",
254
264
  "aria-label": "Collapse workspace",
255
265
  title: "Collapse workspace",
256
- onClick: () => setCollapsed(true),
266
+ onClick: () => setThreadCollapsed(true),
257
267
  className: ICON_BUTTON_CLASS,
258
268
  children: /* @__PURE__ */ jsx(PanelRightCloseIcon, { className: "size-4" })
259
269
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SessionWorkbench.js","sources":["../../src/workbench/SessionWorkbench.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuiState } from '@assistant-ui/react';\nimport { readDeepAgentStreamValues } from '@athenaintel/agent-runtime';\nimport { QueryClientProvider, useQueryClient } from '@tanstack/react-query';\nimport {\n LayersIcon,\n Maximize2Icon,\n Minimize2Icon,\n PanelRightCloseIcon,\n PanelRightOpenIcon,\n PlusIcon,\n SearchIcon,\n XIcon,\n} from 'lucide-react';\nimport {\n type FC,\n type KeyboardEvent,\n useContext,\n useEffect,\n useId,\n useMemo,\n useReducer,\n useRef,\n useState,\n} from 'react';\nimport { useAssetPanelStore } from '../assets/asset-panel-store';\nimport { AssetPanel } from '../assets/AssetPanel';\nimport { useAthenaQueryClient } from '../lib/use-athena-rest-query';\nimport { cn } from '../lib/utils';\nimport { useAthenaStatewireThreadList } from '../threads/statewire-thread-list';\nimport { AthenaStatewireThreadIdContext } from '../threads/thread-id-context';\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';\nimport {\n type SessionWorkbenchPane,\n type SessionWorkbenchPaneContext,\n SessionWorkbenchContext,\n} from './session-workbench-context';\nimport {\n filterWorkbenchPanes,\n threadTabs,\n visibleThreadTabs,\n type WorkbenchThreadTabs,\n workbenchTabsReducer,\n} from './session-workbench-model';\nimport {\n DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS,\n DEFAULT_SESSION_WORKBENCH_PANES,\n} from './session-workbench-panes';\nimport { SESSION_WORKSPACE_QUERY_KEY } from './use-session-workspace';\nimport { useWorkbenchLayout } from './workbench-layout-context';\n\n/** The tab `AthenaLayout`'s workbench shows the open assets in. */\nexport const SESSION_WORKBENCH_ASSETS_PANE_ID = 'open-assets';\n\nconst OPEN_ASSETS_PANE: SessionWorkbenchPane = {\n id: SESSION_WORKBENCH_ASSETS_PANE_ID,\n title: 'Open assets',\n description: 'The documents, sheets and decks open in this session',\n icon: LayersIcon,\n render: () => <AssetPanel manageAssetPanelHostRegistration={false} />,\n};\n\nexport interface SessionWorkbenchProps {\n /**\n * The panes the \"+\" menu offers, in order. Defaults to the built-in Tasks,\n * Shell, Computer, Pull requests and Attachments\n * (`DEFAULT_SESSION_WORKBENCH_PANES`); spread it to add your own.\n */\n panes?: readonly SessionWorkbenchPane[];\n /**\n * The panes a thread opens with until the person changes its tabs (ids from\n * `panes`; unknown ids are ignored). Defaults to Tasks, Shell and Computer.\n */\n defaultOpenPaneIds?: readonly string[];\n className?: string;\n}\n\nconst ICON_BUTTON_CLASS =\n 'inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring/60';\n\n/**\n * A Devin-style workspace pane for the active session: a tab strip of panes\n * (Tasks, Shell, Computer, Pull requests, Attachments — and any the host\n * adds), a \"+\" menu to open more, closable tabs, and a collapse to a rail.\n *\n * Mount it inside `<AthenaProvider>` — as `AthenaLayout`'s `workbench` for a\n * resizable right-hand column, or anywhere in your own layout. Each thread\n * keeps its own open tabs while the workbench stays mounted (in memory only);\n * a thread it has not seen opens `defaultOpenPaneIds`. In `AthenaLayout`, the\n * assets the agent or the person opens show here too, as an \"Open assets\" tab.\n */\nexport const SessionWorkbench: FC<SessionWorkbenchProps> = (props) => {\n // One client for every pane, so the workspace read several panes share is\n // fetched once even when the host provides no QueryClient.\n const queryClient = useAthenaQueryClient();\n return (\n <QueryClientProvider client={queryClient}>\n <SessionWorkbenchInner {...props} />\n </QueryClientProvider>\n );\n};\n\nconst SessionWorkbenchInner: FC<SessionWorkbenchProps> = ({\n panes = DEFAULT_SESSION_WORKBENCH_PANES,\n defaultOpenPaneIds = DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS,\n className,\n}) => {\n const layout = useWorkbenchLayout();\n const [ownCollapsed, setOwnCollapsed] = useState(false);\n const collapsed = layout ? layout.collapsed : ownCollapsed;\n const setCollapsed = layout ? layout.setCollapsed : setOwnCollapsed;\n // Only a hosting layout can give the workbench the chat's width.\n const maximized = layout?.maximized ?? false;\n const setMaximized = layout?.setMaximized;\n const hostsAssetPanel = layout?.hostsAssetPanel ?? false;\n\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const threadList = useAthenaStatewireThreadList();\n const hasMessages = useAuiState((state) => state.thread.messages.length > 0);\n // The attach snapshot says whether the backend minted the thread's session\n // (`sessionPersisted`) — true for an existing thread before any message.\n const sessionPersisted = useAuiState(\n (state) =>\n readDeepAgentStreamValues((state.thread.extras as { state?: unknown } | undefined)?.state)\n .sessionPersisted,\n );\n const isRunning = useAuiState(\n (state) => (state.thread.extras as { status?: unknown } | undefined)?.status === 'running',\n );\n // In thread-list mode the list knows whether the backend has the thread; a\n // single-thread provider's thread is known once the host says its session\n // exists, or once it has history (a host that predates the flag).\n const isPersisted = threadList\n ? !threadList.isNewChat\n : sessionPersisted === true || hasMessages;\n const paneContext = useMemo<SessionWorkbenchPaneContext>(\n () => ({ threadId, isPersisted, isRunning }),\n [threadId, isPersisted, isRunning],\n );\n\n // Polling stops with the run; read the workspace once more as it settles so\n // what the run's last steps created shows without waiting for a refocus.\n const queryClient = useQueryClient();\n const wasRunning = useRef(isRunning);\n useEffect(() => {\n if (wasRunning.current && !isRunning) {\n void queryClient.invalidateQueries({ queryKey: SESSION_WORKSPACE_QUERY_KEY });\n }\n wasRunning.current = isRunning;\n }, [isRunning, queryClient]);\n\n const assetPanelOpen = useAssetPanelStore((state) => state.isOpen);\n const closeAssetPanel = useAssetPanelStore((state) => state.closePanel);\n const allPanes = useMemo<readonly SessionWorkbenchPane[]>(\n () => (hostsAssetPanel && assetPanelOpen ? [...panes, OPEN_ASSETS_PANE] : panes),\n [assetPanelOpen, hostsAssetPanel, panes],\n );\n const paneById = useMemo(() => new Map(allPanes.map((pane) => [pane.id, pane])), [allPanes]);\n const paneIds = useMemo(() => new Set(paneById.keys()), [paneById]);\n\n const defaults = useMemo<WorkbenchThreadTabs>(() => {\n const open = defaultOpenPaneIds.filter((id) => panes.some((pane) => pane.id === id));\n return { open, active: open[0] ?? null };\n }, [defaultOpenPaneIds, panes]);\n const [tabsState, dispatchTabs] = useReducer(workbenchTabsReducer, {});\n const threadKey = threadId ?? '';\n let tabs = visibleThreadTabs(threadTabs(tabsState, threadKey, defaults), paneIds);\n // Assets already open when the workbench mounts (or when a thread with open\n // assets comes back) show their tab without an opening to react to.\n if (\n paneIds.has(SESSION_WORKBENCH_ASSETS_PANE_ID) &&\n !tabs.open.includes(SESSION_WORKBENCH_ASSETS_PANE_ID)\n ) {\n tabs = {\n open: [...tabs.open, SESSION_WORKBENCH_ASSETS_PANE_ID],\n active: tabs.active ?? SESSION_WORKBENCH_ASSETS_PANE_ID,\n };\n }\n\n // An asset opening (a tool card, a citation, the Attachments pane) brings\n // the \"Open assets\" tab forward and the workbench out of its rail. A thread\n // switch restoring that thread's open assets (`setCurrentThread`) is not an\n // opening: the thread keeps the pane it had selected. Read through refs:\n // the store notifies outside React's render.\n const latest = useRef({ threadKey, defaults, setCollapsed });\n latest.current = { threadKey, defaults, setCollapsed };\n useEffect(() => {\n if (!hostsAssetPanel) return undefined;\n return useAssetPanelStore.subscribe((state, previous) => {\n if (state.currentThreadId !== previous.currentThreadId) return;\n // Every open request, a repeat click on the asset already showing too.\n if (!state.isOpen || state.openRequestSeq === previous.openRequestSeq) return;\n dispatchTabs({\n type: 'open',\n threadKey: latest.current.threadKey,\n paneId: SESSION_WORKBENCH_ASSETS_PANE_ID,\n defaults: latest.current.defaults,\n });\n latest.current.setCollapsed(false);\n });\n }, [hostsAssetPanel]);\n\n // Showing a tab the strip renders is an `open`: it activates a stored tab\n // and records one the strip only derived (the \"Open assets\" tab above).\n const openPane = (paneId: string) =>\n dispatchTabs({ type: 'open', threadKey, paneId, defaults });\n const activatePane = openPane;\n const closePane = (paneId: string) => {\n if (paneId === SESSION_WORKBENCH_ASSETS_PANE_ID) closeAssetPanel();\n dispatchTabs({ type: 'close', threadKey, paneId, defaults });\n };\n\n const baseId = useId();\n const tabId = (paneId: string) => `${baseId}-tab-${paneId}`;\n const panelId = `${baseId}-panel`;\n\n const onTabKeyDown = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0;\n if (step === 0 || tabs.open.length === 0) return;\n event.preventDefault();\n const nextId = tabs.open[(index + step + tabs.open.length) % tabs.open.length];\n if (!nextId) return;\n activatePane(nextId);\n event.currentTarget.ownerDocument.getElementById(tabId(nextId))?.focus();\n };\n\n const activePane = tabs.active ? paneById.get(tabs.active) : undefined;\n const assetsPane = paneById.get(SESSION_WORKBENCH_ASSETS_PANE_ID);\n const assetsActive = activePane?.id === SESSION_WORKBENCH_ASSETS_PANE_ID;\n\n // One tree for both states: the panel stays mounted (hidden) while the\n // workbench is a rail, and the open assets stay mounted (hidden) while\n // another pane shows — their iframes keep their editing session, as the\n // asset panel's own tabs do.\n return (\n <SessionWorkbenchContext.Provider value={paneContext}>\n <aside\n aria-label=\"Session workspace\"\n className={cn(\n 'athena-sdk-chrome flex h-full flex-col border-border border-l bg-background',\n collapsed ? 'w-10 shrink-0 items-center gap-1 py-2' : 'min-h-0 w-full min-w-0',\n className,\n )}\n >\n {collapsed ? (\n <>\n <button\n type=\"button\"\n aria-label=\"Expand workspace\"\n title=\"Expand workspace\"\n onClick={() => setCollapsed(false)}\n className={ICON_BUTTON_CLASS}\n >\n <PanelRightOpenIcon className=\"size-4\" />\n </button>\n {tabs.open.map((paneId) => {\n const pane = paneById.get(paneId);\n if (!pane) return null;\n const Icon = pane.icon ?? LayersIcon;\n return (\n <button\n key={paneId}\n type=\"button\"\n aria-label={`Show ${pane.title}`}\n title={pane.title}\n onClick={() => {\n activatePane(paneId);\n setCollapsed(false);\n }}\n className={cn(\n ICON_BUTTON_CLASS,\n paneId === tabs.active && 'bg-muted text-foreground',\n )}\n >\n <Icon className=\"size-4\" />\n </button>\n );\n })}\n </>\n ) : (\n <div className=\"flex h-11 w-full shrink-0 items-center gap-1 border-border border-b pr-1.5 pl-2\">\n <div\n role=\"tablist\"\n aria-label=\"Workspace panes\"\n className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none]\"\n >\n {tabs.open.map((paneId, index) => {\n const pane = paneById.get(paneId);\n if (!pane) return null;\n const Icon = pane.icon;\n const selected = paneId === tabs.active;\n return (\n <div\n key={paneId}\n data-selected={selected ? '' : undefined}\n className={cn(\n 'group/tab flex h-7 shrink-0 items-center rounded-md text-[13px] transition-colors',\n selected\n ? 'bg-foreground/[0.06] text-foreground'\n : 'text-muted-foreground hover:bg-foreground/[0.04] hover:text-foreground',\n )}\n >\n <button\n type=\"button\"\n role=\"tab\"\n id={tabId(paneId)}\n aria-selected={selected}\n aria-controls={panelId}\n tabIndex={selected ? 0 : -1}\n onClick={() => activatePane(paneId)}\n onKeyDown={(event) => onTabKeyDown(event, index)}\n className=\"flex h-full items-center gap-1.5 rounded-md pr-1 pl-2 outline-none focus-visible:ring-2 focus-visible:ring-ring/60\"\n >\n {Icon ? <Icon className=\"size-3.5\" /> : null}\n {pane.title}\n </button>\n {/* Quiet until the tab is hovered, focused or selected, as in Devin's strip. */}\n <button\n type=\"button\"\n aria-label={`Close ${pane.title}`}\n title={`Close ${pane.title}`}\n onClick={() => closePane(paneId)}\n className={cn(\n 'mr-1 rounded p-0.5 transition-opacity hover:bg-background hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60',\n selected ? 'opacity-60' : 'opacity-0 group-hover/tab:opacity-60',\n )}\n >\n <XIcon className=\"size-3\" />\n </button>\n </div>\n );\n })}\n </div>\n <AddPaneMenu panes={allPanes} openIds={tabs.open} onOpen={openPane} />\n <div className=\"min-w-0 flex-1\" />\n {setMaximized ? (\n <button\n type=\"button\"\n aria-label={maximized ? 'Restore workspace' : 'Maximize workspace'}\n title={maximized ? 'Restore workspace' : 'Maximize workspace'}\n aria-pressed={maximized}\n onClick={() => setMaximized(!maximized)}\n className={ICON_BUTTON_CLASS}\n >\n {maximized ? <Minimize2Icon className=\"size-4\" /> : <Maximize2Icon className=\"size-4\" />}\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label=\"Collapse workspace\"\n title=\"Collapse workspace\"\n onClick={() => setCollapsed(true)}\n className={ICON_BUTTON_CLASS}\n >\n <PanelRightCloseIcon className=\"size-4\" />\n </button>\n </div>\n )}\n <div\n role=\"tabpanel\"\n id={panelId}\n aria-labelledby={activePane ? tabId(activePane.id) : undefined}\n className={cn(\n 'min-h-0 w-full flex-1',\n assetsActive ? 'flex flex-col' : 'overflow-y-auto',\n collapsed && 'hidden',\n )}\n >\n {assetsActive ? null : activePane ? (\n activePane.render(paneContext)\n ) : (\n <NoOpenPane panes={allPanes} onOpen={openPane} />\n )}\n {assetsPane ? (\n <div\n key={SESSION_WORKBENCH_ASSETS_PANE_ID}\n data-workbench-assets=\"\"\n className={cn(assetsActive ? 'flex min-h-0 flex-1 flex-col' : 'hidden')}\n >\n {assetsPane.render(paneContext)}\n </div>\n ) : null}\n </div>\n </aside>\n </SessionWorkbenchContext.Provider>\n );\n};\n\nconst PaneMenuItems: FC<{\n panes: readonly SessionWorkbenchPane[];\n openIds: readonly string[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, openIds, onOpen }) => (\n <ul className=\"flex flex-col\">\n {panes.map((pane) => {\n const Icon = pane.icon ?? LayersIcon;\n const isOpen = openIds.includes(pane.id);\n return (\n <li key={pane.id}>\n <button\n type=\"button\"\n title={pane.description}\n onClick={() => onOpen(pane.id)}\n className=\"flex h-[30px] w-full items-center gap-2 rounded-md px-2 text-left text-[13px] transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none\"\n >\n <Icon className=\"size-4 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate\">{pane.title}</span>\n {isOpen ? <span className=\"text-[11px] text-muted-foreground\">Open</span> : null}\n </button>\n </li>\n );\n })}\n </ul>\n);\n\nconst AddPaneMenu: FC<{\n panes: readonly SessionWorkbenchPane[];\n openIds: readonly string[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, openIds, onOpen }) => {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const shown = filterWorkbenchPanes(panes, query);\n const choose = (paneId: string) => {\n onOpen(paneId);\n setOpen(false);\n setQuery('');\n };\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setQuery('');\n }}\n >\n <PopoverTrigger asChild>\n <button type=\"button\" aria-label=\"Open a pane\" title=\"Open a pane\" className={ICON_BUTTON_CLASS}>\n <PlusIcon className=\"size-4\" />\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-56 p-1\" aria-label=\"Panes\">\n <div className=\"flex items-center gap-2 border-border border-b px-2 pb-1.5 pt-1\">\n <SearchIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <input\n // biome-ignore lint/a11y/noAutofocus: the menu opens to type into, as a command menu does.\n autoFocus\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={(event) => {\n const first = shown[0];\n if (event.key === 'Enter' && first) {\n event.preventDefault();\n choose(first.id);\n }\n }}\n placeholder=\"Search\"\n aria-label=\"Search panes\"\n className=\"h-7 min-w-0 flex-1 bg-transparent text-[13px] outline-none placeholder:text-muted-foreground\"\n />\n </div>\n <div className=\"pt-1\">\n {shown.length > 0 ? (\n <PaneMenuItems panes={shown} openIds={openIds} onOpen={choose} />\n ) : (\n <p className=\"px-2 py-1.5 text-[13px] text-muted-foreground\">No pane matches.</p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n};\n\n/** Nothing open: every pane the \"+\" menu offers, with what each shows, centred in the column. */\nconst NoOpenPane: FC<{\n panes: readonly SessionWorkbenchPane[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, onOpen }) => (\n <div className=\"flex min-h-full items-center justify-center p-6\">\n <ul aria-label=\"Available panes\" className=\"flex w-full max-w-xs flex-col gap-1\">\n {panes.map((pane) => {\n const Icon = pane.icon ?? LayersIcon;\n return (\n <li key={pane.id}>\n <button\n type=\"button\"\n onClick={() => onOpen(pane.id)}\n className=\"flex w-full items-start gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none\"\n >\n <Icon className=\"mt-0.5 size-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"text-[13px] text-foreground\">{pane.title}</span>\n {pane.description ? (\n <span className=\"text-muted-foreground text-xs\">{pane.description}</span>\n ) : null}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n);\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SessionWorkbench.js","sources":["../../src/workbench/SessionWorkbench.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAuiState } from '@assistant-ui/react';\nimport { readDeepAgentStreamValues } from '@athenaintel/agent-runtime';\nimport { QueryClientProvider, useQueryClient } from '@tanstack/react-query';\nimport {\n LayersIcon,\n Maximize2Icon,\n Minimize2Icon,\n PanelRightCloseIcon,\n PanelRightOpenIcon,\n PlusIcon,\n SearchIcon,\n XIcon,\n} from 'lucide-react';\nimport {\n type FC,\n type KeyboardEvent,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n} from 'react';\nimport { useAssetPanelStore } from '../assets/asset-panel-store';\nimport { AssetPanel } from '../assets/AssetPanel';\nimport { useAthenaQueryClient } from '../lib/use-athena-rest-query';\nimport { cn } from '../lib/utils';\nimport { useAthenaStatewireThreadList } from '../threads/statewire-thread-list';\nimport { AthenaStatewireThreadIdContext } from '../threads/thread-id-context';\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';\nimport {\n type SessionWorkbenchPane,\n type SessionWorkbenchPaneContext,\n SessionWorkbenchContext,\n} from './session-workbench-context';\nimport {\n filterWorkbenchPanes,\n threadTabs,\n visibleThreadTabs,\n type WorkbenchThreadTabs,\n workbenchTabsReducer,\n} from './session-workbench-model';\nimport {\n DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS,\n DEFAULT_SESSION_WORKBENCH_PANES,\n} from './session-workbench-panes';\nimport { SESSION_WORKSPACE_QUERY_KEY } from './use-session-workspace';\nimport { useWorkbenchLayout } from './workbench-layout-context';\n\n/** The tab `AthenaLayout`'s workbench shows the open assets in. */\nexport const SESSION_WORKBENCH_ASSETS_PANE_ID = 'open-assets';\n\nconst OPEN_ASSETS_PANE: SessionWorkbenchPane = {\n id: SESSION_WORKBENCH_ASSETS_PANE_ID,\n title: 'Open assets',\n description: 'The documents, sheets and decks open in this session',\n icon: LayersIcon,\n render: () => <AssetPanel manageAssetPanelHostRegistration={false} />,\n};\n\nexport interface SessionWorkbenchProps {\n /**\n * The panes the \"+\" menu offers, in order. Defaults to the built-in Tasks,\n * Shell, Computer, Pull requests and Attachments\n * (`DEFAULT_SESSION_WORKBENCH_PANES`); spread it to add your own.\n */\n panes?: readonly SessionWorkbenchPane[];\n /**\n * The panes a thread opens with until the person changes its tabs (ids from\n * `panes`; unknown ids are ignored). Defaults to Tasks, Shell and Computer.\n */\n defaultOpenPaneIds?: readonly string[];\n className?: string;\n}\n\nconst ICON_BUTTON_CLASS =\n 'inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring/60';\n\n/**\n * A Devin-style workspace pane for the active session: a tab strip of panes\n * (Tasks, Shell, Computer, Pull requests, Attachments — and any the host\n * adds), a \"+\" menu to open more, closable tabs, and a collapse to a rail.\n *\n * Mount it inside `<AthenaProvider>` — as `AthenaLayout`'s `workbench` for a\n * resizable right-hand column, or anywhere in your own layout. Each thread\n * keeps its own open tabs, and whether the workspace is open or collapsed,\n * while the workbench stays mounted (in memory only); a thread it has not seen\n * opens `defaultOpenPaneIds`, collapsed when the layout's\n * `defaultWorkbenchCollapsed` says so. In `AthenaLayout`, the assets the agent\n * or the person opens show here too, as an \"Open assets\" tab.\n */\nexport const SessionWorkbench: FC<SessionWorkbenchProps> = (props) => {\n // One client for every pane, so the workspace read several panes share is\n // fetched once even when the host provides no QueryClient.\n const queryClient = useAthenaQueryClient();\n return (\n <QueryClientProvider client={queryClient}>\n <SessionWorkbenchInner {...props} />\n </QueryClientProvider>\n );\n};\n\nconst SessionWorkbenchInner: FC<SessionWorkbenchProps> = ({\n panes = DEFAULT_SESSION_WORKBENCH_PANES,\n defaultOpenPaneIds = DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS,\n className,\n}) => {\n const layout = useWorkbenchLayout();\n const [ownCollapsed, setOwnCollapsed] = useState(false);\n const collapsed = layout ? layout.collapsed : ownCollapsed;\n const setCollapsed = layout ? layout.setCollapsed : setOwnCollapsed;\n // Only a hosting layout can give the workbench the chat's width.\n const maximized = layout?.maximized ?? false;\n const setMaximized = layout?.setMaximized;\n const hostsAssetPanel = layout?.hostsAssetPanel ?? false;\n\n const threadId = useContext(AthenaStatewireThreadIdContext);\n const threadList = useAthenaStatewireThreadList();\n const hasMessages = useAuiState((state) => state.thread.messages.length > 0);\n // The attach snapshot says whether the backend minted the thread's session\n // (`sessionPersisted`) — true for an existing thread before any message.\n const sessionPersisted = useAuiState(\n (state) =>\n readDeepAgentStreamValues((state.thread.extras as { state?: unknown } | undefined)?.state)\n .sessionPersisted,\n );\n const isRunning = useAuiState(\n (state) => (state.thread.extras as { status?: unknown } | undefined)?.status === 'running',\n );\n // In thread-list mode the list knows whether the backend has the thread; a\n // single-thread provider's thread is known once the host says its session\n // exists, or once it has history (a host that predates the flag).\n const isPersisted = threadList\n ? !threadList.isNewChat\n : sessionPersisted === true || hasMessages;\n const paneContext = useMemo<SessionWorkbenchPaneContext>(\n () => ({ threadId, isPersisted, isRunning }),\n [threadId, isPersisted, isRunning],\n );\n\n // Polling stops with the run; read the workspace once more as it settles so\n // what the run's last steps created shows without waiting for a refocus.\n const queryClient = useQueryClient();\n const wasRunning = useRef(isRunning);\n useEffect(() => {\n if (wasRunning.current && !isRunning) {\n void queryClient.invalidateQueries({ queryKey: SESSION_WORKSPACE_QUERY_KEY });\n }\n wasRunning.current = isRunning;\n }, [isRunning, queryClient]);\n\n const assetPanelOpen = useAssetPanelStore((state) => state.isOpen);\n const closeAssetPanel = useAssetPanelStore((state) => state.closePanel);\n const allPanes = useMemo<readonly SessionWorkbenchPane[]>(\n () => (hostsAssetPanel && assetPanelOpen ? [...panes, OPEN_ASSETS_PANE] : panes),\n [assetPanelOpen, hostsAssetPanel, panes],\n );\n const paneById = useMemo(() => new Map(allPanes.map((pane) => [pane.id, pane])), [allPanes]);\n const paneIds = useMemo(() => new Set(paneById.keys()), [paneById]);\n\n const defaults = useMemo<WorkbenchThreadTabs>(() => {\n const open = defaultOpenPaneIds.filter((id) => panes.some((pane) => pane.id === id));\n return { open, active: open[0] ?? null };\n }, [defaultOpenPaneIds, panes]);\n const [tabsState, dispatchTabs] = useReducer(workbenchTabsReducer, {});\n const threadKey = threadId ?? '';\n\n // Whether the workspace is open is kept per thread, like its tabs: a thread\n // the person has not opened or collapsed it in shows the layout's default (a\n // new session, or one switched to), so one session's open workspace never\n // follows the person into the next. Applied before paint, so a switch never\n // flashes the previous thread's state.\n const defaultCollapsed = layout?.defaultCollapsed ?? false;\n const collapsedByThread = useRef(new Map<string, boolean>());\n const setThreadCollapsed = (next: boolean) => {\n collapsedByThread.current.set(threadKey, next);\n setCollapsed(next);\n };\n useLayoutEffect(() => {\n setCollapsed(collapsedByThread.current.get(threadKey) ?? defaultCollapsed);\n }, [threadKey, defaultCollapsed, setCollapsed]);\n let tabs = visibleThreadTabs(threadTabs(tabsState, threadKey, defaults), paneIds);\n // Assets already open when the workbench mounts (or when a thread with open\n // assets comes back) show their tab without an opening to react to.\n if (\n paneIds.has(SESSION_WORKBENCH_ASSETS_PANE_ID) &&\n !tabs.open.includes(SESSION_WORKBENCH_ASSETS_PANE_ID)\n ) {\n tabs = {\n open: [...tabs.open, SESSION_WORKBENCH_ASSETS_PANE_ID],\n active: tabs.active ?? SESSION_WORKBENCH_ASSETS_PANE_ID,\n };\n }\n\n // An asset opening (a tool card, a citation, the Attachments pane) brings\n // the \"Open assets\" tab forward and the workbench out of its rail. A thread\n // switch restoring that thread's open assets (`setCurrentThread`) is not an\n // opening: the thread keeps the pane it had selected. Read through refs:\n // the store notifies outside React's render.\n const latest = useRef({ threadKey, defaults, setCollapsed });\n latest.current = { threadKey, defaults, setCollapsed };\n useEffect(() => {\n if (!hostsAssetPanel) return undefined;\n return useAssetPanelStore.subscribe((state, previous) => {\n if (state.currentThreadId !== previous.currentThreadId) return;\n // Every open request, a repeat click on the asset already showing too.\n if (!state.isOpen || state.openRequestSeq === previous.openRequestSeq) return;\n dispatchTabs({\n type: 'open',\n threadKey: latest.current.threadKey,\n paneId: SESSION_WORKBENCH_ASSETS_PANE_ID,\n defaults: latest.current.defaults,\n });\n collapsedByThread.current.set(latest.current.threadKey, false);\n latest.current.setCollapsed(false);\n });\n }, [hostsAssetPanel]);\n\n // Showing a tab the strip renders is an `open`: it activates a stored tab\n // and records one the strip only derived (the \"Open assets\" tab above).\n const openPane = (paneId: string) =>\n dispatchTabs({ type: 'open', threadKey, paneId, defaults });\n const activatePane = openPane;\n const closePane = (paneId: string) => {\n if (paneId === SESSION_WORKBENCH_ASSETS_PANE_ID) closeAssetPanel();\n dispatchTabs({ type: 'close', threadKey, paneId, defaults });\n };\n\n const baseId = useId();\n const tabId = (paneId: string) => `${baseId}-tab-${paneId}`;\n const panelId = `${baseId}-panel`;\n\n const onTabKeyDown = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0;\n if (step === 0 || tabs.open.length === 0) return;\n event.preventDefault();\n const nextId = tabs.open[(index + step + tabs.open.length) % tabs.open.length];\n if (!nextId) return;\n activatePane(nextId);\n event.currentTarget.ownerDocument.getElementById(tabId(nextId))?.focus();\n };\n\n const activePane = tabs.active ? paneById.get(tabs.active) : undefined;\n const assetsPane = paneById.get(SESSION_WORKBENCH_ASSETS_PANE_ID);\n const assetsActive = activePane?.id === SESSION_WORKBENCH_ASSETS_PANE_ID;\n\n // One tree for both states: the panel stays mounted (hidden) while the\n // workbench is a rail, and the open assets stay mounted (hidden) while\n // another pane shows — their iframes keep their editing session, as the\n // asset panel's own tabs do.\n return (\n <SessionWorkbenchContext.Provider value={paneContext}>\n <aside\n aria-label=\"Session workspace\"\n className={cn(\n 'athena-sdk-chrome flex h-full flex-col border-border border-l bg-background',\n collapsed ? 'w-10 shrink-0 items-center gap-1 py-2' : 'min-h-0 w-full min-w-0',\n className,\n )}\n >\n {collapsed ? (\n <>\n <button\n type=\"button\"\n aria-label=\"Expand workspace\"\n title=\"Expand workspace\"\n onClick={() => setThreadCollapsed(false)}\n className={ICON_BUTTON_CLASS}\n >\n <PanelRightOpenIcon className=\"size-4\" />\n </button>\n {tabs.open.map((paneId) => {\n const pane = paneById.get(paneId);\n if (!pane) return null;\n const Icon = pane.icon ?? LayersIcon;\n return (\n <button\n key={paneId}\n type=\"button\"\n aria-label={`Show ${pane.title}`}\n title={pane.title}\n onClick={() => {\n activatePane(paneId);\n setThreadCollapsed(false);\n }}\n className={cn(\n ICON_BUTTON_CLASS,\n paneId === tabs.active && 'bg-muted text-foreground',\n )}\n >\n <Icon className=\"size-4\" />\n </button>\n );\n })}\n </>\n ) : (\n <div className=\"flex h-11 w-full shrink-0 items-center gap-1 border-border border-b pr-1.5 pl-2\">\n <div\n role=\"tablist\"\n aria-label=\"Workspace panes\"\n className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none]\"\n >\n {tabs.open.map((paneId, index) => {\n const pane = paneById.get(paneId);\n if (!pane) return null;\n const Icon = pane.icon;\n const selected = paneId === tabs.active;\n return (\n <div\n key={paneId}\n data-selected={selected ? '' : undefined}\n className={cn(\n 'group/tab flex h-7 shrink-0 items-center rounded-md text-[13px] transition-colors',\n selected\n ? 'bg-foreground/[0.06] text-foreground'\n : 'text-muted-foreground hover:bg-foreground/[0.04] hover:text-foreground',\n )}\n >\n <button\n type=\"button\"\n role=\"tab\"\n id={tabId(paneId)}\n aria-selected={selected}\n aria-controls={panelId}\n tabIndex={selected ? 0 : -1}\n onClick={() => activatePane(paneId)}\n onKeyDown={(event) => onTabKeyDown(event, index)}\n className=\"flex h-full items-center gap-1.5 rounded-md pr-1 pl-2 outline-none focus-visible:ring-2 focus-visible:ring-ring/60\"\n >\n {Icon ? <Icon className=\"size-3.5\" /> : null}\n {pane.title}\n </button>\n {/* Quiet until the tab is hovered, focused or selected, as in Devin's strip. */}\n <button\n type=\"button\"\n aria-label={`Close ${pane.title}`}\n title={`Close ${pane.title}`}\n onClick={() => closePane(paneId)}\n className={cn(\n 'mr-1 rounded p-0.5 transition-opacity hover:bg-background hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60',\n selected ? 'opacity-60' : 'opacity-0 group-hover/tab:opacity-60',\n )}\n >\n <XIcon className=\"size-3\" />\n </button>\n </div>\n );\n })}\n </div>\n <AddPaneMenu panes={allPanes} openIds={tabs.open} onOpen={openPane} />\n <div className=\"min-w-0 flex-1\" />\n {setMaximized ? (\n <button\n type=\"button\"\n aria-label={maximized ? 'Restore workspace' : 'Maximize workspace'}\n title={maximized ? 'Restore workspace' : 'Maximize workspace'}\n aria-pressed={maximized}\n onClick={() => setMaximized(!maximized)}\n className={ICON_BUTTON_CLASS}\n >\n {maximized ? <Minimize2Icon className=\"size-4\" /> : <Maximize2Icon className=\"size-4\" />}\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label=\"Collapse workspace\"\n title=\"Collapse workspace\"\n onClick={() => setThreadCollapsed(true)}\n className={ICON_BUTTON_CLASS}\n >\n <PanelRightCloseIcon className=\"size-4\" />\n </button>\n </div>\n )}\n <div\n role=\"tabpanel\"\n id={panelId}\n aria-labelledby={activePane ? tabId(activePane.id) : undefined}\n className={cn(\n 'min-h-0 w-full flex-1',\n assetsActive ? 'flex flex-col' : 'overflow-y-auto',\n collapsed && 'hidden',\n )}\n >\n {assetsActive ? null : activePane ? (\n activePane.render(paneContext)\n ) : (\n <NoOpenPane panes={allPanes} onOpen={openPane} />\n )}\n {assetsPane ? (\n <div\n key={SESSION_WORKBENCH_ASSETS_PANE_ID}\n data-workbench-assets=\"\"\n className={cn(assetsActive ? 'flex min-h-0 flex-1 flex-col' : 'hidden')}\n >\n {assetsPane.render(paneContext)}\n </div>\n ) : null}\n </div>\n </aside>\n </SessionWorkbenchContext.Provider>\n );\n};\n\nconst PaneMenuItems: FC<{\n panes: readonly SessionWorkbenchPane[];\n openIds: readonly string[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, openIds, onOpen }) => (\n <ul className=\"flex flex-col\">\n {panes.map((pane) => {\n const Icon = pane.icon ?? LayersIcon;\n const isOpen = openIds.includes(pane.id);\n return (\n <li key={pane.id}>\n <button\n type=\"button\"\n title={pane.description}\n onClick={() => onOpen(pane.id)}\n className=\"flex h-[30px] w-full items-center gap-2 rounded-md px-2 text-left text-[13px] transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none\"\n >\n <Icon className=\"size-4 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate\">{pane.title}</span>\n {isOpen ? <span className=\"text-[11px] text-muted-foreground\">Open</span> : null}\n </button>\n </li>\n );\n })}\n </ul>\n);\n\nconst AddPaneMenu: FC<{\n panes: readonly SessionWorkbenchPane[];\n openIds: readonly string[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, openIds, onOpen }) => {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const shown = filterWorkbenchPanes(panes, query);\n const choose = (paneId: string) => {\n onOpen(paneId);\n setOpen(false);\n setQuery('');\n };\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setQuery('');\n }}\n >\n <PopoverTrigger asChild>\n <button type=\"button\" aria-label=\"Open a pane\" title=\"Open a pane\" className={ICON_BUTTON_CLASS}>\n <PlusIcon className=\"size-4\" />\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-56 p-1\" aria-label=\"Panes\">\n <div className=\"flex items-center gap-2 border-border border-b px-2 pb-1.5 pt-1\">\n <SearchIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <input\n // biome-ignore lint/a11y/noAutofocus: the menu opens to type into, as a command menu does.\n autoFocus\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={(event) => {\n const first = shown[0];\n if (event.key === 'Enter' && first) {\n event.preventDefault();\n choose(first.id);\n }\n }}\n placeholder=\"Search\"\n aria-label=\"Search panes\"\n className=\"h-7 min-w-0 flex-1 bg-transparent text-[13px] outline-none placeholder:text-muted-foreground\"\n />\n </div>\n <div className=\"pt-1\">\n {shown.length > 0 ? (\n <PaneMenuItems panes={shown} openIds={openIds} onOpen={choose} />\n ) : (\n <p className=\"px-2 py-1.5 text-[13px] text-muted-foreground\">No pane matches.</p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n};\n\n/** Nothing open: every pane the \"+\" menu offers, with what each shows, centred in the column. */\nconst NoOpenPane: FC<{\n panes: readonly SessionWorkbenchPane[];\n onOpen: (paneId: string) => void;\n}> = ({ panes, onOpen }) => (\n <div className=\"flex min-h-full items-center justify-center p-6\">\n <ul aria-label=\"Available panes\" className=\"flex w-full max-w-xs flex-col gap-1\">\n {panes.map((pane) => {\n const Icon = pane.icon ?? LayersIcon;\n return (\n <li key={pane.id}>\n <button\n type=\"button\"\n onClick={() => onOpen(pane.id)}\n className=\"flex w-full items-start gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none\"\n >\n <Icon className=\"mt-0.5 size-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"text-[13px] text-foreground\">{pane.title}</span>\n {pane.description ? (\n <span className=\"text-muted-foreground text-xs\">{pane.description}</span>\n ) : null}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAsDO,MAAM,mCAAmC;AAEhD,MAAM,mBAAyC;AAAA,EAC7C,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,QAAQ,MAAM,oBAAC,YAAA,EAAW,kCAAkC,MAAA,CAAO;AACrE;AAiBA,MAAM,oBACJ;AAeK,MAAM,mBAA8C,CAAC,UAAU;AAGpE,QAAM,cAAc,qBAAA;AACpB,SACE,oBAAC,uBAAoB,QAAQ,aAC3B,8BAAC,uBAAA,EAAuB,GAAG,OAAO,EAAA,CACpC;AAEJ;AAEA,MAAM,wBAAmD,CAAC;AAAA,EACxD,QAAQ;AAAA,EACR,qBAAqB;AAAA,EACrB;AACF,MAAM;AACJ,QAAM,SAAS,mBAAA;AACf,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,YAAY,SAAS,OAAO,YAAY;AAC9C,QAAM,eAAe,SAAS,OAAO,eAAe;AAEpD,QAAM,aAAY,iCAAQ,cAAa;AACvC,QAAM,eAAe,iCAAQ;AAC7B,QAAM,mBAAkB,iCAAQ,oBAAmB;AAEnD,QAAM,WAAW,WAAW,8BAA8B;AAC1D,QAAM,aAAa,6BAAA;AACnB,QAAM,cAAc,YAAY,CAAC,UAAU,MAAM,OAAO,SAAS,SAAS,CAAC;AAG3E,QAAM,mBAAmB;AAAA,IACvB,CAAC,UAAA;;AACC,wCAA2B,WAAM,OAAO,WAAb,mBAAyD,KAAK,EACtF;AAAA;AAAA,EAAA;AAEP,QAAM,YAAY;AAAA,IAChB,CAAC,UAAA;;AAAW,0BAAM,OAAO,WAAb,mBAA0D,YAAW;AAAA;AAAA,EAAA;AAKnF,QAAM,cAAc,aAChB,CAAC,WAAW,YACZ,qBAAqB,QAAQ;AACjC,QAAM,cAAc;AAAA,IAClB,OAAO,EAAE,UAAU,aAAa;IAChC,CAAC,UAAU,aAAa,SAAS;AAAA,EAAA;AAKnC,QAAM,cAAc,eAAA;AACpB,QAAM,aAAa,OAAO,SAAS;AACnC,YAAU,MAAM;AACd,QAAI,WAAW,WAAW,CAAC,WAAW;AACpC,WAAK,YAAY,kBAAkB,EAAE,UAAU,6BAA6B;AAAA,IAC9E;AACA,eAAW,UAAU;AAAA,EACvB,GAAG,CAAC,WAAW,WAAW,CAAC;AAE3B,QAAM,iBAAiB,mBAAmB,CAAC,UAAU,MAAM,MAAM;AACjE,QAAM,kBAAkB,mBAAmB,CAAC,UAAU,MAAM,UAAU;AACtE,QAAM,WAAW;AAAA,IACf,MAAO,mBAAmB,iBAAiB,CAAC,GAAG,OAAO,gBAAgB,IAAI;AAAA,IAC1E,CAAC,gBAAgB,iBAAiB,KAAK;AAAA,EAAA;AAEzC,QAAM,WAAW,QAAQ,MAAM,IAAI,IAAI,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC3F,QAAM,UAAU,QAAQ,MAAM,IAAI,IAAI,SAAS,KAAA,CAAM,GAAG,CAAC,QAAQ,CAAC;AAElE,QAAM,WAAW,QAA6B,MAAM;AAClD,UAAM,OAAO,mBAAmB,OAAO,CAAC,OAAO,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;AACnF,WAAO,EAAE,MAAM,QAAQ,KAAK,CAAC,KAAK,KAAA;AAAA,EACpC,GAAG,CAAC,oBAAoB,KAAK,CAAC;AAC9B,QAAM,CAAC,WAAW,YAAY,IAAI,WAAW,sBAAsB,CAAA,CAAE;AACrE,QAAM,YAAY,YAAY;AAO9B,QAAM,oBAAmB,iCAAQ,qBAAoB;AACrD,QAAM,oBAAoB,OAAO,oBAAI,KAAsB;AAC3D,QAAM,qBAAqB,CAAC,SAAkB;AAC5C,sBAAkB,QAAQ,IAAI,WAAW,IAAI;AAC7C,iBAAa,IAAI;AAAA,EACnB;AACA,kBAAgB,MAAM;AACpB,iBAAa,kBAAkB,QAAQ,IAAI,SAAS,KAAK,gBAAgB;AAAA,EAC3E,GAAG,CAAC,WAAW,kBAAkB,YAAY,CAAC;AAC9C,MAAI,OAAO,kBAAkB,WAAW,WAAW,WAAW,QAAQ,GAAG,OAAO;AAGhF,MACE,QAAQ,IAAI,gCAAgC,KAC5C,CAAC,KAAK,KAAK,SAAS,gCAAgC,GACpD;AACA,WAAO;AAAA,MACL,MAAM,CAAC,GAAG,KAAK,MAAM,gCAAgC;AAAA,MACrD,QAAQ,KAAK,UAAU;AAAA,IAAA;AAAA,EAE3B;AAOA,QAAM,SAAS,OAAO,EAAE,WAAW,UAAU,cAAc;AAC3D,SAAO,UAAU,EAAE,WAAW,UAAU,aAAA;AACxC,YAAU,MAAM;AACd,QAAI,CAAC,gBAAiB,QAAO;AAC7B,WAAO,mBAAmB,UAAU,CAAC,OAAO,aAAa;AACvD,UAAI,MAAM,oBAAoB,SAAS,gBAAiB;AAExD,UAAI,CAAC,MAAM,UAAU,MAAM,mBAAmB,SAAS,eAAgB;AACvE,mBAAa;AAAA,QACX,MAAM;AAAA,QACN,WAAW,OAAO,QAAQ;AAAA,QAC1B,QAAQ;AAAA,QACR,UAAU,OAAO,QAAQ;AAAA,MAAA,CAC1B;AACD,wBAAkB,QAAQ,IAAI,OAAO,QAAQ,WAAW,KAAK;AAC7D,aAAO,QAAQ,aAAa,KAAK;AAAA,IACnC,CAAC;AAAA,EACH,GAAG,CAAC,eAAe,CAAC;AAIpB,QAAM,WAAW,CAAC,WAChB,aAAa,EAAE,MAAM,QAAQ,WAAW,QAAQ,UAAU;AAC5D,QAAM,eAAe;AACrB,QAAM,YAAY,CAAC,WAAmB;AACpC,QAAI,WAAW,iCAAkC,iBAAA;AACjD,iBAAa,EAAE,MAAM,SAAS,WAAW,QAAQ,UAAU;AAAA,EAC7D;AAEA,QAAM,SAAS,MAAA;AACf,QAAM,QAAQ,CAAC,WAAmB,GAAG,MAAM,QAAQ,MAAM;AACzD,QAAM,UAAU,GAAG,MAAM;AAEzB,QAAM,eAAe,CAAC,OAAyC,UAAkB;;AAC/E,UAAM,OAAO,MAAM,QAAQ,eAAe,IAAI,MAAM,QAAQ,cAAc,KAAK;AAC/E,QAAI,SAAS,KAAK,KAAK,KAAK,WAAW,EAAG;AAC1C,UAAM,eAAA;AACN,UAAM,SAAS,KAAK,MAAM,QAAQ,OAAO,KAAK,KAAK,UAAU,KAAK,KAAK,MAAM;AAC7E,QAAI,CAAC,OAAQ;AACb,iBAAa,MAAM;AACnB,gBAAM,cAAc,cAAc,eAAe,MAAM,MAAM,CAAC,MAA9D,mBAAiE;AAAA,EACnE;AAEA,QAAM,aAAa,KAAK,SAAS,SAAS,IAAI,KAAK,MAAM,IAAI;AAC7D,QAAM,aAAa,SAAS,IAAI,gCAAgC;AAChE,QAAM,gBAAe,yCAAY,QAAO;AAMxC,SACE,oBAAC,wBAAwB,UAAxB,EAAiC,OAAO,aACvC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAW;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA,YAAY,0CAA0C;AAAA,QACtD;AAAA,MAAA;AAAA,MAGD,UAAA;AAAA,QAAA,YACC,qBAAA,UAAA,EACE,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,OAAM;AAAA,cACN,SAAS,MAAM,mBAAmB,KAAK;AAAA,cACvC,WAAW;AAAA,cAEX,UAAA,oBAAC,oBAAA,EAAmB,WAAU,SAAA,CAAS;AAAA,YAAA;AAAA,UAAA;AAAA,UAExC,KAAK,KAAK,IAAI,CAAC,WAAW;AACzB,kBAAM,OAAO,SAAS,IAAI,MAAM;AAChC,gBAAI,CAAC,KAAM,QAAO;AAClB,kBAAM,OAAO,KAAK,QAAQ;AAC1B,mBACE;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,cAAY,QAAQ,KAAK,KAAK;AAAA,gBAC9B,OAAO,KAAK;AAAA,gBACZ,SAAS,MAAM;AACb,+BAAa,MAAM;AACnB,qCAAmB,KAAK;AAAA,gBAC1B;AAAA,gBACA,WAAW;AAAA,kBACT;AAAA,kBACA,WAAW,KAAK,UAAU;AAAA,gBAAA;AAAA,gBAG5B,UAAA,oBAAC,MAAA,EAAK,WAAU,SAAA,CAAS;AAAA,cAAA;AAAA,cAbpB;AAAA,YAAA;AAAA,UAgBX,CAAC;AAAA,QAAA,EAAA,CACH,IAEA,qBAAC,OAAA,EAAI,WAAU,mFACb,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,WAAU;AAAA,cAET,UAAA,KAAK,KAAK,IAAI,CAAC,QAAQ,UAAU;AAChC,sBAAM,OAAO,SAAS,IAAI,MAAM;AAChC,oBAAI,CAAC,KAAM,QAAO;AAClB,sBAAM,OAAO,KAAK;AAClB,sBAAM,WAAW,WAAW,KAAK;AACjC,uBACE;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBAEC,iBAAe,WAAW,KAAK;AAAA,oBAC/B,WAAW;AAAA,sBACT;AAAA,sBACA,WACI,yCACA;AAAA,oBAAA;AAAA,oBAGN,UAAA;AAAA,sBAAA;AAAA,wBAAC;AAAA,wBAAA;AAAA,0BACC,MAAK;AAAA,0BACL,MAAK;AAAA,0BACL,IAAI,MAAM,MAAM;AAAA,0BAChB,iBAAe;AAAA,0BACf,iBAAe;AAAA,0BACf,UAAU,WAAW,IAAI;AAAA,0BACzB,SAAS,MAAM,aAAa,MAAM;AAAA,0BAClC,WAAW,CAAC,UAAU,aAAa,OAAO,KAAK;AAAA,0BAC/C,WAAU;AAAA,0BAET,UAAA;AAAA,4BAAA,OAAO,oBAAC,MAAA,EAAK,WAAU,WAAA,CAAW,IAAK;AAAA,4BACvC,KAAK;AAAA,0BAAA;AAAA,wBAAA;AAAA,sBAAA;AAAA,sBAGR;AAAA,wBAAC;AAAA,wBAAA;AAAA,0BACC,MAAK;AAAA,0BACL,cAAY,SAAS,KAAK,KAAK;AAAA,0BAC/B,OAAO,SAAS,KAAK,KAAK;AAAA,0BAC1B,SAAS,MAAM,UAAU,MAAM;AAAA,0BAC/B,WAAW;AAAA,4BACT;AAAA,4BACA,WAAW,eAAe;AAAA,0BAAA;AAAA,0BAG5B,UAAA,oBAAC,OAAA,EAAM,WAAU,SAAA,CAAS;AAAA,wBAAA;AAAA,sBAAA;AAAA,oBAC5B;AAAA,kBAAA;AAAA,kBAnCK;AAAA,gBAAA;AAAA,cAsCX,CAAC;AAAA,YAAA;AAAA,UAAA;AAAA,UAEH,oBAAC,eAAY,OAAO,UAAU,SAAS,KAAK,MAAM,QAAQ,UAAU;AAAA,UACpE,oBAAC,OAAA,EAAI,WAAU,iBAAA,CAAiB;AAAA,UAC/B,eACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,YAAY,sBAAsB;AAAA,cAC9C,OAAO,YAAY,sBAAsB;AAAA,cACzC,gBAAc;AAAA,cACd,SAAS,MAAM,aAAa,CAAC,SAAS;AAAA,cACtC,WAAW;AAAA,cAEV,UAAA,gCAAa,eAAA,EAAc,WAAU,UAAS,IAAK,oBAAC,eAAA,EAAc,WAAU,SAAA,CAAS;AAAA,YAAA;AAAA,UAAA,IAEtF;AAAA,UACJ;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,OAAM;AAAA,cACN,SAAS,MAAM,mBAAmB,IAAI;AAAA,cACtC,WAAW;AAAA,cAEX,UAAA,oBAAC,qBAAA,EAAoB,WAAU,SAAA,CAAS;AAAA,YAAA;AAAA,UAAA;AAAA,QAC1C,GACF;AAAA,QAEF;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,IAAI;AAAA,YACJ,mBAAiB,aAAa,MAAM,WAAW,EAAE,IAAI;AAAA,YACrD,WAAW;AAAA,cACT;AAAA,cACA,eAAe,kBAAkB;AAAA,cACjC,aAAa;AAAA,YAAA;AAAA,YAGd,UAAA;AAAA,cAAA,eAAe,OAAO,aACrB,WAAW,OAAO,WAAW,IAE7B,oBAAC,YAAA,EAAW,OAAO,UAAU,QAAQ,SAAA,CAAU;AAAA,cAEhD,aACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBAEC,yBAAsB;AAAA,kBACtB,WAAW,GAAG,eAAe,iCAAiC,QAAQ;AAAA,kBAErE,UAAA,WAAW,OAAO,WAAW;AAAA,gBAAA;AAAA,gBAJzB;AAAA,cAAA,IAML;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACN;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ;AAEA,MAAM,gBAID,CAAC,EAAE,OAAO,SAAS,OAAA,MACtB,oBAAC,MAAA,EAAG,WAAU,iBACX,UAAA,MAAM,IAAI,CAAC,SAAS;AACnB,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,SAAS,QAAQ,SAAS,KAAK,EAAE;AACvC,6BACG,MAAA,EACC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,OAAO,KAAK;AAAA,MACZ,SAAS,MAAM,OAAO,KAAK,EAAE;AAAA,MAC7B,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA,oBAAC,MAAA,EAAK,WAAU,+BAAA,CAA+B;AAAA,QAC/C,oBAAC,QAAA,EAAK,WAAU,2BAA2B,eAAK,OAAM;AAAA,QACrD,SAAS,oBAAC,QAAA,EAAK,WAAU,qCAAoC,kBAAI,IAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,EAC9E,GAVO,KAAK,EAWd;AAEJ,CAAC,GACH;AAGF,MAAM,cAID,CAAC,EAAE,OAAO,SAAS,aAAa;AACnC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,QAAQ,qBAAqB,OAAO,KAAK;AAC/C,QAAM,SAAS,CAAC,WAAmB;AACjC,WAAO,MAAM;AACb,YAAQ,KAAK;AACb,aAAS,EAAE;AAAA,EACb;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,cAAc,CAAC,SAAS;AACtB,gBAAQ,IAAI;AACZ,YAAI,CAAC,KAAM,UAAS,EAAE;AAAA,MACxB;AAAA,MAEA,UAAA;AAAA,QAAA,oBAAC,kBAAe,SAAO,MACrB,8BAAC,UAAA,EAAO,MAAK,UAAS,cAAW,eAAc,OAAM,eAAc,WAAW,mBAC5E,UAAA,oBAAC,YAAS,WAAU,UAAS,GAC/B,EAAA,CACF;AAAA,6BACC,gBAAA,EAAe,OAAM,SAAQ,WAAU,YAAW,cAAW,SAC5D,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,mEACb,UAAA;AAAA,YAAA,oBAAC,YAAA,EAAW,WAAU,2CAA0C,eAAW,MAAC;AAAA,YAC5E;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,WAAS;AAAA,gBACT,OAAO;AAAA,gBACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,gBAChD,WAAW,CAAC,UAAU;AACpB,wBAAM,QAAQ,MAAM,CAAC;AACrB,sBAAI,MAAM,QAAQ,WAAW,OAAO;AAClC,0BAAM,eAAA;AACN,2BAAO,MAAM,EAAE;AAAA,kBACjB;AAAA,gBACF;AAAA,gBACA,aAAY;AAAA,gBACZ,cAAW;AAAA,gBACX,WAAU;AAAA,cAAA;AAAA,YAAA;AAAA,UACZ,GACF;AAAA,UACA,oBAAC,SAAI,WAAU,QACZ,gBAAM,SAAS,wBACb,eAAA,EAAc,OAAO,OAAO,SAAkB,QAAQ,QAAQ,IAE/D,oBAAC,OAAE,WAAU,iDAAgD,8BAAgB,EAAA,CAEjF;AAAA,QAAA,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;AAGA,MAAM,aAGD,CAAC,EAAE,OAAO,OAAA,0BACZ,OAAA,EAAI,WAAU,mDACb,UAAA,oBAAC,MAAA,EAAG,cAAW,mBAAkB,WAAU,uCACxC,UAAA,MAAM,IAAI,CAAC,SAAS;AACnB,QAAM,OAAO,KAAK,QAAQ;AAC1B,6BACG,MAAA,EACC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS,MAAM,OAAO,KAAK,EAAE;AAAA,MAC7B,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA,oBAAC,MAAA,EAAK,WAAU,+CAAA,CAA+C;AAAA,QAC/D,qBAAC,QAAA,EAAK,WAAU,yBACd,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,+BAA+B,UAAA,KAAK,OAAM;AAAA,UACzD,KAAK,cACJ,oBAAC,QAAA,EAAK,WAAU,iCAAiC,UAAA,KAAK,aAAY,IAChE;AAAA,QAAA,EAAA,CACN;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,EACF,GAbO,KAAK,EAcd;AAEJ,CAAC,GACH,EAAA,CACF;"}
@@ -1 +1 @@
1
- {"version":3,"file":"workbench-layout-context.cjs","sources":["../../src/workbench/workbench-layout-context.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\n/**\n * What `AthenaLayout` tells the `SessionWorkbench` it hosts in its right-hand\n * column: whether that column is collapsed to a rail or maximized over the\n * chat (the layout owns its width), and that the open assets render inside\n * the workbench (as its \"Open assets\" tab) instead of in a column of their own.\n */\nexport interface WorkbenchLayoutState {\n collapsed: boolean;\n setCollapsed: (collapsed: boolean) => void;\n /** The column fills the layout and the chat is hidden until it is restored. */\n maximized: boolean;\n setMaximized: (maximized: boolean) => void;\n hostsAssetPanel: boolean;\n}\n\nexport const WorkbenchLayoutContext = createContext<WorkbenchLayoutState | null>(null);\n\n/** The hosting layout's state, or `null` for a workbench the host placed itself. */\nexport function useWorkbenchLayout(): WorkbenchLayoutState | null {\n return useContext(WorkbenchLayoutContext);\n}\n"],"names":["createContext","useContext"],"mappings":";;;AAiBO,MAAM,yBAAyBA,MAAAA,cAA2C,IAAI;AAG9E,SAAS,qBAAkD;AAChE,SAAOC,MAAAA,WAAW,sBAAsB;AAC1C;;;"}
1
+ {"version":3,"file":"workbench-layout-context.cjs","sources":["../../src/workbench/workbench-layout-context.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\n/**\n * What `AthenaLayout` tells the `SessionWorkbench` it hosts in its right-hand\n * column: whether that column is collapsed to a rail or maximized over the\n * chat (the layout owns its width), and that the open assets render inside\n * the workbench (as its \"Open assets\" tab) instead of in a column of their own.\n */\nexport interface WorkbenchLayoutState {\n collapsed: boolean;\n setCollapsed: (collapsed: boolean) => void;\n /** Whether a session the person has not opened the workspace in starts collapsed. */\n defaultCollapsed: boolean;\n /** The column fills the layout and the chat is hidden until it is restored. */\n maximized: boolean;\n setMaximized: (maximized: boolean) => void;\n hostsAssetPanel: boolean;\n}\n\nexport const WorkbenchLayoutContext = createContext<WorkbenchLayoutState | null>(null);\n\n/** The hosting layout's state, or `null` for a workbench the host placed itself. */\nexport function useWorkbenchLayout(): WorkbenchLayoutState | null {\n return useContext(WorkbenchLayoutContext);\n}\n"],"names":["createContext","useContext"],"mappings":";;;AAmBO,MAAM,yBAAyBA,MAAAA,cAA2C,IAAI;AAG9E,SAAS,qBAAkD;AAChE,SAAOC,MAAAA,WAAW,sBAAsB;AAC1C;;;"}
@@ -7,6 +7,8 @@
7
7
  export interface WorkbenchLayoutState {
8
8
  collapsed: boolean;
9
9
  setCollapsed: (collapsed: boolean) => void;
10
+ /** Whether a session the person has not opened the workspace in starts collapsed. */
11
+ defaultCollapsed: boolean;
10
12
  /** The column fills the layout and the chat is hidden until it is restored. */
11
13
  maximized: boolean;
12
14
  setMaximized: (maximized: boolean) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"workbench-layout-context.js","sources":["../../src/workbench/workbench-layout-context.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\n/**\n * What `AthenaLayout` tells the `SessionWorkbench` it hosts in its right-hand\n * column: whether that column is collapsed to a rail or maximized over the\n * chat (the layout owns its width), and that the open assets render inside\n * the workbench (as its \"Open assets\" tab) instead of in a column of their own.\n */\nexport interface WorkbenchLayoutState {\n collapsed: boolean;\n setCollapsed: (collapsed: boolean) => void;\n /** The column fills the layout and the chat is hidden until it is restored. */\n maximized: boolean;\n setMaximized: (maximized: boolean) => void;\n hostsAssetPanel: boolean;\n}\n\nexport const WorkbenchLayoutContext = createContext<WorkbenchLayoutState | null>(null);\n\n/** The hosting layout's state, or `null` for a workbench the host placed itself. */\nexport function useWorkbenchLayout(): WorkbenchLayoutState | null {\n return useContext(WorkbenchLayoutContext);\n}\n"],"names":[],"mappings":";AAiBO,MAAM,yBAAyB,cAA2C,IAAI;AAG9E,SAAS,qBAAkD;AAChE,SAAO,WAAW,sBAAsB;AAC1C;"}
1
+ {"version":3,"file":"workbench-layout-context.js","sources":["../../src/workbench/workbench-layout-context.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\n/**\n * What `AthenaLayout` tells the `SessionWorkbench` it hosts in its right-hand\n * column: whether that column is collapsed to a rail or maximized over the\n * chat (the layout owns its width), and that the open assets render inside\n * the workbench (as its \"Open assets\" tab) instead of in a column of their own.\n */\nexport interface WorkbenchLayoutState {\n collapsed: boolean;\n setCollapsed: (collapsed: boolean) => void;\n /** Whether a session the person has not opened the workspace in starts collapsed. */\n defaultCollapsed: boolean;\n /** The column fills the layout and the chat is hidden until it is restored. */\n maximized: boolean;\n setMaximized: (maximized: boolean) => void;\n hostsAssetPanel: boolean;\n}\n\nexport const WorkbenchLayoutContext = createContext<WorkbenchLayoutState | null>(null);\n\n/** The hosting layout's state, or `null` for a workbench the host placed itself. */\nexport function useWorkbenchLayout(): WorkbenchLayoutState | null {\n return useContext(WorkbenchLayoutContext);\n}\n"],"names":[],"mappings":";AAmBO,MAAM,yBAAyB,cAA2C,IAAI;AAG9E,SAAS,qBAAkD;AAChE,SAAO,WAAW,sBAAsB;AAC1C;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@athenaintel/react",
3
- "version": "0.36.1",
3
+ "version": "0.37.0",
4
4
  "description": "Athena React SDK — Build AI-powered chat applications with the Athena platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",