@paperclipai/plugin-workspace-diff 0.1.0 → 2026.620.0-canary.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Paperclip AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -192,6 +192,7 @@ export declare const workspaceDiffFileSchema: z.ZodObject<{
192
192
  message: string;
193
193
  path: string | null;
194
194
  }[];
195
+ sizeBytes: number | null;
195
196
  untracked: boolean;
196
197
  staged: boolean;
197
198
  unstaged: boolean;
@@ -201,7 +202,6 @@ export declare const workspaceDiffFileSchema: z.ZodObject<{
201
202
  oversized: boolean;
202
203
  truncated: boolean;
203
204
  oldPath: string | null;
204
- sizeBytes: number | null;
205
205
  patches: {
206
206
  kind: "head" | "untracked" | "staged" | "unstaged";
207
207
  warnings: {
@@ -224,6 +224,7 @@ export declare const workspaceDiffFileSchema: z.ZodObject<{
224
224
  message: string;
225
225
  path: string | null;
226
226
  }[];
227
+ sizeBytes: number | null;
227
228
  untracked: boolean;
228
229
  staged: boolean;
229
230
  unstaged: boolean;
@@ -233,7 +234,6 @@ export declare const workspaceDiffFileSchema: z.ZodObject<{
233
234
  oversized: boolean;
234
235
  truncated: boolean;
235
236
  oldPath: string | null;
236
- sizeBytes: number | null;
237
237
  patches: {
238
238
  kind: "head" | "untracked" | "staged" | "unstaged";
239
239
  warnings: {
@@ -371,6 +371,7 @@ export declare const workspaceDiffResponseSchema: z.ZodObject<{
371
371
  message: string;
372
372
  path: string | null;
373
373
  }[];
374
+ sizeBytes: number | null;
374
375
  untracked: boolean;
375
376
  staged: boolean;
376
377
  unstaged: boolean;
@@ -380,7 +381,6 @@ export declare const workspaceDiffResponseSchema: z.ZodObject<{
380
381
  oversized: boolean;
381
382
  truncated: boolean;
382
383
  oldPath: string | null;
383
- sizeBytes: number | null;
384
384
  patches: {
385
385
  kind: "head" | "untracked" | "staged" | "unstaged";
386
386
  warnings: {
@@ -403,6 +403,7 @@ export declare const workspaceDiffResponseSchema: z.ZodObject<{
403
403
  message: string;
404
404
  path: string | null;
405
405
  }[];
406
+ sizeBytes: number | null;
406
407
  untracked: boolean;
407
408
  staged: boolean;
408
409
  unstaged: boolean;
@@ -412,7 +413,6 @@ export declare const workspaceDiffResponseSchema: z.ZodObject<{
412
413
  oversized: boolean;
413
414
  truncated: boolean;
414
415
  oldPath: string | null;
415
- sizeBytes: number | null;
416
416
  patches: {
417
417
  kind: "head" | "untracked" | "staged" | "unstaged";
418
418
  warnings: {
@@ -506,6 +506,7 @@ export declare const workspaceDiffResponseSchema: z.ZodObject<{
506
506
  message: string;
507
507
  path: string | null;
508
508
  }[];
509
+ sizeBytes: number | null;
509
510
  untracked: boolean;
510
511
  staged: boolean;
511
512
  unstaged: boolean;
@@ -515,7 +516,6 @@ export declare const workspaceDiffResponseSchema: z.ZodObject<{
515
516
  oversized: boolean;
516
517
  truncated: boolean;
517
518
  oldPath: string | null;
518
- sizeBytes: number | null;
519
519
  patches: {
520
520
  kind: "head" | "untracked" | "staged" | "unstaged";
521
521
  warnings: {
@@ -571,6 +571,7 @@ export declare const workspaceDiffResponseSchema: z.ZodObject<{
571
571
  message: string;
572
572
  path: string | null;
573
573
  }[];
574
+ sizeBytes: number | null;
574
575
  untracked: boolean;
575
576
  staged: boolean;
576
577
  unstaged: boolean;
@@ -580,7 +581,6 @@ export declare const workspaceDiffResponseSchema: z.ZodObject<{
580
581
  oversized: boolean;
581
582
  truncated: boolean;
582
583
  oldPath: string | null;
583
- sizeBytes: number | null;
584
584
  patches: {
585
585
  kind: "head" | "untracked" | "staged" | "unstaged";
586
586
  warnings: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AA8NvE,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,OAAO,GACR,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,2CA6BA;AAqFD,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,oBAAoB,2CAkR3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAgSvE,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,OAAO,GACR,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,2CA6BA;AAuFD,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,oBAAoB,2CA6Z3D"}
package/dist/ui/index.js CHANGED
@@ -24845,7 +24845,14 @@ function mergeFileDiffOptions({ options, hasCustomHeader, hasGutterRenderUtility
24845
24845
  }
24846
24846
 
24847
24847
  // src/ui/index.tsx
24848
- import { createElement, useCallback as useCallback4, useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState3 } from "react";
24848
+ import {
24849
+ createElement,
24850
+ useCallback as useCallback4,
24851
+ useEffect as useEffect3,
24852
+ useMemo,
24853
+ useRef as useRef3,
24854
+ useState as useState3
24855
+ } from "react";
24849
24856
 
24850
24857
  // src/diff-model.ts
24851
24858
  var STATUS_LABELS = {
@@ -24932,6 +24939,11 @@ function initialExpandedFileSet(files) {
24932
24939
 
24933
24940
  // src/ui/index.tsx
24934
24941
  import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
24942
+ var DEFAULT_FILE_SIDEBAR_WIDTH = 280;
24943
+ var MIN_FILE_SIDEBAR_WIDTH = 220;
24944
+ var MAX_FILE_SIDEBAR_WIDTH = 520;
24945
+ var FILE_SIDEBAR_WIDTH_STEP = 16;
24946
+ var FILE_SIDEBAR_WIDTH_STORAGE_KEY = "paperclip.workspace-diff.files-sidebar-width";
24935
24947
  function makeLucideIcon(paths) {
24936
24948
  return function LucideIcon({ size = 16 }) {
24937
24949
  return /* @__PURE__ */ jsx3(
@@ -24962,6 +24974,10 @@ function readInitialView() {
24962
24974
  if (typeof window === "undefined") return "working-tree";
24963
24975
  return new URLSearchParams(window.location.search).get("diffView") === "head" ? "head" : "working-tree";
24964
24976
  }
24977
+ function hasInitialViewParam() {
24978
+ if (typeof window === "undefined") return false;
24979
+ return new URLSearchParams(window.location.search).has("diffView");
24980
+ }
24965
24981
  function readInitialBaseRef() {
24966
24982
  if (typeof window === "undefined") return "";
24967
24983
  return new URLSearchParams(window.location.search).get("baseRef") ?? "";
@@ -24978,6 +24994,41 @@ function iconButtonClass(active = false) {
24978
24994
  active ? "border-foreground/20 bg-foreground text-background" : "border-border bg-background text-muted-foreground hover:text-foreground"
24979
24995
  ].join(" ");
24980
24996
  }
24997
+ function clampFileSidebarWidth(width) {
24998
+ return Math.min(MAX_FILE_SIDEBAR_WIDTH, Math.max(MIN_FILE_SIDEBAR_WIDTH, width));
24999
+ }
25000
+ function readStoredFileSidebarWidth() {
25001
+ if (typeof window === "undefined") return DEFAULT_FILE_SIDEBAR_WIDTH;
25002
+ try {
25003
+ const stored = window.localStorage.getItem(FILE_SIDEBAR_WIDTH_STORAGE_KEY);
25004
+ if (!stored) return DEFAULT_FILE_SIDEBAR_WIDTH;
25005
+ const parsed = Number.parseInt(stored, 10);
25006
+ return Number.isFinite(parsed) ? clampFileSidebarWidth(parsed) : DEFAULT_FILE_SIDEBAR_WIDTH;
25007
+ } catch {
25008
+ return DEFAULT_FILE_SIDEBAR_WIDTH;
25009
+ }
25010
+ }
25011
+ function writeStoredFileSidebarWidth(width) {
25012
+ if (typeof window === "undefined") return;
25013
+ try {
25014
+ window.localStorage.setItem(FILE_SIDEBAR_WIDTH_STORAGE_KEY, String(clampFileSidebarWidth(width)));
25015
+ } catch {
25016
+ }
25017
+ }
25018
+ function useIsDesktopDiffLayout() {
25019
+ const [isDesktop, setIsDesktop] = useState3(() => {
25020
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
25021
+ return window.matchMedia("(min-width: 1024px)").matches;
25022
+ });
25023
+ useEffect3(() => {
25024
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
25025
+ const query = window.matchMedia("(min-width: 1024px)");
25026
+ const update = () => setIsDesktop(query.matches);
25027
+ query.addEventListener("change", update);
25028
+ return () => query.removeEventListener("change", update);
25029
+ }, []);
25030
+ return isDesktop;
25031
+ }
24981
25032
  function warningText(file) {
24982
25033
  if (file.binary) return "Binary file";
24983
25034
  if (file.oversized) return "Too large to render";
@@ -25127,7 +25178,8 @@ function ErrorState({
25127
25178
  }
25128
25179
  function FileDiffPanel({
25129
25180
  file,
25130
- mode
25181
+ mode,
25182
+ lineWrap
25131
25183
  }) {
25132
25184
  const warning = warningText(file);
25133
25185
  if (warning) {
@@ -25147,7 +25199,7 @@ function FileDiffPanel({
25147
25199
  patch: patch.patch,
25148
25200
  options: {
25149
25201
  diffStyle: mode,
25150
- overflow: "scroll",
25202
+ overflow: lineWrap ? "wrap" : "scroll",
25151
25203
  disableLineNumbers: false,
25152
25204
  themeType: "system"
25153
25205
  }
@@ -25182,24 +25234,37 @@ function CollapsedFilePanel({
25182
25234
  function ChangesTab({ context }) {
25183
25235
  const toast = usePluginToast();
25184
25236
  const [mode, setMode] = useState3("split");
25237
+ const [lineWrap, setLineWrap] = useState3(false);
25185
25238
  const [view, setView] = useState3(() => readInitialView());
25186
25239
  const [baseRef, setBaseRef] = useState3(() => readInitialBaseRef());
25187
25240
  const baseRefTouchedRef = useRef3(Boolean(baseRef.trim()));
25241
+ const viewTouchedRef = useRef3(hasInitialViewParam());
25188
25242
  const [includeUntracked, setIncludeUntracked] = useState3(false);
25189
25243
  const [expandedFiles, setExpandedFiles] = useState3(() => /* @__PURE__ */ new Set());
25190
25244
  const [selectedPath, setSelectedPath] = useState3(null);
25245
+ const [fileSidebarWidth, setFileSidebarWidth] = useState3(() => readStoredFileSidebarWidth());
25246
+ const [fileSidebarResizing, setFileSidebarResizing] = useState3(false);
25247
+ const fileSidebarWidthRef = useRef3(fileSidebarWidth);
25248
+ const fileSidebarDragRef = useRef3(null);
25191
25249
  const fileSectionRefs = useRef3(/* @__PURE__ */ new Map());
25192
25250
  const diffScrollRef = useRef3(null);
25193
25251
  const scrollSyncFrameRef = useRef3(null);
25252
+ const usesDesktopDiffLayout = useIsDesktopDiffLayout();
25253
+ const requestedBaseRef = baseRef.trim();
25254
+ const effectiveView = view === "head" && !requestedBaseRef ? "working-tree" : view;
25255
+ const fileSidebarStyle = useMemo(
25256
+ () => usesDesktopDiffLayout ? { width: `${fileSidebarWidth}px` } : void 0,
25257
+ [fileSidebarWidth, usesDesktopDiffLayout]
25258
+ );
25194
25259
  const params = useMemo(() => ({
25195
25260
  workspaceId: context.entityId,
25196
25261
  companyId: context.companyId ?? "",
25197
25262
  projectId: context.projectId ?? "",
25198
25263
  entityType: context.entityType,
25199
- view,
25200
- baseRef: baseRef.trim() || null,
25264
+ view: effectiveView,
25265
+ baseRef: requestedBaseRef || null,
25201
25266
  includeUntracked
25202
- }), [baseRef, context.companyId, context.entityId, context.entityType, context.projectId, includeUntracked, view]);
25267
+ }), [context.companyId, context.entityId, context.entityType, context.projectId, effectiveView, includeUntracked, requestedBaseRef]);
25203
25268
  const { data, loading, error, refresh } = usePluginData("workspace-diff", params);
25204
25269
  const files = useMemo(() => toFileViewModels(data), [data]);
25205
25270
  const summary = useMemo(() => diffSummary(data), [data]);
@@ -25244,11 +25309,61 @@ function ChangesTab({ context }) {
25244
25309
  syncSelectedPathFromScroll();
25245
25310
  });
25246
25311
  }, [syncSelectedPathFromScroll]);
25312
+ const commitFileSidebarWidth = useCallback4((nextWidth) => {
25313
+ const clamped = clampFileSidebarWidth(nextWidth);
25314
+ fileSidebarWidthRef.current = clamped;
25315
+ setFileSidebarWidth(clamped);
25316
+ writeStoredFileSidebarWidth(clamped);
25317
+ }, []);
25318
+ const handleFileSidebarPointerDown = useCallback4((event) => {
25319
+ if (!usesDesktopDiffLayout) return;
25320
+ event.preventDefault();
25321
+ event.currentTarget.setPointerCapture(event.pointerId);
25322
+ fileSidebarDragRef.current = {
25323
+ startX: event.clientX,
25324
+ startWidth: fileSidebarWidthRef.current
25325
+ };
25326
+ setFileSidebarResizing(true);
25327
+ }, [usesDesktopDiffLayout]);
25328
+ const handleFileSidebarPointerMove = useCallback4((event) => {
25329
+ const drag = fileSidebarDragRef.current;
25330
+ if (!drag) return;
25331
+ const nextWidth = clampFileSidebarWidth(drag.startWidth + event.clientX - drag.startX);
25332
+ fileSidebarWidthRef.current = nextWidth;
25333
+ setFileSidebarWidth(nextWidth);
25334
+ }, []);
25335
+ const endFileSidebarResize = useCallback4(() => {
25336
+ if (!fileSidebarDragRef.current) return;
25337
+ fileSidebarDragRef.current = null;
25338
+ setFileSidebarResizing(false);
25339
+ writeStoredFileSidebarWidth(fileSidebarWidthRef.current);
25340
+ }, []);
25341
+ const handleFileSidebarKeyDown = useCallback4((event) => {
25342
+ if (!usesDesktopDiffLayout) return;
25343
+ if (event.key === "ArrowLeft") {
25344
+ event.preventDefault();
25345
+ commitFileSidebarWidth(fileSidebarWidth - FILE_SIDEBAR_WIDTH_STEP);
25346
+ } else if (event.key === "ArrowRight") {
25347
+ event.preventDefault();
25348
+ commitFileSidebarWidth(fileSidebarWidth + FILE_SIDEBAR_WIDTH_STEP);
25349
+ } else if (event.key === "Home") {
25350
+ event.preventDefault();
25351
+ commitFileSidebarWidth(MIN_FILE_SIDEBAR_WIDTH);
25352
+ } else if (event.key === "End") {
25353
+ event.preventDefault();
25354
+ commitFileSidebarWidth(MAX_FILE_SIDEBAR_WIDTH);
25355
+ }
25356
+ }, [commitFileSidebarWidth, fileSidebarWidth, usesDesktopDiffLayout]);
25247
25357
  useEffect3(() => {
25248
25358
  const defaultBaseRef = data?.defaultBaseRef?.trim();
25249
- if (!defaultBaseRef || baseRef.trim() || baseRefTouchedRef.current) return;
25250
- setBaseRef(defaultBaseRef);
25251
- }, [baseRef, data?.defaultBaseRef]);
25359
+ if (!defaultBaseRef) return;
25360
+ if (!baseRef.trim() && !baseRefTouchedRef.current) {
25361
+ setBaseRef(defaultBaseRef);
25362
+ }
25363
+ if (view === "working-tree" && !viewTouchedRef.current) {
25364
+ setView("head");
25365
+ }
25366
+ }, [baseRef, data?.defaultBaseRef, view]);
25252
25367
  useEffect3(() => {
25253
25368
  if (files.length === 0) {
25254
25369
  setExpandedFiles(/* @__PURE__ */ new Set());
@@ -25265,6 +25380,17 @@ function ChangesTab({ context }) {
25265
25380
  }
25266
25381
  };
25267
25382
  }, []);
25383
+ useEffect3(() => {
25384
+ if (!fileSidebarResizing || typeof document === "undefined") return;
25385
+ const previousCursor = document.body.style.cursor;
25386
+ const previousUserSelect = document.body.style.userSelect;
25387
+ document.body.style.cursor = "col-resize";
25388
+ document.body.style.userSelect = "none";
25389
+ return () => {
25390
+ document.body.style.cursor = previousCursor;
25391
+ document.body.style.userSelect = previousUserSelect;
25392
+ };
25393
+ }, [fileSidebarResizing]);
25268
25394
  const copyPath = async (filePath) => {
25269
25395
  try {
25270
25396
  await navigator.clipboard.writeText(filePath);
@@ -25292,9 +25418,45 @@ function ChangesTab({ context }) {
25292
25418
  /* @__PURE__ */ jsx3("button", { type: "button", className: buttonClass(mode === "split"), onClick: () => setMode("split"), children: "Split" }, "split"),
25293
25419
  /* @__PURE__ */ jsx3("button", { type: "button", className: buttonClass(mode === "unified"), onClick: () => setMode("unified"), children: "Unified" }, "unified")
25294
25420
  ] }, "layout"),
25421
+ /* @__PURE__ */ jsx3(
25422
+ "button",
25423
+ {
25424
+ type: "button",
25425
+ className: buttonClass(lineWrap),
25426
+ onClick: () => setLineWrap((value) => !value),
25427
+ title: lineWrap ? "Disable line wrapping" : "Enable line wrapping",
25428
+ "aria-pressed": lineWrap,
25429
+ children: lineWrap ? "Wrap on" : "Wrap lines"
25430
+ },
25431
+ "line-wrap"
25432
+ ),
25295
25433
  /* @__PURE__ */ jsxs("div", { className: "inline-flex gap-1", "aria-label": "Diff comparison", children: [
25296
- /* @__PURE__ */ jsx3("button", { type: "button", className: buttonClass(view === "working-tree"), onClick: () => setView("working-tree"), children: "Working tree" }, "working-tree"),
25297
- /* @__PURE__ */ jsx3("button", { type: "button", className: buttonClass(view === "head"), onClick: () => setView("head"), children: "Against ref" }, "head")
25434
+ /* @__PURE__ */ jsx3(
25435
+ "button",
25436
+ {
25437
+ type: "button",
25438
+ className: buttonClass(effectiveView === "working-tree"),
25439
+ onClick: () => {
25440
+ viewTouchedRef.current = true;
25441
+ setView("working-tree");
25442
+ },
25443
+ children: "Working tree"
25444
+ },
25445
+ "working-tree"
25446
+ ),
25447
+ /* @__PURE__ */ jsx3(
25448
+ "button",
25449
+ {
25450
+ type: "button",
25451
+ className: buttonClass(effectiveView === "head"),
25452
+ onClick: () => {
25453
+ viewTouchedRef.current = true;
25454
+ setView("head");
25455
+ },
25456
+ children: "Against ref"
25457
+ },
25458
+ "head"
25459
+ )
25298
25460
  ] }, "view"),
25299
25461
  view === "head" ? /* @__PURE__ */ jsx3(
25300
25462
  "input",
@@ -25334,27 +25496,59 @@ function ChangesTab({ context }) {
25334
25496
  )
25335
25497
  ] }, "actions")
25336
25498
  ] }, "toolbar"),
25337
- loading ? /* @__PURE__ */ jsx3(LoadingState, {}) : error ? /* @__PURE__ */ jsx3(ErrorState, { message: error.message, onRetry: refresh }) : files.length === 0 ? /* @__PURE__ */ jsx3(EmptyState, {}) : /* @__PURE__ */ jsxs("div", { className: "grid gap-3 lg:h-[70vh] lg:min-h-[560px] lg:max-h-[820px] lg:grid-cols-[280px_minmax(0,1fr)]", children: [
25338
- /* @__PURE__ */ jsxs("aside", { className: "flex min-w-0 flex-col border border-border bg-background lg:h-full lg:overflow-hidden", children: [
25339
- /* @__PURE__ */ jsx3("div", { className: "border-b border-border px-3 py-2 text-xs font-medium uppercase tracking-[0.14em] text-muted-foreground", children: "Files" }, "heading"),
25340
- /* @__PURE__ */ jsx3("div", { className: "max-h-[70vh] overflow-auto lg:max-h-none lg:flex-1", children: files.map((file, index) => /* @__PURE__ */ jsx3(
25341
- FileRow,
25342
- {
25343
- file,
25344
- active: file.path === selectedFile?.path,
25345
- expanded: expandedFiles.has(file.path),
25346
- onSelect: () => selectFile(file.path),
25347
- onToggle: () => setExpandedFiles((current) => nextExpandedFileSet(current, file.path)),
25348
- onCopy: () => void copyPath(file.path)
25349
- },
25350
- `${file.path}:${index}`
25351
- )) }, "list")
25352
- ] }, "files"),
25499
+ loading ? /* @__PURE__ */ jsx3(LoadingState, {}) : error ? /* @__PURE__ */ jsx3(ErrorState, { message: error.message, onRetry: refresh }) : files.length === 0 ? /* @__PURE__ */ jsx3(EmptyState, {}) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 lg:h-[70vh] lg:min-h-[560px] lg:max-h-[820px] lg:flex-row", children: [
25500
+ /* @__PURE__ */ jsxs(
25501
+ "aside",
25502
+ {
25503
+ className: "relative flex min-w-0 flex-col border border-border bg-background lg:h-full lg:shrink-0 lg:overflow-hidden",
25504
+ style: fileSidebarStyle,
25505
+ children: [
25506
+ /* @__PURE__ */ jsx3("div", { className: "border-b border-border px-3 py-2 text-xs font-medium uppercase tracking-[0.14em] text-muted-foreground", children: "Files" }, "heading"),
25507
+ /* @__PURE__ */ jsx3("div", { className: "max-h-[70vh] overflow-auto lg:max-h-none lg:flex-1", children: files.map((file, index) => /* @__PURE__ */ jsx3(
25508
+ FileRow,
25509
+ {
25510
+ file,
25511
+ active: file.path === selectedFile?.path,
25512
+ expanded: expandedFiles.has(file.path),
25513
+ onSelect: () => selectFile(file.path),
25514
+ onToggle: () => setExpandedFiles((current) => nextExpandedFileSet(current, file.path)),
25515
+ onCopy: () => void copyPath(file.path)
25516
+ },
25517
+ `${file.path}:${index}`
25518
+ )) }, "list"),
25519
+ /* @__PURE__ */ jsx3(
25520
+ "div",
25521
+ {
25522
+ role: "separator",
25523
+ "aria-label": "Resize file list",
25524
+ "aria-orientation": "vertical",
25525
+ "aria-valuemin": MIN_FILE_SIDEBAR_WIDTH,
25526
+ "aria-valuemax": MAX_FILE_SIDEBAR_WIDTH,
25527
+ "aria-valuenow": fileSidebarWidth,
25528
+ tabIndex: 0,
25529
+ className: [
25530
+ "absolute inset-y-0 right-0 z-20 hidden w-3 cursor-col-resize touch-none outline-none lg:block",
25531
+ "before:absolute before:inset-y-0 before:left-1/2 before:w-px before:-translate-x-1/2 before:bg-transparent before:transition-colors",
25532
+ "hover:before:bg-border focus-visible:before:bg-ring",
25533
+ fileSidebarResizing ? "before:bg-ring" : ""
25534
+ ].join(" "),
25535
+ onPointerDown: handleFileSidebarPointerDown,
25536
+ onPointerMove: handleFileSidebarPointerMove,
25537
+ onPointerUp: endFileSidebarResize,
25538
+ onPointerCancel: endFileSidebarResize,
25539
+ onLostPointerCapture: endFileSidebarResize,
25540
+ onKeyDown: handleFileSidebarKeyDown
25541
+ }
25542
+ )
25543
+ ]
25544
+ },
25545
+ "files"
25546
+ ),
25353
25547
  /* @__PURE__ */ jsx3(
25354
25548
  "main",
25355
25549
  {
25356
25550
  ref: diffScrollRef,
25357
- className: "max-h-[70vh] min-w-0 space-y-3 overflow-auto lg:h-full lg:max-h-none lg:pr-1",
25551
+ className: "max-h-[70vh] min-w-0 flex-1 space-y-3 overflow-auto lg:h-full lg:max-h-none lg:pr-1",
25358
25552
  onScroll: handleDiffScroll,
25359
25553
  children: files.map((file, index) => /* @__PURE__ */ jsxs(
25360
25554
  "section",
@@ -25362,51 +25556,58 @@ function ChangesTab({ context }) {
25362
25556
  ref: setFileSectionRef(file.path),
25363
25557
  className: file.path === selectedFile?.path ? "scroll-mt-2" : void 0,
25364
25558
  children: [
25365
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center justify-between gap-3 border border-b-0 border-border bg-muted/35 px-3 py-2", children: [
25366
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
25367
- /* @__PURE__ */ jsx3(
25368
- "button",
25369
- {
25370
- type: "button",
25371
- className: "mt-0.5 text-muted-foreground hover:text-foreground",
25372
- title: expandedFiles.has(file.path) ? "Collapse file" : "Expand file",
25373
- "aria-label": expandedFiles.has(file.path) ? `Collapse ${file.path}` : `Expand ${file.path}`,
25374
- onClick: () => setExpandedFiles((current) => nextExpandedFileSet(current, file.path)),
25375
- children: expandedFiles.has(file.path) ? "\u2212" : "+"
25376
- },
25377
- "collapse"
25378
- ),
25379
- /* @__PURE__ */ jsxs(
25380
- "button",
25381
- {
25382
- type: "button",
25383
- className: "min-w-0 text-left",
25384
- onClick: () => selectFile(file.path),
25385
- children: [
25386
- /* @__PURE__ */ jsx3("div", { className: "truncate text-sm font-medium", children: file.path }, "path"),
25387
- file.oldPath ? /* @__PURE__ */ jsxs("div", { className: "truncate font-mono text-[11px] text-muted-foreground", children: [
25388
- "from ",
25389
- file.oldPath
25390
- ] }, "old-path") : null
25391
- ]
25392
- },
25393
- "select"
25394
- )
25395
- ] }, "left"),
25396
- /* @__PURE__ */ jsx3("div", { className: "flex shrink-0 items-center gap-1", children: /* @__PURE__ */ jsx3(
25397
- "button",
25398
- {
25399
- type: "button",
25400
- className: iconButtonClass(false),
25401
- title: "Copy path",
25402
- "aria-label": `Copy ${file.path}`,
25403
- onClick: () => void copyPath(file.path),
25404
- children: "\u29C9"
25405
- },
25406
- "copy"
25407
- ) }, "actions")
25408
- ] }, "header"),
25409
- expandedFiles.has(file.path) ? /* @__PURE__ */ jsx3(FileDiffPanel, { file, mode }, "diff") : /* @__PURE__ */ jsx3(
25559
+ /* @__PURE__ */ jsxs(
25560
+ "div",
25561
+ {
25562
+ className: "sticky top-0 z-30 flex min-w-0 items-center justify-between gap-3 border border-b-0 border-border bg-background px-3 py-2 shadow-sm",
25563
+ children: [
25564
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
25565
+ /* @__PURE__ */ jsx3(
25566
+ "button",
25567
+ {
25568
+ type: "button",
25569
+ className: "mt-0.5 text-muted-foreground hover:text-foreground",
25570
+ title: expandedFiles.has(file.path) ? "Collapse file" : "Expand file",
25571
+ "aria-label": expandedFiles.has(file.path) ? `Collapse ${file.path}` : `Expand ${file.path}`,
25572
+ onClick: () => setExpandedFiles((current) => nextExpandedFileSet(current, file.path)),
25573
+ children: expandedFiles.has(file.path) ? "\u2212" : "+"
25574
+ },
25575
+ "collapse"
25576
+ ),
25577
+ /* @__PURE__ */ jsxs(
25578
+ "button",
25579
+ {
25580
+ type: "button",
25581
+ className: "min-w-0 text-left",
25582
+ onClick: () => selectFile(file.path),
25583
+ children: [
25584
+ /* @__PURE__ */ jsx3("div", { className: "truncate text-sm font-medium", children: file.path }, "path"),
25585
+ file.oldPath ? /* @__PURE__ */ jsxs("div", { className: "truncate font-mono text-[11px] text-muted-foreground", children: [
25586
+ "from ",
25587
+ file.oldPath
25588
+ ] }, "old-path") : null
25589
+ ]
25590
+ },
25591
+ "select"
25592
+ )
25593
+ ] }, "left"),
25594
+ /* @__PURE__ */ jsx3("div", { className: "flex shrink-0 items-center gap-1", children: /* @__PURE__ */ jsx3(
25595
+ "button",
25596
+ {
25597
+ type: "button",
25598
+ className: iconButtonClass(false),
25599
+ title: "Copy path",
25600
+ "aria-label": `Copy ${file.path}`,
25601
+ onClick: () => void copyPath(file.path),
25602
+ children: "\u29C9"
25603
+ },
25604
+ "copy"
25605
+ ) }, "actions")
25606
+ ]
25607
+ },
25608
+ "header"
25609
+ ),
25610
+ expandedFiles.has(file.path) ? /* @__PURE__ */ jsx3(FileDiffPanel, { file, mode, lineWrap }, "diff") : /* @__PURE__ */ jsx3(
25410
25611
  CollapsedFilePanel,
25411
25612
  {
25412
25613
  file,