@alivault/pico 0.10.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  import { i as __toESM } from "../_runtime.mjs";
2
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";
3
3
  import { t as cva } from "../_libs/class-variance-authority+clsx.mjs";
4
- import { A as Ellipsis, B as ChevronDown, C as ImagePlus, D as FolderPlus, E as Folder, H as ArrowUp, I as ChevronsUpDown, K as ArrowLeft, L as ChevronsDownUp, M as Download, N as Copy, O as FileDiff, P as Command, T as GitCommitHorizontal, U as ArrowUpToLine, V as Check, W as ArrowUpRight, _ 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 EllipsisVertical, k as ExternalLink, l as Settings2, m as PanelLeftOpen, n as WandSparkles, o as Tag, p as PanelLeft, q as ArrowDown, r as Upload, s as Square, t as X, u as Search, v as Minimize2, w as GitBranch, x as ListEnd, y as Maximize2, z as ChevronRight } from "../_libs/lucide-react.mjs";
5
- import { $ as readStoredAutoScrollEnabled, C as THEME_COLOR_MODES, D as TitleTooltip, E as THEME_STORAGE_KEY, F as buildItemsFromSync, I as cn, J as normalizeStoredDirectoryList, K as normalizePromptImage, M as VIEWER_CONTEXT_STORAGE_KEY, N as appliedThemeClass, O as Tooltip$1, Q as promptDraftKey, R as createContextId, S as Spinner, T as THEME_FAMILIES, U as flattenTree, W as getSessionTitle, X as normalizeThemeFamily, Y as normalizeThemeColorMode, Z as previewUrlForImage, _ as RIGHT_SIDEBAR_WIDTHS_STORAGE_KEY, _t as sessionListEntryKey, a as COLLAPSED_DIRECTORIES_STORAGE_KEY, at as readStoredPromptDraft, b as SESSION_DONE_SOUND_ENABLED_STORAGE_KEY, bt as thinkingSummaryText, c as Kbd, ct as readStoredSessionDoneDesktopNotificationsEnabled, d as RECENT_DIRECTORIES_STORAGE_KEY, dt as readStoredTheme, et as readStoredCenterMessages, f as RIGHT_SIDEBAR_ACTIVE_TAB_STORAGE_KEY, ft as readStoredThemeColorMode, g as RIGHT_SIDEBAR_OPEN_STORAGE_KEY, gt as sameContextUsage, h as RIGHT_SIDEBAR_HISTORY_TAB_STORAGE_KEY, ht as safeLocalStorageSetItem, i as CENTER_MESSAGES_STORAGE_KEY, it as readStoredPinnedSessionKeys, j as TooltipTrigger, k as TooltipContent, l as KeyboardShortcutContent, lt as readStoredSessionDoneSoundEnabled, m as RIGHT_SIDEBAR_HISTORY_HEIGHT_STORAGE_KEY, mt as safeLocalStorageGetItem, n as APPLIED_THEME_STORAGE_KEY, nt as readStoredDraftDirectory, o as DRAFT_DIRECTORY_STORAGE_KEY, ot as readStoredRecentDirectories, p as RIGHT_SIDEBAR_FILE_TREE_WIDTH_STORAGE_KEY, pt as rememberStoredPromptDraft, q as normalizeSessionSelectionKeys, r as AUTO_SCROLL_ENABLED_STORAGE_KEY, rt as readStoredHideToolBlocks, s as HIDE_TOOL_BLOCKS_STORAGE_KEY, st as readStoredRightSidebarOpen, tt as readStoredCollapsedDirectories, u as PINNED_SESSIONS_STORAGE_KEY, ut as readStoredSidebarDirectories, v as Route, vt as themeColorModeLabel, w as THEME_COLOR_MODE_STORAGE_KEY, x as SIDEBAR_DIRECTORIES_STORAGE_KEY, y as SESSION_DONE_DESKTOP_NOTIFICATIONS_ENABLED_STORAGE_KEY, yt as themeFamilyLabel, z as createInitialSessionState } from "./routes-D6yf6dMU.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-CHNxptm_.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";
@@ -10,10 +10,10 @@ import { t as shallow } from "../_libs/tanstack__store.mjs";
10
10
  import { t as useSelector } from "../_libs/tanstack__react-store.mjs";
11
11
  import { n as z } from "../_libs/next-themes.mjs";
12
12
  import { n as toast } from "../_libs/sonner.mjs";
13
+ import { a as registerCustomTheme, i as parsePatchFiles, n as MultiFileDiff, o as RegisteredCustomThemes, r as FileDiff$1, t as PatchDiff, u as createCssVariablesTheme } from "../_libs/@pierre/diffs+[...].mjs";
13
14
  import { i as AsyncDebouncer, r as Batcher, t as Throttler } from "../_libs/tanstack__pacer.mjs";
14
15
  import { t as _e } from "../_libs/cmdk.mjs";
15
16
  import { t as Drawer } from "../_libs/vaul.mjs";
16
- import { i as parsePatchFiles, n as MultiFileDiff, r as FileDiff$1, t as PatchDiff } from "../_libs/@pierre/diffs+[...].mjs";
17
17
  import { t as Markdown } from "../_libs/react-markdown+[...].mjs";
18
18
  import { t as rehypeRaw } from "../_libs/rehype-raw.mjs";
19
19
  import { n as defaultSchema } from "../_libs/hast-util-sanitize.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-CSvcOSyF.js
25
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-CELGJtcT.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;
@@ -980,7 +980,7 @@ function CommandGroup({ className, ...props }) {
980
980
  function CommandItem({ className, children, ...props }) {
981
981
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(_e.Item, {
982
982
  "data-slot": "command-item",
983
- className: cn("group/command-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-muted data-selected:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-selected:*:[svg]:text-foreground", className),
983
+ className: cn("group/command-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-accent data-selected:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-selected:*:[svg]:text-foreground", className),
984
984
  ...props,
985
985
  children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100" })]
986
986
  });
@@ -3100,19 +3100,84 @@ function AppShellSessionsDialogController({ ref, openStateRef, ...props }) {
3100
3100
  });
3101
3101
  }
3102
3102
  var THEME_OPTIONS = [...THEME_FAMILIES];
3103
- var THEME_COLOR_MODE_OPTIONS = [...THEME_COLOR_MODES];
3104
- function createInitialSettingsDialogState(currentTheme) {
3103
+ var THEME_SELECTION_SECTIONS = [
3104
+ {
3105
+ heading: "Auto",
3106
+ colorMode: "auto"
3107
+ },
3108
+ {
3109
+ heading: "Dark",
3110
+ colorMode: "dark"
3111
+ },
3112
+ {
3113
+ heading: "Light",
3114
+ colorMode: "light"
3115
+ }
3116
+ ].map((section) => ({
3117
+ ...section,
3118
+ themes: THEME_OPTIONS.filter((theme) => themeFamilySupportsColorMode(theme, section.colorMode))
3119
+ }));
3120
+ var SETTINGS_DIALOG_AUTO_FOCUS_MEDIA = "(min-width: 768px)";
3121
+ function shouldAutoFocusSettingsDialogInput() {
3122
+ return typeof window !== "undefined" && window.matchMedia(SETTINGS_DIALOG_AUTO_FOCUS_MEDIA).matches;
3123
+ }
3124
+ function isThemeColorMode(value) {
3125
+ return value === "auto" || value === "light" || value === "dark";
3126
+ }
3127
+ function themeSelectionKey(selection) {
3128
+ return `${selection.colorMode}:${selection.theme}`;
3129
+ }
3130
+ function themeSelectionFromKey(value) {
3131
+ const separatorIndex = value.indexOf(":");
3132
+ if (separatorIndex < 0) return void 0;
3133
+ const colorMode = value.slice(0, separatorIndex);
3134
+ const theme = value.slice(separatorIndex + 1);
3135
+ if (!isThemeColorMode(colorMode)) return void 0;
3136
+ if (!THEME_OPTIONS.includes(theme)) return void 0;
3137
+ const normalizedTheme = theme;
3138
+ if (!themeFamilySupportsColorMode(normalizedTheme, colorMode)) return;
3139
+ return {
3140
+ theme: normalizedTheme,
3141
+ colorMode
3142
+ };
3143
+ }
3144
+ function themeSelectionForCurrent(theme, colorMode) {
3145
+ return {
3146
+ theme,
3147
+ colorMode: themeFamilyFixedMode(theme) ?? colorMode
3148
+ };
3149
+ }
3150
+ function themeSelectionModeLabel(selection, systemTheme) {
3151
+ if (selection.colorMode === "auto") return themeColorModeLabel(selection.colorMode, systemTheme);
3152
+ return selection.colorMode === "light" ? "Light" : "Dark";
3153
+ }
3154
+ function themeSelectionValueLabel(selection, systemTheme) {
3155
+ return `${themeFamilyLabel(selection.theme)} · ${themeSelectionModeLabel(selection, systemTheme)}`;
3156
+ }
3157
+ function themeSelectionDescription(selection) {
3158
+ if (selection.colorMode === "auto") return `Auto: ${themeFamilyDescription(selection.theme)}`;
3159
+ if (themeFamilyFixedMode(selection.theme)) return `${themeSelectionModeLabel(selection)} only: ${themeFamilyDescription(selection.theme)}`;
3160
+ return themeFamilyDescription(selection.theme);
3161
+ }
3162
+ function themeSelectionKeywords(selection) {
3163
+ return [
3164
+ selection.colorMode,
3165
+ themeSelectionModeLabel(selection),
3166
+ ...themeFamilyKeywords(selection.theme)
3167
+ ];
3168
+ }
3169
+ function createInitialSettingsDialogState(currentTheme, currentThemeColorMode) {
3105
3170
  return {
3106
3171
  query: "",
3107
3172
  themeQuery: "",
3108
3173
  stage: "browse",
3109
3174
  selectedCommandId: "theme",
3110
- selectedTheme: currentTheme
3175
+ selectedTheme: themeSelectionForCurrent(currentTheme, currentThemeColorMode)
3111
3176
  };
3112
3177
  }
3113
3178
  function settingsDialogReducer(state, action) {
3114
3179
  switch (action.type) {
3115
- case "reset": return createInitialSettingsDialogState(action.currentTheme);
3180
+ case "reset": return createInitialSettingsDialogState(action.currentTheme, action.currentThemeColorMode);
3116
3181
  case "set-query": return {
3117
3182
  ...state,
3118
3183
  query: action.query
@@ -3128,7 +3193,7 @@ function settingsDialogReducer(state, action) {
3128
3193
  case "open-theme": return {
3129
3194
  ...state,
3130
3195
  stage: "theme",
3131
- selectedTheme: action.currentTheme
3196
+ selectedTheme: themeSelectionForCurrent(action.currentTheme, action.currentThemeColorMode)
3132
3197
  };
3133
3198
  case "close-theme": return {
3134
3199
  ...state,
@@ -3150,23 +3215,6 @@ function desktopNotificationPermissionLabel(permission) {
3150
3215
  function formatToggleValue(enabled) {
3151
3216
  return enabled ? "On" : "Off";
3152
3217
  }
3153
- function nextThemeColorMode(currentMode) {
3154
- return THEME_COLOR_MODE_OPTIONS[(THEME_COLOR_MODE_OPTIONS.indexOf(currentMode) + 1) % THEME_COLOR_MODE_OPTIONS.length] ?? "auto";
3155
- }
3156
- function themeDescription(theme) {
3157
- switch (theme) {
3158
- case "default": return "Use Pico's default palette.";
3159
- case "flexoki": return "Use Flexoki's warm paper and inky dark palettes.";
3160
- default: return "Use this theme.";
3161
- }
3162
- }
3163
- function themeKeywords(theme) {
3164
- return [
3165
- theme,
3166
- themeFamilyLabel(theme),
3167
- themeDescription(theme)
3168
- ];
3169
- }
3170
3218
  function settingsCommandKeywords(command) {
3171
3219
  return [
3172
3220
  command.title,
@@ -3180,7 +3228,7 @@ function settingsCommandFilter(value, search, keywords) {
3180
3228
  if (!query) return 1;
3181
3229
  return [value, ...keywords ?? []].join(" ").toLowerCase().includes(query) ? 1 : 0;
3182
3230
  }
3183
- function getSettingsCommandGroups({ currentTheme, currentThemeColorMode, systemTheme, hideThinkingBlocks, onHideThinkingBlocksChange, hideToolBlocks, onHideToolBlocksChange, centerMessages, onCenterMessagesChange, autoScrollEnabled, onAutoScrollEnabledChange, sessionDoneSoundEnabled, onSessionDoneSoundEnabledChange, sessionDoneDesktopNotificationsEnabled, onSessionDoneDesktopNotificationsEnabledChange, desktopNotificationPermission, onLoginProviders, onLogoutProviders, onThemeCommand, onThemeColorModeCommand }) {
3231
+ function getSettingsCommandGroups({ currentTheme, currentThemeColorMode, systemTheme, hideThinkingBlocks, onHideThinkingBlocksChange, hideToolBlocks, onHideToolBlocksChange, centerMessages, onCenterMessagesChange, autoScrollEnabled, onAutoScrollEnabledChange, sessionDoneSoundEnabled, onSessionDoneSoundEnabledChange, sessionDoneDesktopNotificationsEnabled, onSessionDoneDesktopNotificationsEnabledChange, desktopNotificationPermission, onLoginProviders, onLogoutProviders, onThemeCommand }) {
3184
3232
  const desktopNotificationDescription = desktopNotificationPermissionLabel(desktopNotificationPermission);
3185
3233
  return [
3186
3234
  {
@@ -3188,29 +3236,19 @@ function getSettingsCommandGroups({ currentTheme, currentThemeColorMode, systemT
3188
3236
  commands: [{
3189
3237
  id: "theme",
3190
3238
  title: "Theme",
3191
- description: "Choose between Default and Flexoki.",
3192
- valueLabel: themeFamilyLabel(currentTheme),
3193
- keywords: [
3194
- "color",
3195
- "mode",
3196
- "default",
3197
- "flexoki"
3198
- ],
3199
- onSelect: onThemeCommand
3200
- }, {
3201
- id: "theme-color-mode",
3202
- title: "Light/dark mode",
3203
- description: "Follow OS appearance, or force light or dark mode.",
3204
- valueLabel: themeColorModeLabel(currentThemeColorMode, systemTheme),
3239
+ description: "Choose an auto, dark, or light color palette.",
3240
+ valueLabel: themeSelectionValueLabel(themeSelectionForCurrent(currentTheme, currentThemeColorMode), systemTheme),
3205
3241
  keywords: [
3206
3242
  "auto",
3207
3243
  "system",
3208
3244
  "light",
3209
3245
  "dark",
3246
+ "color",
3210
3247
  "mode",
3211
- "color"
3248
+ "palette",
3249
+ ...THEME_OPTIONS.flatMap((theme) => themeFamilyKeywords(theme))
3212
3250
  ],
3213
- onSelect: onThemeColorModeCommand
3251
+ onSelect: onThemeCommand
3214
3252
  }]
3215
3253
  },
3216
3254
  {
@@ -3334,6 +3372,11 @@ function getSettingsCommandGroups({ currentTheme, currentThemeColorMode, systemT
3334
3372
  ];
3335
3373
  }
3336
3374
  function SettingsBrowseBody({ query, selectedCommandId, settingGroups, onQueryChange, onSelectedCommandIdChange, onSelectCommand }) {
3375
+ const inputRef = import_react.useRef(null);
3376
+ import_react.useEffect(() => {
3377
+ if (!shouldAutoFocusSettingsDialogInput()) return;
3378
+ inputRef.current?.focus();
3379
+ }, []);
3337
3380
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Command$1, {
3338
3381
  shouldFilter: true,
3339
3382
  filter: settingsCommandFilter,
@@ -3343,6 +3386,7 @@ function SettingsBrowseBody({ query, selectedCommandId, settingGroups, onQueryCh
3343
3386
  className: "min-h-0 flex-1",
3344
3387
  children: [
3345
3388
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandInput, {
3389
+ ref: inputRef,
3346
3390
  value: query,
3347
3391
  onValueChange: onQueryChange,
3348
3392
  placeholder: "Search settings",
@@ -3380,7 +3424,12 @@ function SettingsBrowseBody({ query, selectedCommandId, settingGroups, onQueryCh
3380
3424
  ]
3381
3425
  });
3382
3426
  }
3383
- function SettingsThemeBody({ currentTheme, selectedTheme, themeQuery, onThemeQueryChange, onThemePreview, onThemeSelect, onCancelThemePreview }) {
3427
+ function SettingsThemeBody({ currentTheme, currentThemeColorMode, selectedTheme, systemTheme, themeQuery, onThemeQueryChange, onThemePreview, onThemeSelect, onCancelThemePreview }) {
3428
+ const inputRef = import_react.useRef(null);
3429
+ import_react.useEffect(() => {
3430
+ if (!shouldAutoFocusSettingsDialogInput()) return;
3431
+ inputRef.current?.focus();
3432
+ }, []);
3384
3433
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
3385
3434
  role: "presentation",
3386
3435
  className: "flex min-h-0 flex-1 flex-col",
@@ -3406,17 +3455,25 @@ function SettingsThemeBody({ currentTheme, selectedTheme, themeQuery, onThemeQue
3406
3455
  children: "Choose theme"
3407
3456
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
3408
3457
  className: "truncate text-xs text-muted-foreground",
3409
- children: ["Current: ", themeFamilyLabel(currentTheme)]
3458
+ children: [
3459
+ "Current:",
3460
+ " ",
3461
+ themeSelectionValueLabel(themeSelectionForCurrent(currentTheme, currentThemeColorMode), systemTheme)
3462
+ ]
3410
3463
  })]
3411
3464
  })]
3412
3465
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Command$1, {
3413
3466
  shouldFilter: true,
3414
3467
  loop: true,
3415
- value: selectedTheme,
3416
- onValueChange: (value) => onThemePreview(value),
3468
+ value: themeSelectionKey(selectedTheme),
3469
+ onValueChange: (value) => {
3470
+ const selection = themeSelectionFromKey(value);
3471
+ if (selection) onThemePreview(selection);
3472
+ },
3417
3473
  className: "min-h-0 flex-1 rounded-none!",
3418
3474
  children: [
3419
3475
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandInput, {
3476
+ ref: inputRef,
3420
3477
  value: themeQuery,
3421
3478
  onValueChange: onThemeQueryChange,
3422
3479
  placeholder: "Search themes",
@@ -3424,27 +3481,35 @@ function SettingsThemeBody({ currentTheme, selectedTheme, themeQuery, onThemeQue
3424
3481
  }),
3425
3482
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandList, {
3426
3483
  className: "max-h-none min-h-0 flex-1 md:max-h-[min(70vh,28rem)]",
3427
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandEmpty, { children: "No themes found." }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandGroup, {
3428
- heading: "Themes",
3429
- children: THEME_OPTIONS.map((theme) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandItem, {
3430
- value: theme,
3431
- keywords: themeKeywords(theme),
3432
- onMouseEnter: () => onThemePreview(theme),
3433
- onSelect: () => onThemeSelect(theme),
3434
- "data-checked": theme === currentTheme ? true : void 0,
3435
- className: "items-start py-2",
3436
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
3437
- className: "flex min-w-0 flex-1 flex-col gap-0.5",
3438
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
3439
- className: "truncate font-medium",
3440
- children: themeFamilyLabel(theme)
3441
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
3442
- className: "line-clamp-2 text-xs text-muted-foreground",
3443
- children: themeDescription(theme)
3444
- })]
3445
- })
3446
- }, theme))
3447
- })]
3484
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandEmpty, { children: "No themes found." }), THEME_SELECTION_SECTIONS.map((section) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandGroup, {
3485
+ heading: section.heading,
3486
+ className: "**:[[cmdk-group-heading]]:sticky **:[[cmdk-group-heading]]:top-0 **:[[cmdk-group-heading]]:z-10 **:[[cmdk-group-heading]]:bg-popover",
3487
+ children: section.themes.map((theme) => {
3488
+ const selection = {
3489
+ theme,
3490
+ colorMode: section.colorMode
3491
+ };
3492
+ const selectionKey = themeSelectionKey(selection);
3493
+ const currentSelection = themeSelectionForCurrent(currentTheme, currentThemeColorMode);
3494
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandItem, {
3495
+ value: selectionKey,
3496
+ keywords: themeSelectionKeywords(selection),
3497
+ onSelect: () => onThemeSelect(selection),
3498
+ "data-checked": selectionKey === themeSelectionKey(currentSelection) ? true : void 0,
3499
+ className: "items-start py-2",
3500
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
3501
+ className: "flex min-w-0 flex-1 flex-col gap-0.5",
3502
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
3503
+ className: "truncate font-medium",
3504
+ children: themeFamilyLabel(theme)
3505
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
3506
+ className: "line-clamp-2 text-xs text-muted-foreground",
3507
+ children: themeSelectionDescription(selection)
3508
+ })]
3509
+ })
3510
+ }, selectionKey);
3511
+ })
3512
+ }, section.colorMode))]
3448
3513
  }),
3449
3514
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
3450
3515
  className: "hidden border-t border-border/70 px-3 py-2 text-xs text-muted-foreground md:block",
@@ -3459,18 +3524,23 @@ function SettingsDialogBody({ stage, browseBodyProps, themeBodyProps }) {
3459
3524
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingsBrowseBody, { ...browseBodyProps });
3460
3525
  }
3461
3526
  function AppShellSettingsDialog({ open, onOpenChange, currentTheme, currentThemeColorMode, onThemeChange, onThemeColorModeChange, onThemePreviewChange, systemTheme, hideThinkingBlocks, onHideThinkingBlocksChange, hideToolBlocks, onHideToolBlocksChange, centerMessages, onCenterMessagesChange, autoScrollEnabled, onAutoScrollEnabledChange, sessionDoneSoundEnabled, onSessionDoneSoundEnabledChange, sessionDoneDesktopNotificationsEnabled, onSessionDoneDesktopNotificationsEnabledChange, desktopNotificationPermission, onLoginProviders, onLogoutProviders }) {
3462
- const [state, dispatch] = import_react.useReducer(settingsDialogReducer, currentTheme, createInitialSettingsDialogState);
3463
- const themePreviewInitialRef = import_react.useRef(currentTheme);
3527
+ const [state, dispatch] = import_react.useReducer(settingsDialogReducer, {
3528
+ currentTheme,
3529
+ currentThemeColorMode
3530
+ }, ({ currentTheme: initialTheme, currentThemeColorMode: initialColorMode }) => createInitialSettingsDialogState(initialTheme, initialColorMode));
3531
+ const themePreviewInitialRef = import_react.useRef(themeSelectionForCurrent(currentTheme, currentThemeColorMode));
3464
3532
  const isMobile = useIsMobile();
3465
3533
  import_react.useEffect(() => {
3466
3534
  if (open) return;
3467
- if (state.stage === "theme") onThemePreviewChange(themePreviewInitialRef.current);
3535
+ if (state.stage === "theme") onThemePreviewChange(themePreviewInitialRef.current.theme, themePreviewInitialRef.current.colorMode);
3468
3536
  dispatch({
3469
3537
  type: "reset",
3470
- currentTheme
3538
+ currentTheme,
3539
+ currentThemeColorMode
3471
3540
  });
3472
3541
  }, [
3473
3542
  currentTheme,
3543
+ currentThemeColorMode,
3474
3544
  onThemePreviewChange,
3475
3545
  open,
3476
3546
  state.stage
@@ -3480,21 +3550,24 @@ function AppShellSettingsDialog({ open, onOpenChange, currentTheme, currentTheme
3480
3550
  console.error(error);
3481
3551
  });
3482
3552
  };
3483
- const handleThemePreview = (theme) => {
3484
- if (!THEME_OPTIONS.includes(theme)) return;
3553
+ const handleThemePreview = (selection) => {
3554
+ if (!THEME_OPTIONS.includes(selection.theme)) return;
3555
+ if (!themeFamilySupportsColorMode(selection.theme, selection.colorMode)) return;
3485
3556
  dispatch({
3486
3557
  type: "preview-theme",
3487
- selectedTheme: theme
3558
+ selectedTheme: selection
3488
3559
  });
3489
- onThemePreviewChange(theme);
3560
+ onThemePreviewChange(selection.theme, selection.colorMode);
3490
3561
  };
3491
- const handleThemeSelect = (theme) => {
3562
+ const handleThemeSelect = (selection) => {
3492
3563
  dispatch({
3493
3564
  type: "preview-theme",
3494
- selectedTheme: theme
3565
+ selectedTheme: selection
3495
3566
  });
3496
- themePreviewInitialRef.current = theme;
3497
- onThemeChange(theme);
3567
+ themePreviewInitialRef.current = selection;
3568
+ onThemeColorModeChange(selection.colorMode);
3569
+ onThemeChange(selection.theme);
3570
+ onOpenChange(false);
3498
3571
  };
3499
3572
  const cancelThemePreview = () => {
3500
3573
  const initialTheme = themePreviewInitialRef.current;
@@ -3502,7 +3575,7 @@ function AppShellSettingsDialog({ open, onOpenChange, currentTheme, currentTheme
3502
3575
  type: "close-theme",
3503
3576
  selectedTheme: initialTheme
3504
3577
  });
3505
- onThemePreviewChange(initialTheme);
3578
+ onThemePreviewChange(initialTheme.theme, initialTheme.colorMode);
3506
3579
  };
3507
3580
  const settingGroups = getSettingsCommandGroups({
3508
3581
  currentTheme,
@@ -3524,13 +3597,13 @@ function AppShellSettingsDialog({ open, onOpenChange, currentTheme, currentTheme
3524
3597
  onLoginProviders,
3525
3598
  onLogoutProviders,
3526
3599
  onThemeCommand: () => {
3527
- themePreviewInitialRef.current = currentTheme;
3600
+ themePreviewInitialRef.current = themeSelectionForCurrent(currentTheme, currentThemeColorMode);
3528
3601
  dispatch({
3529
3602
  type: "open-theme",
3530
- currentTheme
3603
+ currentTheme,
3604
+ currentThemeColorMode
3531
3605
  });
3532
- },
3533
- onThemeColorModeCommand: () => onThemeColorModeChange(nextThemeColorMode(currentThemeColorMode))
3606
+ }
3534
3607
  });
3535
3608
  const settingsCommandBody = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingsDialogBody, {
3536
3609
  stage: state.stage,
@@ -3550,7 +3623,9 @@ function AppShellSettingsDialog({ open, onOpenChange, currentTheme, currentTheme
3550
3623
  },
3551
3624
  themeBodyProps: {
3552
3625
  currentTheme,
3626
+ currentThemeColorMode,
3553
3627
  selectedTheme: state.selectedTheme,
3628
+ systemTheme,
3554
3629
  themeQuery: state.themeQuery,
3555
3630
  onThemeQueryChange: (themeQuery) => dispatch({
3556
3631
  type: "set-theme-query",
@@ -5286,8 +5361,8 @@ function AppShellTreeDialogController({ ref, openStateRef, viewerContextId, sess
5286
5361
  })
5287
5362
  });
5288
5363
  },
5289
- onSuccess: async () => {
5290
- await queryClient.invalidateQueries({
5364
+ onSuccess: () => {
5365
+ queryClient.invalidateQueries({
5291
5366
  queryKey,
5292
5367
  exact: true,
5293
5368
  refetchType: "active"
@@ -5318,6 +5393,7 @@ function AppShellTreeDialogController({ ref, openStateRef, viewerContextId, sess
5318
5393
  };
5319
5394
  const navigateTreeNode = async (targetId, options) => {
5320
5395
  if (!viewerContextId) return;
5396
+ setOpenState(false);
5321
5397
  try {
5322
5398
  const response = await navigateTreeNodeMutation.mutateAsync({
5323
5399
  targetId,
@@ -5332,7 +5408,6 @@ function AppShellTreeDialogController({ ref, openStateRef, viewerContextId, sess
5332
5408
  toast.info("Tree navigation cancelled");
5333
5409
  return;
5334
5410
  }
5335
- setOpenState(false);
5336
5411
  toast.success(options?.summarize ? "Continued from summarized branch" : "Moved session tree cursor");
5337
5412
  } catch (error) {
5338
5413
  toast.error(error instanceof Error ? error.message : "Failed to navigate tree");
@@ -6068,6 +6143,74 @@ function updateDirectoryIndexLoadingState(current, directories, loading) {
6068
6143
  }
6069
6144
  return changed ? next : current;
6070
6145
  }
6146
+ function upsertOptimisticSidebarSessionEntry(current, entry) {
6147
+ const directory = entry.cwd.trim();
6148
+ const optimisticId = entry.id.trim();
6149
+ if (!directory || !optimisticId) return current;
6150
+ const currentSnapshot = current[directory];
6151
+ const currentSessions = currentSnapshot?.sessions || [];
6152
+ const existingEntry = currentSessions.find((session) => session.id === optimisticId);
6153
+ const nextSessions = [{
6154
+ ...existingEntry,
6155
+ ...entry,
6156
+ id: optimisticId,
6157
+ cwd: directory,
6158
+ optimistic: true
6159
+ }, ...currentSessions.filter((session) => session.id !== optimisticId)];
6160
+ const totalCountBaseline = Math.max(currentSnapshot?.totalCount ?? 0, currentSessions.length);
6161
+ return {
6162
+ ...current,
6163
+ [directory]: {
6164
+ directory,
6165
+ totalCount: existingEntry ? totalCountBaseline : totalCountBaseline + 1,
6166
+ revision: `optimistic:${optimisticId}:${currentSnapshot?.revision || "initial"}:${entry.modified || ""}`,
6167
+ sessions: nextSessions
6168
+ }
6169
+ };
6170
+ }
6171
+ function removeOptimisticSidebarSessionEntry(current, optimisticId) {
6172
+ const targetId = optimisticId?.trim() || "";
6173
+ if (!targetId) return current;
6174
+ let changed = false;
6175
+ const next = { ...current };
6176
+ for (const [directory, snapshot] of Object.entries(current)) {
6177
+ const nextSessions = snapshot.sessions.filter((entry) => entry.id !== targetId);
6178
+ if (nextSessions.length === snapshot.sessions.length) continue;
6179
+ changed = true;
6180
+ next[directory] = {
6181
+ ...snapshot,
6182
+ totalCount: Math.max(0, snapshot.totalCount - 1),
6183
+ revision: `remove-optimistic:${targetId}:${snapshot.revision}`,
6184
+ sessions: nextSessions
6185
+ };
6186
+ }
6187
+ return changed ? next : current;
6188
+ }
6189
+ function isOptimisticSidebarSessionEntry(entry) {
6190
+ return Boolean(entry.optimistic || entry.id?.startsWith("optimistic:") && !entry.path);
6191
+ }
6192
+ function mergeDirectoryIndexDataPreservingOptimistic(current, next) {
6193
+ let merged = mergeDirectoryIndexData(current, next);
6194
+ for (const [directory, snapshot] of Object.entries(current)) {
6195
+ const optimisticSessions = snapshot.sessions.filter(isOptimisticSidebarSessionEntry);
6196
+ if (optimisticSessions.length === 0) continue;
6197
+ const targetSnapshot = merged[directory];
6198
+ if (!targetSnapshot) continue;
6199
+ const targetIds = new Set(targetSnapshot.sessions.flatMap((entry) => entry.id ? [entry.id] : []));
6200
+ const missingOptimisticSessions = optimisticSessions.filter((entry) => entry.id && !targetIds.has(entry.id));
6201
+ if (missingOptimisticSessions.length === 0) continue;
6202
+ merged = {
6203
+ ...merged,
6204
+ [directory]: {
6205
+ ...targetSnapshot,
6206
+ totalCount: targetSnapshot.totalCount + missingOptimisticSessions.length,
6207
+ revision: `preserve-optimistic:${targetSnapshot.revision}`,
6208
+ sessions: [...missingOptimisticSessions, ...targetSnapshot.sessions]
6209
+ }
6210
+ };
6211
+ }
6212
+ return merged;
6213
+ }
6071
6214
  function sameSessionEntryRecord(left, right) {
6072
6215
  const leftKeys = Object.keys(left);
6073
6216
  const rightKeys = Object.keys(right);
@@ -6239,11 +6382,11 @@ function computeAppShellSidebarDerived(state) {
6239
6382
  const sidebarSessions = Array.from(sidebarSessionEntriesByKey.values());
6240
6383
  const pinnedSidebarSessions = state.pinnedSidebarSessionKeys.flatMap((key) => {
6241
6384
  const entry = sidebarSessionEntriesByKey.get(key);
6242
- return entry?.path || entry?.id ? [entry] : [];
6385
+ return entry?.path || entry?.id && !isOptimisticSidebarSessionEntry(entry) ? [entry] : [];
6243
6386
  });
6244
6387
  const selectedSidebarSessions = state.selectedSidebarSessionKeys.flatMap((key) => {
6245
6388
  const entry = sidebarSessionEntriesByKey.get(key);
6246
- return entry?.path || entry?.id ? [entry] : [];
6389
+ return entry?.path || entry?.id && !isOptimisticSidebarSessionEntry(entry) ? [entry] : [];
6247
6390
  });
6248
6391
  const workspaceVersion = [
6249
6392
  baseSidebarDirectories.join("\n"),
@@ -7383,54 +7526,98 @@ function FooterKbd({ children }) {
7383
7526
  }
7384
7527
  function RenameSessionDialog({ open, onOpenChange, renameValue, onRenameValueChange, onRenameSession }) {
7385
7528
  const isMobile = useIsMobile();
7386
- const renameInput = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
7387
- value: renameValue,
7388
- onChange: (event) => onRenameValueChange(event.target.value),
7389
- onKeyDown: (event) => {
7390
- if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
7391
- event.preventDefault();
7392
- onRenameSession();
7393
- },
7394
- placeholder: "Session name"
7529
+ const renameInputRef = import_react.useRef(null);
7530
+ import_react.useEffect(() => {
7531
+ if (!open) return;
7532
+ const frame = window.requestAnimationFrame(() => {
7533
+ renameInputRef.current?.focus();
7534
+ renameInputRef.current?.select();
7535
+ });
7536
+ return () => {
7537
+ window.cancelAnimationFrame(frame);
7538
+ };
7539
+ }, [open]);
7540
+ const body = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7541
+ className: "flex min-h-0 flex-1 flex-col",
7542
+ children: [
7543
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7544
+ className: "border-b border-border/70 px-3 py-2",
7545
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7546
+ className: "truncate text-sm font-medium",
7547
+ children: "Rename session"
7548
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7549
+ className: "truncate text-xs text-muted-foreground",
7550
+ children: "Update the display name shown in the sidebar."
7551
+ })]
7552
+ }),
7553
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7554
+ className: "flex min-h-0 flex-1 items-center p-3",
7555
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input$1, {
7556
+ ref: renameInputRef,
7557
+ value: renameValue,
7558
+ onChange: (event) => onRenameValueChange(event.target.value),
7559
+ onKeyDown: (event) => {
7560
+ if (event.key === "Escape") {
7561
+ event.preventDefault();
7562
+ event.stopPropagation();
7563
+ onOpenChange(false);
7564
+ return;
7565
+ }
7566
+ if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
7567
+ event.preventDefault();
7568
+ event.stopPropagation();
7569
+ onRenameSession();
7570
+ },
7571
+ placeholder: "Session name",
7572
+ className: "min-w-0 flex-1"
7573
+ })
7574
+ }),
7575
+ isMobile ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7576
+ className: "hidden flex-wrap items-center gap-x-3 gap-y-1 border-t border-border/70 px-3 py-2 text-xs text-muted-foreground md:flex",
7577
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
7578
+ className: "inline-flex items-center gap-1",
7579
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd, { children: "Enter" }), " Save"]
7580
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
7581
+ className: "inline-flex items-center gap-1",
7582
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd, { children: "Esc" }), " Cancel"]
7583
+ })]
7584
+ })
7585
+ ]
7395
7586
  });
7396
7587
  if (isMobile) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Drawer$1, {
7397
7588
  open,
7398
7589
  onOpenChange,
7399
7590
  autoFocus: false,
7400
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerContent, { children: [
7401
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerTitle, { children: "Rename session" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerDescription, { children: "Update the display name shown in the sidebar." })] }),
7402
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7403
- className: "px-4 pb-2",
7404
- children: renameInput
7405
- }),
7406
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerFooter, {
7407
- className: "border-t border-border/70",
7408
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7409
- variant: "outline",
7410
- onClick: () => onOpenChange(false),
7411
- children: "Cancel"
7412
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7413
- onClick: onRenameSession,
7414
- children: "Save"
7415
- })]
7416
- })
7417
- ] })
7591
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerContent, {
7592
+ className: "max-h-[90svh] overflow-hidden",
7593
+ children: [
7594
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerTitle, { children: "Rename session" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerDescription, { children: "Update the display name shown in the sidebar." })] }),
7595
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7596
+ className: "flex min-h-0 flex-1 flex-col overflow-hidden px-4 pb-4",
7597
+ children: body
7598
+ }),
7599
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerFooter, {
7600
+ className: "border-t border-border/70",
7601
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7602
+ variant: "outline",
7603
+ onClick: () => onOpenChange(false),
7604
+ children: "Cancel"
7605
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7606
+ onClick: onRenameSession,
7607
+ children: "Save"
7608
+ })]
7609
+ })
7610
+ ]
7611
+ })
7418
7612
  });
7419
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
7613
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandDialog, {
7420
7614
  open,
7421
7615
  onOpenChange,
7422
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, { children: [
7423
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: "Rename session" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, { children: "Update the display name shown in the sidebar." })] }),
7424
- renameInput,
7425
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7426
- variant: "outline",
7427
- onClick: () => onOpenChange(false),
7428
- children: "Cancel"
7429
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7430
- onClick: onRenameSession,
7431
- children: "Save"
7432
- })] })
7433
- ] })
7616
+ title: "Rename session",
7617
+ description: "Update the display name shown in the sidebar.",
7618
+ className: "sm:max-w-md",
7619
+ initialFocus: true,
7620
+ children: body
7434
7621
  });
7435
7622
  }
7436
7623
  function RenameSessionDialogController({ ref, openStateRef, onRenameSession }) {
@@ -7480,35 +7667,138 @@ function RenameSessionDialogController({ ref, openStateRef, onRenameSession }) {
7480
7667
  }
7481
7668
  });
7482
7669
  }
7483
- function DeleteSessionsDialog({ open, onOpenChange, title, description, onDeleteSession }) {
7484
- if (useIsMobile()) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Drawer$1, {
7670
+ function DeleteSessionsDialog({ open, onOpenChange, title, description, targets, onDeleteSession }) {
7671
+ const isMobile = useIsMobile();
7672
+ const deletePanelRef = import_react.useRef(null);
7673
+ import_react.useEffect(() => {
7674
+ if (!open || isMobile) return;
7675
+ const frame = window.requestAnimationFrame(() => {
7676
+ deletePanelRef.current?.focus();
7677
+ });
7678
+ return () => {
7679
+ window.cancelAnimationFrame(frame);
7680
+ };
7681
+ }, [isMobile, open]);
7682
+ import_react.useEffect(() => {
7683
+ if (!open || isMobile) return;
7684
+ const handleDeleteKeyDown = (event) => {
7685
+ if (event.key === "Escape") {
7686
+ event.preventDefault();
7687
+ event.stopPropagation();
7688
+ onOpenChange(false);
7689
+ return;
7690
+ }
7691
+ if (event.key !== "Enter" || event.isComposing) return;
7692
+ event.preventDefault();
7693
+ event.stopPropagation();
7694
+ onDeleteSession();
7695
+ };
7696
+ window.addEventListener("keydown", handleDeleteKeyDown, true);
7697
+ return () => {
7698
+ window.removeEventListener("keydown", handleDeleteKeyDown, true);
7699
+ };
7700
+ }, [
7701
+ isMobile,
7702
+ onDeleteSession,
7703
+ onOpenChange,
7704
+ open
7705
+ ]);
7706
+ const previewTargets = targets.slice(0, 8);
7707
+ const remainingTargetCount = Math.max(0, targets.length - previewTargets.length);
7708
+ const body = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7709
+ ref: deletePanelRef,
7710
+ role: "presentation",
7711
+ tabIndex: -1,
7712
+ className: "flex min-h-0 flex-1 flex-col outline-none",
7713
+ onKeyDown: (event) => {
7714
+ if (event.key === "Escape") {
7715
+ event.preventDefault();
7716
+ event.stopPropagation();
7717
+ onOpenChange(false);
7718
+ return;
7719
+ }
7720
+ if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
7721
+ event.preventDefault();
7722
+ event.stopPropagation();
7723
+ onDeleteSession();
7724
+ },
7725
+ children: [
7726
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7727
+ className: "border-b border-border/70 px-3 py-2",
7728
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7729
+ className: "truncate text-sm font-medium",
7730
+ children: title
7731
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7732
+ className: "truncate text-xs text-muted-foreground",
7733
+ children: description
7734
+ })]
7735
+ }),
7736
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7737
+ className: "flex min-h-0 flex-1 flex-col gap-3 p-3",
7738
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
7739
+ className: "text-sm text-muted-foreground",
7740
+ children: "This cannot be undone."
7741
+ }), targets.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7742
+ className: "max-h-48 space-y-1 overflow-y-auto rounded-lg border bg-muted/30 p-2 text-sm",
7743
+ children: [previewTargets.map((target) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7744
+ className: "truncate",
7745
+ children: target.title || "New session"
7746
+ }, target.path || target.id)), remainingTargetCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7747
+ className: "text-muted-foreground",
7748
+ children: [
7749
+ "…and ",
7750
+ remainingTargetCount,
7751
+ " more"
7752
+ ]
7753
+ }) : null]
7754
+ }) : null]
7755
+ }),
7756
+ isMobile ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
7757
+ className: "hidden flex-wrap items-center gap-x-3 gap-y-1 border-t border-border/70 px-3 py-2 text-xs text-muted-foreground md:flex",
7758
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
7759
+ className: "inline-flex items-center gap-1",
7760
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd, { children: "Enter" }), " Delete"]
7761
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
7762
+ className: "inline-flex items-center gap-1",
7763
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FooterKbd, { children: "Esc" }), " Cancel"]
7764
+ })]
7765
+ })
7766
+ ]
7767
+ });
7768
+ if (isMobile) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Drawer$1, {
7485
7769
  open,
7486
7770
  onOpenChange,
7487
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerTitle, { children: title }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerDescription, { children: description })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerFooter, {
7488
- className: "border-t border-border/70",
7489
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7490
- variant: "outline",
7491
- onClick: () => onOpenChange(false),
7492
- children: "Cancel"
7493
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7494
- variant: "destructive",
7495
- onClick: onDeleteSession,
7496
- children: "Delete"
7497
- })]
7498
- })] })
7771
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerContent, {
7772
+ className: "max-h-[90svh] overflow-hidden",
7773
+ children: [
7774
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerTitle, { children: title }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerDescription, { children: description })] }),
7775
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7776
+ className: "flex min-h-0 flex-1 flex-col overflow-hidden px-4 pb-4",
7777
+ children: body
7778
+ }),
7779
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DrawerFooter, {
7780
+ className: "border-t border-border/70",
7781
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7782
+ variant: "outline",
7783
+ onClick: () => onOpenChange(false),
7784
+ children: "Cancel"
7785
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7786
+ variant: "destructive",
7787
+ onClick: onDeleteSession,
7788
+ children: "Delete"
7789
+ })]
7790
+ })
7791
+ ]
7792
+ })
7499
7793
  });
7500
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
7794
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandDialog, {
7501
7795
  open,
7502
7796
  onOpenChange,
7503
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: title }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, { children: description })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogFooter, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7504
- variant: "outline",
7505
- onClick: () => onOpenChange(false),
7506
- children: "Cancel"
7507
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
7508
- variant: "destructive",
7509
- onClick: onDeleteSession,
7510
- children: "Delete"
7511
- })] })] })
7797
+ title,
7798
+ description,
7799
+ className: "sm:max-w-md",
7800
+ initialFocus: true,
7801
+ children: body
7512
7802
  });
7513
7803
  }
7514
7804
  function DeleteOldDirectorySessionsDialog({ open, onOpenChange, directory, daysValue, preview, previewLoading, deleteLoading, onDaysValueChange, onPreview, onDelete }) {
@@ -7750,6 +8040,7 @@ function DeleteSessionsDialogController({ ref, openStateRef, onDeleteSession })
7750
8040
  onOpenChange: setOpenState,
7751
8041
  title: targets.length === 1 ? "Delete session" : "Delete sessions",
7752
8042
  description: targets.length === 1 ? `Delete "${targets[0]?.title || "New session"}" from disk?` : `Delete ${targets.length} selected sessions from disk?`,
8043
+ targets,
7753
8044
  onDeleteSession: () => {
7754
8045
  submitDelete();
7755
8046
  }
@@ -9409,6 +9700,24 @@ function formatComposerSkillName(skillName = "") {
9409
9700
  return [`${part.charAt(0).toUpperCase()}${part.slice(1)}`];
9410
9701
  }).join(" ");
9411
9702
  }
9703
+ function formatComposerDiffLineCommentReference(comment) {
9704
+ const sideLabel = comment.side === "deletions" ? "old" : "new";
9705
+ return `${comment.path}:L${comment.lineNumber} (${sideLabel})`;
9706
+ }
9707
+ function formatComposerDiffLineCommentForPrompt(comment) {
9708
+ const sourceLabel = comment.commit === "working-tree" ? "working tree changes" : comment.shortHash;
9709
+ return `- ${formatComposerDiffLineCommentReference(comment)} in ${sourceLabel}: ${comment.text}`;
9710
+ }
9711
+ function buildComposerPromptMessage({ diffLineComments, skillName, text }) {
9712
+ const baseMessage = serializeComposerDraft({
9713
+ text,
9714
+ skillName
9715
+ }).trim();
9716
+ const comments = diffLineComments?.filter((comment) => comment.text.trim().length > 0);
9717
+ if (!comments || comments.length === 0) return baseMessage;
9718
+ const commentBlock = `Diff comments:\n${comments.map(formatComposerDiffLineCommentForPrompt).join("\n")}`;
9719
+ return baseMessage ? `${baseMessage}\n\n${commentBlock}` : commentBlock;
9720
+ }
9412
9721
  function parseSlashCommandInput(value = "") {
9413
9722
  const rawValue = typeof value === "string" ? value : "";
9414
9723
  const trimmedStart = rawValue.trimStart();
@@ -9653,9 +9962,12 @@ function readQuotedAttribute(tag, name) {
9653
9962
  }
9654
9963
  function normalizeHighlightClassName(value) {
9655
9964
  if (!value) return void 0;
9656
- const classNames = value.split(/\s+/).filter((className) => /^sh__[A-Za-z0-9_-]+$/.test(className));
9965
+ const classNames = value.split(/\s+/).filter((className) => className === "line");
9657
9966
  return classNames.length > 0 ? classNames.join(" ") : void 0;
9658
9967
  }
9968
+ function isSafeCssVariableColor(value) {
9969
+ return /^var\(--[A-Za-z0-9-]+(?:,\s*var\(--[A-Za-z0-9-]+\))?\)$/.test(value);
9970
+ }
9659
9971
  function normalizeHighlightStyle(value) {
9660
9972
  if (!value) return void 0;
9661
9973
  for (const declaration of value.split(";")) {
@@ -9663,7 +9975,7 @@ function normalizeHighlightStyle(value) {
9663
9975
  const property = rawProperty?.trim().toLowerCase();
9664
9976
  const color = rawValueParts.join(":").trim();
9665
9977
  if (property !== "color") continue;
9666
- if (!/^var\(--sh-[A-Za-z0-9-]+\)$/.test(color)) continue;
9978
+ if (!isSafeCssVariableColor(color)) continue;
9667
9979
  return { color };
9668
9980
  }
9669
9981
  }
@@ -9709,7 +10021,7 @@ function renderHighlightedChild(child) {
9709
10021
  }
9710
10022
  function HighlightedCode({ className, html, language }) {
9711
10023
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
9712
- className: cn(language && `language-${language}`, className),
10024
+ className: cn("shiki", language && `language-${language}`, className),
9713
10025
  children: parseHighlightedHtml(html).map(renderHighlightedChild)
9714
10026
  });
9715
10027
  }
@@ -10013,6 +10325,78 @@ var MarkdownBlock = import_react.memo(function MarkdownBlock({ streaming = false
10013
10325
  })
10014
10326
  });
10015
10327
  });
10328
+ var PICO_DIFF_THEME_PAIR = {
10329
+ light: PICO_DIFF_SHIKI_THEMES.light,
10330
+ dark: PICO_DIFF_SHIKI_THEMES.dark
10331
+ };
10332
+ var PICO_DIFF_COLOR_CSS = `
10333
+ :host {
10334
+ --diffs-addition-color-override: var(--success);
10335
+ --diffs-deletion-color-override: var(--danger);
10336
+ --diffs-modified-color-override: var(--primary);
10337
+ --diffs-bg-addition-emphasis-override: color-mix(in lab, var(--diffs-bg) 42%, var(--diffs-addition-base));
10338
+ --diffs-bg-deletion-emphasis-override: color-mix(in lab, var(--diffs-bg) 42%, var(--diffs-deletion-base));
10339
+ }
10340
+
10341
+ @media (max-width: 640px) {
10342
+ [data-annotation-content] {
10343
+ inline-size: min(100%, calc(100vw - var(--diffs-column-number-width, 0px) - 1rem));
10344
+ }
10345
+ }
10346
+
10347
+ [data-line-type="change-addition"]:is(
10348
+ [data-gutter-buffer],
10349
+ [data-column-number],
10350
+ [data-line],
10351
+ [data-no-newline]
10352
+ ) {
10353
+ --mix-light: 84%;
10354
+ --mix-dark: 76%;
10355
+ }
10356
+
10357
+ [data-line-type="change-deletion"]:is(
10358
+ [data-gutter-buffer],
10359
+ [data-column-number],
10360
+ [data-line],
10361
+ [data-no-newline]
10362
+ ) {
10363
+ --mix-light: 84%;
10364
+ --mix-dark: 76%;
10365
+ }
10366
+ `;
10367
+ function registerPicoDiffTheme(name, type) {
10368
+ if (RegisteredCustomThemes.has(name)) return;
10369
+ const theme = createCssVariablesTheme({
10370
+ name,
10371
+ variablePrefix: "--diffs-",
10372
+ variableDefaults: { ...PICO_SHIKI_VARIABLE_DEFAULTS },
10373
+ fontStyle: false
10374
+ });
10375
+ registerCustomTheme(name, () => Promise.resolve({
10376
+ ...theme,
10377
+ type
10378
+ }));
10379
+ }
10380
+ function ensurePicoDiffThemes() {
10381
+ registerPicoDiffTheme(PICO_DIFF_SHIKI_THEMES.light, "light");
10382
+ registerPicoDiffTheme(PICO_DIFF_SHIKI_THEMES.dark, "dark");
10383
+ }
10384
+ function resolvePicoDiffThemeType({ resolvedTheme, systemTheme, theme }) {
10385
+ return appliedThemeClassColorMode(theme) ?? appliedThemeClassColorMode(resolvedTheme) ?? (resolvedTheme === "dark" || systemTheme === "dark" ? "dark" : "light");
10386
+ }
10387
+ ensurePicoDiffThemes();
10388
+ function usePicoDiffThemeOptions() {
10389
+ const { resolvedTheme, systemTheme, theme } = z();
10390
+ return {
10391
+ theme: PICO_DIFF_THEME_PAIR,
10392
+ themeType: resolvePicoDiffThemeType({
10393
+ resolvedTheme,
10394
+ systemTheme,
10395
+ theme
10396
+ }),
10397
+ unsafeCSS: PICO_DIFF_COLOR_CSS
10398
+ };
10399
+ }
10016
10400
  function Collapsible$1({ ...props }) {
10017
10401
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CollapsibleRoot, {
10018
10402
  "data-slot": "collapsible",
@@ -11033,6 +11417,7 @@ function EditDiffStatCountsView({ stats }) {
11033
11417
  });
11034
11418
  }
11035
11419
  function EditDiffBlock({ isBorderless = false, patch }) {
11420
+ const themeOptions = usePicoDiffThemeOptions();
11036
11421
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
11037
11422
  className: cn("overflow-hidden text-xs", !isBorderless && "rounded-md border bg-background"),
11038
11423
  style: { "--diffs-font-family": "var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace)" },
@@ -11040,6 +11425,7 @@ function EditDiffBlock({ isBorderless = false, patch }) {
11040
11425
  patch,
11041
11426
  disableWorkerPool: true,
11042
11427
  options: {
11428
+ ...themeOptions,
11043
11429
  diffStyle: "unified",
11044
11430
  disableFileHeader: true,
11045
11431
  lineDiffType: "word-alt",
@@ -11210,11 +11596,11 @@ function ToolBlockCardBody({ block }) {
11210
11596
  const isSelfContainedWriteTool = block.name === "write" && !block.isError && getWriteToolPayload(block)?.content !== void 0;
11211
11597
  const hasSelfContainedToolOutput = isSuccessfulEditTool || isSelfContainedWriteTool;
11212
11598
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
11213
- className: "border-t pt-3",
11599
+ className: cn("border-t", !isSuccessfulEditTool && "pt-3"),
11214
11600
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
11215
11601
  ref: block.name === "bash" ? autoScroll.ref : void 0,
11216
11602
  onScroll: block.name === "bash" ? autoScroll.onScroll : void 0,
11217
- className: cn(isSelfContainedWriteTool ? "overflow-hidden rounded-lg" : "max-h-96 overflow-auto", hasSelfContainedToolOutput ? "rounded-lg" : "rounded-lg border bg-background/80 p-3"),
11603
+ className: cn(isSelfContainedWriteTool ? "overflow-hidden rounded-lg" : "max-h-96 overflow-auto", isSuccessfulEditTool ? "rounded-t-none rounded-b-xl" : hasSelfContainedToolOutput ? "rounded-lg" : "rounded-lg border bg-background/80 p-3"),
11218
11604
  children: block.name === "bash" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
11219
11605
  className: cn("overflow-x-auto font-mono text-xs leading-5 break-words whitespace-pre-wrap", block.isError && "text-destructive"),
11220
11606
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnsiText, { text: shellBodyText })
@@ -11268,6 +11654,7 @@ var ToolBlockCard = import_react.memo(function ToolBlockCard({ blockKey, store }
11268
11654
  function ToolBlockCardContent({ blockKey, header, store }) {
11269
11655
  const [isOpen, setIsOpen] = useRememberedToolCollapsibleOpen(header.openStateKey);
11270
11656
  const bodyBlock = useAssistantToolBlockBody(store, blockKey, isOpen);
11657
+ const isSuccessfulEditTool = header.name === "edit" && !header.running && !header.isError;
11271
11658
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Collapsible$1, {
11272
11659
  open: isOpen,
11273
11660
  onOpenChange: setIsOpen,
@@ -11293,7 +11680,7 @@ function ToolBlockCardContent({ blockKey, header, store }) {
11293
11680
  }), isOpen && bodyBlock ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CollapsibleContent, {
11294
11681
  className: "overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up",
11295
11682
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
11296
- className: "h-(--collapsible-panel-height) px-3 pt-0 pb-3 data-ending-style:h-0 data-starting-style:h-0",
11683
+ className: cn("h-(--collapsible-panel-height) data-ending-style:h-0 data-starting-style:h-0", !isSuccessfulEditTool && "px-3 pt-0 pb-3"),
11297
11684
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToolBlockCardBody, { block: bodyBlock })
11298
11685
  })
11299
11686
  }) : null]
@@ -12074,7 +12461,7 @@ function getClipboardImageFiles(data) {
12074
12461
  if (itemFiles.length > 0) return itemFiles;
12075
12462
  return Array.from(data.files).filter((file) => file.type.startsWith("image/"));
12076
12463
  }
12077
- function ComposerPanel({ activeSessionId, currentPendingMessages, composerImages, composerText, composerSkill, composerSyncNonce, centerMessages, contextUsageStore, displaySettingsStore, sessionStore, isSubmitting, isStreaming, awaitingFirstTurn, disabled = false, flush = false, topContent, viewerContextId, fileInputRef, onComposerTextChange, onPickImages, onRemoveComposerImage, onSubmitPrompt, onAbort, onEditPendingMessage, onRemovePendingMessage, onReorderPending, onRunBuiltinSlashCommand, onSelectModel, onSelectThinkingLevel, requestPathCompletions, requestFileCompletions, ref }) {
12464
+ function ComposerPanel({ activeSessionId, currentPendingMessages, composerDiffLineComments, composerImages, composerText, composerSkill, composerSyncNonce, centerMessages, contextUsageStore, displaySettingsStore, sessionStore, isSubmitting, isStreaming, awaitingFirstTurn, disabled = false, flush = false, topContent, viewerContextId, fileInputRef, onComposerTextChange, onPickImages, onRemoveComposerDiffLineComment, onRemoveComposerImage, onSubmitPrompt, onAbort, onEditPendingMessage, onRemovePendingMessage, onReorderPending, onRunBuiltinSlashCommand, onSelectModel, onSelectThinkingLevel, requestPathCompletions, requestFileCompletions, ref }) {
12078
12465
  const promptRef = import_react.useRef(null);
12079
12466
  const [pickerState, dispatchPickerState] = import_react.useReducer(composerPickerReducer, INITIAL_COMPOSER_PICKER_STATE);
12080
12467
  const { modelPickerOpen, thinkingPickerOpen, modelQuery } = pickerState;
@@ -12108,6 +12495,7 @@ function ComposerPanel({ activeSessionId, currentPendingMessages, composerImages
12108
12495
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
12109
12496
  className: "overflow-visible rounded-[18px] border bg-card",
12110
12497
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComposerPromptEditor, {
12498
+ composerDiffLineComments,
12111
12499
  composerImages,
12112
12500
  composerText,
12113
12501
  composerSkill,
@@ -12122,6 +12510,7 @@ function ComposerPanel({ activeSessionId, currentPendingMessages, composerImages
12122
12510
  sessionStore,
12123
12511
  onComposerTextChange,
12124
12512
  onPickImages,
12513
+ onRemoveComposerDiffLineComment,
12125
12514
  onRemoveComposerImage,
12126
12515
  onSubmitPrompt,
12127
12516
  onAbort,
@@ -12195,7 +12584,7 @@ var ComposerAttachments = import_react.memo(function ComposerAttachments({ image
12195
12584
  }, promptImageKey(image)))
12196
12585
  });
12197
12586
  });
12198
- var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ composerImages, composerText, composerSkill, composerSyncNonce, isSubmitting, isStreaming, awaitingFirstTurn, disabled = false, fileInputRef, promptRef, displaySettingsStore, sessionStore, onComposerTextChange, onPickImages, onRemoveComposerImage, onSubmitPrompt, onAbort, onRunBuiltinSlashCommand, requestPathCompletions, requestFileCompletions }) {
12587
+ var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ composerDiffLineComments, composerImages, composerText, composerSkill, composerSyncNonce, isSubmitting, isStreaming, awaitingFirstTurn, disabled = false, fileInputRef, promptRef, displaySettingsStore, sessionStore, onComposerTextChange, onPickImages, onRemoveComposerDiffLineComment, onRemoveComposerImage, onSubmitPrompt, onAbort, onRunBuiltinSlashCommand, requestPathCompletions, requestFileCompletions }) {
12199
12588
  const slashCommands = useComposerSlashCommands({
12200
12589
  displaySettingsStore,
12201
12590
  sessionStore
@@ -12282,8 +12671,9 @@ var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ com
12282
12671
  dismissMenus,
12283
12672
  promptRef
12284
12673
  ]);
12285
- const hasSubmittableContent = hasDraftText || composerImages.length > 0;
12286
- const acceptFollowUps = isStreaming || awaitingFirstTurn;
12674
+ const hasSubmittableContent = hasDraftText || composerDiffLineComments.length > 0 || composerImages.length > 0;
12675
+ const responseInFlight = isStreaming || awaitingFirstTurn || isSubmitting;
12676
+ const acceptFollowUps = responseInFlight;
12287
12677
  const setAssistPointerSelectionSuppressedValue = (next) => {
12288
12678
  assistPointerSelectionSuppressedRef.current = next;
12289
12679
  setAssistPointerSelectionSuppressed(next);
@@ -12358,6 +12748,11 @@ var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ com
12358
12748
  applyDraft("", void 0, { immediate: true });
12359
12749
  return;
12360
12750
  }
12751
+ if (event.key === "Backspace" && !draftText && !currentDraftSkill && composerDiffLineComments.length > 0) {
12752
+ const lastComment = composerDiffLineComments.at(-1);
12753
+ if (lastComment) onRemoveComposerDiffLineComment(lastComment.id);
12754
+ return;
12755
+ }
12361
12756
  if (visibleCompletion && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
12362
12757
  event.preventDefault();
12363
12758
  setAssistPointerSelectionSuppressedValue(true);
@@ -12416,7 +12811,7 @@ var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ com
12416
12811
  dismissMenus();
12417
12812
  return;
12418
12813
  }
12419
- if (isStreaming && !event.repeat) {
12814
+ if (responseInFlight && !event.repeat) {
12420
12815
  event.preventDefault();
12421
12816
  event.stopPropagation();
12422
12817
  onAbort();
@@ -12463,39 +12858,62 @@ var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ com
12463
12858
  className: "min-w-0",
12464
12859
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
12465
12860
  className: "flex min-w-0 flex-wrap items-start gap-2",
12466
- children: [draftSkill ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
12467
- className: "inline-flex h-7 max-w-[45%] shrink-0 items-center gap-0 overflow-hidden rounded-full bg-primary/10 pr-0.5 pl-2 text-sm font-medium text-primary",
12468
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
12469
- className: "truncate",
12470
- children: ["Skill: ", formatComposerSkillName(draftSkill)]
12471
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
12472
- size: "icon-sm",
12473
- variant: "ghost",
12474
- className: "ml-1 rounded-full text-primary hover:bg-primary/10 hover:text-primary",
12475
- "aria-label": `Remove skill ${formatComposerSkillName(draftSkill)}`,
12861
+ children: [
12862
+ draftSkill ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
12863
+ className: "inline-flex h-7 max-w-[45%] shrink-0 items-center gap-0 overflow-hidden rounded-full bg-primary/10 pr-0.5 pl-2 text-sm font-medium text-primary",
12864
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
12865
+ className: "truncate",
12866
+ children: ["Skill: ", formatComposerSkillName(draftSkill)]
12867
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
12868
+ size: "icon-sm",
12869
+ variant: "ghost",
12870
+ className: "ml-1 rounded-full text-primary hover:bg-primary/10 hover:text-primary",
12871
+ "aria-label": `Remove skill ${formatComposerSkillName(draftSkill)}`,
12872
+ disabled,
12873
+ onClick: () => applyDraft(draftTextRef.current, void 0, { immediate: true }),
12874
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "size-3.5" })
12875
+ })]
12876
+ }) : null,
12877
+ composerDiffLineComments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
12878
+ title: `${formatComposerDiffLineCommentReference(comment)}: ${comment.text}`,
12879
+ className: "inline-flex h-7 max-w-[45%] shrink-0 items-center gap-1 overflow-hidden rounded-full bg-amber-500/10 pr-0.5 pl-2 text-sm font-medium text-amber-700 dark:text-amber-300",
12880
+ children: [
12881
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitPullRequestArrow, { className: "size-3.5 shrink-0" }),
12882
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
12883
+ className: "truncate",
12884
+ children: ["Diff: ", formatComposerDiffLineCommentReference(comment)]
12885
+ }),
12886
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
12887
+ size: "icon-sm",
12888
+ variant: "ghost",
12889
+ className: "ml-1 rounded-full text-amber-700 hover:bg-amber-500/10 hover:text-amber-700 dark:text-amber-300 dark:hover:text-amber-300",
12890
+ "aria-label": `Remove diff comment for ${formatComposerDiffLineCommentReference(comment)}`,
12891
+ disabled,
12892
+ onClick: () => onRemoveComposerDiffLineComment(comment.id),
12893
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "size-3.5" })
12894
+ })
12895
+ ]
12896
+ }, comment.id)),
12897
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
12898
+ ref: promptRef,
12899
+ name: "prompt",
12900
+ rows: 1,
12901
+ autoComplete: "off",
12902
+ "data-1p-ignore": "true",
12903
+ "data-form-type": "other",
12904
+ "data-lpignore": "true",
12905
+ defaultValue: composerText,
12906
+ onChange: handleTextChange,
12907
+ onClick: syncSelection,
12908
+ onKeyUp: syncSelection,
12909
+ onSelect: syncSelection,
12910
+ onKeyDown: handleKeyDown,
12911
+ onPaste: handlePaste,
12476
12912
  disabled,
12477
- onClick: () => applyDraft(draftTextRef.current, void 0, { immediate: true }),
12478
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "size-3.5" })
12479
- })]
12480
- }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
12481
- ref: promptRef,
12482
- name: "prompt",
12483
- rows: 1,
12484
- autoComplete: "off",
12485
- "data-1p-ignore": "true",
12486
- "data-form-type": "other",
12487
- "data-lpignore": "true",
12488
- defaultValue: composerText,
12489
- onChange: handleTextChange,
12490
- onClick: syncSelection,
12491
- onKeyUp: syncSelection,
12492
- onSelect: syncSelection,
12493
- onKeyDown: handleKeyDown,
12494
- onPaste: handlePaste,
12495
- disabled,
12496
- placeholder: acceptFollowUps ? "Add a message to the queue..." : draftSkill ? `Ask with ${formatComposerSkillName(draftSkill)}…` : "Ask anything…",
12497
- className: "max-h-[min(40dvh,18rem)] min-h-[22px] max-w-full min-w-0 flex-1 basis-[min(240px,100%)] resize-none overflow-y-auto rounded-none border-0 bg-transparent p-0 text-base shadow-none ring-0 focus-visible:border-transparent focus-visible:ring-0 disabled:cursor-text disabled:bg-transparent disabled:opacity-60 md:text-sm dark:bg-transparent dark:disabled:bg-transparent"
12498
- })]
12913
+ placeholder: acceptFollowUps ? "Add a message to the queue..." : draftSkill ? `Ask with ${formatComposerSkillName(draftSkill)}…` : "Ask anything…",
12914
+ className: "max-h-[min(40dvh,18rem)] min-h-[22px] max-w-full min-w-0 flex-1 basis-[min(240px,100%)] resize-none overflow-y-auto rounded-none border-0 bg-transparent p-0 text-base shadow-none ring-0 focus-visible:border-transparent focus-visible:ring-0 disabled:cursor-text disabled:bg-transparent disabled:opacity-60 md:text-sm dark:bg-transparent dark:disabled:bg-transparent"
12915
+ })
12916
+ ]
12499
12917
  })
12500
12918
  })]
12501
12919
  }),
@@ -12550,7 +12968,7 @@ var ComposerPromptEditor = import_react.memo(function ComposerPromptEditor({ com
12550
12968
  onClick: () => runPrimaryComposerAction("steer"),
12551
12969
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ListStart, { "data-icon": "inline-start" }), "Steer"]
12552
12970
  }),
12553
- isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
12971
+ responseInFlight ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
12554
12972
  title: "Abort",
12555
12973
  kbd: "Esc",
12556
12974
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
@@ -12929,6 +13347,7 @@ var AppShellComposerController = import_react.memo(function AppShellComposerCont
12929
13347
  ref: composerPanelRef,
12930
13348
  activeSessionId: snapshot.activeSessionId,
12931
13349
  currentPendingMessages: snapshot.currentPendingMessages,
13350
+ composerDiffLineComments: snapshot.composerDiffLineComments,
12932
13351
  composerImages: snapshot.composerImages,
12933
13352
  composerText: snapshot.composerText,
12934
13353
  composerSkill: snapshot.composerSkill,
@@ -12947,6 +13366,7 @@ var AppShellComposerController = import_react.memo(function AppShellComposerCont
12947
13366
  fileInputRef,
12948
13367
  onComposerTextChange,
12949
13368
  onPickImages,
13369
+ onRemoveComposerDiffLineComment: actionsRef.current.removeDiffLineComment,
12950
13370
  onRemoveComposerImage,
12951
13371
  onSubmitPrompt,
12952
13372
  onAbort,
@@ -13892,21 +14312,29 @@ function ConversationScrollRevisionObserver({ conversationItemsStore, disabled,
13892
14312
  }, [conversationRevision, disabled]);
13893
14313
  return null;
13894
14314
  }
13895
- function ConversationPreviousMessageButton({ onClick, scrollStateStore }) {
14315
+ function ConversationPreviousMessageButton({ centerMessages, onClick, scrollStateStore }) {
13896
14316
  const isDisabled = useMessageScrollValue(scrollStateStore, (snapshot) => !snapshot.hasPreviousMessageJumpTarget);
13897
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
14317
+ const button = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
13898
14318
  title: "Jump to previous message",
13899
14319
  kbd: formatShortcutLabel("Control+ArrowLeft"),
13900
14320
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
13901
14321
  variant: "secondary",
13902
14322
  size: "icon-lg",
13903
14323
  disabled: isDisabled,
13904
- className: "absolute right-4 bottom-4 z-10 rounded-full border-0 shadow-[0_10px_24px_rgba(0,0,0,0.28)] disabled:pointer-events-none disabled:opacity-0 md:right-[18px] md:bottom-[18px]",
14324
+ className: cn("rounded-full border-0 shadow-[0_10px_24px_rgba(0,0,0,0.28)] disabled:pointer-events-none disabled:opacity-0", centerMessages ? "pointer-events-auto" : "absolute right-4 bottom-4 z-10 md:right-[18px] md:bottom-[18px]"),
13905
14325
  "aria-label": "Jump to previous message",
13906
14326
  onClick,
13907
14327
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowUpToLine, { className: "size-4" })
13908
14328
  })
13909
14329
  });
14330
+ if (!centerMessages) return button;
14331
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14332
+ className: "pointer-events-none absolute inset-x-4 bottom-4 z-10 md:bottom-[18px]",
14333
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14334
+ className: "mx-auto flex w-full max-w-[80ch] justify-end",
14335
+ children: button
14336
+ })
14337
+ });
13910
14338
  }
13911
14339
  var ConversationContentChangeContext = import_react.createContext(null);
13912
14340
  function useAppShellConversationSessionState(store) {
@@ -13918,7 +14346,7 @@ function useAppShellConversationSessionState(store) {
13918
14346
  streaming: sessionState.streaming
13919
14347
  }), { compare: shallowRecordEqual });
13920
14348
  }
13921
- function AppShellConversationFrame({ autoScrollEnabled, children, conversationItemsStore, isSessionViewLoading, ref, sessionState }) {
14349
+ function AppShellConversationFrame({ autoScrollEnabled, children, centerMessages, conversationItemsStore, isSessionViewLoading, ref, sessionState }) {
13922
14350
  const { bottomRef, jumpToNextMessage, jumpToPreviousMessage, messagesContentRef, messagesScrollAreaRef, scrollConversationToBottom, scrollConversationToTop, scrollStateStore, syncAfterConversationChange } = useAppShellMessageScroll({
13923
14351
  autoScrollEnabled,
13924
14352
  isSessionViewLoading,
@@ -13966,6 +14394,7 @@ function AppShellConversationFrame({ autoScrollEnabled, children, conversationIt
13966
14394
  onClick: scrollConversationToBottom,
13967
14395
  scrollStateStore
13968
14396
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationPreviousMessageButton, {
14397
+ centerMessages,
13969
14398
  onClick: jumpToPreviousMessage,
13970
14399
  scrollStateStore
13971
14400
  })] }) : null]
@@ -13973,9 +14402,11 @@ function AppShellConversationFrame({ autoScrollEnabled, children, conversationIt
13973
14402
  }
13974
14403
  function AppShellWorkingIndicatorLabel({ fallbackLabel, hiddenThinkingPreviewStore, useHiddenThinkingPreview }) {
13975
14404
  const hiddenThinkingPreview = useSelector(hiddenThinkingPreviewStore);
14405
+ const visibleLabel = useHiddenThinkingPreview && hiddenThinkingPreview ? hiddenThinkingPreview : fallbackLabel;
14406
+ if (!visibleLabel.trim()) return null;
13976
14407
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
13977
14408
  className: "font-medium text-foreground",
13978
- children: useHiddenThinkingPreview && hiddenThinkingPreview ? hiddenThinkingPreview : fallbackLabel
14409
+ children: visibleLabel
13979
14410
  });
13980
14411
  }
13981
14412
  function AppShellMessagesWorkingIndicator({ hiddenThinkingPreviewStore, onCancel, state, useHiddenThinkingPreview }) {
@@ -14113,7 +14544,7 @@ function AppShellConversationEmptyState({ awaitingFirstTurn, conversationItemsSt
14113
14544
  const conversationLoadingLabel = isSessionViewLoading ? "Loading session…" : displayedWorkingState && !displayedWorkingState.done ? displayedWorkingState.label : "Loading…";
14114
14545
  if (showConversationLoadingState) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
14115
14546
  className: "flex flex-1 flex-col items-center justify-center gap-3 py-10 text-sm text-muted-foreground",
14116
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: conversationLoadingLabel })]
14547
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}), conversationLoadingLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: conversationLoadingLabel }) : null]
14117
14548
  });
14118
14549
  if (hasMessages) return null;
14119
14550
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Empty, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(EmptyHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyTitle, { children: draft ? "New session" : "Start a new conversation" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyDescription, { children: draft ? void 0 : "This is the native Pico session view backed by the new TypeScript runtime." })] }), draft ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(EmptyContent, {
@@ -14179,6 +14610,7 @@ var AppShellSessionConversation = import_react.memo(function AppShellSessionConv
14179
14610
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AppShellConversationFrame, {
14180
14611
  ref: conversationFrameRef,
14181
14612
  autoScrollEnabled,
14613
+ centerMessages,
14182
14614
  conversationItemsStore,
14183
14615
  isSessionViewLoading,
14184
14616
  sessionState,
@@ -16327,7 +16759,7 @@ var GIT_GRAPH_LANE_COLORS = [
16327
16759
  ];
16328
16760
  var GIT_GRAPH_NULL_VERTEX_ID = -1;
16329
16761
  var GIT_GRAPH_ROW_HEIGHT = 32;
16330
- var GIT_GRAPH_COMMIT_DETAILS_HEIGHT = 300;
16762
+ var GIT_GRAPH_COMMIT_DETAILS_HEIGHT = 200;
16331
16763
  var GIT_GRAPH_LANE_WIDTH = 14;
16332
16764
  var GIT_GRAPH_OFFSET_X = 12;
16333
16765
  var initialGitCommitRowState = {
@@ -17361,7 +17793,7 @@ function useGitCommitRowView({ viewerContextId, cwd, graphWidth, line, value, on
17361
17793
  }, contextMenuKey)
17362
17794
  }),
17363
17795
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionContent, {
17364
- className: "relative h-[300px] border-b border-border/70 bg-muted/50 p-0",
17796
+ className: "relative h-[200px] border-b border-border/70 bg-muted/50 p-0",
17365
17797
  children: [
17366
17798
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
17367
17799
  ref: commitBodyScrollRef,
@@ -17767,6 +18199,124 @@ function GitFileDiff({ file }) {
17767
18199
  }) : null]
17768
18200
  });
17769
18201
  }
18202
+ function reviewDiffCommentSideLabel(side) {
18203
+ return side === "deletions" ? "old" : "new";
18204
+ }
18205
+ function reviewDiffCommentLineFromRange(range) {
18206
+ return Math.min(range.start, range.end);
18207
+ }
18208
+ function reviewDiffCommentSideFromRange(range) {
18209
+ return range.side || range.endSide || "additions";
18210
+ }
18211
+ function reviewDiffCommentPath(file, side) {
18212
+ return side === "deletions" ? file.previousPath || file.path : file.path;
18213
+ }
18214
+ function reviewDiffCommentTabKey(file) {
18215
+ return `changes:${reviewFileValue(file)}`;
18216
+ }
18217
+ function reviewDiffLineCommentAnnotations({ comments, pendingComment }) {
18218
+ const annotations = comments.map((comment) => ({
18219
+ lineNumber: comment.lineNumber,
18220
+ metadata: comment,
18221
+ side: comment.side
18222
+ }));
18223
+ if (pendingComment) annotations.push({
18224
+ lineNumber: pendingComment.lineNumber,
18225
+ metadata: pendingComment,
18226
+ side: pendingComment.side
18227
+ });
18228
+ return annotations;
18229
+ }
18230
+ function ReviewDiffPendingLineComment({ comment }) {
18231
+ const [text, setText] = import_react.useState("");
18232
+ const textareaRef = import_react.useRef(null);
18233
+ import_react.useEffect(() => {
18234
+ textareaRef.current?.focus();
18235
+ }, []);
18236
+ const submit = () => {
18237
+ const normalizedText = text.trim();
18238
+ if (!normalizedText) return;
18239
+ comment.onSubmit(normalizedText);
18240
+ };
18241
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", {
18242
+ className: "m-2 w-[min(38rem,calc(100vw_-_5rem))] max-w-[calc(100%_-_1rem)] rounded-lg border border-border/70 bg-background p-2 shadow-sm",
18243
+ onPointerDown: (event) => event.stopPropagation(),
18244
+ onSubmit: (event) => {
18245
+ event.preventDefault();
18246
+ submit();
18247
+ },
18248
+ children: [
18249
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18250
+ className: "mb-2 flex min-w-0 items-center justify-between gap-2 text-xs text-muted-foreground",
18251
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
18252
+ className: "min-w-0 truncate",
18253
+ children: [
18254
+ "Comment on ",
18255
+ comment.path,
18256
+ ":L",
18257
+ comment.lineNumber,
18258
+ " (",
18259
+ reviewDiffCommentSideLabel(comment.side),
18260
+ ")"
18261
+ ]
18262
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
18263
+ type: "button",
18264
+ variant: "ghost",
18265
+ size: "icon-sm",
18266
+ "aria-label": "Cancel line comment",
18267
+ onClick: comment.onCancel,
18268
+ children: "×"
18269
+ })]
18270
+ }),
18271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Textarea, {
18272
+ ref: textareaRef,
18273
+ value: text,
18274
+ rows: 2,
18275
+ placeholder: "Add a note for the prompt…",
18276
+ className: "min-h-16 resize-none text-sm",
18277
+ onChange: (event) => setText(event.target.value),
18278
+ onKeyDown: (event) => {
18279
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
18280
+ event.preventDefault();
18281
+ submit();
18282
+ }
18283
+ if (event.key === "Escape") {
18284
+ event.preventDefault();
18285
+ comment.onCancel();
18286
+ }
18287
+ }
18288
+ }),
18289
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18290
+ className: "mt-2 flex flex-wrap justify-end gap-2",
18291
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
18292
+ type: "button",
18293
+ variant: "ghost",
18294
+ onClick: comment.onCancel,
18295
+ children: "Cancel"
18296
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
18297
+ type: "submit",
18298
+ disabled: !text.trim(),
18299
+ children: "Add to prompt"
18300
+ })]
18301
+ })
18302
+ ]
18303
+ });
18304
+ }
18305
+ function ReviewDiffLineAnnotation({ annotation }) {
18306
+ const comment = annotation.metadata;
18307
+ if (!comment) return null;
18308
+ if (comment.kind === "pending") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReviewDiffPendingLineComment, { comment });
18309
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18310
+ className: "my-1 ml-2 inline-flex max-w-[min(32rem,calc(100vw_-_5rem),90%)] items-center gap-1 rounded-full border border-amber-500/20 bg-amber-500/10 px-2 py-1 text-[11px] text-amber-800 dark:text-amber-200",
18311
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18312
+ className: "shrink-0",
18313
+ children: "💬"
18314
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18315
+ className: "truncate",
18316
+ children: comment.text
18317
+ })]
18318
+ });
18319
+ }
17770
18320
  var GIT_HISTORY_PANEL_MIN_HEIGHT = 160;
17771
18321
  var GIT_HISTORY_PANEL_MAX_STORED_HEIGHT = 1600;
17772
18322
  function clampStoredGitHistoryPanelHeight(height) {
@@ -17866,7 +18416,7 @@ function gitFileShouldPreviewPatch(file) {
17866
18416
  function FileReviewContent(props) {
17867
18417
  return useFileReviewContentView(props);
17868
18418
  }
17869
- function useFileReviewContentView({ viewerContextId, cwd, active, onMaximizeHistory, onOpenCommitDiff, onOpenFile, showEmbeddedHistory = true }) {
18419
+ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineComments = [], onAddDiffLineComment, onMaximizeHistory, onOpenCommitDiff, onOpenFile, showEmbeddedHistory = true }) {
17870
18420
  const normalizedCwd = normalizeCwd(cwd);
17871
18421
  const isMobile = useIsMobile();
17872
18422
  const [state, dispatch] = import_react.useReducer(fileReviewReducer, initialFileReviewState, (initialState) => ({
@@ -18102,10 +18652,12 @@ function useFileReviewContentView({ viewerContextId, cwd, active, onMaximizeHist
18102
18652
  viewerContextId,
18103
18653
  cwd: normalizedCwd,
18104
18654
  active,
18655
+ diffLineComments,
18105
18656
  diffStyle,
18106
18657
  file,
18107
18658
  open: openFiles.includes(reviewFileValue(file)),
18108
18659
  stuck: stickyReviewFileValue === reviewFileValue(file),
18660
+ onAddDiffLineComment,
18109
18661
  onOpenFile
18110
18662
  }, reviewFileValue(file)))
18111
18663
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
@@ -18174,10 +18726,12 @@ function useFileReviewContentView({ viewerContextId, cwd, active, onMaximizeHist
18174
18726
  ]
18175
18727
  });
18176
18728
  }
18177
- function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffStyle, file, open, stuck, onOpenFile }) {
18729
+ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffLineComments = [], diffStyle, file, open, stuck, onAddDiffLineComment, onOpenFile }) {
18178
18730
  const normalizedCwd = normalizeCwd(cwd);
18179
18731
  const queryClient = useQueryClient();
18180
18732
  const value = reviewFileValue(file);
18733
+ const commentTabKey = reviewDiffCommentTabKey(file);
18734
+ const [commentTarget, setCommentTarget] = import_react.useState(null);
18181
18735
  const [fullContextRequested, setFullContextRequested] = import_react.useState(false);
18182
18736
  const previewPatch = gitFileShouldPreviewPatch(file) && !fullContextRequested;
18183
18737
  const diffQuery = useQuery({
@@ -18210,6 +18764,41 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffStyle, file
18210
18764
  const oldContent = reviewQuery.data?.oldContent ?? "";
18211
18765
  const newContent = reviewQuery.data?.newContent ?? "";
18212
18766
  const patch = diffQuery.data?.patch ?? "";
18767
+ const commentsForFile = diffLineComments.filter((comment) => comment.cwd === normalizedCwd && comment.tabKey === commentTabKey);
18768
+ const startLineComment = (range) => {
18769
+ if (!onAddDiffLineComment) return;
18770
+ const side = reviewDiffCommentSideFromRange(range);
18771
+ setCommentTarget({
18772
+ commit: "working-tree",
18773
+ lineNumber: reviewDiffCommentLineFromRange(range),
18774
+ mode: "head",
18775
+ path: reviewDiffCommentPath(file, side),
18776
+ shortHash: "working tree",
18777
+ side,
18778
+ tabKey: commentTabKey,
18779
+ tabTitle: "Working tree changes"
18780
+ });
18781
+ };
18782
+ const lineAnnotations = reviewDiffLineCommentAnnotations({
18783
+ comments: commentsForFile,
18784
+ pendingComment: commentTarget ? {
18785
+ ...commentTarget,
18786
+ kind: "pending",
18787
+ onCancel: () => setCommentTarget(null),
18788
+ onSubmit: (text) => {
18789
+ onAddDiffLineComment?.({
18790
+ ...commentTarget,
18791
+ text
18792
+ });
18793
+ setCommentTarget(null);
18794
+ }
18795
+ } : null
18796
+ });
18797
+ const commentableOptions = onAddDiffLineComment ? {
18798
+ enableGutterUtility: true,
18799
+ onGutterUtilityClick: startLineComment
18800
+ } : {};
18801
+ const themeOptions = usePicoDiffThemeOptions();
18213
18802
  const [indexStatus, worktreeStatus] = gitFileStatusCharacters(file.status);
18214
18803
  const canStage = worktreeStatus !== " " || indexStatus === "?";
18215
18804
  const invalidateChangedFileQueries = async () => {
@@ -18267,6 +18856,15 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffStyle, file
18267
18856
  }
18268
18857
  });
18269
18858
  const actionPending = stageMutation.isPending || discardMutation.isPending;
18859
+ import_react.useEffect(() => {
18860
+ setCommentTarget(null);
18861
+ }, [
18862
+ value,
18863
+ previewPatch,
18864
+ patch,
18865
+ oldContent,
18866
+ newContent
18867
+ ]);
18270
18868
  const openFile = (event) => {
18271
18869
  event?.stopPropagation();
18272
18870
  onOpenFile?.(file.path, { pin: true });
@@ -18375,9 +18973,13 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffStyle, file
18375
18973
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PatchDiff, {
18376
18974
  patch,
18377
18975
  disableWorkerPool: true,
18976
+ lineAnnotations,
18977
+ renderAnnotation: (annotation) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReviewDiffLineAnnotation, { annotation }),
18378
18978
  options: {
18979
+ ...themeOptions,
18379
18980
  diffStyle,
18380
18981
  disableFileHeader: true,
18982
+ ...commentableOptions,
18381
18983
  overflow: "wrap"
18382
18984
  }
18383
18985
  })] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -18402,9 +19004,13 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffStyle, file
18402
19004
  contents: newContent
18403
19005
  },
18404
19006
  disableWorkerPool: true,
19007
+ lineAnnotations,
19008
+ renderAnnotation: (annotation) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReviewDiffLineAnnotation, { annotation }),
18405
19009
  options: {
19010
+ ...themeOptions,
18406
19011
  diffStyle,
18407
19012
  disableFileHeader: true,
19013
+ ...commentableOptions,
18408
19014
  overflow: "wrap"
18409
19015
  }
18410
19016
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -18414,6 +19020,7 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffStyle, file
18414
19020
  })]
18415
19021
  });
18416
19022
  }
19023
+ var EMPTY_RIGHT_SIDEBAR_DIFF_LINE_COMMENTS = [];
18417
19024
  var EMPTY_RIGHT_SIDEBAR_FILE_TABS = [];
18418
19025
  function readStoredHistoryAsTab() {
18419
19026
  return safeLocalStorageGetItem(RIGHT_SIDEBAR_HISTORY_TAB_STORAGE_KEY) === "1";
@@ -18535,7 +19142,160 @@ function commitFileDiffLineCounts(fileDiff) {
18535
19142
  deletions: 0
18536
19143
  });
18537
19144
  }
18538
- function CommitFileDiffAccordionItem({ diffStyle, fileDiff, index, stuck }) {
19145
+ function commitDiffCommentSideLabel(side) {
19146
+ return side === "deletions" ? "old" : "new";
19147
+ }
19148
+ function commitDiffLineCommentAnnotations({ comments, pendingComment }) {
19149
+ const annotations = comments.map((comment) => ({
19150
+ lineNumber: comment.lineNumber,
19151
+ metadata: comment,
19152
+ side: comment.side
19153
+ }));
19154
+ if (pendingComment) annotations.push({
19155
+ lineNumber: pendingComment.lineNumber,
19156
+ metadata: pendingComment,
19157
+ side: pendingComment.side
19158
+ });
19159
+ return annotations;
19160
+ }
19161
+ function assignClassName(element, className) {
19162
+ for (const classItem of className.split(/\s+/)) if (classItem) element.classList.add(classItem);
19163
+ }
19164
+ function renderPendingCommitDiffLineComment(pendingComment) {
19165
+ const form = document.createElement("form");
19166
+ assignClassName(form, "m-2 w-[min(38rem,calc(100vw_-_5rem))] max-w-[calc(100%_-_1rem)] rounded-lg border border-border/70 bg-background p-2 shadow-sm");
19167
+ const header = document.createElement("div");
19168
+ assignClassName(header, "mb-2 flex min-w-0 items-center justify-between gap-2 text-xs text-muted-foreground");
19169
+ const label = document.createElement("span");
19170
+ label.textContent = `Comment on ${pendingComment.path}:L${pendingComment.lineNumber} (${commitDiffCommentSideLabel(pendingComment.side)})`;
19171
+ assignClassName(label, "min-w-0 truncate");
19172
+ const cancelButton = document.createElement("button");
19173
+ cancelButton.type = "button";
19174
+ cancelButton.textContent = "×";
19175
+ cancelButton.setAttribute("aria-label", "Cancel line comment");
19176
+ assignClassName(cancelButton, "inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground");
19177
+ cancelButton.addEventListener("click", () => pendingComment.onCancel());
19178
+ header.append(label, cancelButton);
19179
+ const textarea = document.createElement("textarea");
19180
+ textarea.rows = 2;
19181
+ textarea.placeholder = "Add a note for the prompt…";
19182
+ assignClassName(textarea, "flex min-h-16 w-full resize-none rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50");
19183
+ textarea.addEventListener("keydown", (event) => {
19184
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
19185
+ event.preventDefault();
19186
+ submit();
19187
+ }
19188
+ if (event.key === "Escape") {
19189
+ event.preventDefault();
19190
+ pendingComment.onCancel();
19191
+ }
19192
+ });
19193
+ const footer = document.createElement("div");
19194
+ assignClassName(footer, "mt-2 flex flex-wrap justify-end gap-2");
19195
+ const secondaryCancelButton = document.createElement("button");
19196
+ secondaryCancelButton.type = "button";
19197
+ secondaryCancelButton.textContent = "Cancel";
19198
+ assignClassName(secondaryCancelButton, "inline-flex h-8 items-center rounded-md px-3 text-sm hover:bg-muted");
19199
+ secondaryCancelButton.addEventListener("click", () => pendingComment.onCancel());
19200
+ const submitButton = document.createElement("button");
19201
+ submitButton.type = "submit";
19202
+ submitButton.textContent = "Add to prompt";
19203
+ assignClassName(submitButton, "inline-flex h-8 items-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground disabled:pointer-events-none disabled:opacity-50");
19204
+ function syncSubmitDisabled() {
19205
+ submitButton.disabled = textarea.value.trim().length === 0;
19206
+ }
19207
+ function submit() {
19208
+ const text = textarea.value.trim();
19209
+ if (!text) return;
19210
+ pendingComment.onSubmit(text);
19211
+ }
19212
+ textarea.addEventListener("input", syncSubmitDisabled);
19213
+ form.addEventListener("submit", (event) => {
19214
+ event.preventDefault();
19215
+ submit();
19216
+ });
19217
+ footer.append(secondaryCancelButton, submitButton);
19218
+ form.append(header, textarea, footer);
19219
+ syncSubmitDisabled();
19220
+ window.setTimeout(() => textarea.focus(), 0);
19221
+ return form;
19222
+ }
19223
+ function renderCommitDiffLineAnnotation(annotation) {
19224
+ const comment = annotation.metadata;
19225
+ if (!comment) return void 0;
19226
+ if (comment.kind === "pending") return renderPendingCommitDiffLineComment(comment);
19227
+ const savedComment = comment;
19228
+ const wrapper = document.createElement("div");
19229
+ assignClassName(wrapper, "my-1 ml-2 inline-flex max-w-[min(32rem,calc(100vw_-_5rem),90%)] items-center gap-1 rounded-full border border-amber-500/20 bg-amber-500/10 px-2 py-1 text-[11px] text-amber-800 dark:text-amber-200");
19230
+ const icon = document.createElement("span");
19231
+ icon.textContent = "💬";
19232
+ assignClassName(icon, "shrink-0");
19233
+ const text = document.createElement("span");
19234
+ text.textContent = savedComment.text;
19235
+ assignClassName(text, "truncate");
19236
+ wrapper.append(icon, text);
19237
+ return wrapper;
19238
+ }
19239
+ function CommitDiffFileRenderer({ comments, diffStyle, disableFileHeader = false, fileDiff, onStartComment, pendingComment }) {
19240
+ const [container, setContainer] = import_react.useState(null);
19241
+ const instanceRef = import_react.useRef(null);
19242
+ const lineAnnotations = commitDiffLineCommentAnnotations({
19243
+ comments,
19244
+ pendingComment
19245
+ });
19246
+ const options = {
19247
+ ...usePicoDiffThemeOptions(),
19248
+ diffStyle,
19249
+ disableFileHeader,
19250
+ enableGutterUtility: true,
19251
+ lineDiffType: "word-alt",
19252
+ lineHoverHighlight: "both",
19253
+ maxLineDiffLength: 1e3,
19254
+ onGutterUtilityClick: (range) => {
19255
+ onStartComment({
19256
+ lineNumber: Math.min(range.start, range.end),
19257
+ path: fileDiff.name,
19258
+ side: range.side || range.endSide || "additions"
19259
+ });
19260
+ },
19261
+ onLineNumberClick: (props) => {
19262
+ onStartComment({
19263
+ lineNumber: props.lineNumber,
19264
+ path: fileDiff.name,
19265
+ side: props.annotationSide
19266
+ });
19267
+ },
19268
+ overflow: "wrap",
19269
+ renderAnnotation: renderCommitDiffLineAnnotation
19270
+ };
19271
+ import_react.useLayoutEffect(() => {
19272
+ if (!container) return;
19273
+ const existing = instanceRef.current;
19274
+ if (existing) {
19275
+ existing.setOptions(options);
19276
+ existing.render({
19277
+ containerWrapper: container,
19278
+ fileDiff,
19279
+ forceRender: true,
19280
+ lineAnnotations
19281
+ });
19282
+ return;
19283
+ }
19284
+ const instance = new FileDiff$1(options, void 0, false);
19285
+ instanceRef.current = instance;
19286
+ instance.render({
19287
+ containerWrapper: container,
19288
+ fileDiff,
19289
+ lineAnnotations
19290
+ });
19291
+ });
19292
+ import_react.useLayoutEffect(() => () => {
19293
+ instanceRef.current?.cleanUp();
19294
+ instanceRef.current = null;
19295
+ }, []);
19296
+ return import_react.createElement("div", { ref: setContainer });
19297
+ }
19298
+ function CommitFileDiffAccordionItem({ comments, diffStyle, fileDiff, index, onStartComment, pendingComment, stuck }) {
18539
19299
  const value = commitFileDiffValue(fileDiff, index);
18540
19300
  const counts = commitFileDiffLineCounts(fileDiff);
18541
19301
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionItem, {
@@ -18586,21 +19346,18 @@ function CommitFileDiffAccordionItem({ diffStyle, fileDiff, index, stuck }) {
18586
19346
  })
18587
19347
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccordionContent, {
18588
19348
  className: "bg-background p-0",
18589
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileDiff$1, {
19349
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitDiffFileRenderer, {
19350
+ comments,
19351
+ diffStyle,
19352
+ disableFileHeader: true,
18590
19353
  fileDiff,
18591
- disableWorkerPool: true,
18592
- options: {
18593
- diffStyle,
18594
- disableFileHeader: true,
18595
- lineDiffType: "word-alt",
18596
- maxLineDiffLength: 1e3,
18597
- overflow: "wrap"
18598
- }
19354
+ onStartComment,
19355
+ pendingComment
18599
19356
  })
18600
19357
  })]
18601
19358
  });
18602
19359
  }
18603
- function CommitSingleFileDiff({ diffStyle, patch }) {
19360
+ function CommitSingleFileDiff({ comments, diffStyle, onStartComment, patch, pendingComment }) {
18604
19361
  const fileDiff = parsePatchFiles(patch).flatMap((parsed) => parsed.files)[0];
18605
19362
  if (!fileDiff) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18606
19363
  className: "p-4",
@@ -18609,19 +19366,16 @@ function CommitSingleFileDiff({ diffStyle, patch }) {
18609
19366
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18610
19367
  className: "min-h-full text-xs",
18611
19368
  style: { "--diffs-font-family": "var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace)" },
18612
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileDiff$1, {
19369
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitDiffFileRenderer, {
19370
+ comments,
19371
+ diffStyle,
18613
19372
  fileDiff,
18614
- disableWorkerPool: true,
18615
- options: {
18616
- diffStyle,
18617
- lineDiffType: "word-alt",
18618
- maxLineDiffLength: 1e3,
18619
- overflow: "wrap"
18620
- }
19373
+ onStartComment,
19374
+ pendingComment
18621
19375
  })
18622
19376
  });
18623
19377
  }
18624
- function CommitPatchDiffs({ diffStyle, patch, stuckFileValue }) {
19378
+ function CommitPatchDiffs({ comments, diffStyle, onStartComment, patch, pendingComment, stuckFileValue }) {
18625
19379
  const fileDiffs = parsePatchFiles(patch).flatMap((parsed) => parsed.files);
18626
19380
  const firstValue = fileDiffs[0] ? commitFileDiffValue(fileDiffs[0], 0) : void 0;
18627
19381
  if (fileDiffs.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -18636,9 +19390,12 @@ function CommitPatchDiffs({ diffStyle, patch, stuckFileValue }) {
18636
19390
  defaultValue: firstValue ? [firstValue] : [],
18637
19391
  className: "border-b border-border/80",
18638
19392
  children: fileDiffs.map((fileDiff, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitFileDiffAccordionItem, {
19393
+ comments: comments.filter((comment) => comment.path === fileDiff.name),
18639
19394
  diffStyle,
18640
19395
  fileDiff,
18641
19396
  index,
19397
+ onStartComment,
19398
+ pendingComment: pendingComment?.path === fileDiff.name ? pendingComment : null,
18642
19399
  stuck: stuckFileValue === commitFileDiffValue(fileDiff, index)
18643
19400
  }, commitFileDiffValue(fileDiff, index)))
18644
19401
  })
@@ -18684,9 +19441,10 @@ function CommitDiffHeaderTitle({ tab }) {
18684
19441
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitDiffPathBreadcrumb, { path: tab.path })
18685
19442
  });
18686
19443
  }
18687
- function CommitDiffContent({ viewerContextId, cwd, active, tab }) {
19444
+ function CommitDiffContent({ viewerContextId, cwd, active, diffLineComments, onAddDiffLineComment, tab }) {
18688
19445
  const [diffStyle, setDiffStyle] = import_react.useState("unified");
18689
19446
  const [stickyCommitFileValue, setStickyCommitFileValue] = import_react.useState("");
19447
+ const [commentTarget, setCommentTarget] = import_react.useState(null);
18690
19448
  const diffScrollRef = import_react.useRef(null);
18691
19449
  const updateStickyCommitFileHeader = (scrollElement) => {
18692
19450
  setDerivedScrollState(setStickyCommitFileValue, getStuckScrollTriggerValue({
@@ -18712,7 +19470,34 @@ function CommitDiffContent({ viewerContextId, cwd, active, tab }) {
18712
19470
  "error"
18713
19471
  ]
18714
19472
  });
19473
+ const commentsForTab = tab ? diffLineComments.filter((comment) => comment.tabKey === tab.key && comment.commit === tab.commit && comment.mode === tab.mode) : EMPTY_RIGHT_SIDEBAR_DIFF_LINE_COMMENTS;
19474
+ const startLineComment = (lineTarget) => {
19475
+ if (!tab || !onAddDiffLineComment) return;
19476
+ setCommentTarget({
19477
+ commit: tab.commit,
19478
+ lineNumber: lineTarget.lineNumber,
19479
+ mode: tab.mode,
19480
+ path: lineTarget.path,
19481
+ shortHash: tab.shortHash,
19482
+ side: lineTarget.side,
19483
+ tabKey: tab.key,
19484
+ tabTitle: tab.title
19485
+ });
19486
+ };
19487
+ const pendingComment = commentTarget ? {
19488
+ ...commentTarget,
19489
+ kind: "pending",
19490
+ onCancel: () => setCommentTarget(null),
19491
+ onSubmit: (text) => {
19492
+ onAddDiffLineComment?.({
19493
+ ...commentTarget,
19494
+ text
19495
+ });
19496
+ setCommentTarget(null);
19497
+ }
19498
+ } : null;
18715
19499
  import_react.useEffect(() => {
19500
+ setCommentTarget(null);
18716
19501
  setDerivedScrollState(setStickyCommitFileValue, "");
18717
19502
  updateStickyCommitFileHeader(diffScrollRef.current);
18718
19503
  }, [tab?.key, diffQuery.data?.patch]);
@@ -18755,11 +19540,20 @@ function CommitDiffContent({ viewerContextId, cwd, active, tab }) {
18755
19540
  children: getErrorMessage(diffQuery.error, "Failed to load commit diff")
18756
19541
  })
18757
19542
  }) : diffQuery.data?.patch && tab.path ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitSingleFileDiff, {
19543
+ comments: commentsForTab.filter((comment) => {
19544
+ if (!tab.path) return true;
19545
+ return comment.path === tab.path || comment.path === tab.previousPath;
19546
+ }),
18758
19547
  diffStyle,
18759
- patch: diffQuery.data.patch
19548
+ onStartComment: startLineComment,
19549
+ patch: diffQuery.data.patch,
19550
+ pendingComment
18760
19551
  }) : diffQuery.data?.patch ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitPatchDiffs, {
19552
+ comments: commentsForTab,
18761
19553
  diffStyle,
19554
+ onStartComment: startLineComment,
18762
19555
  patch: diffQuery.data.patch,
19556
+ pendingComment,
18763
19557
  stuckFileValue: stickyCommitFileValue
18764
19558
  }, `${diffQuery.data.commit}:${diffQuery.data.mode}`) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18765
19559
  className: "p-4",
@@ -18768,7 +19562,7 @@ function CommitDiffContent({ viewerContextId, cwd, active, tab }) {
18768
19562
  })]
18769
19563
  });
18770
19564
  }
18771
- function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activeTab: controlledActiveTab, filePreviewPath = "", fileTabs = EMPTY_RIGHT_SIDEBAR_FILE_TABS, fileTreeCollapsed = false, onActiveFileChange, onActiveTabChange, onCloseAllFiles, onCloseFile, onCloseFilesToRight, onCloseOtherFiles, onFileTreeCollapsedChange, onOpenFile, onReorderFiles, showToolbar = true }) {
19565
+ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activeTab: controlledActiveTab, diffLineComments = EMPTY_RIGHT_SIDEBAR_DIFF_LINE_COMMENTS, filePreviewPath = "", fileTabs = EMPTY_RIGHT_SIDEBAR_FILE_TABS, fileTreeCollapsed = false, onActiveFileChange, onActiveTabChange, onAddDiffLineComment, onCloseAllFiles, onCloseFile, onCloseFilesToRight, onCloseOtherFiles, onFileTreeCollapsedChange, onOpenFile, onReorderFiles, showToolbar = true }) {
18772
19566
  const normalizedCwd = normalizeCwd(cwd);
18773
19567
  const isMobile = useIsMobile();
18774
19568
  const [localState, dispatchLocal] = import_react.useReducer(rightSidebarLocalReducer, initialRightSidebarLocalState);
@@ -18912,96 +19706,88 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
18912
19706
  showHistory: historyAsTab,
18913
19707
  showReview: true
18914
19708
  }),
18915
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Activity, {
18916
- mode: activeTab === "review" ? "visible" : "hidden",
18917
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18918
- className: "min-h-0 flex-1 overflow-hidden",
18919
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileReviewContent, {
18920
- viewerContextId,
18921
- cwd: normalizedCwd,
18922
- active: active && activeTab === "review",
18923
- onMaximizeHistory: () => {
18924
- setHistoryAsTab(true);
18925
- setActiveTab("history");
18926
- },
18927
- onOpenCommitDiff: openCommitDiff,
18928
- onOpenFile: openFile,
18929
- showEmbeddedHistory: !historyAsTab
18930
- })
19709
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19710
+ className: activeTab === "review" ? "min-h-0 flex-1 overflow-hidden" : "hidden",
19711
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileReviewContent, {
19712
+ viewerContextId,
19713
+ cwd: normalizedCwd,
19714
+ active: active && activeTab === "review",
19715
+ diffLineComments,
19716
+ onMaximizeHistory: () => {
19717
+ setHistoryAsTab(true);
19718
+ setActiveTab("history");
19719
+ },
19720
+ onAddDiffLineComment,
19721
+ onOpenCommitDiff: openCommitDiff,
19722
+ onOpenFile: openFile,
19723
+ showEmbeddedHistory: !historyAsTab
18931
19724
  })
18932
19725
  }),
18933
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Activity, {
18934
- mode: activeTab === "history" ? "visible" : "hidden",
18935
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18936
- className: "min-h-0 flex-1 overflow-hidden",
18937
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitCommitsSection, {
18938
- viewerContextId,
18939
- cwd: normalizedCwd,
18940
- active: active && activeTab === "history",
18941
- flush: true,
18942
- onOpenCommitDiff: openCommitDiff,
18943
- onRestoreEmbedded: () => {
18944
- setHistoryAsTab(false);
18945
- setActiveTab("review");
18946
- }
18947
- })
19726
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19727
+ className: activeTab === "history" ? "min-h-0 flex-1 overflow-hidden" : "hidden",
19728
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitCommitsSection, {
19729
+ viewerContextId,
19730
+ cwd: normalizedCwd,
19731
+ active: active && activeTab === "history",
19732
+ flush: true,
19733
+ onOpenCommitDiff: openCommitDiff,
19734
+ onRestoreEmbedded: () => {
19735
+ setHistoryAsTab(false);
19736
+ setActiveTab("review");
19737
+ }
18948
19738
  })
18949
19739
  }),
18950
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Activity, {
18951
- mode: activeTab === "commit-diff" ? "visible" : "hidden",
18952
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18953
- className: "min-h-0 flex-1 overflow-hidden",
18954
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitDiffContent, {
18955
- viewerContextId,
18956
- cwd: normalizedCwd,
18957
- active: active && activeTab === "commit-diff",
18958
- tab: activeCommitDiffTab
18959
- })
19740
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19741
+ className: activeTab === "commit-diff" ? "min-h-0 flex-1 overflow-hidden" : "hidden",
19742
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitDiffContent, {
19743
+ viewerContextId,
19744
+ cwd: normalizedCwd,
19745
+ active: active && activeTab === "commit-diff",
19746
+ diffLineComments,
19747
+ onAddDiffLineComment,
19748
+ tab: activeCommitDiffTab
18960
19749
  })
18961
19750
  }),
18962
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Activity, {
18963
- mode: activeTab === "files" ? "visible" : "hidden",
18964
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18965
- className: "min-h-0 flex-1 overflow-hidden",
18966
- children: previewMode === "external" && hasOpenFileTabs ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18967
- className: "flex h-full min-h-0 min-w-0 flex-col overflow-hidden",
18968
- children: [currentFilePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilePathBreadcrumb, {
18969
- path: currentFilePath,
18970
- fileTreeCollapsed,
18971
- onFileTreeCollapsedChange
18972
- }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18973
- className: "flex min-h-0 flex-1 overflow-hidden",
18974
- children: [fileTreeCollapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFileTreePane, {
19751
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19752
+ className: activeTab === "files" ? "min-h-0 flex-1 overflow-hidden" : "hidden",
19753
+ children: previewMode === "external" && hasOpenFileTabs ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19754
+ className: "flex h-full min-h-0 min-w-0 flex-col overflow-hidden",
19755
+ children: [currentFilePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilePathBreadcrumb, {
19756
+ path: currentFilePath,
19757
+ fileTreeCollapsed,
19758
+ onFileTreeCollapsedChange
19759
+ }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19760
+ className: "flex min-h-0 flex-1 overflow-hidden",
19761
+ children: [fileTreeCollapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFileTreePane, {
19762
+ viewerContextId,
19763
+ cwd: normalizedCwd,
19764
+ active: active && activeTab === "files",
19765
+ activeFilePath: currentFilePath,
19766
+ onOpenFile: openFile,
19767
+ previewMode
19768
+ }), currentFilePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19769
+ className: "min-h-0 min-w-0 flex-1 overflow-hidden",
19770
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFileContent, {
18975
19771
  viewerContextId,
18976
19772
  cwd: normalizedCwd,
18977
19773
  active: active && activeTab === "files",
18978
- activeFilePath: currentFilePath,
18979
- onOpenFile: openFile,
18980
- previewMode
18981
- }), currentFilePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18982
- className: "min-h-0 min-w-0 flex-1 overflow-hidden",
18983
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFileContent, {
18984
- viewerContextId,
18985
- cwd: normalizedCwd,
18986
- active: active && activeTab === "files",
18987
- path: currentFilePath
18988
- })
18989
- }) : null]
18990
- })]
18991
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFilesWorkspace, {
18992
- viewerContextId,
18993
- cwd: normalizedCwd,
18994
- active: active && activeTab === "files",
18995
- activeFilePath: currentFilePath,
18996
- onCloseFile: () => {
18997
- dispatchLocal({
18998
- type: "set-inline-active-file",
18999
- path: ""
19000
- });
19001
- },
19002
- onOpenFile: openFile,
19003
- previewMode
19004
- })
19774
+ path: currentFilePath
19775
+ })
19776
+ }) : null]
19777
+ })]
19778
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFilesWorkspace, {
19779
+ viewerContextId,
19780
+ cwd: normalizedCwd,
19781
+ active: active && activeTab === "files",
19782
+ activeFilePath: currentFilePath,
19783
+ onCloseFile: () => {
19784
+ dispatchLocal({
19785
+ type: "set-inline-active-file",
19786
+ path: ""
19787
+ });
19788
+ },
19789
+ onOpenFile: openFile,
19790
+ previewMode
19005
19791
  })
19006
19792
  }),
19007
19793
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectOpenFileDialog, {
@@ -19151,13 +19937,14 @@ function AppShellTabsList({ sessionStore, viewerContextId }) {
19151
19937
  })]
19152
19938
  });
19153
19939
  }
19154
- var AppShellGitPanelController = import_react.memo(function AppShellGitPanelController({ active, onCloseAllFiles, onCloseFile, onCloseFilesToRight, onCloseOtherFiles, onOpenFile, onReorderFiles, rightSidebarStore, sessionStore, viewerContextId }) {
19940
+ var AppShellGitPanelController = import_react.memo(function AppShellGitPanelController({ active, composerDiffLineComments, onAddDiffLineComment, onCloseAllFiles, onCloseFile, onCloseFilesToRight, onCloseOtherFiles, onOpenFile, onReorderFiles, rightSidebarStore, sessionStore, viewerContextId }) {
19155
19941
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RightSidebar, {
19156
19942
  viewerContextId,
19157
19943
  cwd: useSelector(sessionStore, (sessionState) => sessionState.cwd),
19158
19944
  active,
19159
19945
  activeFilePath: useSelector(rightSidebarStore, (state) => state.fileActivePath),
19160
19946
  activeTab: useSelector(rightSidebarStore, (state) => state.activeTab),
19947
+ diffLineComments: composerDiffLineComments,
19161
19948
  filePreviewPath: useSelector(rightSidebarStore, (state) => state.filePreviewPath),
19162
19949
  fileTabs: useSelector(rightSidebarStore, selectRightSidebarVisibleFileTabs, { compare: sameStringArray$3 }),
19163
19950
  fileTreeCollapsed: useSelector(rightSidebarStore, (state) => state.fileTreeCollapsed),
@@ -19167,6 +19954,7 @@ var AppShellGitPanelController = import_react.memo(function AppShellGitPanelCont
19167
19954
  onActiveTabChange: (tab) => {
19168
19955
  setRightSidebarActiveTab(rightSidebarStore, tab);
19169
19956
  },
19957
+ onAddDiffLineComment,
19170
19958
  onCloseAllFiles,
19171
19959
  onCloseFile,
19172
19960
  onCloseFilesToRight,
@@ -19178,7 +19966,7 @@ var AppShellGitPanelController = import_react.memo(function AppShellGitPanelCont
19178
19966
  onReorderFiles
19179
19967
  });
19180
19968
  });
19181
- function AppShellDesktopGitPanel({ active, onCloseAllFiles, onCloseFile, onCloseFilesToRight, onCloseOtherFiles, onOpenFile, onReorderFiles, rightSidebarStore, sessionStore, viewerContextId }) {
19969
+ function AppShellDesktopGitPanel({ active, composerDiffLineComments, onAddDiffLineComment, onCloseAllFiles, onCloseFile, onCloseFilesToRight, onCloseOtherFiles, onOpenFile, onReorderFiles, rightSidebarStore, sessionStore, viewerContextId }) {
19182
19970
  const cwd = useSelector(sessionStore, (sessionState) => sessionState.cwd);
19183
19971
  const activeFilePath = useSelector(rightSidebarStore, (state) => state.fileActivePath);
19184
19972
  const activeTab = useSelector(rightSidebarStore, (state) => state.activeTab);
@@ -19196,6 +19984,7 @@ function AppShellDesktopGitPanel({ active, onCloseAllFiles, onCloseFile, onClose
19196
19984
  active,
19197
19985
  activeFilePath,
19198
19986
  activeTab,
19987
+ diffLineComments: composerDiffLineComments,
19199
19988
  filePreviewPath,
19200
19989
  fileTabs,
19201
19990
  fileTreeCollapsed,
@@ -19205,6 +19994,7 @@ function AppShellDesktopGitPanel({ active, onCloseAllFiles, onCloseFile, onClose
19205
19994
  onActiveTabChange: (tab) => {
19206
19995
  setRightSidebarActiveTab(rightSidebarStore, tab);
19207
19996
  },
19997
+ onAddDiffLineComment,
19208
19998
  onCloseAllFiles,
19209
19999
  onCloseFile,
19210
20000
  onCloseFilesToRight,
@@ -19383,7 +20173,7 @@ function AppShellDesktopResizeHandle({ label, max, min, onResize, onResizeEnd, o
19383
20173
  function AppShellTabsController(props) {
19384
20174
  return useAppShellTabsControllerView(props);
19385
20175
  }
19386
- function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTurn, composerPanelRef, contextUsageStore, conversationFrameRef, conversationItemsStore, defaultNewSessionDirectory, displaySettingsStore, fileInputRef, gitPanelOpen, hiddenThinkingPreviewStore, isSessionViewLoading, isSubmitting, isMobile, newSessionDirectoryOptions, onCancelCompaction, onCreateSession, onOpenAddDirectoryDialog, onCloseAllFileViewTabs, onCloseFileViewTab, onCloseFileViewTabsToRight, onCloseOtherFileViewTabs, onOpenFileViewTab, onReorderFileViewTabs, onValueChange, rightSidebarStore, sessionStore, store, viewerContextId, workingStateStore }) {
20176
+ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTurn, composerDiffLineComments, composerPanelRef, contextUsageStore, conversationFrameRef, conversationItemsStore, defaultNewSessionDirectory, displaySettingsStore, fileInputRef, gitPanelOpen, hiddenThinkingPreviewStore, isSessionViewLoading, isSubmitting, isMobile, newSessionDirectoryOptions, onCancelCompaction, onCreateSession, onOpenAddDirectoryDialog, onCloseAllFileViewTabs, onCloseFileViewTab, onCloseFileViewTabsToRight, onCloseOtherFileViewTabs, onAddDiffLineComment, onOpenFileViewTab, onReorderFileViewTabs, onValueChange, rightSidebarStore, sessionStore, store, viewerContextId, workingStateStore }) {
19387
20177
  const currentTab = useSelector(appUiStore, (state) => state.currentTab);
19388
20178
  const isDraftSession = useSelector(sessionStore, (sessionState) => sessionState.draft);
19389
20179
  const showTabsList = isMobile || !isDraftSession || isSessionViewLoading;
@@ -19461,7 +20251,9 @@ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTu
19461
20251
  viewerContextId,
19462
20252
  sessionStore,
19463
20253
  active: currentTab === "git",
20254
+ composerDiffLineComments,
19464
20255
  rightSidebarStore,
20256
+ onAddDiffLineComment,
19465
20257
  onCloseAllFiles: onCloseAllFileViewTabs,
19466
20258
  onCloseFile: onCloseFileViewTab,
19467
20259
  onCloseFilesToRight: onCloseFileViewTabsToRight,
@@ -19564,7 +20356,9 @@ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTu
19564
20356
  viewerContextId,
19565
20357
  sessionStore,
19566
20358
  active: desktopGitPanelOpen,
20359
+ composerDiffLineComments,
19567
20360
  rightSidebarStore,
20361
+ onAddDiffLineComment,
19568
20362
  onCloseAllFiles: onCloseAllFileViewTabs,
19569
20363
  onCloseFile: onCloseFileViewTab,
19570
20364
  onCloseFilesToRight: onCloseFileViewTabsToRight,
@@ -20089,10 +20883,11 @@ function SidebarSessionItem({ entryKey, directorySessionsStore, activeSessionSto
20089
20883
  const pinnedDirectoryLabel = isPinned ? formatDisplayPath$1(entry.cwd?.trim() || entry.path?.trim() || "") : "";
20090
20884
  const exactTimestamp = hasTimestamp && timestamp ? new Date(timestamp).toLocaleString() : void 0;
20091
20885
  const showUnread = Boolean(entry.unread) && !entry.streaming;
20886
+ const isOptimisticSession = Boolean(entry.optimistic || entry.id?.startsWith("optimistic:") && !entry.path);
20092
20887
  const availableMoveTargetDirectories = moveTargetDirectories.filter((directory) => directory && directory !== entry.cwd);
20093
20888
  const canMoveAnyDirectory = Boolean(entry.path && onMoveSessionAnyDirectory);
20094
20889
  const hasMoveActions = Boolean(entry.path && onMoveSession) && availableMoveTargetDirectories.length > 0 || canMoveAnyDirectory;
20095
- const hasSessionActions = Boolean(onTogglePinnedSession || entry.path && (onRenameSession || onDeleteSession || onSetSessionUnread) || hasMoveActions) && !overlay;
20890
+ const hasSessionActions = Boolean(!isOptimisticSession && onTogglePinnedSession || entry.path && (onRenameSession || onDeleteSession || onSetSessionUnread) || hasMoveActions) && !overlay;
20096
20891
  const sessionButton = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarMenuButton, {
20097
20892
  type: "button",
20098
20893
  "data-sidebar-session-item": true,
@@ -20165,7 +20960,7 @@ function SidebarSessionItem({ entryKey, directorySessionsStore, activeSessionSto
20165
20960
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarMenuItem, { children: hasSessionActions ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenu$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextMenuTrigger, { render: sessionRow }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenuContent, {
20166
20961
  className: "w-44",
20167
20962
  children: [
20168
- onTogglePinnedSession ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextMenuItem, {
20963
+ onTogglePinnedSession && !isOptimisticSession ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextMenuItem, {
20169
20964
  onClick: () => onTogglePinnedSession(entry),
20170
20965
  children: isPinned ? "Unpin" : "Pin to sidebar"
20171
20966
  }) : null,
@@ -20201,7 +20996,7 @@ function SidebarSessionItem({ entryKey, directorySessionsStore, activeSessionSto
20201
20996
  function sameDirectorySessionEntry(left, right) {
20202
20997
  if (left === right) return true;
20203
20998
  if (!left || !right) return false;
20204
- return left.path === right.path && left.id === right.id && left.cwd === right.cwd && left.name === right.name && left.title === right.title && left.modified === right.modified && left.lastUserMessageAt === right.lastUserMessageAt && left.lastMessageAt === right.lastMessageAt && left.lastMessagePreview === right.lastMessagePreview && left.messageCount === right.messageCount && JSON.stringify(left.contextUsage ?? null) === JSON.stringify(right.contextUsage ?? null) && Boolean(left.streaming) === Boolean(right.streaming) && Boolean(left.unread) === Boolean(right.unread);
20999
+ return left.path === right.path && left.id === right.id && left.cwd === right.cwd && left.name === right.name && left.title === right.title && left.modified === right.modified && left.lastUserMessageAt === right.lastUserMessageAt && left.lastMessageAt === right.lastMessageAt && left.lastMessagePreview === right.lastMessagePreview && left.messageCount === right.messageCount && JSON.stringify(left.contextUsage ?? null) === JSON.stringify(right.contextUsage ?? null) && Boolean(left.streaming) === Boolean(right.streaming) && Boolean(left.unread) === Boolean(right.unread) && Boolean(left.optimistic) === Boolean(right.optimistic);
20205
21000
  }
20206
21001
  function buildDirectorySessionEntryMap(sessionsByDirectory) {
20207
21002
  const entriesByKey = /* @__PURE__ */ new Map();
@@ -20259,7 +21054,7 @@ function useDirectorySessions(store, directory) {
20259
21054
  function useDirectorySessionEntry(store, entryKey) {
20260
21055
  return useSelector(store, (state) => state.entriesByKey.get(entryKey), { compare: sameDirectorySessionEntry });
20261
21056
  }
20262
- var DirectorySessionGroup = import_react.memo(function DirectorySessionGroup({ directory, directorySessionsStore, collapsedDirectoryStore, searchActive, directoryOrderingEnabled, moveTargetDirectories, activeSessionStore, selectedSessionKeyStore, pinnedSessionKeys, isMobile, setOpenMobile, isDragging, isSessionDropTarget, overlay = false, attributes, listeners, onSessionClick, onRenameSession, onDeleteSession, onSetSessionUnread, onMoveSession, onMoveSessionAnyDirectory, onTogglePinnedSession, onCreateSessionInDirectory, onDeleteOldSessionsInDirectory, onRemoveDirectory }) {
21057
+ var DirectorySessionGroup = import_react.memo(function DirectorySessionGroup({ directory, directorySessionsStore, collapsedDirectoryStore, searchActive, moveTargetDirectories, activeSessionStore, selectedSessionKeyStore, pinnedSessionKeys, isMobile, setOpenMobile, isDragging, isSessionDropTarget, overlay = false, attributes, listeners, onSessionClick, onRenameSession, onDeleteSession, onSetSessionUnread, onMoveSession, onMoveSessionAnyDirectory, onTogglePinnedSession, onCreateSessionInDirectory, onDeleteOldSessionsInDirectory, onRemoveDirectory }) {
20263
21058
  const [renderCount, setRenderCount] = import_react.useState(5);
20264
21059
  const { isLoadingSessions, sessionKeys } = useDirectorySessions(directorySessionsStore, directory);
20265
21060
  const collapsed = useDirectoryCollapsed(collapsedDirectoryStore, directory, searchActive);
@@ -20277,7 +21072,7 @@ var DirectorySessionGroup = import_react.memo(function DirectorySessionGroup({ d
20277
21072
  children: [
20278
21073
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CollapsibleTrigger, {
20279
21074
  type: "button",
20280
- className: cn("group/directory-label flex min-w-0 flex-1 items-center gap-2 text-left text-sm text-sidebar-foreground", directoryOrderingEnabled && !overlay && "cursor-grab active:cursor-grabbing", searchActive && "cursor-default"),
21075
+ className: cn("group/directory-label flex min-w-0 flex-1 cursor-default items-center gap-2 text-left text-sm text-sidebar-foreground"),
20281
21076
  disabled: searchActive || overlay,
20282
21077
  ...attributes ?? {},
20283
21078
  ...listeners ?? {},
@@ -20724,7 +21519,6 @@ function useAppSidebarView({ sessionSearch, onOpenSessionsDialog, visibleDirecto
20724
21519
  directorySessionsStore,
20725
21520
  collapsedDirectoryStore,
20726
21521
  searchActive,
20727
- directoryOrderingEnabled,
20728
21522
  moveTargetDirectories: visibleDirectories,
20729
21523
  activeSessionStore,
20730
21524
  selectedSessionKeyStore,
@@ -20756,7 +21550,6 @@ function useAppSidebarView({ sessionSearch, onOpenSessionsDialog, visibleDirecto
20756
21550
  directorySessionsStore,
20757
21551
  collapsedDirectoryStore,
20758
21552
  searchActive,
20759
- directoryOrderingEnabled,
20760
21553
  moveTargetDirectories: visibleDirectories,
20761
21554
  activeSessionStore,
20762
21555
  selectedSessionKeyStore,
@@ -21004,7 +21797,7 @@ function useAppShellSidebarControllerView({ viewerContextId, sidebarStore, sessi
21004
21797
  return indexData ? [[directory, indexData]] : [];
21005
21798
  }));
21006
21799
  sidebarStore.setSidebarState((current) => {
21007
- const nextDirectoryIndexDataByPath = Object.keys(activeDirectoryIndexes).length > 0 ? mergeDirectoryIndexData(current.directoryIndexDataByPath, activeDirectoryIndexes) : current.directoryIndexDataByPath;
21800
+ const nextDirectoryIndexDataByPath = Object.keys(activeDirectoryIndexes).length > 0 ? mergeDirectoryIndexDataPreservingOptimistic(current.directoryIndexDataByPath, activeDirectoryIndexes) : current.directoryIndexDataByPath;
21008
21801
  const nextDirectoryIndexLoading = updateDirectoryIndexLoadingState(current.directoryIndexLoading, activeDirectories, false);
21009
21802
  if (nextDirectoryIndexDataByPath === current.directoryIndexDataByPath && nextDirectoryIndexLoading === current.directoryIndexLoading) return current;
21010
21803
  return {
@@ -21094,7 +21887,7 @@ function useAppShellSidebarControllerView({ viewerContextId, sidebarStore, sessi
21094
21887
  return indexData ? [[directory, indexData]] : [];
21095
21888
  }));
21096
21889
  sidebarStore.setSidebarState((current) => {
21097
- const nextDirectoryIndexDataByPath = Object.keys(activeDirectoryIndexes).length > 0 ? mergeDirectoryIndexData(current.directoryIndexDataByPath, activeDirectoryIndexes) : current.directoryIndexDataByPath;
21890
+ const nextDirectoryIndexDataByPath = Object.keys(activeDirectoryIndexes).length > 0 ? mergeDirectoryIndexDataPreservingOptimistic(current.directoryIndexDataByPath, activeDirectoryIndexes) : current.directoryIndexDataByPath;
21098
21891
  const nextDirectoryIndexLoading = updateDirectoryIndexLoadingState(current.directoryIndexLoading, activeDirectories, false);
21099
21892
  if (nextDirectoryIndexDataByPath === current.directoryIndexDataByPath && nextDirectoryIndexLoading === current.directoryIndexLoading) return current;
21100
21893
  return {
@@ -21185,7 +21978,7 @@ function useAppShellSidebarControllerView({ viewerContextId, sidebarStore, sessi
21185
21978
  return;
21186
21979
  }
21187
21980
  setSidebarSelection([key], key);
21188
- if (entry.id) sessionWorkspaceRef.current?.selectSession(entry.id, { sessionPath: entry.path });
21981
+ if (entry.id && !isOptimisticSidebarSessionEntry(entry)) sessionWorkspaceRef.current?.selectSession(entry.id, { sessionPath: entry.path });
21189
21982
  };
21190
21983
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppSidebar, {
21191
21984
  sessionSearch,
@@ -21626,8 +22419,30 @@ function movePendingDraftFollowUpMessage(messages, pendingId, direction) {
21626
22419
  next.splice(targetIndex, 0, movedItem);
21627
22420
  return next;
21628
22421
  }
22422
+ var EMPTY_COMPOSER_DIFF_LINE_COMMENTS = [];
21629
22423
  var EMPTY_COMPOSER_IMAGES = [];
21630
22424
  var EMPTY_COMPOSER_PENDING_MESSAGES = [];
22425
+ function sameComposerDiffLineComments(left, right) {
22426
+ if (left === right) return true;
22427
+ if (left.length !== right.length) return false;
22428
+ for (let index = 0; index < left.length; index += 1) {
22429
+ const leftComment = left[index];
22430
+ const rightComment = right[index];
22431
+ if (!leftComment || !rightComment) return false;
22432
+ if (leftComment.id !== rightComment.id) return false;
22433
+ if (leftComment.cwd !== rightComment.cwd) return false;
22434
+ if (leftComment.path !== rightComment.path) return false;
22435
+ if (leftComment.lineNumber !== rightComment.lineNumber) return false;
22436
+ if (leftComment.side !== rightComment.side) return false;
22437
+ if (leftComment.text !== rightComment.text) return false;
22438
+ if (leftComment.tabKey !== rightComment.tabKey) return false;
22439
+ if (leftComment.tabTitle !== rightComment.tabTitle) return false;
22440
+ if (leftComment.commit !== rightComment.commit) return false;
22441
+ if (leftComment.shortHash !== rightComment.shortHash) return false;
22442
+ if (leftComment.mode !== rightComment.mode) return false;
22443
+ }
22444
+ return true;
22445
+ }
21631
22446
  function sameComposerPromptImages(left, right) {
21632
22447
  if (left === right) return true;
21633
22448
  if (left.length !== right.length) return false;
@@ -21656,13 +22471,14 @@ function sameComposerPendingMessages(left, right) {
21656
22471
  return true;
21657
22472
  }
21658
22473
  function sameAppShellComposerSnapshot(left, right) {
21659
- return left.activeSessionId === right.activeSessionId && left.awaitingFirstTurn === right.awaitingFirstTurn && left.centerMessages === right.centerMessages && left.composerSkill === right.composerSkill && left.composerSyncNonce === right.composerSyncNonce && left.composerText === right.composerText && left.disabled === right.disabled && left.isStreaming === right.isStreaming && left.isSubmitting === right.isSubmitting && left.viewerContextId === right.viewerContextId && sameComposerPromptImages(left.composerImages, right.composerImages) && sameComposerPendingMessages(left.currentPendingMessages, right.currentPendingMessages);
22474
+ return left.activeSessionId === right.activeSessionId && left.awaitingFirstTurn === right.awaitingFirstTurn && left.centerMessages === right.centerMessages && sameComposerDiffLineComments(left.composerDiffLineComments, right.composerDiffLineComments) && left.composerSkill === right.composerSkill && left.composerSyncNonce === right.composerSyncNonce && left.composerText === right.composerText && left.disabled === right.disabled && left.isStreaming === right.isStreaming && left.isSubmitting === right.isSubmitting && left.viewerContextId === right.viewerContextId && sameComposerPromptImages(left.composerImages, right.composerImages) && sameComposerPendingMessages(left.currentPendingMessages, right.currentPendingMessages);
21660
22475
  }
21661
22476
  function createInitialAppShellComposerSnapshot(viewerContextId) {
21662
22477
  return {
21663
22478
  activeSessionId: void 0,
21664
22479
  awaitingFirstTurn: false,
21665
22480
  centerMessages: false,
22481
+ composerDiffLineComments: EMPTY_COMPOSER_DIFF_LINE_COMMENTS,
21666
22482
  composerImages: EMPTY_COMPOSER_IMAGES,
21667
22483
  composerSkill: void 0,
21668
22484
  composerSyncNonce: 0,
@@ -21732,10 +22548,23 @@ function createPicoLatestThrottler(options) {
21732
22548
  };
21733
22549
  }
21734
22550
  function samePromptMutationSessionSnapshot(left, right) {
21735
- return left.cwd === right.cwd && left.draft === right.draft && left.sessionFile === right.sessionFile && left.sessionId === right.sessionId && left.sessionKey === right.sessionKey && left.streaming === right.streaming;
22551
+ return left.assistantOutputSignature === right.assistantOutputSignature && left.cwd === right.cwd && left.draft === right.draft && left.sessionFile === right.sessionFile && left.sessionId === right.sessionId && left.sessionKey === right.sessionKey && left.streaming === right.streaming;
22552
+ }
22553
+ function sessionAssistantOutputSignature(items) {
22554
+ let outputCount = 0;
22555
+ let lastOutputKey = "";
22556
+ for (let index = 0; index < items.length; index += 1) {
22557
+ const item = items[index];
22558
+ if (!item || item.kind !== "assistant") continue;
22559
+ if (!item.blocks.some((block) => block.type === "text" && block.text.trim())) continue;
22560
+ outputCount += 1;
22561
+ lastOutputKey = item.itemKey || item.renderKey || `index:${index}`;
22562
+ }
22563
+ return `${outputCount}:${lastOutputKey}`;
21736
22564
  }
21737
22565
  function usePromptMutationSessionSnapshot(store) {
21738
22566
  return useSelector(store, (sessionState) => ({
22567
+ assistantOutputSignature: sessionAssistantOutputSignature(sessionState.items),
21739
22568
  cwd: sessionState.cwd,
21740
22569
  draft: sessionState.draft,
21741
22570
  sessionFile: sessionState.sessionFile,
@@ -21756,7 +22585,7 @@ function buildCurrentSessionRequestUrl(path, { contextId, sessionState, fallback
21756
22585
  const shouldUseSessionKey = Boolean(sessionKey && (sessionState.draft || !sessionState.sessionId));
21757
22586
  return buildRequestUrl(path, {
21758
22587
  contextId,
21759
- sessionId: shouldUseSessionKey ? void 0 : sessionState.sessionId ?? fallbackSessionId,
22588
+ sessionId: shouldUseSessionKey || sessionState.draft ? void 0 : sessionState.sessionId ?? fallbackSessionId,
21760
22589
  searchParams: shouldUseSessionKey ? { sessionKey } : void 0
21761
22590
  });
21762
22591
  }
@@ -21809,13 +22638,14 @@ function movePendingComposerMessage(messages, pendingId, direction) {
21809
22638
  next.splice(targetIndex, 0, movedItem);
21810
22639
  return next;
21811
22640
  }
21812
- function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultNewSessionDirectory, sessionStore, sessionStateRef, draftSessionLoadingOwnerKey, pendingDraftPrompt, pendingDraftFollowUps, awaitingFirstTurn, pendingMessages, composerImagesRef, composerTextRef, composerSkillRef, replaceComposerDraft, lastSyncedEditorTextRef, rememberRecentDirectory, prefetchDirectorySessionsIndex, addOptimisticUserMessage, removeOptimisticUserMessage, setSidebarDirectories, setStoredDraftDirectory, setDraftSessionLoadingOwnerKey, setPendingDraftPrompt, setPendingDraftFollowUps, setPendingMessages, setAwaitingFirstTurn, setIsSubmitting, setComposerImages }) {
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 }) {
21813
22642
  const queryClient = useQueryClient();
21814
22643
  const draftSessionLoadingOwnerKeyRef = import_react.useRef(draftSessionLoadingOwnerKey);
21815
22644
  const pendingDraftPromptRef = import_react.useRef(pendingDraftPrompt);
21816
22645
  const pendingDraftFollowUpsRef = import_react.useRef(pendingDraftFollowUps);
21817
22646
  const pendingMessagesRef = import_react.useRef(pendingMessages);
21818
22647
  const awaitingFirstTurnRef = import_react.useRef(awaitingFirstTurn);
22648
+ const awaitingFirstTurnAssistantOutputSignatureRef = import_react.useRef(null);
21819
22649
  const sessionSnapshot = usePromptMutationSessionSnapshot(sessionStore);
21820
22650
  const sessionStreamingRef = import_react.useRef(sessionSnapshot.streaming);
21821
22651
  import_react.useEffect(() => {
@@ -21888,16 +22718,19 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
21888
22718
  const nextPrompt = pendingDraftPromptRef.current;
21889
22719
  if (!nextPrompt || nextPrompt.ownerKey !== ownerKey) return false;
21890
22720
  removeOptimisticUserMessage(nextPrompt.optimisticId);
22721
+ removeOptimisticSidebarSession(nextPrompt.optimisticSidebarSessionId);
21891
22722
  for (const followUp of pendingDraftFollowUpsRef.current) removeOptimisticUserMessage(followUp.optimisticId);
21892
22723
  pendingDraftPromptRef.current = null;
21893
22724
  pendingDraftFollowUpsRef.current = [];
21894
22725
  setPendingDraftPrompt(null);
21895
22726
  setPendingDraftFollowUps([]);
22727
+ awaitingFirstTurnAssistantOutputSignatureRef.current = null;
21896
22728
  awaitingFirstTurnRef.current = false;
21897
22729
  setAwaitingFirstTurn(false);
21898
22730
  return applyPendingDraftPromptToComposer(nextPrompt);
21899
22731
  }, [
21900
22732
  applyPendingDraftPromptToComposer,
22733
+ removeOptimisticSidebarSession,
21901
22734
  removeOptimisticUserMessage,
21902
22735
  setAwaitingFirstTurn,
21903
22736
  setPendingDraftFollowUps,
@@ -21930,15 +22763,20 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
21930
22763
  ]);
21931
22764
  const createSessionRequest = import_react.useCallback(async ({ cwd }) => {
21932
22765
  if (!viewerContextId) throw new Error("Viewer context unavailable");
21933
- return await fetchJson(buildRequestUrl("/api/session/new", {
22766
+ return await fetchJson(buildCurrentSessionRequestUrl("/api/session/new", {
21934
22767
  contextId: viewerContextId,
21935
- sessionId: activeSessionId
22768
+ sessionState: sessionStateRef.current,
22769
+ fallbackSessionId: activeSessionId
21936
22770
  }), {
21937
22771
  method: "POST",
21938
22772
  headers: { "content-type": "application/json" },
21939
22773
  body: JSON.stringify({ cwd })
21940
22774
  });
21941
- }, [activeSessionId, viewerContextId]);
22775
+ }, [
22776
+ activeSessionId,
22777
+ sessionStateRef,
22778
+ viewerContextId
22779
+ ]);
21942
22780
  const createSession = import_react.useCallback(async (cwdOverride) => {
21943
22781
  if (!viewerContextId) return false;
21944
22782
  const nextCwd = resolveNewSessionCwd({
@@ -21977,9 +22815,10 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
21977
22815
  const queuePendingDraftPrompt = import_react.useCallback((streamingBehavior) => {
21978
22816
  const ownerKey = draftSessionLoadingOwnerKeyRef.current;
21979
22817
  if (!ownerKey) return false;
21980
- const message = serializeComposerDraft({
22818
+ const message = buildComposerPromptMessage({
21981
22819
  text: composerTextRef.current,
21982
- skillName: composerSkillRef.current
22820
+ skillName: composerSkillRef.current,
22821
+ diffLineComments: composerDiffLineCommentsRef.current
21983
22822
  }).trim();
21984
22823
  const images = composerImagesRef.current.map((image) => ({ ...image }));
21985
22824
  if (!message && images.length === 0) return false;
@@ -21992,7 +22831,8 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
21992
22831
  message,
21993
22832
  images,
21994
22833
  queued: false
21995
- })
22834
+ }),
22835
+ optimisticSidebarSessionId: addOptimisticSidebarSession()
21996
22836
  };
21997
22837
  pendingDraftPromptRef.current = nextPrompt;
21998
22838
  setPendingDraftPrompt(nextPrompt);
@@ -22012,17 +22852,21 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22012
22852
  setPendingDraftFollowUps(nextFollowUps);
22013
22853
  }
22014
22854
  replaceComposerDraft("", void 0, { forceSync: true });
22855
+ setComposerDiffLineComments([]);
22015
22856
  setComposerImages([]);
22016
22857
  lastSyncedEditorTextRef.current = "";
22017
22858
  return true;
22018
22859
  }, [
22860
+ addOptimisticSidebarSession,
22019
22861
  addOptimisticUserMessage,
22862
+ composerDiffLineCommentsRef,
22020
22863
  composerImagesRef,
22021
22864
  composerSkillRef,
22022
22865
  composerTextRef,
22023
22866
  lastSyncedEditorTextRef,
22024
22867
  replaceComposerDraft,
22025
22868
  setAwaitingFirstTurn,
22869
+ setComposerDiffLineComments,
22026
22870
  setComposerImages,
22027
22871
  setPendingDraftFollowUps,
22028
22872
  setPendingDraftPrompt
@@ -22056,32 +22900,37 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22056
22900
  const submitPrompt = import_react.useCallback(async (streamingBehavior, options) => {
22057
22901
  if (!viewerContextId) return false;
22058
22902
  if (draftSessionLoadingOwnerKeyRef.current) return queuePendingDraftPrompt(streamingBehavior);
22059
- const message = serializeComposerDraft({
22903
+ const submittedDiffLineComments = composerDiffLineCommentsRef.current.map((comment) => ({ ...comment }));
22904
+ const message = buildComposerPromptMessage({
22060
22905
  text: composerTextRef.current,
22061
- skillName: composerSkillRef.current
22906
+ skillName: composerSkillRef.current,
22907
+ diffLineComments: submittedDiffLineComments
22062
22908
  }).trim();
22063
22909
  if (!message && composerImagesRef.current.length === 0) return false;
22064
22910
  const treatAsQueuedPrompt = options?.forceFirstPrompt ? false : Boolean(sessionStreamingRef.current || awaitingFirstTurnRef.current);
22065
22911
  const normalizedStreamingBehavior = treatAsQueuedPrompt ? normalizeQueuedStreamingBehavior(streamingBehavior) : void 0;
22066
22912
  const submittedImages = composerImagesRef.current.map((image) => ({ ...image }));
22067
22913
  const queuedPendingId = treatAsQueuedPrompt ? createLocalPendingId() : void 0;
22914
+ if (!treatAsQueuedPrompt) {
22915
+ awaitingFirstTurnAssistantOutputSignatureRef.current = sessionAssistantOutputSignature(sessionStateRef.current.items);
22916
+ awaitingFirstTurnRef.current = true;
22917
+ setAwaitingFirstTurn(true);
22918
+ }
22068
22919
  const optimisticId = options?.optimisticId ?? (treatAsQueuedPrompt ? void 0 : addOptimisticUserMessage({
22069
22920
  message,
22070
22921
  images: submittedImages,
22071
22922
  queued: false
22072
22923
  }));
22924
+ const optimisticSidebarSessionId = options?.optimisticSidebarSessionId ?? (!treatAsQueuedPrompt && sessionStateRef.current.draft ? addOptimisticSidebarSession() : void 0);
22073
22925
  if (queuedPendingId && normalizedStreamingBehavior) addOptimisticPendingMessage({
22074
22926
  pendingId: queuedPendingId,
22075
22927
  text: message,
22076
22928
  images: submittedImages.map((image) => ({ ...image })),
22077
22929
  streamingBehavior: normalizedStreamingBehavior
22078
22930
  });
22079
- if (!treatAsQueuedPrompt) {
22080
- awaitingFirstTurnRef.current = true;
22081
- setAwaitingFirstTurn(true);
22082
- }
22083
22931
  setIsSubmitting(true);
22084
22932
  replaceComposerDraft("", void 0, { forceSync: true });
22933
+ setComposerDiffLineComments([]);
22085
22934
  setComposerImages([]);
22086
22935
  lastSyncedEditorTextRef.current = "";
22087
22936
  try {
@@ -22097,15 +22946,18 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22097
22946
  } catch (error) {
22098
22947
  if (queuedPendingId) removeOptimisticPendingMessage(queuedPendingId);
22099
22948
  removeOptimisticUserMessage(optimisticId);
22949
+ removeOptimisticSidebarSession(optimisticSidebarSessionId);
22100
22950
  if (!treatAsQueuedPrompt) {
22951
+ awaitingFirstTurnAssistantOutputSignatureRef.current = null;
22101
22952
  awaitingFirstTurnRef.current = false;
22102
22953
  setAwaitingFirstTurn(false);
22103
22954
  }
22104
22955
  if (!serializeComposerDraft({
22105
22956
  text: composerTextRef.current,
22106
22957
  skillName: composerSkillRef.current
22107
- }).trim()) {
22958
+ }).trim() && composerDiffLineCommentsRef.current.length === 0) {
22108
22959
  replaceComposerDraft(message);
22960
+ setComposerDiffLineComments((current) => current.length === 0 ? submittedDiffLineComments.map((comment) => ({ ...comment })) : current);
22109
22961
  setComposerImages((current) => current.length === 0 ? submittedImages.map((image) => ({ ...image })) : current);
22110
22962
  }
22111
22963
  toast.error(error instanceof Error ? error.message : "Failed to submit prompt");
@@ -22115,7 +22967,9 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22115
22967
  }
22116
22968
  }, [
22117
22969
  addOptimisticPendingMessage,
22970
+ addOptimisticSidebarSession,
22118
22971
  addOptimisticUserMessage,
22972
+ composerDiffLineCommentsRef,
22119
22973
  composerImagesRef,
22120
22974
  composerSkillRef,
22121
22975
  composerTextRef,
@@ -22124,9 +22978,11 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22124
22978
  queuePendingDraftPrompt,
22125
22979
  sessionStateRef,
22126
22980
  removeOptimisticPendingMessage,
22981
+ removeOptimisticSidebarSession,
22127
22982
  removeOptimisticUserMessage,
22128
22983
  replaceComposerDraft,
22129
22984
  setAwaitingFirstTurn,
22985
+ setComposerDiffLineComments,
22130
22986
  setComposerImages,
22131
22987
  setIsSubmitting,
22132
22988
  viewerContextId
@@ -22183,8 +23039,10 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22183
23039
  applyPendingDraftPromptToComposer(nextPrompt);
22184
23040
  if (!await submitPrompt(nextPrompt.streamingBehavior, {
22185
23041
  forceFirstPrompt: true,
22186
- optimisticId: nextPrompt.optimisticId
23042
+ optimisticId: nextPrompt.optimisticId,
23043
+ optimisticSidebarSessionId: nextPrompt.optimisticSidebarSessionId
22187
23044
  })) {
23045
+ removeOptimisticSidebarSession(nextPrompt.optimisticSidebarSessionId);
22188
23046
  for (const followUp of pendingDraftFollowUpsRef.current) removeOptimisticUserMessage(followUp.optimisticId);
22189
23047
  pendingDraftFollowUpsRef.current = [];
22190
23048
  setPendingDraftFollowUps([]);
@@ -22195,6 +23053,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22195
23053
  }, [
22196
23054
  applyPendingDraftPromptToComposer,
22197
23055
  flushPendingDraftFollowUps,
23056
+ removeOptimisticSidebarSession,
22198
23057
  removeOptimisticUserMessage,
22199
23058
  setPendingDraftFollowUps,
22200
23059
  setPendingDraftPrompt,
@@ -22205,7 +23064,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22205
23064
  const currentSessionState = sessionStateRef.current;
22206
23065
  if (currentSessionState.sessionKey?.startsWith("optimistic:")) return;
22207
23066
  const currentOwnerKey = promptDraftKey(currentSessionState);
22208
- if (!currentSessionState.draft || currentOwnerKey !== draftSessionLoadingOwnerKey) return;
23067
+ if (!currentSessionState.draft || !promptDraftKeyMatchesOwner(currentOwnerKey, draftSessionLoadingOwnerKey)) return;
22209
23068
  draftSessionLoadingOwnerKeyRef.current = null;
22210
23069
  setDraftSessionLoadingOwnerKey(null);
22211
23070
  if (pendingDraftPromptRef.current?.ownerKey === draftSessionLoadingOwnerKey) flushPendingDraftPrompt(draftSessionLoadingOwnerKey);
@@ -22218,15 +23077,17 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22218
23077
  ]);
22219
23078
  import_react.useEffect(() => {
22220
23079
  if (!awaitingFirstTurn) return;
22221
- const currentSessionState = sessionStateRef.current;
22222
- const hasAssistantOutput = currentSessionState.items.some((item) => item.kind === "assistant" && item.blocks.some((block) => block.type === "text" && block.text.trim()));
22223
- if (currentSessionState.streaming || hasAssistantOutput || pendingMessages.length > 0) {
23080
+ const baselineAssistantOutputSignature = awaitingFirstTurnAssistantOutputSignatureRef.current;
23081
+ const assistantOutputStarted = baselineAssistantOutputSignature !== null && sessionSnapshot.assistantOutputSignature !== baselineAssistantOutputSignature;
23082
+ if (sessionStateRef.current.streaming || assistantOutputStarted || pendingMessages.length > 0) {
23083
+ awaitingFirstTurnAssistantOutputSignatureRef.current = null;
22224
23084
  awaitingFirstTurnRef.current = false;
22225
23085
  setAwaitingFirstTurn(false);
22226
23086
  }
22227
23087
  }, [
22228
23088
  awaitingFirstTurn,
22229
23089
  pendingMessages.length,
23090
+ sessionSnapshot.assistantOutputSignature,
22230
23091
  sessionSnapshot.streaming,
22231
23092
  sessionStateRef,
22232
23093
  setAwaitingFirstTurn
@@ -22370,20 +23231,33 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
22370
23231
  }
22371
23232
  function usePicoTheme() {
22372
23233
  const { setTheme, systemTheme } = z();
22373
- const [themeFamily, setThemeFamilyState] = import_react.useState(() => readStoredTheme());
22374
- const [colorMode, setColorModeState] = import_react.useState(() => readStoredThemeColorMode());
23234
+ const initialThemeRef = import_react.useRef(void 0);
23235
+ const initialTheme = initialThemeRef.current ?? readStoredTheme();
23236
+ initialThemeRef.current = initialTheme;
23237
+ const [themeFamily, setThemeFamilyState] = import_react.useState(initialTheme);
23238
+ const [colorMode, setColorModeState] = import_react.useState(() => themeFamilyFixedMode(initialTheme) ?? readStoredThemeColorMode());
22375
23239
  const appliedTheme = appliedThemeClass(themeFamily, colorMode, systemTheme);
22376
23240
  import_react.useEffect(() => {
22377
23241
  setTheme(appliedTheme);
22378
23242
  safeLocalStorageSetItem(APPLIED_THEME_STORAGE_KEY, appliedTheme);
23243
+ const appliedThemeMode = appliedThemeClassColorMode(appliedTheme);
23244
+ if (appliedThemeMode) document.documentElement.dataset.picoThemeMode = appliedThemeMode;
22379
23245
  }, [appliedTheme, setTheme]);
22380
- const previewThemeFamily = (value) => {
22381
- setThemeFamilyState(normalizeThemeFamily(value));
23246
+ const previewThemeFamily = (value, nextColorMode) => {
23247
+ const nextTheme = normalizeThemeFamily(value);
23248
+ const fixedMode = themeFamilyFixedMode(nextTheme);
23249
+ setThemeFamilyState(nextTheme);
23250
+ if (nextColorMode || fixedMode) setColorModeState(normalizeThemeColorMode(nextColorMode ?? fixedMode));
22382
23251
  };
22383
23252
  const setThemeFamily = (value) => {
22384
23253
  const nextTheme = normalizeThemeFamily(value);
23254
+ const fixedMode = themeFamilyFixedMode(nextTheme);
22385
23255
  setThemeFamilyState(nextTheme);
22386
23256
  safeLocalStorageSetItem(THEME_STORAGE_KEY, nextTheme);
23257
+ if (fixedMode) {
23258
+ setColorModeState(fixedMode);
23259
+ safeLocalStorageSetItem(THEME_COLOR_MODE_STORAGE_KEY, fixedMode);
23260
+ }
22387
23261
  };
22388
23262
  const setColorMode = (value) => {
22389
23263
  const nextMode = normalizeThemeColorMode(value);
@@ -23096,6 +23970,7 @@ function sameSessionListEntries(left = [], right = []) {
23096
23970
  if (!sameContextUsage(leftEntry.contextUsage, rightEntry.contextUsage)) return false;
23097
23971
  if (Boolean(leftEntry.streaming) !== Boolean(rightEntry.streaming)) return false;
23098
23972
  if (Boolean(leftEntry.unread) !== Boolean(rightEntry.unread)) return false;
23973
+ if (Boolean(leftEntry.optimistic) !== Boolean(rightEntry.optimistic)) return false;
23099
23974
  }
23100
23975
  return true;
23101
23976
  }
@@ -23169,20 +24044,28 @@ function shouldPublishSessionState(previous, next) {
23169
24044
  if (previous === next) return false;
23170
24045
  return !sameSessionStateExceptConversation(previous, next) || !samePublishableUiState(previous.uiState, next.uiState) || previous.items.length === 0 !== (next.items.length === 0);
23171
24046
  }
23172
- function stateSyncPayloadConfirmsPendingDraft(payload, ownerKey) {
23173
- if (!ownerKey || payload.draft !== true) return false;
24047
+ function stateSyncPayloadDraftKey(payload) {
23174
24048
  return promptDraftKey({
23175
24049
  cwd: typeof payload.cwd === "string" ? payload.cwd : void 0,
23176
24050
  sessionFile: typeof payload.sessionFile === "string" ? payload.sessionFile : void 0,
23177
24051
  sessionId: typeof payload.sessionId === "string" ? payload.sessionId : void 0
23178
- }) === ownerKey;
24052
+ });
24053
+ }
24054
+ function stateSyncPayloadConfirmsPendingDraft(payload, ownerKey) {
24055
+ if (!ownerKey || payload.draft !== true) return false;
24056
+ return promptDraftKeyMatchesOwner(stateSyncPayloadDraftKey(payload), ownerKey);
24057
+ }
24058
+ function stateSyncPayloadConfirmsOptimisticDraft(payload, previousState) {
24059
+ if (payload.draft !== true) return false;
24060
+ return promptDraftKeyMatchesOwner(stateSyncPayloadDraftKey(payload), promptDraftKey(previousState));
23179
24061
  }
23180
24062
  function shouldIgnoreOptimisticDraftStateSync(options) {
23181
24063
  const previousSessionKey = options.previousState.sessionKey || "";
23182
24064
  const payloadSessionKey = typeof options.payload.sessionKey === "string" ? options.payload.sessionKey : "";
23183
24065
  if (!previousSessionKey.startsWith("optimistic:")) return false;
23184
24066
  if (!payloadSessionKey || payloadSessionKey === previousSessionKey) return false;
23185
- return !stateSyncPayloadConfirmsPendingDraft(options.payload, options.ownerKey);
24067
+ if (stateSyncPayloadConfirmsPendingDraft(options.payload, options.ownerKey) || stateSyncPayloadConfirmsOptimisticDraft(options.payload, options.previousState)) return false;
24068
+ return true;
23186
24069
  }
23187
24070
  function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadingOwnerKey, bootstrapSidebarDirectories, hideToolBlocksRef, sessionStore, sessionStateRef, composerTextRef, composerSkillRef, replaceComposerDraftRef, handleSelectSessionRef, pendingRouteSessionIdRef, pendingRouteSessionPathRef, setSessionState, setConversationItems, setHiddenThinkingPreview, setWorkingState, setCompactRunningState, setComposerContextUsage, setComposerStreaming, setSessionsEvent, setSessionDoneEvents, applySidebarSessionStatusRef, setComposerImages, setPendingMessages, pendingUiRequestHandlerRef, lastSyncedEditorTextRef }) {
23188
24071
  const queryClient = useQueryClient();
@@ -23191,6 +24074,7 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
23191
24074
  const draftSessionLoadingOwnerKeyRef = import_react.useRef(draftSessionLoadingOwnerKey);
23192
24075
  const currentSourceRef = import_react.useRef(null);
23193
24076
  const hasReceivedStateSyncRef = import_react.useRef(false);
24077
+ const [hasReceivedStateSync, setHasReceivedStateSync] = import_react.useState(false);
23194
24078
  const backgroundedAtRef = import_react.useRef(null);
23195
24079
  const wasBackgroundedRef = import_react.useRef(false);
23196
24080
  const [eventsReconnectNonce, setEventsReconnectNonce] = import_react.useState(0);
@@ -23205,8 +24089,8 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
23205
24089
  initialEventsSessionIdRef.current = sessionId;
23206
24090
  }, [sessionId]);
23207
24091
  const syncedSessionId = useSelector(sessionStore, (sessionState) => sessionState.sessionId);
23208
- const syncedSessionIdRef = import_react.useRef(syncedSessionId);
23209
24092
  const latestActivationRevisionRef = import_react.useRef(0);
24093
+ const sessionSelectNonceRef = import_react.useRef(Date.now() * 1e3);
23210
24094
  const observedRouteSessionIdRef = import_react.useRef(void 0);
23211
24095
  const syncedSessionDraft = useSelector(sessionStore, (sessionState) => sessionState.draft);
23212
24096
  import_react.useEffect(() => {
@@ -23320,7 +24204,10 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
23320
24204
  const activationRevision = payload.activationRevision;
23321
24205
  if (typeof activationRevision === "number" && activationRevision < latestActivationRevisionRef.current) return;
23322
24206
  if (typeof activationRevision === "number") latestActivationRevisionRef.current = activationRevision;
23323
- hasReceivedStateSyncRef.current = true;
24207
+ if (!hasReceivedStateSyncRef.current) {
24208
+ hasReceivedStateSyncRef.current = true;
24209
+ setHasReceivedStateSync(true);
24210
+ }
23324
24211
  const previousState = sessionStateRef.current;
23325
24212
  if (shouldIgnoreOptimisticDraftStateSync({
23326
24213
  ownerKey: draftSessionLoadingOwnerKeyRef.current,
@@ -23339,7 +24226,7 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
23339
24226
  const currentDraftOwnerKey = draftSessionLoadingOwnerKeyRef.current;
23340
24227
  const confirmedDraft = nextState.draft && !nextState.sessionKey?.startsWith("optimistic:");
23341
24228
  const previousWasOptimisticDraft = previousState.draft && previousState.sessionKey?.startsWith("optimistic:");
23342
- const draftMatchesPendingOwner = currentDraftOwnerKey ? promptDraftKey(nextState) === currentDraftOwnerKey : false;
24229
+ const draftMatchesPendingOwner = promptDraftKeyMatchesOwner(promptDraftKey(nextState), currentDraftOwnerKey);
23343
24230
  const shouldClearDraftRoute = Boolean(currentRouteSessionId && !pendingRouteSessionIdRef.current && confirmedDraft && (draftMatchesPendingOwner || previousWasOptimisticDraft));
23344
24231
  const preserveLocalPrompt = !sessionChanged && localPromptText !== previousEditorText;
23345
24232
  const nextPromptText = preserveLocalPrompt ? localPromptText : readStoredPromptDraft(nextState) ?? nextState.uiState.editorText ?? "";
@@ -23455,6 +24342,8 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
23455
24342
  import_react.useEffect(() => {
23456
24343
  if (!viewerContextId) return;
23457
24344
  hasReceivedStateSyncRef.current = false;
24345
+ setHasReceivedStateSync(false);
24346
+ latestActivationRevisionRef.current = 0;
23458
24347
  const gitInvalidationBatcher = new Batcher((items) => {
23459
24348
  const scopesByCwd = /* @__PURE__ */ new Map();
23460
24349
  for (const item of items) {
@@ -23528,21 +24417,23 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
23528
24417
  queryClient,
23529
24418
  viewerContextId
23530
24419
  ]);
23531
- import_react.useEffect(() => {
23532
- syncedSessionIdRef.current = syncedSessionId;
23533
- }, [syncedSessionId]);
23534
24420
  import_react.useEffect(() => {
23535
24421
  if (!viewerContextId || !sessionId) return;
23536
24422
  if (draftSessionLoadingOwnerKey) return;
23537
- if (!hasReceivedStateSyncRef.current) return;
23538
- if (sessionId === syncedSessionIdRef.current) return;
24423
+ if (!hasReceivedStateSync) return;
24424
+ if (sessionId === syncedSessionId) return;
23539
24425
  if (pendingRouteSessionIdRef.current !== sessionId) return;
23540
24426
  const abortController = new AbortController();
23541
24427
  const sessionPath = pendingRouteSessionPathRef.current;
24428
+ const selectionNonce = Math.max(sessionSelectNonceRef.current + 1, Date.now() * 1e3);
24429
+ sessionSelectNonceRef.current = selectionNonce;
23542
24430
  fetchJson(buildRequestUrl("/api/session/select", {
23543
24431
  contextId: viewerContextId,
23544
24432
  sessionId,
23545
- searchParams: { sessionPath }
24433
+ searchParams: {
24434
+ selectionNonce,
24435
+ sessionPath
24436
+ }
23546
24437
  }), {
23547
24438
  method: "POST",
23548
24439
  signal: abortController.signal
@@ -23555,8 +24446,10 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
23555
24446
  };
23556
24447
  }, [
23557
24448
  draftSessionLoadingOwnerKey,
24449
+ hasReceivedStateSync,
23558
24450
  pendingRouteSessionIdRef,
23559
24451
  pendingRouteSessionPathRef,
24452
+ syncedSessionId,
23560
24453
  viewerContextId,
23561
24454
  sessionId
23562
24455
  ]);
@@ -23654,13 +24547,12 @@ function useAppShellShortcuts({ addDirectoryOpenRef, commandPaletteOpenRef, comp
23654
24547
  event.preventDefault();
23655
24548
  sessionButtons[nextIndex]?.focus();
23656
24549
  };
23657
- const handleSidebarDelete = (event) => {
24550
+ const handleSidebarBackspaceDelete = (event) => {
23658
24551
  const context = getShortcutContext(event);
23659
24552
  if (context.modalOpen || isEditableTarget(event.target) && !context.targetIsSessionSearch) return;
23660
- const targetsToDelete = context.selectedSidebarSessions.length > 0 ? context.selectedSidebarSessions : context.focusedSidebarSession?.path ? [context.focusedSidebarSession] : [];
23661
- if (targetsToDelete.length === 0) return;
24553
+ if (context.selectedSidebarSessions.length <= 1) return;
23662
24554
  event.preventDefault();
23663
- shortcutActionsRef.current.openDeleteDialog(targetsToDelete);
24555
+ shortcutActionsRef.current.openDeleteDialog(context.selectedSidebarSessions);
23664
24556
  };
23665
24557
  const handleFocusPrompt = (event) => {
23666
24558
  const context = getShortcutContext(event);
@@ -23832,11 +24724,11 @@ function useAppShellShortcuts({ addDirectoryOpenRef, commandPaletteOpenRef, comp
23832
24724
  } }
23833
24725
  })),
23834
24726
  {
23835
- hotkey: "Delete",
23836
- callback: handleSidebarDelete,
24727
+ hotkey: "Backspace",
24728
+ callback: handleSidebarBackspaceDelete,
23837
24729
  options: { meta: {
23838
24730
  name: "Delete selected sidebar sessions",
23839
- description: "Open the delete dialog for focused or selected sessions."
24731
+ description: "Open the delete dialog for multiple selected sidebar sessions."
23840
24732
  } }
23841
24733
  },
23842
24734
  {
@@ -23965,6 +24857,8 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
23965
24857
  if (!composerImagesStoreRef.current) composerImagesStoreRef.current = createPicoStore([]);
23966
24858
  const composerImagesStore = composerImagesStoreRef.current;
23967
24859
  const composerImagesRef = import_react.useRef([]);
24860
+ const composerDiffLineCommentsRef = import_react.useRef([]);
24861
+ const [composerDiffLineComments, setComposerDiffLineCommentsState] = import_react.useState([]);
23968
24862
  const displaySettingsStoreRef = import_react.useRef(null);
23969
24863
  if (!displaySettingsStoreRef.current) displaySettingsStoreRef.current = createPicoStore({
23970
24864
  autoScrollEnabled: true,
@@ -24070,6 +24964,18 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
24070
24964
  const composerStoreRef = import_react.useRef(null);
24071
24965
  if (!composerStoreRef.current) composerStoreRef.current = createPicoStore(createInitialAppShellComposerSnapshot(viewerContextId), sameAppShellComposerSnapshot);
24072
24966
  const composerStore = composerStoreRef.current;
24967
+ const setComposerDiffLineComments = import_react.useCallback((action) => {
24968
+ const current = composerDiffLineCommentsRef.current;
24969
+ const next = applyStoreAction(current, action);
24970
+ if (next === current) return;
24971
+ composerDiffLineCommentsRef.current = next;
24972
+ setComposerDiffLineCommentsState(next);
24973
+ const currentComposerSnapshot = composerStore.state;
24974
+ setStoreState(composerStore, {
24975
+ ...currentComposerSnapshot,
24976
+ composerDiffLineComments: currentComposerSnapshot.disabled ? [] : next
24977
+ });
24978
+ }, [composerStore]);
24073
24979
  const contextUsageStoreRef = import_react.useRef(null);
24074
24980
  if (!contextUsageStoreRef.current) contextUsageStoreRef.current = createPicoStore(void 0);
24075
24981
  const contextUsageStore = contextUsageStoreRef.current;
@@ -24217,8 +25123,8 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
24217
25123
  ...currentComposerSnapshot,
24218
25124
  awaitingFirstTurn: currentComposerSnapshot.disabled ? false : next
24219
25125
  });
24220
- if (next && !sessionStateRef.current.streaming) setStoreState(workingStateStore, { label: "Waiting for first response…" });
24221
- else if (!next && workingStateStore.state?.label === "Waiting for first response…") setStoreState(workingStateStore, null);
25126
+ if (next && !sessionStateRef.current.streaming) setStoreState(workingStateStore, { label: "" });
25127
+ else if (!next && workingStateStore.state?.label === "") setStoreState(workingStateStore, null);
24222
25128
  });
24223
25129
  }, [
24224
25130
  awaitingFirstTurnStore,
@@ -24979,6 +25885,64 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
24979
25885
  });
24980
25886
  };
24981
25887
  const prefetchDirectorySessionsIndex = import_react.useCallback((_directory) => {}, []);
25888
+ const addOptimisticSidebarSession = import_react.useCallback((options) => {
25889
+ const currentState = sessionStateRef.current;
25890
+ const optimisticId = (options?.id || currentState.sessionKey || `optimistic:${promptDraftKey(currentState)}`).trim();
25891
+ const cwd = (options?.cwd || currentState.cwd || defaultNewSessionDirectory).trim();
25892
+ if (!optimisticId || !cwd) return void 0;
25893
+ const modified = (/* @__PURE__ */ new Date()).toISOString();
25894
+ const entry = {
25895
+ id: optimisticId,
25896
+ cwd,
25897
+ title: "New session",
25898
+ modified,
25899
+ lastUserMessageAt: modified,
25900
+ streaming: true,
25901
+ unread: false,
25902
+ optimistic: true
25903
+ };
25904
+ const entryKey = sessionListEntryKey(entry);
25905
+ sidebarStore.setSidebarDirectories((current) => {
25906
+ const normalizedCurrent = normalizeStoredDirectoryList(current);
25907
+ if (normalizedCurrent.includes(cwd)) return current;
25908
+ const nextDirectories = normalizeStoredDirectoryList([cwd, ...normalizedCurrent]);
25909
+ safeLocalStorageSetItem(SIDEBAR_DIRECTORIES_STORAGE_KEY, JSON.stringify(nextDirectories));
25910
+ return nextDirectories;
25911
+ });
25912
+ const optimisticEntry = {
25913
+ ...entry,
25914
+ id: optimisticId,
25915
+ cwd
25916
+ };
25917
+ sidebarStore.setDirectoryIndexDataByPath((current) => upsertOptimisticSidebarSessionEntry(current, optimisticEntry));
25918
+ sidebarStore.setSessionsEvent((current) => current ? {
25919
+ ...current,
25920
+ directoryIndexes: upsertOptimisticSidebarSessionEntry(current.directoryIndexes || {}, optimisticEntry)
25921
+ } : current);
25922
+ if (entryKey) {
25923
+ sidebarStore.setSelectedSidebarSessionKeys((current) => sameStringArray$3(current, [entryKey]) ? current : [entryKey]);
25924
+ sidebarStore.setSidebarSessionSelectionAnchor((current) => current === entryKey ? current : entryKey);
25925
+ }
25926
+ return optimisticId;
25927
+ }, [
25928
+ defaultNewSessionDirectory,
25929
+ sessionStateRef,
25930
+ sidebarStore
25931
+ ]);
25932
+ const removeOptimisticSidebarSession = import_react.useCallback((optimisticId) => {
25933
+ const targetId = optimisticId?.trim() || "";
25934
+ if (!targetId) return;
25935
+ const entryKey = sessionListEntryKey({ id: targetId });
25936
+ sidebarStore.setDirectoryIndexDataByPath((current) => removeOptimisticSidebarSessionEntry(current, targetId));
25937
+ sidebarStore.setSessionsEvent((current) => current?.directoryIndexes ? {
25938
+ ...current,
25939
+ directoryIndexes: removeOptimisticSidebarSessionEntry(current.directoryIndexes, targetId)
25940
+ } : current);
25941
+ if (entryKey) {
25942
+ sidebarStore.setSelectedSidebarSessionKeys((current) => current.filter((key) => key !== entryKey));
25943
+ sidebarStore.setSidebarSessionSelectionAnchor((current) => current === entryKey ? "" : current);
25944
+ }
25945
+ }, [sidebarStore]);
24982
25946
  const clearSelectedSidebarSelection = import_react.useCallback(() => {
24983
25947
  sidebarStore.setSelectedSidebarSessionKeys((current) => current.length === 0 ? current : []);
24984
25948
  sidebarStore.setSidebarSessionSelectionAnchor((current) => current ? "" : current);
@@ -24999,6 +25963,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
24999
25963
  pendingDraftFollowUps,
25000
25964
  awaitingFirstTurn,
25001
25965
  pendingMessages,
25966
+ composerDiffLineCommentsRef,
25002
25967
  composerImagesRef,
25003
25968
  composerTextRef,
25004
25969
  composerSkillRef,
@@ -25008,6 +25973,8 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25008
25973
  prefetchDirectorySessionsIndex,
25009
25974
  addOptimisticUserMessage,
25010
25975
  removeOptimisticUserMessage,
25976
+ addOptimisticSidebarSession,
25977
+ removeOptimisticSidebarSession,
25011
25978
  setSidebarDirectories: sidebarStore.setSidebarDirectories,
25012
25979
  setStoredDraftDirectory,
25013
25980
  setDraftSessionLoadingOwnerKey,
@@ -25016,6 +25983,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25016
25983
  setPendingMessages,
25017
25984
  setAwaitingFirstTurn,
25018
25985
  setIsSubmitting,
25986
+ setComposerDiffLineComments,
25019
25987
  setComposerImages
25020
25988
  });
25021
25989
  const switchEmptyDraftDirectory = import_react.useCallback((directory) => {
@@ -25060,6 +26028,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25060
26028
  const ownerKey = promptDraftKey({ cwd: nextCwd });
25061
26029
  const optimisticSessionKey = `optimistic:${ownerKey}`;
25062
26030
  const shouldCloseMobileSidebar = Boolean(options?.closeMobileSidebar) && isMobile && openMobile;
26031
+ const createSessionPromise = requestCreateSession(nextCwd);
25063
26032
  handleSelectSession(void 0);
25064
26033
  clearSelectedSidebarSelection();
25065
26034
  if (shouldCloseMobileSidebar) {
@@ -25076,7 +26045,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25076
26045
  sessionStateRef.current = nextState;
25077
26046
  conversationItemsStore.setItems(nextState.items);
25078
26047
  setSessionState(nextState);
25079
- if (await requestCreateSession(nextCwd)) return;
26048
+ if (await createSessionPromise) return;
25080
26049
  if (sessionStateRef.current.sessionKey !== optimisticSessionKey) return;
25081
26050
  sessionStateRef.current = previousState;
25082
26051
  conversationItemsStore.setItems(previousState.items);
@@ -25096,6 +26065,22 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25096
26065
  setPendingMessages,
25097
26066
  setSessionState
25098
26067
  ]);
26068
+ const createDiffLineCommentId = () => {
26069
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return `diff-comment:${crypto.randomUUID()}`;
26070
+ return `diff-comment:${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
26071
+ };
26072
+ const addComposerDiffLineComment = (comment) => {
26073
+ const text = comment.text.trim();
26074
+ const cwd = sessionStateRef.current.cwd?.trim() || "";
26075
+ if (!text || !cwd) return;
26076
+ setComposerDiffLineComments((current) => [...current, {
26077
+ ...comment,
26078
+ id: createDiffLineCommentId(),
26079
+ cwd,
26080
+ text
26081
+ }]);
26082
+ composerPanelRef.current?.focusPrompt({ preventScroll: true });
26083
+ };
25099
26084
  const onPickImages = async (files) => {
25100
26085
  if (!files || files.length === 0) return;
25101
26086
  const imageFiles = Array.from(files).filter((file) => file.type.startsWith("image/"));
@@ -25113,6 +26098,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25113
26098
  }));
25114
26099
  const currentPendingMessages = pendingDraftFollowUpMessages.length > 0 || pendingMessages.length > 0 ? [...pendingDraftFollowUpMessages, ...pendingMessages] : EMPTY_COMPOSER_PENDING_MESSAGES;
25115
26100
  const composerDisabled = isSessionViewLoading;
26101
+ const displayedDiffLineComments = composerDisabled ? EMPTY_COMPOSER_DIFF_LINE_COMMENTS : composerDiffLineComments;
25116
26102
  const displayedPendingMessages = composerDisabled ? EMPTY_COMPOSER_PENDING_MESSAGES : currentPendingMessages;
25117
26103
  const displayedComposerImages = composerDisabled ? EMPTY_COMPOSER_IMAGES : composerImages;
25118
26104
  const displayedComposerText = composerDisabled ? "" : composerDraftSeed.text;
@@ -25395,8 +26381,8 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25395
26381
  const handleThemeChange = (value) => {
25396
26382
  setThemeFamily(value);
25397
26383
  };
25398
- const handleThemePreviewChange = (value) => {
25399
- previewThemeFamily(value);
26384
+ const handleThemePreviewChange = (value, colorMode) => {
26385
+ previewThemeFamily(value, colorMode);
25400
26386
  };
25401
26387
  const handleThemeColorModeChange = (value) => {
25402
26388
  setColorMode(value);
@@ -25405,6 +26391,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25405
26391
  activeSessionId,
25406
26392
  awaitingFirstTurn: composerDisabled ? false : awaitingFirstTurn,
25407
26393
  centerMessages: displaySettingsRef.current.centerMessages,
26394
+ composerDiffLineComments: displayedDiffLineComments,
25408
26395
  composerImages: displayedComposerImages,
25409
26396
  composerSkill: displayedComposerSkill,
25410
26397
  composerSyncNonce: composerDraftSeed.syncNonce,
@@ -25421,6 +26408,9 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25421
26408
  const composerActionsRef = useLatestRef({
25422
26409
  abortSession,
25423
26410
  onPickImages,
26411
+ removeDiffLineComment: (id) => {
26412
+ setComposerDiffLineComments((current) => current.filter((comment) => comment.id !== id));
26413
+ },
25424
26414
  onRemoveComposerImage: (index) => {
25425
26415
  setComposerImages((current) => current.filter((_, imageIndex) => imageIndex !== index));
25426
26416
  },
@@ -26012,6 +27002,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
26012
27002
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellTabsController, {
26013
27003
  actionsRef: composerActionsRef,
26014
27004
  appUiStore,
27005
+ composerDiffLineComments: displayedDiffLineComments,
26015
27006
  composerPanelRef,
26016
27007
  contextUsageStore,
26017
27008
  conversationFrameRef,
@@ -26034,6 +27025,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
26034
27025
  onCloseFileViewTab: closeFileViewTab,
26035
27026
  onCloseFileViewTabsToRight: closeFileViewTabsToRight,
26036
27027
  onCloseOtherFileViewTabs: closeOtherFileViewTabs,
27028
+ onAddDiffLineComment: addComposerDiffLineComment,
26037
27029
  onOpenFileViewTab: openFileViewTab,
26038
27030
  onReorderFileViewTabs: reorderFileViewTabs,
26039
27031
  rightSidebarStore,