@alivault/pico 0.10.3 → 0.12.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.
@@ -1,8 +1,8 @@
1
1
  import { i as __toESM } from "../_runtime.mjs";
2
- import { $ as require_react, A as TabsTab, B as Input, C as MenuPositioner, D as MenuGroupLabel, E as MenuItem, F as DialogPopup, H as useRender, I as DialogDescription$1, L as DialogClose, M as DialogTitle$1, N as DialogRoot, O as MenuGroup, P as DialogPortal$1, Q as require_jsx_runtime, R as DialogBackdrop, S as MenuRoot, T as MenuPopup, V as Button, Y as mergeProps, _ as AccordionItem$1, a as ContextMenuRoot, b as MenuTrigger, c as CollapsibleRoot, d as PopoverPortal, f as PopoverTrigger$1, g as AccordionHeader, h as AccordionTrigger$1, i as ContextMenuTrigger$1, j as TabsRoot, k as TabsList$1, l as PopoverPopup, m as AccordionPanel, n as ToggleGroup, o as CollapsiblePanel, p as PopoverRoot, r as Toggle, s as CollapsibleTrigger$1, t as AlertDialogRoot, u as PopoverPositioner, v as AccordionRoot, w as MenuPortal, x as MenuSubmenuRoot, y as MenuSubmenuTrigger, z as Separator } from "../_libs/@base-ui/react+[...].mjs";
2
+ import { $ as require_react, A as TabsTab, B as Input, C as MenuPositioner, D as MenuGroupLabel, E as MenuItem, F as DialogPopup, H as useRender, I as DialogDescription$1, L as DialogClose$1, M as DialogTitle$1, N as DialogRoot, O as MenuGroup, P as DialogPortal$1, Q as require_jsx_runtime, R as DialogBackdrop, S as MenuRoot, T as MenuPopup, V as Button, Y as mergeProps, _ as AccordionItem$1, a as ContextMenuRoot, b as MenuTrigger, c as CollapsibleRoot, d as PopoverPortal, f as PopoverTrigger$1, g as AccordionHeader, h as AccordionTrigger$1, i as ContextMenuTrigger$1, j as TabsRoot, k as TabsList$1, l as PopoverPopup, m as AccordionPanel, n as ToggleGroup, o as CollapsiblePanel, p as PopoverRoot, r as Toggle, s as CollapsibleTrigger$1, t as AlertDialogRoot, u as PopoverPositioner, v as AccordionRoot, w as MenuPortal, x as MenuSubmenuRoot, y as MenuSubmenuTrigger, z as Separator } from "../_libs/@base-ui/react+[...].mjs";
3
3
  import { t as cva } from "../_libs/class-variance-authority+clsx.mjs";
4
- import { A as ExternalLink, B as ChevronRight, C as ImagePlus, D as Folder, E as GitCommitHorizontal, F as Command, G as ArrowUpRight, H as Check, J as ArrowDown, L as ChevronsUpDown, M as EllipsisVertical, N as Download, O as FolderPlus, P as Copy, R as ChevronsDownUp, T as GitBranch, U as ArrowUp, V as ChevronDown, W as ArrowUpToLine, _ as OctagonX, a as TriangleAlert, b as ListStart, c as SquarePen, d as Plus, f as PanelRight, h as PanelLeftClose, i as Undo2, j as Ellipsis, k as FileDiff, l as Settings2, m as PanelLeftOpen, n as WandSparkles, o as Tag, p as PanelLeft, q as ArrowLeft, r as Upload, s as Square, t as X, u as Search, v as Minimize2, w as GitPullRequestArrow, x as ListEnd, y as Maximize2 } from "../_libs/lucide-react.mjs";
5
- import { $ as normalizeThemeFamily, A as Tooltip$1, C as SESSION_DONE_SOUND_ENABLED_STORAGE_KEY, Ct as themeFamilyDescription, D as THEME_FAMILIES, Dt as themeFamilySupportsColorMode, E as THEME_COLOR_MODE_STORAGE_KEY, Et as themeFamilyLabel, F as appliedThemeClass, H as createInitialSessionState, I as appliedThemeClassColorMode, K as flattenTree, N as TooltipTrigger, O as THEME_STORAGE_KEY, Ot as thinkingSummaryText, P as VIEWER_CONTEXT_STORAGE_KEY, Q as normalizeThemeColorMode, R as buildItemsFromSync, S as SESSION_DONE_DESKTOP_NOTIFICATIONS_ENABLED_STORAGE_KEY, St as themeColorModeLabel, T as Spinner, Tt as themeFamilyKeywords, V as createContextId, X as normalizeSessionSelectionKeys, Y as normalizePromptImage, Z as normalizeStoredDirectoryList, _ as RIGHT_SIDEBAR_HISTORY_HEIGHT_STORAGE_KEY, _t as rememberStoredPromptDraft, a as COLLAPSED_DIRECTORIES_STORAGE_KEY, at as readStoredCollapsedDirectories, b as RIGHT_SIDEBAR_WIDTHS_STORAGE_KEY, bt as sameContextUsage, c as Kbd, ct as readStoredPinnedSessionKeys, d as PICO_DIFF_SHIKI_THEMES, dt as readStoredRightSidebarOpen, et as previewUrlForImage, f as PICO_SHIKI_VARIABLE_DEFAULTS, ft as readStoredSessionDoneDesktopNotificationsEnabled, g as RIGHT_SIDEBAR_FILE_TREE_WIDTH_STORAGE_KEY, gt as readStoredThemeColorMode, h as RIGHT_SIDEBAR_ACTIVE_TAB_STORAGE_KEY, ht as readStoredTheme, i as CENTER_MESSAGES_STORAGE_KEY, it as readStoredCenterMessages, j as TooltipContent, k as TitleTooltip, l as KeyboardShortcutContent, lt as readStoredPromptDraft, m as RECENT_DIRECTORIES_STORAGE_KEY, mt as readStoredSidebarDirectories, n as APPLIED_THEME_STORAGE_KEY, nt as promptDraftKeyMatchesOwner, o as DRAFT_DIRECTORY_STORAGE_KEY, ot as readStoredDraftDirectory, p as PINNED_SESSIONS_STORAGE_KEY, pt as readStoredSessionDoneSoundEnabled, q as getSessionTitle, r as AUTO_SCROLL_ENABLED_STORAGE_KEY, rt as readStoredAutoScrollEnabled, s as HIDE_TOOL_BLOCKS_STORAGE_KEY, st as readStoredHideToolBlocks, tt as promptDraftKey, ut as readStoredRecentDirectories, v as RIGHT_SIDEBAR_HISTORY_TAB_STORAGE_KEY, vt as safeLocalStorageGetItem, w as SIDEBAR_DIRECTORIES_STORAGE_KEY, wt as themeFamilyFixedMode, x as Route, xt as sessionListEntryKey, y as RIGHT_SIDEBAR_OPEN_STORAGE_KEY, yt as safeLocalStorageSetItem, z as cn } from "./routes-DMdIR42g.mjs";
4
+ import { A as FolderPlus, B as ChevronsDownUp, C as ListEnd, D as GitBranch, E as GitPullRequestArrow, F as Download, G as ArrowUp, H as ChevronRight, I as Copy, K as ArrowUpToLine, L as Command, M as ExternalLink, N as Ellipsis, O as GitCommitHorizontal, P as EllipsisVertical, S as ListStart, T as ImagePlus, U as ChevronDown, W as Check, X as ArrowDown, Y as ArrowLeft, a as TriangleAlert, b as Minimize2, c as Square, d as Search, f as Plus, g as PanelLeftClose, h as PanelLeftOpen, i as Undo2, j as FileDiff, k as Folder, l as SquarePen, m as PanelLeft, n as WandSparkles, o as Trash2, p as PanelRight, q as ArrowUpRight, r as Upload, s as Tag, t as X, u as Settings2, v as OctagonX, x as Maximize2, y as Minus, z as ChevronsUpDown } from "../_libs/lucide-react.mjs";
5
+ import { $ as normalizeThemeFamily, A as Tooltip$1, C as SESSION_DONE_SOUND_ENABLED_STORAGE_KEY, Ct as themeFamilyDescription, D as THEME_FAMILIES, Dt as themeFamilySupportsColorMode, E as THEME_COLOR_MODE_STORAGE_KEY, Et as themeFamilyLabel, F as appliedThemeClass, H as createInitialSessionState, I as appliedThemeClassColorMode, K as flattenTree, N as TooltipTrigger, O as THEME_STORAGE_KEY, Ot as thinkingSummaryText, P as VIEWER_CONTEXT_STORAGE_KEY, Q as normalizeThemeColorMode, R as buildItemsFromSync, S as SESSION_DONE_DESKTOP_NOTIFICATIONS_ENABLED_STORAGE_KEY, St as themeColorModeLabel, T as Spinner, Tt as themeFamilyKeywords, V as createContextId, X as normalizeSessionSelectionKeys, Y as normalizePromptImage, Z as normalizeStoredDirectoryList, _ as RIGHT_SIDEBAR_HISTORY_HEIGHT_STORAGE_KEY, _t as rememberStoredPromptDraft, a as COLLAPSED_DIRECTORIES_STORAGE_KEY, at as readStoredCollapsedDirectories, b as RIGHT_SIDEBAR_WIDTHS_STORAGE_KEY, bt as sameContextUsage, c as Kbd, ct as readStoredPinnedSessionKeys, d as PICO_DIFF_SHIKI_THEMES, dt as readStoredRightSidebarOpen, et as previewUrlForImage, f as PICO_SHIKI_VARIABLE_DEFAULTS, ft as readStoredSessionDoneDesktopNotificationsEnabled, g as RIGHT_SIDEBAR_FILE_TREE_WIDTH_STORAGE_KEY, gt as readStoredThemeColorMode, h as RIGHT_SIDEBAR_ACTIVE_TAB_STORAGE_KEY, ht as readStoredTheme, i as CENTER_MESSAGES_STORAGE_KEY, it as readStoredCenterMessages, j as TooltipContent, k as TitleTooltip, l as KeyboardShortcutContent, lt as readStoredPromptDraft, m as RECENT_DIRECTORIES_STORAGE_KEY, mt as readStoredSidebarDirectories, n as APPLIED_THEME_STORAGE_KEY, nt as promptDraftKeyMatchesOwner, o as DRAFT_DIRECTORY_STORAGE_KEY, ot as readStoredDraftDirectory, p as PINNED_SESSIONS_STORAGE_KEY, pt as readStoredSessionDoneSoundEnabled, q as getSessionTitle, r as AUTO_SCROLL_ENABLED_STORAGE_KEY, rt as readStoredAutoScrollEnabled, s as HIDE_TOOL_BLOCKS_STORAGE_KEY, st as readStoredHideToolBlocks, tt as promptDraftKey, ut as readStoredRecentDirectories, v as RIGHT_SIDEBAR_HISTORY_TAB_STORAGE_KEY, vt as safeLocalStorageGetItem, w as SIDEBAR_DIRECTORIES_STORAGE_KEY, wt as themeFamilyFixedMode, x as Route, xt as sessionListEntryKey, y as RIGHT_SIDEBAR_OPEN_STORAGE_KEY, yt as safeLocalStorageSetItem, z as cn } from "./routes-9nZumcXQ.mjs";
6
6
  import { i as useQuery, n as useIsMutating, o as useQueryClient, r as useIsFetching, t as useMutation } from "../_libs/tanstack__react-query.mjs";
7
7
  import { t as useHotkeys } from "../_libs/tanstack__react-hotkeys.mjs";
8
8
  import { a as matchesKeyboardEvent, i as batch, o as formatForDisplay, r as createStore } from "../_libs/@tanstack/hotkeys+[...].mjs";
@@ -22,7 +22,7 @@ import { t as remarkGfm } from "../_libs/remark-gfm.mjs";
22
22
  import { a as getBuiltInSpriteSheet, i as getBuiltInFileIconColor, n as FileTree, r as createFileTreeIconResolver, t as useFileTree } from "../_libs/@pierre/trees+[...].mjs";
23
23
  import { a as closestCenter, c as useDraggable, d as useSensors, f as CSS$1, i as PointerSensor, n as DragOverlay, t as DndContext, u as useSensor } from "../_libs/@dnd-kit/core+[...].mjs";
24
24
  import { a as verticalListSortingStrategy, i as useSortable, n as arrayMove, r as horizontalListSortingStrategy, t as SortableContext } from "../_libs/dnd-kit__sortable.mjs";
25
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-Ia1sMc23.js
25
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-C8qT6Oju.js
26
26
  var import_react = /* @__PURE__ */ __toESM(require_react());
27
27
  var import_jsx_runtime = require_jsx_runtime();
28
28
  var MOBILE_BREAKPOINT = 768;
@@ -75,7 +75,7 @@ function parseCssLengthToPixels$1(value, fallback) {
75
75
  if (trimmedValue.endsWith("rem")) return numericValue * (typeof window === "undefined" ? 16 : Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize) || 16);
76
76
  return numericValue;
77
77
  }
78
- function clamp(value, min, max) {
78
+ function clamp$1(value, min, max) {
79
79
  return Math.min(Math.max(value, min), max);
80
80
  }
81
81
  function getResizeSide(element) {
@@ -216,7 +216,7 @@ function useSidebarResize({ enabled = true, expandThreshold = SIDEBAR_EXPAND_DIS
216
216
  collapseSidebar(pointerEvent.clientX);
217
217
  return;
218
218
  }
219
- const nextWidth = clamp(attemptedWidth, minWidthPx, maxWidthPx);
219
+ const nextWidth = clamp$1(attemptedWidth, minWidthPx, maxWidthPx);
220
220
  previewSidebarWidth(dragState, `${Math.round(nextWidth)}px`);
221
221
  };
222
222
  const handlePointerUp = (pointerEvent) => {
@@ -319,7 +319,7 @@ function SheetContent({ className, children, side = "right", showCloseButton = t
319
319
  "data-side": side,
320
320
  className: cn("fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm", className),
321
321
  ...props,
322
- children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogClose, {
322
+ children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogClose$1, {
323
323
  "data-slot": "sheet-close",
324
324
  render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
325
325
  variant: "ghost",
@@ -813,6 +813,12 @@ function DialogPortal({ ...props }) {
813
813
  ...props
814
814
  });
815
815
  }
816
+ function DialogClose({ ...props }) {
817
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogClose$1, {
818
+ "data-slot": "dialog-close",
819
+ ...props
820
+ });
821
+ }
816
822
  function DialogOverlay({ className, ...props }) {
817
823
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogBackdrop, {
818
824
  "data-slot": "dialog-overlay",
@@ -825,7 +831,7 @@ function DialogContent({ className, children, showCloseButton = true, ...props }
825
831
  "data-slot": "dialog-content",
826
832
  className: cn("fixed top-1/2 left-1/2 z-50 grid max-h-dvh w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-h-[80vh] sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
827
833
  ...props,
828
- children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogClose, {
834
+ children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogClose$1, {
829
835
  "data-slot": "dialog-close",
830
836
  render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
831
837
  variant: "ghost",
@@ -851,7 +857,7 @@ function DialogFooter({ className, showCloseButton = false, children, ...props }
851
857
  "data-slot": "dialog-footer",
852
858
  className: cn("-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end", className),
853
859
  ...props,
854
- children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogClose, {
860
+ children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogClose$1, {
855
861
  render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, { variant: "outline" }),
856
862
  children: "Close"
857
863
  })]
@@ -1000,13 +1006,13 @@ function dispatchDrawerClosedEvent() {
1000
1006
  window.dispatchEvent(new CustomEvent(PICO_DIALOG_CLOSED_EVENT));
1001
1007
  }, 160);
1002
1008
  }
1003
- function Drawer$1({ shouldScaleBackground = false, open, ...props }) {
1009
+ function Drawer$1({ restorePromptFocusOnClose = true, shouldScaleBackground = false, open, ...props }) {
1004
1010
  const wasOpenRef = import_react.useRef(open === true);
1005
1011
  import_react.useLayoutEffect(() => {
1006
1012
  const nextOpen = open === true;
1007
- if (!nextOpen && wasOpenRef.current) dispatchDrawerClosedEvent();
1013
+ if (!nextOpen && wasOpenRef.current && restorePromptFocusOnClose) dispatchDrawerClosedEvent();
1008
1014
  wasOpenRef.current = nextOpen;
1009
- }, [open]);
1015
+ }, [open, restorePromptFocusOnClose]);
1010
1016
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Drawer.Root, {
1011
1017
  "data-slot": "drawer",
1012
1018
  shouldScaleBackground,
@@ -2851,7 +2857,7 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
2851
2857
  children: [loading && sortedSessionItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandItem, {
2852
2858
  value: "loading:sessions",
2853
2859
  disabled: true,
2854
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "size-3.5 text-primary" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2860
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "text-primary" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2855
2861
  className: "text-muted-foreground",
2856
2862
  children: "Loading sessions…"
2857
2863
  })]
@@ -2869,7 +2875,7 @@ function useAppShellSessionsDialogView({ open, onOpenChange, viewerContextId, cu
2869
2875
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2870
2876
  className: "mt-0.5 flex size-4 shrink-0 items-center justify-center",
2871
2877
  children: entry.streaming ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
2872
- className: "size-3.5 text-primary",
2878
+ className: "text-primary",
2873
2879
  "aria-label": "Session streaming"
2874
2880
  }) : showUnread ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
2875
2881
  className: "size-2 rounded-full bg-primary",
@@ -4639,7 +4645,8 @@ function useTreeBrowsePanelView({ isMobile, treeFilterMode, onTreeFilterModeChan
4639
4645
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
4640
4646
  className: "flex h-6 w-2.5 items-center justify-center",
4641
4647
  children: node.streaming || node.id === treeStreamingEntryId ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
4642
- className: "size-3 text-[var(--success)]",
4648
+ size: "xs",
4649
+ className: "text-[var(--success)]",
4643
4650
  "aria-label": "Streaming"
4644
4651
  }) : node.isActivePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeHierarchyIcon, {
4645
4652
  name: "active-path",
@@ -6143,6 +6150,17 @@ function updateDirectoryIndexLoadingState(current, directories, loading) {
6143
6150
  }
6144
6151
  return changed ? next : current;
6145
6152
  }
6153
+ function normalizeOptimisticSidebarPreview(value) {
6154
+ const text = value.replace(/\s+/g, " ").trim();
6155
+ if (!text) return "";
6156
+ if (text.length <= 240) return text;
6157
+ return `${text.slice(0, 239).trimEnd()}…`;
6158
+ }
6159
+ function sidebarEntryMatchesIdentity(entry, identity) {
6160
+ const sessionId = identity.sessionId?.trim() || "";
6161
+ const sessionPath = identity.sessionPath?.trim() || "";
6162
+ return Boolean(sessionPath && entry.path === sessionPath || sessionId && entry.id === sessionId);
6163
+ }
6146
6164
  function upsertOptimisticSidebarSessionEntry(current, entry) {
6147
6165
  const directory = entry.cwd.trim();
6148
6166
  const optimisticId = entry.id.trim();
@@ -6168,6 +6186,40 @@ function upsertOptimisticSidebarSessionEntry(current, entry) {
6168
6186
  }
6169
6187
  };
6170
6188
  }
6189
+ function updateOptimisticSidebarSessionPreviewEntry(current, options) {
6190
+ const preview = normalizeOptimisticSidebarPreview(options.preview);
6191
+ if (!preview) return current;
6192
+ let changed = false;
6193
+ const next = { ...current };
6194
+ for (const [directory, snapshot] of Object.entries(current)) {
6195
+ const updatedEntries = [];
6196
+ let sessionsChanged = false;
6197
+ const sessions = snapshot.sessions.map((entry) => {
6198
+ if (!sidebarEntryMatchesIdentity(entry, options)) return entry;
6199
+ const nextEntry = {
6200
+ ...entry,
6201
+ modified: options.modified,
6202
+ lastUserMessageAt: options.modified,
6203
+ lastMessageAt: options.modified,
6204
+ lastMessagePreview: preview,
6205
+ streaming: true,
6206
+ unread: false
6207
+ };
6208
+ updatedEntries.push(nextEntry);
6209
+ sessionsChanged ||= nextEntry !== entry;
6210
+ return nextEntry;
6211
+ });
6212
+ if (!sessionsChanged) continue;
6213
+ const updatedKeys = new Set(updatedEntries.map(sessionListEntryKey));
6214
+ next[directory] = {
6215
+ ...snapshot,
6216
+ revision: `optimistic-preview:${options.modified}:${snapshot.revision}`,
6217
+ sessions: [...updatedEntries, ...sessions.filter((entry) => !updatedKeys.has(sessionListEntryKey(entry)))]
6218
+ };
6219
+ changed = true;
6220
+ }
6221
+ return changed ? next : current;
6222
+ }
6171
6223
  function removeOptimisticSidebarSessionEntry(current, optimisticId) {
6172
6224
  const targetId = optimisticId?.trim() || "";
6173
6225
  if (!targetId) return current;
@@ -10325,6 +10377,234 @@ var MarkdownBlock = import_react.memo(function MarkdownBlock({ streaming = false
10325
10377
  })
10326
10378
  });
10327
10379
  });
10380
+ var minScale = 1;
10381
+ var maxScale = 5;
10382
+ var initialTransform = {
10383
+ scale: 1,
10384
+ x: 0,
10385
+ y: 0
10386
+ };
10387
+ function clamp(value, min, max) {
10388
+ return Math.min(max, Math.max(min, value));
10389
+ }
10390
+ function getDistance(first, second) {
10391
+ return Math.hypot(first.x - second.x, first.y - second.y);
10392
+ }
10393
+ function getMidpoint(first, second) {
10394
+ return {
10395
+ x: (first.x + second.x) / 2,
10396
+ y: (first.y + second.y) / 2
10397
+ };
10398
+ }
10399
+ function ImageLightbox({ open, onOpenChange, imageSrc, imageAlt, title, description, emptyState, className }) {
10400
+ const containerRef = import_react.useRef(null);
10401
+ const imageRef = import_react.useRef(null);
10402
+ const activePointersRef = import_react.useRef(/* @__PURE__ */ new Map());
10403
+ const clickStartedOnBackdropRef = import_react.useRef(false);
10404
+ const gestureRef = import_react.useRef(null);
10405
+ const transformRef = import_react.useRef(initialTransform);
10406
+ const [transform, setTransform] = import_react.useState(initialTransform);
10407
+ import_react.useEffect(() => {
10408
+ activePointersRef.current.clear();
10409
+ gestureRef.current = null;
10410
+ transformRef.current = initialTransform;
10411
+ setTransform(initialTransform);
10412
+ }, [imageSrc, open]);
10413
+ function getPointFromEvent(event) {
10414
+ return {
10415
+ x: event.clientX,
10416
+ y: event.clientY
10417
+ };
10418
+ }
10419
+ function getPointRelativeToCenter(point) {
10420
+ const container = containerRef.current;
10421
+ if (!container) return point;
10422
+ const rect = container.getBoundingClientRect();
10423
+ return {
10424
+ x: point.x - rect.left - rect.width / 2,
10425
+ y: point.y - rect.top - rect.height / 2
10426
+ };
10427
+ }
10428
+ function clampTransform(nextTransform) {
10429
+ const nextScale = clamp(nextTransform.scale, minScale, maxScale);
10430
+ if (nextScale <= minScale) return initialTransform;
10431
+ const container = containerRef.current;
10432
+ const image = imageRef.current;
10433
+ if (!container || !image) return {
10434
+ scale: nextScale,
10435
+ x: nextTransform.x,
10436
+ y: nextTransform.y
10437
+ };
10438
+ const maxX = Math.max(0, (image.offsetWidth * nextScale - container.clientWidth) / 2);
10439
+ const maxY = Math.max(0, (image.offsetHeight * nextScale - container.clientHeight) / 2);
10440
+ return {
10441
+ scale: nextScale,
10442
+ x: clamp(nextTransform.x, -maxX, maxX),
10443
+ y: clamp(nextTransform.y, -maxY, maxY)
10444
+ };
10445
+ }
10446
+ function updateTransform(nextTransform) {
10447
+ const clampedTransform = clampTransform(nextTransform);
10448
+ transformRef.current = clampedTransform;
10449
+ setTransform(clampedTransform);
10450
+ }
10451
+ function zoomAroundPoint(nextScale, screenPoint) {
10452
+ const currentTransform = transformRef.current;
10453
+ const relativePoint = getPointRelativeToCenter(screenPoint);
10454
+ const clampedScale = clamp(nextScale, minScale, maxScale);
10455
+ if (clampedScale <= minScale) {
10456
+ updateTransform(initialTransform);
10457
+ return;
10458
+ }
10459
+ const imagePoint = {
10460
+ x: (relativePoint.x - currentTransform.x) / currentTransform.scale,
10461
+ y: (relativePoint.y - currentTransform.y) / currentTransform.scale
10462
+ };
10463
+ updateTransform({
10464
+ scale: clampedScale,
10465
+ x: relativePoint.x - imagePoint.x * clampedScale,
10466
+ y: relativePoint.y - imagePoint.y * clampedScale
10467
+ });
10468
+ }
10469
+ function startPinchGesture() {
10470
+ const pointers = [...activePointersRef.current.values()];
10471
+ const firstPointer = pointers[0];
10472
+ const secondPointer = pointers[1];
10473
+ if (!firstPointer || !secondPointer) return;
10474
+ gestureRef.current = {
10475
+ type: "pinch",
10476
+ startDistance: getDistance(firstPointer, secondPointer),
10477
+ startMidpoint: getMidpoint(firstPointer, secondPointer),
10478
+ startTransform: transformRef.current
10479
+ };
10480
+ }
10481
+ function startPanGesture(pointerId, point) {
10482
+ gestureRef.current = {
10483
+ type: "pan",
10484
+ pointerId,
10485
+ startPoint: point,
10486
+ startTransform: transformRef.current
10487
+ };
10488
+ }
10489
+ function finishPointer(pointerId) {
10490
+ activePointersRef.current.delete(pointerId);
10491
+ const remainingPointer = [...activePointersRef.current.entries()][0];
10492
+ if (activePointersRef.current.size >= 2) startPinchGesture();
10493
+ else if (remainingPointer) startPanGesture(remainingPointer[0], remainingPointer[1]);
10494
+ else gestureRef.current = null;
10495
+ }
10496
+ function handlePointerDown(event) {
10497
+ clickStartedOnBackdropRef.current = event.target === event.currentTarget;
10498
+ if (!imageSrc) return;
10499
+ event.currentTarget.setPointerCapture(event.pointerId);
10500
+ const point = getPointFromEvent(event);
10501
+ activePointersRef.current.set(event.pointerId, point);
10502
+ if (activePointersRef.current.size >= 2) startPinchGesture();
10503
+ else startPanGesture(event.pointerId, point);
10504
+ }
10505
+ function handlePointerMove(event) {
10506
+ if (!activePointersRef.current.has(event.pointerId)) return;
10507
+ const point = getPointFromEvent(event);
10508
+ activePointersRef.current.set(event.pointerId, point);
10509
+ const gesture = gestureRef.current;
10510
+ const pointers = [...activePointersRef.current.values()];
10511
+ if (gesture?.type === "pinch" && pointers.length >= 2) {
10512
+ const firstPointer = pointers[0];
10513
+ const secondPointer = pointers[1];
10514
+ if (!firstPointer || !secondPointer || gesture.startDistance <= 0) return;
10515
+ const nextDistance = getDistance(firstPointer, secondPointer);
10516
+ const nextMidpoint = getMidpoint(firstPointer, secondPointer);
10517
+ const nextScale = gesture.startTransform.scale * (nextDistance / gesture.startDistance);
10518
+ const startRelativeMidpoint = getPointRelativeToCenter(gesture.startMidpoint);
10519
+ const nextRelativeMidpoint = getPointRelativeToCenter(nextMidpoint);
10520
+ const imagePoint = {
10521
+ x: (startRelativeMidpoint.x - gesture.startTransform.x) / gesture.startTransform.scale,
10522
+ y: (startRelativeMidpoint.y - gesture.startTransform.y) / gesture.startTransform.scale
10523
+ };
10524
+ const clampedScale = clamp(nextScale, minScale, maxScale);
10525
+ updateTransform({
10526
+ scale: clampedScale,
10527
+ x: nextRelativeMidpoint.x - imagePoint.x * clampedScale,
10528
+ y: nextRelativeMidpoint.y - imagePoint.y * clampedScale
10529
+ });
10530
+ return;
10531
+ }
10532
+ if (gesture?.type === "pan" && gesture.pointerId === event.pointerId) updateTransform({
10533
+ scale: gesture.startTransform.scale,
10534
+ x: gesture.startTransform.x + point.x - gesture.startPoint.x,
10535
+ y: gesture.startTransform.y + point.y - gesture.startPoint.y
10536
+ });
10537
+ }
10538
+ function handleWheel(event) {
10539
+ if (!imageSrc) return;
10540
+ event.preventDefault();
10541
+ const scaleDelta = Math.exp(-event.deltaY * .0015);
10542
+ zoomAroundPoint(transformRef.current.scale * scaleDelta, getPointFromEvent(event));
10543
+ }
10544
+ function handleDoubleClick(event) {
10545
+ if (!imageSrc) return;
10546
+ if (transformRef.current.scale > minScale) updateTransform(initialTransform);
10547
+ else zoomAroundPoint(2, getPointFromEvent(event));
10548
+ }
10549
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
10550
+ open,
10551
+ onOpenChange,
10552
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, {
10553
+ showCloseButton: false,
10554
+ className: cn("top-0 left-0 h-dvh max-h-dvh w-dvw max-w-none translate-x-0 translate-y-0 overflow-hidden rounded-none bg-transparent p-0 shadow-none ring-0 sm:max-h-dvh sm:max-w-none dark:ring-0", className),
10555
+ children: [
10556
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, {
10557
+ className: "sr-only",
10558
+ children: title
10559
+ }),
10560
+ description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, {
10561
+ className: "sr-only",
10562
+ children: description
10563
+ }) : null,
10564
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
10565
+ className: "absolute top-4 right-4 z-10 flex items-center gap-2",
10566
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogClose, {
10567
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
10568
+ variant: "ghost",
10569
+ size: "icon",
10570
+ className: "border border-white/15 bg-black/55 text-white hover:bg-black/80 hover:text-white"
10571
+ }),
10572
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
10573
+ className: "sr-only",
10574
+ children: "Close"
10575
+ })]
10576
+ })
10577
+ }),
10578
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
10579
+ ref: containerRef,
10580
+ className: "flex h-full w-full touch-none items-center justify-center overflow-hidden p-4",
10581
+ onPointerDown: handlePointerDown,
10582
+ onPointerMove: handlePointerMove,
10583
+ onPointerUp: (event) => finishPointer(event.pointerId),
10584
+ onPointerCancel: (event) => finishPointer(event.pointerId),
10585
+ onLostPointerCapture: (event) => finishPointer(event.pointerId),
10586
+ onWheel: handleWheel,
10587
+ onDoubleClick: handleDoubleClick,
10588
+ onClick: (event) => {
10589
+ if (clickStartedOnBackdropRef.current && event.target === event.currentTarget) onOpenChange(false);
10590
+ clickStartedOnBackdropRef.current = false;
10591
+ },
10592
+ children: imageSrc ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
10593
+ ref: imageRef,
10594
+ src: imageSrc,
10595
+ alt: imageAlt,
10596
+ draggable: false,
10597
+ className: "block max-h-full max-w-full cursor-grab object-contain active:cursor-grabbing",
10598
+ style: { transform: `translate3d(${transform.x}px, ${transform.y}px, 0) scale(${transform.scale})` }
10599
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
10600
+ className: "rounded-3xl border border-dashed border-border/70 bg-background/95 px-6 py-10 text-sm text-muted-foreground",
10601
+ children: emptyState || "Unable to load the image preview."
10602
+ })
10603
+ })
10604
+ ]
10605
+ })
10606
+ });
10607
+ }
10328
10608
  var PICO_DIFF_THEME_PAIR = {
10329
10609
  light: PICO_DIFF_SHIKI_THEMES.light,
10330
10610
  dark: PICO_DIFF_SHIKI_THEMES.dark
@@ -10941,18 +11221,22 @@ function assistantMessagesModelLabel(items) {
10941
11221
  function assistantItemIsWorking(item) {
10942
11222
  return Boolean(item.streaming) || item.done === false || item.blocks.some((block) => block.type === "tool" && block.running);
10943
11223
  }
11224
+ function assistantItemIsStreaming(item) {
11225
+ return Boolean(item.streaming);
11226
+ }
10944
11227
  function assistantMessagesShellSnapshotFromBlocks(snapshot, blocks) {
10945
11228
  const anchorBlockIndex = blocks.findIndex(assistantBlockIsMessageJumpAnchor);
10946
11229
  const anchorBlock = anchorBlockIndex >= 0 ? blocks[anchorBlockIndex] : void 0;
10947
11230
  const copyText = assistantCopyTextFromItems(snapshot.items);
10948
11231
  const modelLabel = assistantMessagesModelLabel(snapshot.items);
11232
+ const streaming = snapshot.items.some(assistantItemIsStreaming);
10949
11233
  const working = snapshot.items.some(assistantItemIsWorking);
10950
- const hasFooter = Boolean(copyText.trim() || modelLabel || working);
11234
+ const hasFooter = Boolean(copyText.trim() || modelLabel);
10951
11235
  return {
10952
11236
  ...anchorBlock ? { anchorBlockKey: assistantBlockRenderKey(anchorBlock, anchorBlockIndex) } : {},
10953
11237
  hasBlocks: blocks.length > 0,
10954
11238
  hasFooter,
10955
- streaming: working,
11239
+ streaming,
10956
11240
  working,
10957
11241
  copyText,
10958
11242
  modelLabel
@@ -11767,9 +12051,11 @@ var CompactionBlockCard = import_react.memo(function CompactionBlockCard({ block
11767
12051
  });
11768
12052
  var UserMessageCard = import_react.memo(function UserMessageCard({ item }) {
11769
12053
  const labelText = userMessageLabel(item);
11770
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12054
+ const [selectedImageIndex, setSelectedImageIndex] = import_react.useState(null);
12055
+ const selectedImage = selectedImageIndex === null ? null : item.images[selectedImageIndex] ?? null;
12056
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
11771
12057
  className: "mr-2 ml-auto w-fit max-w-[80%]",
11772
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
12058
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
11773
12059
  className: "relative rounded-3xl bg-primary px-5 py-2.5 text-primary-foreground before:absolute before:-right-1.75 before:bottom-0 before:h-5.5 before:w-4 before:rounded-bl-[16px_14px] before:bg-primary before:content-[''] after:absolute after:-right-6.5 after:bottom-0 after:h-6.25 after:w-6.5 after:rounded-bl-[10px] after:bg-background after:content-['']",
11774
12060
  children: [
11775
12061
  labelText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -11789,14 +12075,31 @@ var UserMessageCard = import_react.memo(function UserMessageCard({ item }) {
11789
12075
  }),
11790
12076
  item.images.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
11791
12077
  className: "mt-3 flex flex-wrap gap-3",
11792
- children: item.images.map((image) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
11793
- src: image.previewUrl,
11794
- alt: "Prompt upload",
11795
- className: "h-28 rounded-lg border object-cover"
11796
- }, promptImageKey(image)))
12078
+ children: item.images.map((image, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
12079
+ type: "button",
12080
+ className: "max-w-full cursor-zoom-in overflow-hidden rounded-lg border border-primary-foreground/20 bg-primary-foreground/10 p-0 transition hover:opacity-90 focus-visible:ring-2 focus-visible:ring-primary-foreground/70 focus-visible:ring-offset-2 focus-visible:ring-offset-primary focus-visible:outline-none",
12081
+ onClick: () => setSelectedImageIndex(index),
12082
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
12083
+ src: image.previewUrl,
12084
+ alt: "Prompt upload",
12085
+ className: "block h-28 max-w-full object-cover"
12086
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
12087
+ className: "sr-only",
12088
+ children: "Open prompt upload"
12089
+ })]
12090
+ }, `${promptImageKey(image)}:${index}`))
11797
12091
  }) : null
11798
12092
  ]
11799
- })
12093
+ }), selectedImage ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageLightbox, {
12094
+ open: true,
12095
+ onOpenChange: (open) => {
12096
+ if (!open) setSelectedImageIndex(null);
12097
+ },
12098
+ imageSrc: selectedImage.previewUrl,
12099
+ imageAlt: "Prompt upload",
12100
+ title: "Prompt upload preview",
12101
+ description: "Expanded preview of an image attached to a user message."
12102
+ }) : null]
11800
12103
  });
11801
12104
  });
11802
12105
  function assistantBlockIsVisible({ block, hideThinking, hideToolBlocks }) {
@@ -11816,7 +12119,7 @@ function assistantMessageHasVisibleBlocks({ item, hideThinking, hideToolBlocks }
11816
12119
  }));
11817
12120
  }
11818
12121
  function assistantMessageHasFooterMeta(item) {
11819
- return Boolean(assistantItemIsWorking(item) || item.model || assistantCopyTextFromItems([item]));
12122
+ return Boolean(item.model || assistantCopyTextFromItems([item]));
11820
12123
  }
11821
12124
  var AssistantBlockGroupView = import_react.memo(function AssistantBlockGroupView({ anchorBlockKey, descriptor, store, streaming }) {
11822
12125
  if (descriptor.type === "explore") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AssistantExploreBlockGroupView, {
@@ -11937,7 +12240,7 @@ function AssistantMessageFooter({ copyText, modelLabel, streaming }) {
11937
12240
  }) : null]
11938
12241
  });
11939
12242
  }
11940
- function AssistantMessagesStoreCard({ hideFooter = false, store }) {
12243
+ function AssistantMessagesStoreCard({ className, hideFooter = false, store }) {
11941
12244
  const initialSnapshot = store.getSnapshot();
11942
12245
  const initialBlocks = visibleAssistantBlocksFromSnapshot(initialSnapshot);
11943
12246
  const assistantBlockStoreRef = import_react.useRef(null);
@@ -11961,20 +12264,21 @@ function AssistantMessagesStoreCard({ hideFooter = false, store }) {
11961
12264
  store
11962
12265
  ]);
11963
12266
  const shellSnapshot = import_react.useSyncExternalStore(shellStore.subscribe, shellStore.getSnapshot, shellStore.getSnapshot);
11964
- if (!shellSnapshot.hasBlocks && !shellSnapshot.hasFooter) return null;
12267
+ const showFooter = Boolean(!hideFooter && !shellSnapshot.streaming && shellSnapshot.hasFooter);
12268
+ if (!shellSnapshot.hasBlocks && !showFooter) return null;
11965
12269
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
11966
- className: "flex flex-col gap-4",
12270
+ className: cn("flex flex-col gap-4", className),
11967
12271
  "data-conversation-assistant-group": "true",
11968
12272
  "data-conversation-streaming": shellSnapshot.streaming ? "true" : void 0,
11969
12273
  children: [shellSnapshot.hasBlocks ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AssistantBlockGroupsView, {
11970
12274
  anchorBlockKey: shellSnapshot.anchorBlockKey,
11971
12275
  store: assistantBlockStore,
11972
12276
  streaming: shellSnapshot.streaming
11973
- }) : null, hideFooter ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AssistantMessageFooter, {
12277
+ }) : null, showFooter ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AssistantMessageFooter, {
11974
12278
  copyText: shellSnapshot.copyText,
11975
12279
  modelLabel: shellSnapshot.modelLabel,
11976
12280
  streaming: shellSnapshot.streaming
11977
- })]
12281
+ }) : null]
11978
12282
  });
11979
12283
  }
11980
12284
  function createSelectionStore(initialValue = 0) {
@@ -13178,7 +13482,10 @@ function NewSessionComposerSelectors({ cwd, defaultNewSessionDirectory, director
13178
13482
  className: "truncate",
13179
13483
  children: branchLabel
13180
13484
  }),
13181
- checkoutBranchMutation.isPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "size-4 shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
13485
+ checkoutBranchMutation.isPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
13486
+ size: "md",
13487
+ className: "shrink-0"
13488
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
13182
13489
  className: "size-5 shrink-0",
13183
13490
  "aria-hidden": "true"
13184
13491
  })
@@ -14403,9 +14710,13 @@ function AppShellConversationFrame({ autoScrollEnabled, children, centerMessages
14403
14710
  function AppShellWorkingIndicatorLabel({ fallbackLabel, hiddenThinkingPreviewStore, useHiddenThinkingPreview }) {
14404
14711
  const hiddenThinkingPreview = useSelector(hiddenThinkingPreviewStore);
14405
14712
  const visibleLabel = useHiddenThinkingPreview && hiddenThinkingPreview ? hiddenThinkingPreview : fallbackLabel;
14406
- if (!visibleLabel.trim()) return null;
14713
+ if (!visibleLabel.trim()) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14714
+ "aria-hidden": "true",
14715
+ className: "invisible leading-5 font-medium",
14716
+ children: "Working…"
14717
+ });
14407
14718
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14408
- className: "font-medium text-foreground",
14719
+ className: "leading-5 font-medium text-foreground",
14409
14720
  children: visibleLabel
14410
14721
  });
14411
14722
  }
@@ -14420,7 +14731,7 @@ function AppShellMessagesWorkingIndicator({ hiddenThinkingPreviewStore, onCancel
14420
14731
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
14421
14732
  className: "min-w-0 flex-1",
14422
14733
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
14423
- className: "flex min-w-0 items-center justify-start gap-2",
14734
+ className: "flex min-h-5 min-w-0 items-center justify-start gap-2",
14424
14735
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14425
14736
  className: "min-w-0 shrink-0",
14426
14737
  children: state.done ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -14507,12 +14818,10 @@ function ConversationAssistantGroupView({ className, groupKey, hideFooter, hideT
14507
14818
  store,
14508
14819
  syncAfterConversationContentChange
14509
14820
  ]);
14510
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14821
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AssistantMessagesStoreCard, {
14511
14822
  className,
14512
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AssistantMessagesStoreCard, {
14513
- hideFooter,
14514
- store: assistantMessagesStore
14515
- })
14823
+ hideFooter,
14824
+ store: assistantMessagesStore
14516
14825
  });
14517
14826
  }
14518
14827
  function AppShellConversationItemGroups({ centerMessages, conversationItemsStore, hideFooter, hideThinking, hideToolBlocks }) {
@@ -14582,14 +14891,13 @@ function AppShellConversationWorkingFooter({ centerMessages, conversationItemsSt
14582
14891
  }
14583
14892
  function AppShellConversationMessageStack({ centerMessages, conversationItemsStore, hideToolBlocks, sessionStore }) {
14584
14893
  const hideThinking = useSelector(sessionStore, (sessionState) => sessionState.hideThinkingBlock);
14585
- const streaming = useSelector(sessionStore, (sessionState) => sessionState.streaming);
14586
14894
  if (!useConversationHasMessages(conversationItemsStore)) return null;
14587
14895
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14588
14896
  className: "flex flex-col gap-4 pt-4",
14589
14897
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellConversationItemGroups, {
14590
14898
  centerMessages,
14591
14899
  conversationItemsStore,
14592
- hideFooter: streaming,
14900
+ hideFooter: false,
14593
14901
  hideThinking,
14594
14902
  hideToolBlocks
14595
14903
  })
@@ -14891,7 +15199,7 @@ function ProjectFileIconSprite() {
14891
15199
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
14892
15200
  ref: installTrustedFileIconSprite,
14893
15201
  "aria-hidden": "true",
14894
- className: "pointer-events-none block size-0 overflow-hidden"
15202
+ className: "pointer-events-none absolute size-0 overflow-hidden"
14895
15203
  });
14896
15204
  }
14897
15205
  function ProjectFileTypeIcon({ path }) {
@@ -15314,6 +15622,7 @@ function ProjectOpenFileDialog({ open, onOpenChange, paths, onOpenFile }) {
15314
15622
  if (isMobile) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Drawer$1, {
15315
15623
  open,
15316
15624
  onOpenChange,
15625
+ restorePromptFocusOnClose: false,
15317
15626
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerContent, {
15318
15627
  className: "max-h-[90svh] overflow-hidden",
15319
15628
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerTitle, { children: "Open Files" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerDescription, { children: "Search project files to open." })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -15601,9 +15910,9 @@ function CommitDiffViewerTabOverlay({ active, tab }) {
15601
15910
  tabCount: 1
15602
15911
  });
15603
15912
  }
15604
- function RightSidebarTabStrip({ activeCommitDiffKey = "", activeFilePath, activeTab, commitDiffTabs = EMPTY_COMMIT_DIFF_TABS, filePreviewPath = "", fileTabs = EMPTY_RIGHT_SIDEBAR_FILE_TABS$1, onActiveCommitDiffChange, onActiveFileChange, onActiveTabChange, onCloseAllCommitDiffs, onCloseAllFiles, onCloseCommitDiff, onCloseCommitDiffsToRight, onCloseFile, onCloseFilesToRight, onCloseOtherCommitDiffs, onCloseOtherFiles, onOpenFileDialog, onReorderCommitDiffs, onReorderFiles, showHistory = false, showReview = false }) {
15605
- const renderTab = ({ label, value }) => {
15606
- const active = activeTab === value;
15913
+ function RightSidebarTabStrip({ activeCommitDiffKey = "", activeFilePath, activeTab, commitDiffTabs = EMPTY_COMMIT_DIFF_TABS, filePreviewPath = "", fileTabs = EMPTY_RIGHT_SIDEBAR_FILE_TABS$1, onActiveCommitDiffChange, onActiveFileChange, onActiveTabChange, onCloseAllCommitDiffs, onCloseAllFiles, onCloseCommitDiff, onCloseCommitDiffsToRight, onCloseFile, onCloseFilesToRight, onCloseOtherCommitDiffs, onCloseOtherFiles, onOpenFileDialog, onReorderCommitDiffs, onReorderFiles, showFiles = false, showHistory = false, showReview = false }) {
15914
+ const renderTab = ({ active: activeOverride, label, value }) => {
15915
+ const active = activeOverride ?? activeTab === value;
15607
15916
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
15608
15917
  type: "button",
15609
15918
  "aria-pressed": active,
@@ -15675,7 +15984,8 @@ function RightSidebarTabStrip({ activeCommitDiffKey = "", activeFilePath, active
15675
15984
  label: "History",
15676
15985
  value: "history"
15677
15986
  }) : null,
15678
- !hasOpenFiles ? renderTab({
15987
+ !hasOpenFiles || showFiles ? renderTab({
15988
+ active: activeTab === "files" && (!hasOpenFiles || !activeFilePath),
15679
15989
  label: "Files",
15680
15990
  value: "files"
15681
15991
  }) : null,
@@ -15999,7 +16309,7 @@ function GitBranchLocalGroup({ branches, branchesPending, checkoutPending, onSwi
15999
16309
  children: [branchesPending && branches.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandItem, {
16000
16310
  value: "loading:branches",
16001
16311
  disabled: true,
16002
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "size-3.5 text-primary" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
16312
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "text-primary" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
16003
16313
  className: "text-muted-foreground",
16004
16314
  children: "Loading branches…"
16005
16315
  })]
@@ -16552,7 +16862,10 @@ function GitPanelToolbar({ viewerContextId, cwd, active }) {
16552
16862
  onBranchClick: () => {
16553
16863
  setBranchDialogOpen(true);
16554
16864
  }
16555
- }), active && refreshing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "size-3 shrink-0 text-primary" }) : null]
16865
+ }), active && refreshing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
16866
+ size: "xs",
16867
+ className: "shrink-0 text-primary"
16868
+ }) : null]
16556
16869
  }),
16557
16870
  showCommitAction || showPushAction || showForcePushAction || showPullAction ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
16558
16871
  className: "flex flex-wrap items-center justify-end gap-2",
@@ -16695,7 +17008,7 @@ function AlertDialogAction({ className, ...props }) {
16695
17008
  });
16696
17009
  }
16697
17010
  function AlertDialogCancel({ className, variant = "outline", size = "default", ...props }) {
16698
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogClose, {
17011
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogClose$1, {
16699
17012
  "data-slot": "alert-dialog-cancel",
16700
17013
  className: cn(className),
16701
17014
  render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
@@ -17128,7 +17441,7 @@ function GitCommitActionsMenu({ disabled, busy, canCopyMessage, onViewDiff, onCo
17128
17441
  "aria-label": "Commit actions",
17129
17442
  disabled: disabled || busy
17130
17443
  }),
17131
- children: busy ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "size-3" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Ellipsis, {})
17444
+ children: busy ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Ellipsis, {})
17132
17445
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuContent, {
17133
17446
  align: "end",
17134
17447
  className: "w-64",
@@ -18122,7 +18435,7 @@ function GitCommitsSection({ viewerContextId, cwd, active, onOpenCommitDiff, onR
18122
18435
  role: "status",
18123
18436
  "aria-label": "Loading more commits",
18124
18437
  className: "inline-flex items-center gap-2 px-1 text-xs text-muted-foreground",
18125
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "size-3" }), "Loading more commits…"]
18438
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }), "Loading more commits…"]
18126
18439
  }) : null
18127
18440
  }) : null]
18128
18441
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
@@ -18199,6 +18512,30 @@ function GitFileDiff({ file }) {
18199
18512
  }) : null]
18200
18513
  });
18201
18514
  }
18515
+ function gitFileCanStage(file) {
18516
+ const [indexStatus, worktreeStatus] = gitFileStatusCharacters(file.status);
18517
+ return worktreeStatus !== " " || indexStatus === "?";
18518
+ }
18519
+ function gitFileCanUnstage(file) {
18520
+ const [indexStatus] = gitFileStatusCharacters(file.status);
18521
+ return indexStatus !== " " && indexStatus !== "?";
18522
+ }
18523
+ function reviewBulkGitActionSuccessLabel(action) {
18524
+ switch (action) {
18525
+ case "discard-all": return "Discarded all changes";
18526
+ case "nuke-working-tree": return "Nuked working tree";
18527
+ case "unstage-all": return "Unstaged all changes";
18528
+ case "stage-all": return "Staged all changes";
18529
+ }
18530
+ }
18531
+ function reviewBulkGitActionErrorFallback(action) {
18532
+ switch (action) {
18533
+ case "discard-all": return "Failed to discard changes";
18534
+ case "nuke-working-tree": return "Failed to nuke working tree";
18535
+ case "unstage-all": return "Failed to unstage changes";
18536
+ case "stage-all": return "Failed to stage changes";
18537
+ }
18538
+ }
18202
18539
  function reviewDiffCommentSideLabel(side) {
18203
18540
  return side === "deletions" ? "old" : "new";
18204
18541
  }
@@ -18418,6 +18755,7 @@ function FileReviewContent(props) {
18418
18755
  }
18419
18756
  function useFileReviewContentView({ viewerContextId, cwd, active, diffLineComments = [], onAddDiffLineComment, onMaximizeHistory, onOpenCommitDiff, onOpenFile, showEmbeddedHistory = true }) {
18420
18757
  const normalizedCwd = normalizeCwd(cwd);
18758
+ const queryClient = useQueryClient();
18421
18759
  const isMobile = useIsMobile();
18422
18760
  const [state, dispatch] = import_react.useReducer(fileReviewReducer, initialFileReviewState, (initialState) => ({
18423
18761
  ...initialState,
@@ -18458,6 +18796,33 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
18458
18796
  });
18459
18797
  const files = filesQuery.data;
18460
18798
  const changedFiles = Array.isArray(files) ? files : [];
18799
+ const canStageAll = changedFiles.some(gitFileCanStage);
18800
+ const canUnstageAll = changedFiles.some(gitFileCanUnstage);
18801
+ const canDiscardAll = changedFiles.length > 0;
18802
+ const [confirmNukeWorkingTreeOpen, setConfirmNukeWorkingTreeOpen] = import_react.useState(false);
18803
+ const bulkGitActionMutation = useMutation({
18804
+ mutationFn: async (action) => {
18805
+ return await fetchJson(buildRequestUrl(action === "discard-all" || action === "nuke-working-tree" ? "/api/git-discard" : "/api/git-stage", { contextId: viewerContextId }), {
18806
+ method: "POST",
18807
+ headers: { "content-type": "application/json" },
18808
+ body: JSON.stringify({
18809
+ action,
18810
+ cwd: normalizedCwd
18811
+ })
18812
+ });
18813
+ },
18814
+ onSuccess: async (_response, action) => {
18815
+ await invalidateGitQueries({
18816
+ queryClient,
18817
+ viewerContextId,
18818
+ cwd: normalizedCwd
18819
+ });
18820
+ toast.success(reviewBulkGitActionSuccessLabel(action));
18821
+ },
18822
+ onError: (error, action) => {
18823
+ toast.error(getErrorMessage(error, reviewBulkGitActionErrorFallback(action)));
18824
+ }
18825
+ });
18461
18826
  import_react.useEffect(() => {
18462
18827
  if (previousNormalizedCwdRef.current === normalizedCwd) return;
18463
18828
  previousNormalizedCwdRef.current = normalizedCwd;
@@ -18560,12 +18925,28 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
18560
18925
  normalizedCwd
18561
18926
  ]);
18562
18927
  const hasOpenFile = openFiles.length > 0;
18928
+ const bulkActionPending = bulkGitActionMutation.isPending;
18929
+ const bulkActionDisabled = bulkActionPending || !viewerContextId || !normalizedCwd || typeof files === "undefined" || files === null || Boolean(filesQuery.error);
18930
+ const discardAllPending = bulkGitActionMutation.isPending && bulkGitActionMutation.variables === "discard-all";
18931
+ const nukeWorkingTreePending = bulkGitActionMutation.isPending && bulkGitActionMutation.variables === "nuke-working-tree";
18932
+ const stageAllPending = bulkGitActionMutation.isPending && bulkGitActionMutation.variables === "stage-all";
18933
+ const unstageAllPending = bulkGitActionMutation.isPending && bulkGitActionMutation.variables === "unstage-all";
18563
18934
  const toggleAll = () => {
18564
18935
  dispatch({
18565
18936
  type: "openFilesChanged",
18566
18937
  openFiles: hasOpenFile ? [] : changedFiles.map(reviewFileValue)
18567
18938
  });
18568
18939
  };
18940
+ const runBulkGitAction = (action) => {
18941
+ bulkGitActionMutation.mutate(action);
18942
+ };
18943
+ const requestNukeWorkingTree = () => {
18944
+ setConfirmNukeWorkingTreeOpen(true);
18945
+ };
18946
+ const runConfirmedNukeWorkingTree = () => {
18947
+ bulkGitActionMutation.mutate("nuke-working-tree");
18948
+ setConfirmNukeWorkingTreeOpen(false);
18949
+ };
18569
18950
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18570
18951
  ref: reviewContentRef,
18571
18952
  className: "flex h-full min-h-0 flex-col bg-background",
@@ -18588,32 +18969,72 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
18588
18969
  children: "Diffs"
18589
18970
  })
18590
18971
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18591
- className: "flex shrink-0 items-center gap-2",
18592
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
18593
- title: hasOpenFile ? "Collapse all" : "Expand all",
18594
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
18972
+ className: "flex shrink-0 flex-wrap items-center justify-end gap-2",
18973
+ children: [
18974
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuTrigger, {
18975
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
18976
+ variant: "outline",
18977
+ size: "icon",
18978
+ "aria-label": "Diff actions",
18979
+ disabled: bulkActionDisabled
18980
+ }),
18981
+ children: bulkActionPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Ellipsis, { className: "size-4" })
18982
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuContent, {
18983
+ align: "end",
18984
+ className: "w-52",
18985
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuGroup, { children: [
18986
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
18987
+ disabled: bulkActionDisabled || !canStageAll,
18988
+ onClick: () => runBulkGitAction("stage-all"),
18989
+ children: [stageAllPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, {}), "Stage all"]
18990
+ }),
18991
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
18992
+ disabled: bulkActionDisabled || !canUnstageAll,
18993
+ onClick: () => runBulkGitAction("unstage-all"),
18994
+ children: [unstageAllPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Minus, {}), "Unstage all"]
18995
+ }),
18996
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
18997
+ variant: "destructive",
18998
+ disabled: bulkActionDisabled || !canDiscardAll,
18999
+ onClick: () => runBulkGitAction("discard-all"),
19000
+ children: [discardAllPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Undo2, {}), "Discard all"]
19001
+ }),
19002
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuSeparator, {}),
19003
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
19004
+ variant: "destructive",
19005
+ disabled: bulkActionDisabled,
19006
+ onClick: requestNukeWorkingTree,
19007
+ children: [nukeWorkingTreePending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2, {}), "Nuke working tree"]
19008
+ })
19009
+ ] })
19010
+ })] }),
19011
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
19012
+ title: hasOpenFile ? "Collapse all" : "Expand all",
19013
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
19014
+ variant: "outline",
19015
+ size: "icon",
19016
+ "aria-label": hasOpenFile ? "Collapse all" : "Expand all",
19017
+ disabled: changedFiles.length === 0,
19018
+ onClick: toggleAll,
19019
+ children: hasOpenFile ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronsDownUp, { className: "size-4" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronsUpDown, { className: "size-4" })
19020
+ })
19021
+ }),
19022
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroup$1, {
18595
19023
  variant: "outline",
18596
- size: "icon",
18597
- "aria-label": hasOpenFile ? "Collapse all" : "Expand all",
18598
- disabled: changedFiles.length === 0,
18599
- onClick: toggleAll,
18600
- children: hasOpenFile ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronsDownUp, { className: "size-4" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronsUpDown, { className: "size-4" })
19024
+ value: [diffStyle],
19025
+ onValueChange: (values) => {
19026
+ const value = values[0];
19027
+ if (value === "unified" || value === "split") dispatch({
19028
+ type: "diffStyleChanged",
19029
+ diffStyle: value
19030
+ });
19031
+ },
19032
+ children: ["unified", "split"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroupItem, {
19033
+ value,
19034
+ children: value === "unified" ? "Unified" : "Split"
19035
+ }, value))
18601
19036
  })
18602
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroup$1, {
18603
- variant: "outline",
18604
- value: [diffStyle],
18605
- onValueChange: (values) => {
18606
- const value = values[0];
18607
- if (value === "unified" || value === "split") dispatch({
18608
- type: "diffStyleChanged",
18609
- diffStyle: value
18610
- });
18611
- },
18612
- children: ["unified", "split"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroupItem, {
18613
- value,
18614
- children: value === "unified" ? "Unified" : "Split"
18615
- }, value))
18616
- })]
19037
+ ]
18617
19038
  })]
18618
19039
  }),
18619
19040
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -18722,7 +19143,21 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
18722
19143
  })
18723
19144
  }) : null
18724
19145
  ]
18725
- }) : null
19146
+ }) : null,
19147
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialog$1, {
19148
+ open: confirmNukeWorkingTreeOpen,
19149
+ onOpenChange: setConfirmNukeWorkingTreeOpen,
19150
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogHeader, { children: [
19151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogMedia, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TriangleAlert, {}) }),
19152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogTitle, { children: "Nuke working tree?" }),
19153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogDescription, { children: "This restores tracked files to HEAD and deletes untracked and ignored files in this repository. This cannot be undone from Pico." })
19154
+ ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogCancel, { children: "Cancel" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogAction, {
19155
+ variant: "destructive",
19156
+ disabled: bulkActionDisabled,
19157
+ onClick: runConfirmedNukeWorkingTree,
19158
+ children: "Nuke working tree"
19159
+ })] })] })
19160
+ })
18726
19161
  ]
18727
19162
  });
18728
19163
  }
@@ -18799,8 +19234,7 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffLineComment
18799
19234
  onGutterUtilityClick: startLineComment
18800
19235
  } : {};
18801
19236
  const themeOptions = usePicoDiffThemeOptions();
18802
- const [indexStatus, worktreeStatus] = gitFileStatusCharacters(file.status);
18803
- const canStage = worktreeStatus !== " " || indexStatus === "?";
19237
+ const canStage = gitFileCanStage(file);
18804
19238
  const invalidateChangedFileQueries = async () => {
18805
19239
  await invalidateGitQueries({
18806
19240
  queryClient,
@@ -19585,9 +20019,9 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
19585
20019
  setHistoryAsTabState(value);
19586
20020
  storeHistoryAsTab(value);
19587
20021
  };
19588
- const previewMode = isMobile ? "inline" : "external";
20022
+ const previewMode = "external";
19589
20023
  const panelHasCardChrome = showToolbar && !isMobile;
19590
- const currentFilePath = previewMode === "inline" ? localState.inlineActiveFilePath : activeFilePath;
20024
+ const currentFilePath = activeFilePath;
19591
20025
  const hasOpenFileTabs = fileTabs.length > 0;
19592
20026
  const activeCommitDiffTab = localState.commitDiffTabs.find((tab) => tab.key === localState.activeCommitDiffKey) ?? localState.commitDiffTabs[0];
19593
20027
  const fileDialogPaths = useQuery({
@@ -19602,13 +20036,6 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
19602
20036
  const openFile = (path, options) => {
19603
20037
  if (!path) return;
19604
20038
  setActiveTab("files");
19605
- if (previewMode === "inline") {
19606
- dispatchLocal({
19607
- type: "set-inline-active-file",
19608
- path
19609
- });
19610
- return;
19611
- }
19612
20039
  onOpenFile?.(path, options);
19613
20040
  onActiveFileChange?.(path);
19614
20041
  };
@@ -19654,6 +20081,10 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
19654
20081
  keys
19655
20082
  });
19656
20083
  };
20084
+ const handleTabStripActiveTabChange = (tab) => {
20085
+ if (isMobile && tab === "files") onActiveFileChange?.("");
20086
+ setActiveTab(tab);
20087
+ };
19657
20088
  import_react.useEffect(() => {
19658
20089
  dispatchLocal({
19659
20090
  type: "reset-navigation",
@@ -19686,7 +20117,7 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
19686
20117
  key
19687
20118
  }),
19688
20119
  onActiveFileChange,
19689
- onActiveTabChange: setActiveTab,
20120
+ onActiveTabChange: handleTabStripActiveTabChange,
19690
20121
  onCloseAllCommitDiffs: closeAllCommitDiffs,
19691
20122
  onCloseAllFiles,
19692
20123
  onCloseCommitDiff: closeCommitDiff,
@@ -19703,6 +20134,7 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
19703
20134
  },
19704
20135
  onReorderCommitDiffs: reorderCommitDiffs,
19705
20136
  onReorderFiles,
20137
+ showFiles: isMobile && hasOpenFileTabs,
19706
20138
  showHistory: historyAsTab,
19707
20139
  showReview: true
19708
20140
  }),
@@ -19750,7 +20182,25 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
19750
20182
  }),
19751
20183
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19752
20184
  className: activeTab === "files" ? "min-h-0 flex-1 overflow-hidden" : "hidden",
19753
- children: previewMode === "external" && hasOpenFileTabs ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20185
+ children: isMobile ? currentFilePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20186
+ className: "flex h-full min-h-0 min-w-0 flex-col overflow-hidden",
20187
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilePathBreadcrumb, { path: currentFilePath }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20188
+ className: "min-h-0 min-w-0 flex-1 overflow-hidden",
20189
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFileContent, {
20190
+ viewerContextId,
20191
+ cwd: normalizedCwd,
20192
+ active: active && activeTab === "files",
20193
+ path: currentFilePath
20194
+ })
20195
+ })]
20196
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFilesWorkspace, {
20197
+ viewerContextId,
20198
+ cwd: normalizedCwd,
20199
+ active: active && activeTab === "files",
20200
+ activeFilePath: currentFilePath,
20201
+ onOpenFile: openFile,
20202
+ previewMode
20203
+ }) : hasOpenFileTabs ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19754
20204
  className: "flex h-full min-h-0 min-w-0 flex-col overflow-hidden",
19755
20205
  children: [currentFilePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilePathBreadcrumb, {
19756
20206
  path: currentFilePath,
@@ -19780,12 +20230,6 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
19780
20230
  cwd: normalizedCwd,
19781
20231
  active: active && activeTab === "files",
19782
20232
  activeFilePath: currentFilePath,
19783
- onCloseFile: () => {
19784
- dispatchLocal({
19785
- type: "set-inline-active-file",
19786
- path: ""
19787
- });
19788
- },
19789
20233
  onOpenFile: openFile,
19790
20234
  previewMode
19791
20235
  })
@@ -20410,7 +20854,7 @@ var AppShellSessionHeader = import_react.memo(function AppShellSessionHeader({ a
20410
20854
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20411
20855
  className: "flex max-w-full min-w-0 items-center justify-center gap-1.5",
20412
20856
  children: [!isSessionViewLoading && sessionHeaderState.sessionStreaming ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
20413
- className: "size-3.5 shrink-0",
20857
+ className: "shrink-0",
20414
20858
  "aria-label": "Session streaming"
20415
20859
  }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
20416
20860
  title: displaySessionTitle,
@@ -20903,10 +21347,7 @@ function SidebarSessionItem({ entryKey, directorySessionsStore, activeSessionSto
20903
21347
  },
20904
21348
  children: [entry.streaming ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20905
21349
  className: "absolute top-2.5 left-2 flex size-4 items-center justify-center",
20906
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
20907
- className: "size-3.5",
20908
- "aria-label": "Session streaming"
20909
- })
21350
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { "aria-label": "Session streaming" })
20910
21351
  }) : showUnread ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20911
21352
  className: "absolute top-2.5 left-2 flex size-4 items-center justify-center",
20912
21353
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
@@ -21083,7 +21524,7 @@ var DirectorySessionGroup = import_react.memo(function DirectorySessionGroup({ d
21083
21524
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DirectoryPathLabel, { path: directory })
21084
21525
  }),
21085
21526
  !searchActive && !overlay ? collapsed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { className: "size-4 shrink-0 opacity-0 transition-opacity group-hover/directory-label:opacity-100 group-focus-visible/directory-label:opacity-100" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "size-4 shrink-0 opacity-0 transition-opacity group-hover/directory-label:opacity-100 group-focus-visible/directory-label:opacity-100" }) : null,
21086
- showHeaderLoadingSpinner ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "size-3.5 shrink-0" }) : null
21527
+ showHeaderLoadingSpinner ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "shrink-0" }) : null
21087
21528
  ]
21088
21529
  }),
21089
21530
  onCreateSessionInDirectory && !overlay && !isMobile ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
@@ -22576,6 +23017,12 @@ function usePromptMutationSessionSnapshot(store) {
22576
23017
  function normalizeQueuedStreamingBehavior(streamingBehavior) {
22577
23018
  return streamingBehavior === "followUp" ? "followUp" : "steer";
22578
23019
  }
23020
+ function promptSidebarPreview(message, images) {
23021
+ const text = message.replace(/\s+/g, " ").trim();
23022
+ if (text) return text;
23023
+ if (images.length === 0) return "";
23024
+ return `${images.length.toLocaleString()} image${images.length === 1 ? "" : "s"}`;
23025
+ }
22579
23026
  function createLocalPendingId() {
22580
23027
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return `optimistic:${crypto.randomUUID()}`;
22581
23028
  return `optimistic:${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
@@ -22638,7 +23085,7 @@ function movePendingComposerMessage(messages, pendingId, direction) {
22638
23085
  next.splice(targetIndex, 0, movedItem);
22639
23086
  return next;
22640
23087
  }
22641
- function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultNewSessionDirectory, sessionStore, sessionStateRef, draftSessionLoadingOwnerKey, pendingDraftPrompt, pendingDraftFollowUps, awaitingFirstTurn, pendingMessages, composerDiffLineCommentsRef, composerImagesRef, composerTextRef, composerSkillRef, replaceComposerDraft, lastSyncedEditorTextRef, rememberRecentDirectory, prefetchDirectorySessionsIndex, addOptimisticUserMessage, removeOptimisticUserMessage, addOptimisticSidebarSession, removeOptimisticSidebarSession, setSidebarDirectories, setStoredDraftDirectory, setDraftSessionLoadingOwnerKey, setPendingDraftPrompt, setPendingDraftFollowUps, setPendingMessages, setAwaitingFirstTurn, setIsSubmitting, setComposerDiffLineComments, setComposerImages }) {
23088
+ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultNewSessionDirectory, sessionStore, sessionStateRef, draftSessionLoadingOwnerKey, pendingDraftPrompt, pendingDraftFollowUps, awaitingFirstTurn, pendingMessages, composerDiffLineCommentsRef, composerImagesRef, composerTextRef, composerSkillRef, replaceComposerDraft, lastSyncedEditorTextRef, rememberRecentDirectory, prefetchDirectorySessionsIndex, addOptimisticUserMessage, removeOptimisticUserMessage, addOptimisticSidebarSession, removeOptimisticSidebarSession, updateOptimisticSidebarSessionPreview, setSidebarDirectories, setStoredDraftDirectory, setDraftSessionLoadingOwnerKey, setPendingDraftPrompt, setPendingDraftFollowUps, setPendingMessages, setAwaitingFirstTurn, setIsSubmitting, setComposerDiffLineComments, setComposerImages }) {
22642
23089
  const queryClient = useQueryClient();
22643
23090
  const draftSessionLoadingOwnerKeyRef = import_react.useRef(draftSessionLoadingOwnerKey);
22644
23091
  const pendingDraftPromptRef = import_react.useRef(pendingDraftPrompt);
@@ -22828,7 +23275,9 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22828
23275
  }).trim();
22829
23276
  const images = composerImagesRef.current.map((image) => ({ ...image }));
22830
23277
  if (!message && images.length === 0) return false;
22831
- if (!(pendingDraftPromptRef.current?.ownerKey === ownerKey ? pendingDraftPromptRef.current : null)) {
23278
+ const currentPendingPrompt = pendingDraftPromptRef.current?.ownerKey === ownerKey ? pendingDraftPromptRef.current : null;
23279
+ const sidebarPreview = promptSidebarPreview(message, images);
23280
+ if (!currentPendingPrompt) {
22832
23281
  const nextPrompt = {
22833
23282
  ownerKey,
22834
23283
  message,
@@ -22838,7 +23287,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22838
23287
  images,
22839
23288
  queued: false
22840
23289
  }),
22841
- optimisticSidebarSessionId: addOptimisticSidebarSession()
23290
+ optimisticSidebarSessionId: addOptimisticSidebarSession({ lastMessagePreview: sidebarPreview })
22842
23291
  };
22843
23292
  pendingDraftPromptRef.current = nextPrompt;
22844
23293
  setPendingDraftPrompt(nextPrompt);
@@ -22848,6 +23297,10 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22848
23297
  } else {
22849
23298
  const queuedStreamingBehavior = normalizeQueuedStreamingBehavior(streamingBehavior);
22850
23299
  const optimisticId = createLocalPendingId();
23300
+ updateOptimisticSidebarSessionPreview({
23301
+ ...currentPendingPrompt.optimisticSidebarSessionId ? { optimisticSidebarSessionId: currentPendingPrompt.optimisticSidebarSessionId } : {},
23302
+ preview: sidebarPreview
23303
+ });
22851
23304
  const nextFollowUps = [...pendingDraftFollowUpsRef.current, {
22852
23305
  message,
22853
23306
  images,
@@ -22875,7 +23328,8 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22875
23328
  setComposerDiffLineComments,
22876
23329
  setComposerImages,
22877
23330
  setPendingDraftFollowUps,
22878
- setPendingDraftPrompt
23331
+ setPendingDraftPrompt,
23332
+ updateOptimisticSidebarSessionPreview
22879
23333
  ]);
22880
23334
  const promptMutation = useMutation({
22881
23335
  mutationFn: async ({ message, images, streamingBehavior, pendingId, thinkingLevel }) => {
@@ -22916,6 +23370,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22916
23370
  const treatAsQueuedPrompt = options?.forceFirstPrompt ? false : Boolean(sessionStreamingRef.current || awaitingFirstTurnRef.current);
22917
23371
  const normalizedStreamingBehavior = treatAsQueuedPrompt ? normalizeQueuedStreamingBehavior(streamingBehavior) : void 0;
22918
23372
  const submittedImages = composerImagesRef.current.map((image) => ({ ...image }));
23373
+ const sidebarPreview = promptSidebarPreview(message, submittedImages);
22919
23374
  const queuedPendingId = treatAsQueuedPrompt ? createLocalPendingId() : void 0;
22920
23375
  if (!treatAsQueuedPrompt) {
22921
23376
  awaitingFirstTurnAssistantOutputSignatureRef.current = sessionAssistantOutputSignature(sessionStateRef.current.items);
@@ -22927,7 +23382,11 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22927
23382
  images: submittedImages,
22928
23383
  queued: false
22929
23384
  }));
22930
- const optimisticSidebarSessionId = options?.optimisticSidebarSessionId ?? (!treatAsQueuedPrompt && sessionStateRef.current.draft ? addOptimisticSidebarSession() : void 0);
23385
+ const optimisticSidebarSessionId = options?.optimisticSidebarSessionId ?? (!treatAsQueuedPrompt && sessionStateRef.current.draft ? addOptimisticSidebarSession({ lastMessagePreview: sidebarPreview }) : void 0);
23386
+ updateOptimisticSidebarSessionPreview({
23387
+ ...optimisticSidebarSessionId ? { optimisticSidebarSessionId } : {},
23388
+ preview: sidebarPreview
23389
+ });
22931
23390
  if (queuedPendingId && normalizedStreamingBehavior) addOptimisticPendingMessage({
22932
23391
  pendingId: queuedPendingId,
22933
23392
  text: message,
@@ -22991,6 +23450,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22991
23450
  setComposerDiffLineComments,
22992
23451
  setComposerImages,
22993
23452
  setIsSubmitting,
23453
+ updateOptimisticSidebarSessionPreview,
22994
23454
  viewerContextId
22995
23455
  ]);
22996
23456
  const flushPendingDraftFollowUps = import_react.useCallback(async () => {
@@ -25279,7 +25739,11 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25279
25739
  if (!path) return;
25280
25740
  batch(() => {
25281
25741
  setGitPanelOpen(true);
25282
- openRightSidebarFile(rightSidebarStore, path, options);
25742
+ if (isMobile) setCurrentTab("git");
25743
+ openRightSidebarFile(rightSidebarStore, path, {
25744
+ ...options,
25745
+ pin: isMobile || options?.pin
25746
+ });
25283
25747
  });
25284
25748
  };
25285
25749
  const closeFileViewTab = (path) => {
@@ -25904,6 +26368,8 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25904
26368
  title: "New session",
25905
26369
  modified,
25906
26370
  lastUserMessageAt: modified,
26371
+ lastMessageAt: modified,
26372
+ ...options?.lastMessagePreview ? { lastMessagePreview: options.lastMessagePreview } : {},
25907
26373
  streaming: true,
25908
26374
  unread: false,
25909
26375
  optimistic: true
@@ -25950,6 +26416,29 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25950
26416
  sidebarStore.setSidebarSessionSelectionAnchor((current) => current === entryKey ? "" : current);
25951
26417
  }
25952
26418
  }, [sidebarStore]);
26419
+ const updateOptimisticSidebarSessionPreview = import_react.useCallback((options) => {
26420
+ const preview = options.preview.trim();
26421
+ if (!preview) return;
26422
+ const currentState = sessionStateRef.current;
26423
+ const modified = (/* @__PURE__ */ new Date()).toISOString();
26424
+ const sessionId = options.optimisticSidebarSessionId || currentState.sessionId || currentState.sessionKey;
26425
+ const sessionPath = currentState.sessionFile;
26426
+ const previewUpdate = {
26427
+ ...sessionId ? { sessionId } : {},
26428
+ ...sessionPath ? { sessionPath } : {},
26429
+ preview,
26430
+ modified
26431
+ };
26432
+ sidebarStore.setDirectoryIndexDataByPath((current) => updateOptimisticSidebarSessionPreviewEntry(current, previewUpdate));
26433
+ sidebarStore.setSessionsEvent((current) => {
26434
+ if (!current?.directoryIndexes) return current;
26435
+ const directoryIndexes = updateOptimisticSidebarSessionPreviewEntry(current.directoryIndexes, previewUpdate);
26436
+ return directoryIndexes === current.directoryIndexes ? current : {
26437
+ ...current,
26438
+ directoryIndexes
26439
+ };
26440
+ });
26441
+ }, [sessionStateRef, sidebarStore]);
25953
26442
  const clearSelectedSidebarSelection = import_react.useCallback(() => {
25954
26443
  sidebarStore.setSelectedSidebarSessionKeys((current) => current.length === 0 ? current : []);
25955
26444
  sidebarStore.setSidebarSessionSelectionAnchor((current) => current ? "" : current);
@@ -25982,6 +26471,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25982
26471
  removeOptimisticUserMessage,
25983
26472
  addOptimisticSidebarSession,
25984
26473
  removeOptimisticSidebarSession,
26474
+ updateOptimisticSidebarSessionPreview,
25985
26475
  setSidebarDirectories: sidebarStore.setSidebarDirectories,
25986
26476
  setStoredDraftDirectory,
25987
26477
  setDraftSessionLoadingOwnerKey,