@tutti-os/workspace-file-reference 0.0.14 → 0.0.16

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/dist/ui/index.js CHANGED
@@ -3,27 +3,25 @@ import {
3
3
  createReferenceSourcePickerController,
4
4
  normalizeDirectoryPath,
5
5
  useWorkspaceFileReferencePickerView
6
- } from "../chunk-CD3YNHYJ.js";
6
+ } from "../chunk-LJZYN4MH.js";
7
7
  import {
8
+ REFERENCE_FILTER_CATEGORIES,
8
9
  WORKSPACE_ROOT_GROUP_NODE_ID,
9
10
  nodeRefKey,
10
11
  selectedReferenceToWorkspaceFileReference
11
- } from "../chunk-7C3RZYQE.js";
12
+ } from "../chunk-JURSITRX.js";
12
13
 
13
14
  // src/ui/internal/reference/WorkspaceFileReferencePicker.tsx
14
- import { useId } from "react";
15
- import { createPortal } from "react-dom";
16
15
  import {
17
- Button as Button3,
18
- Card,
19
- CardContent,
20
- CardHeader,
21
- CardTitle,
22
- CloseIcon,
16
+ Dialog,
17
+ DialogContent,
18
+ DialogHeader,
19
+ DialogTitle,
23
20
  cn as cn2
24
21
  } from "@tutti-os/ui-system";
25
22
 
26
23
  // src/ui/internal/reference/WorkspaceFileReferencePickerSections.tsx
24
+ import { useId, useState as useState2 } from "react";
27
25
  import {
28
26
  Badge,
29
27
  Button as Button2,
@@ -47,6 +45,9 @@ import {
47
45
  ScrollArea,
48
46
  SearchIcon,
49
47
  Spinner,
48
+ Tooltip,
49
+ TooltipContent,
50
+ TooltipTrigger,
50
51
  cn
51
52
  } from "@tutti-os/ui-system";
52
53
  import { AddIcon as AddLinedIcon } from "@tutti-os/ui-system/icons";
@@ -160,6 +161,12 @@ function WorkspaceFileReferencePickerTreeEntry({
160
161
  }, workspaceFileReferenceTreeCollapseDurationMs);
161
162
  return () => window.clearTimeout(timeoutId);
162
163
  }, [expanded]);
164
+ const handleRowSelect = () => {
165
+ onFocusPath(entry.path);
166
+ if (isFolder) {
167
+ onToggleFolder(entry);
168
+ }
169
+ };
163
170
  const shouldBuildChildContent = expanded || shouldRenderChildContent;
164
171
  const childContent = shouldBuildChildContent ? childState?.loading ? /* @__PURE__ */ jsxs(
165
172
  "div",
@@ -202,14 +209,24 @@ function WorkspaceFileReferencePickerTreeEntry({
202
209
  /* @__PURE__ */ jsxs(
203
210
  "div",
204
211
  {
212
+ "aria-current": focused ? "true" : void 0,
205
213
  className: cn(
206
- "flex items-center gap-2 rounded-[6px] py-1.5 pr-1 transition-colors",
214
+ "flex cursor-pointer items-center gap-2 rounded-[6px] py-1.5 pr-1 transition-colors select-none",
207
215
  "nodrag [-webkit-app-region:no-drag]",
208
216
  focused || selected ? "bg-transparency-block" : "hover:bg-transparency-block"
209
217
  ),
218
+ role: "button",
219
+ tabIndex: 0,
210
220
  style: {
211
221
  paddingLeft: `${(childDepth - 1) * workspaceFileReferenceTreeIndent + 8}px`
212
222
  },
223
+ onClick: handleRowSelect,
224
+ onKeyDown: (event) => {
225
+ if (event.key === "Enter" || event.key === " ") {
226
+ event.preventDefault();
227
+ handleRowSelect();
228
+ }
229
+ },
213
230
  children: [
214
231
  isFolder ? /* @__PURE__ */ jsx(
215
232
  "button",
@@ -232,24 +249,10 @@ function WorkspaceFileReferencePickerTreeEntry({
232
249
  )
233
250
  }
234
251
  ) : null,
235
- /* @__PURE__ */ jsxs(
236
- "button",
237
- {
238
- className: "nodrag flex min-w-0 flex-1 items-center gap-2 text-left [-webkit-app-region:no-drag]",
239
- type: "button",
240
- onClick: (event) => {
241
- event.stopPropagation();
242
- onFocusPath(entry.path);
243
- if (entry.kind === "folder") {
244
- onToggleFolder(entry);
245
- }
246
- },
247
- children: [
248
- isFolder ? /* @__PURE__ */ jsx(FolderFilledIcon, { className: "size-4 shrink-0 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx(FileIcon, { className: "size-4 shrink-0 text-[var(--text-tertiary)]" }),
249
- /* @__PURE__ */ jsx("span", { className: "truncate text-[13px] text-[var(--text-primary)]", children: resolveWorkspaceFileReferenceLabel(entry) })
250
- ]
251
- }
252
- ),
252
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2 text-left", children: [
253
+ isFolder ? /* @__PURE__ */ jsx(FolderFilledIcon, { className: "size-4 shrink-0 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx(FileIcon, { className: "size-4 shrink-0 text-[var(--text-tertiary)]" }),
254
+ /* @__PURE__ */ jsx("span", { className: "truncate text-[13px] text-[var(--text-primary)]", children: resolveWorkspaceFileReferenceLabel(entry) })
255
+ ] }),
253
256
  /* @__PURE__ */ jsx(
254
257
  Button,
255
258
  {
@@ -305,30 +308,39 @@ function WorkspaceFileReferencePickerSearchEntry({
305
308
  return /* @__PURE__ */ jsxs(
306
309
  "div",
307
310
  {
311
+ "aria-current": focused ? "true" : void 0,
308
312
  className: cn(
309
- "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-[6px] border py-2.5 pr-1 pl-3 transition-colors",
313
+ "grid cursor-pointer grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-[6px] border py-2.5 pr-1 pl-3 transition-colors select-none",
310
314
  "nodrag [-webkit-app-region:no-drag]",
311
315
  focused || selected ? "border-border bg-transparency-block" : "border-transparent bg-transparent hover:border-border/70 hover:bg-transparency-block"
312
316
  ),
317
+ role: "button",
318
+ tabIndex: 0,
319
+ onClick: () => onFocusPath(entry.path),
320
+ onKeyDown: (event) => {
321
+ if (event.key === "Enter" || event.key === " ") {
322
+ event.preventDefault();
323
+ onFocusPath(entry.path);
324
+ }
325
+ },
313
326
  children: [
314
- /* @__PURE__ */ jsxs(
315
- "button",
316
- {
317
- className: "nodrag flex min-w-0 items-center gap-3 text-left [-webkit-app-region:no-drag]",
318
- type: "button",
319
- onClick: (event) => {
320
- event.stopPropagation();
321
- onFocusPath(entry.path);
322
- },
323
- children: [
324
- /* @__PURE__ */ jsx("span", { className: "grid size-9 shrink-0 place-items-center rounded-lg bg-[var(--transparency-block)] text-[var(--text-tertiary)]", children: isFolder ? /* @__PURE__ */ jsx(FolderFilledIcon, { className: "size-4 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx(FileIcon, { className: "size-4 text-[var(--text-tertiary)]" }) }),
325
- /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
326
- /* @__PURE__ */ jsx("span", { className: "block truncate text-[13px] font-medium text-[var(--text-primary)]", children: resolveWorkspaceFileReferenceLabel(entry) }),
327
- /* @__PURE__ */ jsx("span", { className: "block truncate text-[11px] text-[var(--text-secondary)]", children: entry.path })
328
- ] })
329
- ]
330
- }
331
- ),
327
+ /* @__PURE__ */ jsxs("div", { className: "nodrag flex min-w-0 items-center gap-3 text-left [-webkit-app-region:no-drag]", children: [
328
+ /* @__PURE__ */ jsx("span", { className: "grid size-9 shrink-0 place-items-center rounded-lg bg-[var(--transparency-block)] text-[var(--text-tertiary)]", children: isFolder ? /* @__PURE__ */ jsx(FolderFilledIcon, { className: "size-4 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx(FileIcon, { className: "size-4 text-[var(--text-tertiary)]" }) }),
329
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
330
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-[13px] font-medium text-[var(--text-primary)]", children: resolveWorkspaceFileReferenceLabel(entry) }),
331
+ /* @__PURE__ */ jsxs(Tooltip, { delayDuration: 300, children: [
332
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "block truncate text-[11px] text-[var(--text-secondary)]", children: entry.path }) }),
333
+ /* @__PURE__ */ jsx(
334
+ TooltipContent,
335
+ {
336
+ className: "max-w-[min(520px,calc(100vw-32px))] whitespace-normal text-left [overflow-wrap:anywhere]",
337
+ side: "top",
338
+ children: entry.path
339
+ }
340
+ )
341
+ ] })
342
+ ] })
343
+ ] }),
332
344
  /* @__PURE__ */ jsx(
333
345
  Button,
334
346
  {
@@ -394,6 +406,8 @@ function WorkspaceFileReferencePreviewSurface({
394
406
  directoryMessage: copy.t("referencePicker.previewFolder"),
395
407
  emptyMessage: copy.t("referencePicker.previewUnavailable"),
396
408
  frameClassName: "flex aspect-[3/2] w-full flex-col items-center justify-center overflow-hidden rounded-[8px] border border-[var(--line-2,var(--border-2))] bg-[var(--transparency-block)] p-0 text-center",
409
+ htmlFrameClassName: "items-stretch justify-stretch bg-white",
410
+ htmlTitle: resolveWorkspaceFileReferenceLabel,
397
411
  imageAlt: resolveWorkspaceFileReferenceLabel,
398
412
  imageFrameClassName: "p-3",
399
413
  loadingIndicator: /* @__PURE__ */ jsx2("span", { className: "mx-auto grid size-11 place-items-center rounded-[6px] bg-[var(--transparency-block)]", children: /* @__PURE__ */ jsx2(LoadingIcon, { className: "size-4 animate-spin" }) }),
@@ -427,7 +441,9 @@ function resolveWorkspaceFileReferenceSurfaceState(copy, focusedEntry, previewSt
427
441
  switch (previewState.status) {
428
442
  case "loading":
429
443
  case "image":
444
+ case "video":
430
445
  case "text":
446
+ case "html":
431
447
  return {
432
448
  ...previewState,
433
449
  entry: focusedEntry
@@ -488,6 +504,9 @@ function WorkspaceFileReferencePickerFooter({
488
504
  onConfirm,
489
505
  selectedRefs
490
506
  }) {
507
+ const selectedRefsTooltipId = useId();
508
+ const [selectedRefsTooltipOpen, setSelectedRefsTooltipOpen] = useState2(false);
509
+ const selectedRefsLabel = selectedRefs.map((ref) => resolveWorkspaceFileReferenceLabel(ref)).join("\n");
491
510
  return /* @__PURE__ */ jsxs2("div", { className: "nodrag flex flex-col gap-3 border-t border-[var(--line-1)] px-4 py-4 [-webkit-app-region:no-drag] sm:px-6 lg:flex-row lg:items-center lg:justify-between", children: [
492
511
  /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-wrap items-center gap-2 lg:flex-1", children: [
493
512
  /* @__PURE__ */ jsx2("span", { className: "text-[13px] text-[var(--text-secondary)]", children: copy.t("referencePicker.selectedCount", {
@@ -503,13 +522,48 @@ function WorkspaceFileReferencePickerFooter({
503
522
  ref.path
504
523
  )),
505
524
  selectedRefs.length > 2 ? /* @__PURE__ */ jsxs2(
506
- Badge,
525
+ "span",
507
526
  {
508
- className: workspaceFileReferencePickerSelectedBadgeClassName,
509
- variant: "secondary",
527
+ className: "relative inline-flex",
528
+ onBlur: () => setSelectedRefsTooltipOpen(false),
529
+ onFocus: () => setSelectedRefsTooltipOpen(true),
530
+ onMouseEnter: () => setSelectedRefsTooltipOpen(true),
531
+ onMouseLeave: () => setSelectedRefsTooltipOpen(false),
510
532
  children: [
511
- "+",
512
- selectedRefs.length - 2
533
+ /* @__PURE__ */ jsx2(
534
+ Badge,
535
+ {
536
+ asChild: true,
537
+ className: `${workspaceFileReferencePickerSelectedBadgeClassName} cursor-default`,
538
+ variant: "secondary",
539
+ children: /* @__PURE__ */ jsxs2(
540
+ "button",
541
+ {
542
+ "aria-describedby": selectedRefsTooltipId,
543
+ "aria-label": selectedRefsLabel,
544
+ type: "button",
545
+ children: [
546
+ "+",
547
+ selectedRefs.length - 2
548
+ ]
549
+ }
550
+ )
551
+ }
552
+ ),
553
+ /* @__PURE__ */ jsx2(
554
+ "span",
555
+ {
556
+ "aria-hidden": !selectedRefsTooltipOpen,
557
+ className: "pointer-events-none absolute bottom-[calc(100%+8px)] left-0 z-[var(--z-tooltip,100700)] max-h-[min(20rem,calc(100vh-96px))] w-max max-w-[min(28rem,calc(100vw-32px))] overflow-auto whitespace-pre-line rounded-md border border-[var(--border-1)] bg-[var(--background-fronted)] px-2 py-1 text-left text-[13px] leading-[1.3] text-[var(--text-primary)] shadow-soft transition-opacity duration-100 [overflow-wrap:anywhere]",
558
+ id: selectedRefsTooltipId,
559
+ role: "tooltip",
560
+ style: {
561
+ opacity: selectedRefsTooltipOpen ? 1 : 0,
562
+ visibility: selectedRefsTooltipOpen ? "visible" : "hidden"
563
+ },
564
+ children: selectedRefsLabel
565
+ }
566
+ )
513
567
  ]
514
568
  }
515
569
  ) : null
@@ -546,8 +600,6 @@ function WorkspaceFileReferencePickerFeedback2({
546
600
 
547
601
  // src/ui/internal/reference/WorkspaceFileReferencePicker.tsx
548
602
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
549
- var workspaceFileReferencePickerBackdropMotionClassName = "motion-safe:animate-in motion-safe:fade-in-0 motion-safe:duration-[180ms] motion-safe:ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:animate-none";
550
- var workspaceFileReferencePickerPanelMotionClassName = "motion-safe:animate-in motion-safe:fade-in-0 motion-safe:zoom-in-[0.96] motion-safe:duration-[250ms] motion-safe:ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:animate-none";
551
603
  function WorkspaceFileReferencePicker({
552
604
  copy,
553
605
  fileAdapter,
@@ -555,9 +607,9 @@ function WorkspaceFileReferencePicker({
555
607
  onClose,
556
608
  onConfirm,
557
609
  open,
610
+ scoped = false,
558
611
  workspaceId
559
612
  }) {
560
- const titleId = useId();
561
613
  const {
562
614
  browseRootEntries,
563
615
  directoryStateByPath,
@@ -582,52 +634,27 @@ function WorkspaceFileReferencePicker({
582
634
  open,
583
635
  workspaceId
584
636
  });
585
- if (!open) {
586
- return null;
587
- }
588
- const dialog = /* @__PURE__ */ jsx3(
589
- "div",
637
+ return /* @__PURE__ */ jsx3(
638
+ Dialog,
590
639
  {
591
- className: cn2(
592
- "nodrag fixed inset-0 grid place-items-center bg-[var(--backdrop)] px-3 py-4 backdrop-blur-md [-webkit-app-region:no-drag] sm:px-6 sm:py-8",
593
- workspaceFileReferencePickerBackdropMotionClassName
594
- ),
595
- style: { zIndex: "var(--z-panel)" },
596
- onClick: onClose,
640
+ open,
641
+ onOpenChange: (nextOpen) => {
642
+ if (!nextOpen) {
643
+ onClose();
644
+ }
645
+ },
597
646
  children: /* @__PURE__ */ jsxs3(
598
- Card,
647
+ DialogContent,
599
648
  {
600
- "aria-labelledby": titleId,
601
- "aria-modal": "true",
649
+ "aria-describedby": void 0,
602
650
  className: cn2(
603
- "nodrag flex h-[min(88vh,44rem)] w-full max-w-5xl origin-center flex-col gap-0 overflow-hidden border-[var(--line-1)] bg-[var(--background-fronted)] py-0 text-[var(--text-primary)] shadow-panel [-webkit-app-region:no-drag] sm:h-[min(82vh,44rem)]",
604
- workspaceFileReferencePickerPanelMotionClassName
651
+ "nodrag flex h-[min(88vh,44rem)] w-full max-w-5xl flex-col gap-0 overflow-hidden border-[var(--line-1)] bg-[var(--background-fronted)] p-0 text-[var(--text-primary)] shadow-panel [-webkit-app-region:no-drag] sm:h-[min(82vh,44rem)] sm:max-w-5xl"
605
652
  ),
606
- role: "dialog",
607
- onClick: (event) => event.stopPropagation(),
653
+ overlayClassName: cn2("nodrag", scoped && "!absolute"),
654
+ portaled: !scoped,
608
655
  children: [
609
- /* @__PURE__ */ jsx3(
610
- CardHeader,
611
- {
612
- className: "border-b px-4 py-4 sm:px-6 sm:py-5",
613
- style: { borderBottomColor: "var(--line-1)" },
614
- children: /* @__PURE__ */ jsxs3("div", { className: "flex items-start justify-between gap-4", children: [
615
- /* @__PURE__ */ jsx3("div", { className: "min-w-0", children: /* @__PURE__ */ jsx3(CardTitle, { id: titleId, children: copy.t("referencePicker.title") }) }),
616
- /* @__PURE__ */ jsx3(
617
- Button3,
618
- {
619
- "aria-label": copy.t("actions.cancel"),
620
- size: "icon-sm",
621
- type: "button",
622
- variant: "ghost",
623
- onClick: onClose,
624
- children: /* @__PURE__ */ jsx3(CloseIcon, { size: 16 })
625
- }
626
- )
627
- ] })
628
- }
629
- ),
630
- /* @__PURE__ */ jsxs3(CardContent, { className: "grid min-h-0 flex-1 grid-rows-[minmax(0,1fr)_auto] gap-0 overflow-hidden p-0 lg:grid-cols-[minmax(0,1.15fr)_minmax(20rem,0.85fr)] lg:grid-rows-1", children: [
656
+ /* @__PURE__ */ jsx3(DialogHeader, { className: "flex-none border-b border-[var(--line-1)] px-4 py-4 sm:px-6 sm:py-5", children: /* @__PURE__ */ jsx3(DialogTitle, { children: copy.t("referencePicker.title") }) }),
657
+ /* @__PURE__ */ jsxs3("div", { className: "grid min-h-0 flex-1 grid-rows-[minmax(0,1fr)_auto] gap-0 overflow-hidden p-0 lg:grid-cols-[minmax(0,1.15fr)_minmax(20rem,0.85fr)] lg:grid-rows-1", children: [
631
658
  /* @__PURE__ */ jsx3(
632
659
  WorkspaceFileReferencePickerBrowserPane,
633
660
  {
@@ -671,10 +698,6 @@ function WorkspaceFileReferencePicker({
671
698
  )
672
699
  }
673
700
  );
674
- if (typeof document === "undefined") {
675
- return dialog;
676
- }
677
- return createPortal(dialog, document.body);
678
701
  }
679
702
 
680
703
  // src/ui/internal/reference/ReferenceSourcePicker.tsx
@@ -682,45 +705,58 @@ import {
682
705
  useEffect as useEffect3,
683
706
  useId as useId2,
684
707
  useRef as useRef2,
685
- useState as useState3
708
+ useState as useState4
686
709
  } from "react";
687
- import { createPortal as createPortal2 } from "react-dom";
710
+ import { createPortal } from "react-dom";
688
711
  import {
689
712
  ArrowRightIcon as ArrowRightIcon2,
690
713
  Badge as Badge2,
691
- Button as Button4,
692
- Card as Card2,
693
- CardContent as CardContent2,
694
- CardHeader as CardHeader2,
695
- CardTitle as CardTitle2,
714
+ Button as Button3,
715
+ Card,
716
+ CardContent,
717
+ CardHeader,
718
+ CardTitle,
696
719
  CheckIcon as CheckIcon2,
697
- CloseIcon as CloseIcon2,
720
+ ChevronDownIcon,
721
+ CloseIcon,
698
722
  FileIcon as FileIcon3,
699
723
  FolderFilledIcon as FolderFilledIcon3,
700
724
  Input as Input2,
725
+ IssueIcon,
701
726
  ResizableHandle,
702
727
  ResizablePanel,
703
728
  ResizablePanelGroup,
704
729
  ScrollArea as ScrollArea2,
705
730
  SearchIcon as SearchIcon2,
706
731
  Spinner as Spinner2,
732
+ Tooltip as Tooltip2,
733
+ TooltipContent as TooltipContent2,
734
+ TooltipTrigger as TooltipTrigger2,
707
735
  cn as cn3
708
736
  } from "@tutti-os/ui-system";
709
- import { AddIcon } from "@tutti-os/ui-system/icons";
737
+ import { AddLinedIcon as AddLinedIcon2 } from "@tutti-os/ui-system/icons";
738
+ import {
739
+ WorkspaceFilePreviewSurface
740
+ } from "@tutti-os/workspace-file-preview/react";
710
741
 
711
742
  // src/react/internal/reference/useReferenceSourcePickerView.ts
712
- import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
743
+ import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState3 } from "react";
713
744
  import { useSnapshot } from "valtio";
714
745
  import {
715
746
  sortWorkspaceFileEntriesForArrangeMode
716
747
  } from "@tutti-os/workspace-file-manager/services";
717
- var WORKSPACE_ROOT_GROUP_LABEL = "\u5DE5\u4F5C\u533A";
748
+ import {
749
+ createWorkspaceFilePreviewLoadedState
750
+ } from "@tutti-os/workspace-file-preview";
718
751
  function useReferenceSourcePickerView({
719
752
  aggregator,
720
753
  workspaceId,
721
754
  open,
755
+ workspaceRootGroupLabel,
756
+ initialTarget = null,
722
757
  onClose,
723
- onConfirm
758
+ onConfirm,
759
+ onConfirmBundles
724
760
  }) {
725
761
  const readSnapshot = useSnapshot;
726
762
  const scope = useMemo(() => ({ workspaceId }), [workspaceId]);
@@ -729,11 +765,9 @@ function useReferenceSourcePickerView({
729
765
  [aggregator, scope]
730
766
  );
731
767
  const snapshot = readSnapshot(controller.store);
732
- const [breadcrumbBySource, setBreadcrumbBySource] = useState2({});
733
- const [focusedNode, setFocusedNode] = useState2(null);
734
- const [arrangeMode, setArrangeMode] = useState2("none");
735
- const [expandedSources, setExpandedSources] = useState2({});
736
- const expandSeededRef = useRef(false);
768
+ const [breadcrumbBySource, setBreadcrumbBySource] = useState3({});
769
+ const [focusedNode, setFocusedNode] = useState3(null);
770
+ const [arrangeMode, setArrangeMode] = useState3("none");
737
771
  const sortNodes = useCallback(
738
772
  (nodes) => {
739
773
  if (arrangeMode === "none") {
@@ -757,6 +791,7 @@ function useReferenceSourcePickerView({
757
791
  [arrangeMode]
758
792
  );
759
793
  const autoEnteredSourcesRef = useRef(/* @__PURE__ */ new Set());
794
+ const appliedInitialTargetRef = useRef(null);
760
795
  useEffect2(() => {
761
796
  if (!open) {
762
797
  return;
@@ -765,13 +800,47 @@ function useReferenceSourcePickerView({
765
800
  controller.open();
766
801
  setBreadcrumbBySource({});
767
802
  setFocusedNode(null);
768
- setExpandedSources({});
769
- expandSeededRef.current = false;
770
803
  autoEnteredSourcesRef.current = /* @__PURE__ */ new Set();
804
+ appliedInitialTargetRef.current = null;
771
805
  return () => {
772
806
  controller.close();
773
807
  };
774
808
  }, [controller, open]);
809
+ useEffect2(() => {
810
+ const target = initialTarget;
811
+ if (!open || !target || appliedInitialTargetRef.current === target) {
812
+ return;
813
+ }
814
+ appliedInitialTargetRef.current = target;
815
+ autoEnteredSourcesRef.current.add(target.sourceId);
816
+ let canceled = false;
817
+ void controller.locatePath(target).then((path) => {
818
+ if (canceled) {
819
+ return;
820
+ }
821
+ const group = path[0];
822
+ if (!group) {
823
+ autoEnteredSourcesRef.current.delete(target.sourceId);
824
+ return;
825
+ }
826
+ controller.setActiveSource(target.sourceId);
827
+ setBreadcrumbBySource((current) => ({
828
+ ...current,
829
+ [group.ref.sourceId]: [group]
830
+ }));
831
+ controller.ensureChildren(group);
832
+ for (const node of path.slice(1)) {
833
+ controller.expandNode(node);
834
+ }
835
+ const deepest = path[path.length - 1];
836
+ setFocusedNode(path.length > 1 && deepest ? deepest : null);
837
+ }).catch(() => {
838
+ autoEnteredSourcesRef.current.delete(target.sourceId);
839
+ });
840
+ return () => {
841
+ canceled = true;
842
+ };
843
+ }, [open, initialTarget, controller]);
775
844
  const activeSourceId = snapshot.activeSourceId;
776
845
  const activeTab = useMemo(
777
846
  () => snapshot.tabs.find((tab) => tab.sourceId === activeSourceId) ?? null,
@@ -782,7 +851,8 @@ function useReferenceSourcePickerView({
782
851
  const currentNode = breadcrumb.at(-1) ?? null;
783
852
  const currentKey = currentNode ? nodeRefKey(currentNode.ref) : ROOT_CHILDREN_KEY;
784
853
  const activeTabState = activeSourceId ? snapshot.bySource[activeSourceId] : void 0;
785
- const isSearch = activeTabState?.mode === "search" && activeTabState.searchQuery.trim() !== "";
854
+ const activeFilters = activeTabState?.searchFilters ?? [];
855
+ const isQuery = activeTabState?.mode === "search" && (activeTabState.searchQuery.trim() !== "" || activeFilters.length > 0);
786
856
  const currentChildren = activeTabState?.childrenByKey[currentKey];
787
857
  const currentEntries = useMemo(
788
858
  () => sortNodes(currentChildren?.entries ?? []),
@@ -811,18 +881,51 @@ function useReferenceSourcePickerView({
811
881
  const workspaceRoot = {
812
882
  ref: { sourceId, nodeId: WORKSPACE_ROOT_GROUP_NODE_ID },
813
883
  kind: "folder",
814
- displayName: WORKSPACE_ROOT_GROUP_LABEL
884
+ displayName: workspaceRootGroupLabel
815
885
  };
816
886
  result[sourceId] = [workspaceRoot, ...folders];
817
887
  }
818
888
  }
819
889
  return result;
890
+ }, [
891
+ snapshot.tabs,
892
+ snapshot.bySource,
893
+ aggregator,
894
+ scope,
895
+ workspaceRootGroupLabel
896
+ ]);
897
+ const sidebarHasMoreBySource = useMemo(() => {
898
+ const result = {};
899
+ for (const tab of snapshot.tabs) {
900
+ const sourceId = tab.sourceId;
901
+ const provided = aggregator.getLoadedSource(sourceId)?.listSidebarGroups?.(scope);
902
+ if (provided && provided.length > 0) {
903
+ result[sourceId] = false;
904
+ continue;
905
+ }
906
+ const root = snapshot.bySource[sourceId]?.childrenByKey[ROOT_CHILDREN_KEY];
907
+ result[sourceId] = Boolean(root?.nextCursor);
908
+ }
909
+ return result;
820
910
  }, [snapshot.tabs, snapshot.bySource, aggregator, scope]);
911
+ const sidebarLoadingMoreBySource = useMemo(() => {
912
+ const result = {};
913
+ for (const tab of snapshot.tabs) {
914
+ const root = snapshot.bySource[tab.sourceId]?.childrenByKey[ROOT_CHILDREN_KEY];
915
+ result[tab.sourceId] = Boolean(root?.loaded && root.loading);
916
+ }
917
+ return result;
918
+ }, [snapshot.tabs, snapshot.bySource]);
821
919
  const sidebarGroups = activeSourceId ? sidebarGroupsBySource[activeSourceId] ?? [] : [];
822
- const selectedGroupKey = currentNode != null ? nodeRefKey(currentNode.ref) : activeSourceId && !capabilities?.navigable ? nodeRefKey({
920
+ const rootGroupNode = breadcrumb[0] ?? null;
921
+ const searchScopeNodeId = rootGroupNode ? rootGroupNode.ref.nodeId : null;
922
+ const selectedGroupKey = rootGroupNode ? nodeRefKey(rootGroupNode.ref) : activeSourceId && !capabilities?.navigable ? nodeRefKey({
823
923
  sourceId: activeSourceId,
824
924
  nodeId: WORKSPACE_ROOT_GROUP_NODE_ID
825
925
  }) : null;
926
+ useEffect2(() => {
927
+ controller.setSearchScope(searchScopeNodeId);
928
+ }, [controller, searchScopeNodeId]);
826
929
  const setActiveSource = useCallback(
827
930
  (sourceId) => {
828
931
  controller.setActiveSource(sourceId);
@@ -850,7 +953,7 @@ function useReferenceSourcePickerView({
850
953
  [controller]
851
954
  );
852
955
  useEffect2(() => {
853
- if (!open || !activeSourceId || !capabilities?.navigable) {
956
+ if (!open || !activeSourceId) {
854
957
  return;
855
958
  }
856
959
  if (autoEnteredSourcesRef.current.has(activeSourceId)) {
@@ -867,23 +970,15 @@ function useReferenceSourcePickerView({
867
970
  }
868
971
  autoEnteredSourcesRef.current.add(activeSourceId);
869
972
  enterFolder(firstGroup);
870
- }, [
871
- open,
872
- activeSourceId,
873
- capabilities?.navigable,
874
- sidebarGroups,
875
- breadcrumbBySource,
876
- enterFolder
877
- ]);
973
+ }, [open, activeSourceId, sidebarGroups, breadcrumbBySource, enterFolder]);
878
974
  useEffect2(() => {
879
- if (!open || expandSeededRef.current || !activeSourceId) {
975
+ if (!open) {
880
976
  return;
881
977
  }
882
- expandSeededRef.current = true;
883
- setExpandedSources(
884
- (current) => current[activeSourceId] != null ? current : { ...current, [activeSourceId]: true }
885
- );
886
- }, [open, activeSourceId]);
978
+ for (const tab of snapshot.tabs) {
979
+ controller.ensureSourceRoot(tab.sourceId);
980
+ }
981
+ }, [open, snapshot.tabs, controller]);
887
982
  const navigateToBreadcrumb = useCallback(
888
983
  (index) => {
889
984
  if (!activeSourceId) {
@@ -924,26 +1019,11 @@ function useReferenceSourcePickerView({
924
1019
  navigateToRoot(sourceId);
925
1020
  return;
926
1021
  }
927
- enterFolder(node);
928
- },
929
- [controller, snapshot.activeSourceId, enterFolder, navigateToRoot]
930
- );
931
- const isSourceExpanded = useCallback(
932
- (sourceId) => expandedSources[sourceId] ?? false,
933
- [expandedSources]
934
- );
935
- const toggleSourceExpanded = useCallback(
936
- (sourceId) => {
937
- const willExpand = !(expandedSources[sourceId] ?? false);
938
- setExpandedSources((current) => ({
939
- ...current,
940
- [sourceId]: !(current[sourceId] ?? false)
941
- }));
942
- if (willExpand && !aggregator.getLoadedSource(sourceId)?.listSidebarGroups) {
943
- controller.ensureSourceRoot(sourceId);
944
- }
1022
+ controller.ensureChildren(node);
1023
+ setBreadcrumbBySource((current) => ({ ...current, [sourceId]: [node] }));
1024
+ setFocusedNode(null);
945
1025
  },
946
- [aggregator, controller, expandedSources]
1026
+ [controller, snapshot.activeSourceId, navigateToRoot]
947
1027
  );
948
1028
  const isSelected = useCallback(
949
1029
  (node) => snapshot.selection.some(
@@ -951,23 +1031,144 @@ function useReferenceSourcePickerView({
951
1031
  ),
952
1032
  [snapshot.selection]
953
1033
  );
954
- const [isConfirming, setIsConfirming] = useState2(false);
1034
+ const [isConfirming, setIsConfirming] = useState3(false);
955
1035
  const confirm = useCallback(async () => {
956
1036
  if (isConfirming) {
957
1037
  return;
958
1038
  }
959
1039
  setIsConfirming(true);
960
1040
  try {
961
- const selected = await controller.confirm();
962
- onConfirm(selected.map(selectedReferenceToWorkspaceFileReference));
1041
+ if (onConfirmBundles) {
1042
+ const grouped = await controller.confirmGrouped();
1043
+ onConfirmBundles({
1044
+ files: grouped.files.map(selectedReferenceToWorkspaceFileReference),
1045
+ bundles: grouped.bundles.map((bundle) => ({
1046
+ sourceId: bundle.root.ref.sourceId,
1047
+ nodeId: bundle.root.ref.nodeId,
1048
+ displayName: bundle.root.displayName,
1049
+ iconUrl: bundle.root.iconUrl ?? null,
1050
+ handle: bundle.handle,
1051
+ // 展示用文件数:取节点 childCount(不再展开文件);缺省回退 0。
1052
+ fileCount: bundle.root.childCount ?? 0
1053
+ }))
1054
+ });
1055
+ } else {
1056
+ const selected = await controller.confirm();
1057
+ onConfirm(selected.map(selectedReferenceToWorkspaceFileReference));
1058
+ }
963
1059
  onClose();
964
1060
  } finally {
965
1061
  setIsConfirming(false);
966
1062
  }
967
- }, [controller, isConfirming, onClose, onConfirm]);
1063
+ }, [controller, isConfirming, onClose, onConfirm, onConfirmBundles]);
1064
+ const [previewState, setPreviewState] = useState3({
1065
+ status: "empty"
1066
+ });
1067
+ const previewObjectUrlRef = useRef(null);
1068
+ const revokePreviewObjectUrl = useCallback(() => {
1069
+ if (previewObjectUrlRef.current) {
1070
+ URL.revokeObjectURL(previewObjectUrlRef.current);
1071
+ previewObjectUrlRef.current = null;
1072
+ }
1073
+ }, []);
1074
+ useEffect2(() => {
1075
+ const node = focusedNode;
1076
+ if (!node) {
1077
+ revokePreviewObjectUrl();
1078
+ setPreviewState({ status: "empty" });
1079
+ return;
1080
+ }
1081
+ if (node.kind === "folder") {
1082
+ revokePreviewObjectUrl();
1083
+ setPreviewState({ node, status: "directory" });
1084
+ return;
1085
+ }
1086
+ const previewable = aggregator.getLoadedSource(node.ref.sourceId)?.capabilities.previewable ?? false;
1087
+ if (!previewable) {
1088
+ revokePreviewObjectUrl();
1089
+ setPreviewState({ node, status: "unsupported" });
1090
+ return;
1091
+ }
1092
+ let cancelled = false;
1093
+ revokePreviewObjectUrl();
1094
+ setPreviewState({ node, status: "loading" });
1095
+ void (async () => {
1096
+ try {
1097
+ const preview = await aggregator.readPreview(scope, node);
1098
+ if (cancelled) {
1099
+ return;
1100
+ }
1101
+ if (!preview) {
1102
+ setPreviewState({ node, status: "unsupported" });
1103
+ return;
1104
+ }
1105
+ const loaded = createWorkspaceFilePreviewLoadedState({
1106
+ bytes: preview.bytes,
1107
+ contentType: preview.contentType,
1108
+ entry: {
1109
+ kind: node.kind,
1110
+ name: node.displayName,
1111
+ path: node.ref.nodeId,
1112
+ mtimeMs: node.mtimeMs ?? null,
1113
+ sizeBytes: node.sizeBytes ?? null
1114
+ },
1115
+ renderHtml: true,
1116
+ target: {
1117
+ fileKind: preview.kind,
1118
+ name: node.displayName,
1119
+ path: node.ref.nodeId,
1120
+ mtimeMs: node.mtimeMs ?? null,
1121
+ sizeBytes: node.sizeBytes ?? null
1122
+ }
1123
+ });
1124
+ if (cancelled) {
1125
+ return;
1126
+ }
1127
+ if (loaded.status === "image") {
1128
+ const objectUrl = URL.createObjectURL(
1129
+ new Blob([loaded.bytes], { type: loaded.contentType })
1130
+ );
1131
+ previewObjectUrlRef.current = objectUrl;
1132
+ setPreviewState({ node, objectUrl, status: "image" });
1133
+ return;
1134
+ }
1135
+ if (loaded.status === "video") {
1136
+ const objectUrl = URL.createObjectURL(
1137
+ new Blob([loaded.bytes], { type: loaded.contentType })
1138
+ );
1139
+ previewObjectUrlRef.current = objectUrl;
1140
+ setPreviewState({ node, objectUrl, status: "video" });
1141
+ return;
1142
+ }
1143
+ if (loaded.status === "text") {
1144
+ setPreviewState({ content: loaded.content, node, status: "text" });
1145
+ return;
1146
+ }
1147
+ if (loaded.status === "html") {
1148
+ setPreviewState({ content: loaded.content, node, status: "html" });
1149
+ return;
1150
+ }
1151
+ setPreviewState({
1152
+ node,
1153
+ reason: loaded.reason,
1154
+ ...loaded.maxSizeBytes == null ? {} : { maxSizeBytes: loaded.maxSizeBytes },
1155
+ status: "readonly"
1156
+ });
1157
+ } catch {
1158
+ if (!cancelled) {
1159
+ setPreviewState({ node, status: "error" });
1160
+ }
1161
+ }
1162
+ })();
1163
+ return () => {
1164
+ cancelled = true;
1165
+ };
1166
+ }, [aggregator, focusedNode, revokePreviewObjectUrl, scope]);
1167
+ useEffect2(() => revokePreviewObjectUrl, [revokePreviewObjectUrl]);
968
1168
  return {
969
1169
  tabs: snapshot.tabs,
970
1170
  activeSourceId,
1171
+ previewState,
971
1172
  activeTabLabel: activeTab?.label ?? "",
972
1173
  capabilities,
973
1174
  // 内容区递归就地树:当前选中二级节点的子条目(本地根时为源根子条目)。
@@ -983,15 +1184,25 @@ function useReferenceSourcePickerView({
983
1184
  currentNode,
984
1185
  sidebarGroups,
985
1186
  sidebarGroupsBySource,
986
- isSourceExpanded,
987
- toggleSourceExpanded,
1187
+ sidebarHasMoreBySource,
1188
+ sidebarLoadingMoreBySource,
1189
+ loadMoreSidebarGroups: (sourceId) => controller.loadMoreSourceRoot(sourceId),
988
1190
  selectedGroupKey,
989
1191
  arrangeMode,
990
1192
  setArrangeMode,
991
- isSearch,
1193
+ // 查询态(关键词或筛选任一非空)→ 平铺结果;否则浏览树。
1194
+ isQuery,
992
1195
  searchQuery: activeTabState?.searchQuery ?? "",
993
- isLoading: isSearch ? activeTabState?.isSearchLoading ?? false : currentChildren?.loading ?? false,
994
- hasMore: !isSearch && Boolean(currentChildren?.nextCursor),
1196
+ // 当前源支持的文件类型筛选分类(不支持则空数组,picker 据此决定是否展示筛选下拉)。
1197
+ filterCategories: capabilities?.filterable ? REFERENCE_FILTER_CATEGORIES : [],
1198
+ activeFilters,
1199
+ // 搜索态:仅在「还没有任何结果」时显示 spinner;细化关键词(已有结果)时
1200
+ // 保留旧结果直到新结果就绪,避免内容区在 spinner/结果间反复切换造成闪烁。
1201
+ isLoading: isQuery ? (activeTabState?.isSearchLoading ?? false) && (activeTabState?.searchEntries.length ?? 0) === 0 : currentChildren?.loading ?? false,
1202
+ // 查询态:增长式分页是否还有更多;浏览态:cursor 是否有下一页。
1203
+ hasMore: isQuery ? activeTabState?.searchHasMore ?? false : Boolean(currentChildren?.nextCursor),
1204
+ // 底部「加载更多」在途(查询态 = 增长重查;浏览态 = cursor append)。
1205
+ isLoadingMore: isQuery ? activeTabState?.isSearchLoadingMore ?? false : currentChildren?.loading ?? false,
995
1206
  focusedNode,
996
1207
  selection: snapshot.selection,
997
1208
  selectionCount: snapshot.selection.length,
@@ -1001,9 +1212,10 @@ function useReferenceSourcePickerView({
1001
1212
  navigateToBreadcrumb,
1002
1213
  navigateToRoot,
1003
1214
  setFocusedNode,
1004
- setSearchQuery: (query) => controller.setSearchQuery(query),
1215
+ setSearchQuery: (query) => controller.setSearchQuery(query, searchScopeNodeId),
1216
+ setFilters: (filters) => controller.setSearchFilters(filters, searchScopeNodeId),
1005
1217
  toggleSelection: (node) => controller.toggleSelection(node),
1006
- loadMore: () => controller.loadMore(currentNode),
1218
+ loadMore: () => isQuery ? controller.loadMoreSearch() : controller.loadMore(currentNode),
1007
1219
  isSelected,
1008
1220
  confirm,
1009
1221
  isConfirming
@@ -1012,17 +1224,7 @@ function useReferenceSourcePickerView({
1012
1224
 
1013
1225
  // src/ui/internal/reference/ReferenceSourcePicker.tsx
1014
1226
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1015
- var L = {
1016
- sourceColumn: "\u5206\u7C7B",
1017
- selectGroupHint: "\u4ECE\u5DE6\u4FA7\u9009\u62E9\u4E00\u4E2A\u76EE\u5F55",
1018
- previewSource: "\u4EA7\u51FA\u6765\u6E90",
1019
- previewModified: "\u4EA7\u51FA\u65F6\u95F4",
1020
- previewSize: "\u6587\u4EF6\u5927\u5C0F",
1021
- previewHierarchy: "\u6240\u5C5E\u5C42\u7EA7",
1022
- reference: "\u5F15\u7528",
1023
- loadMore: "\u52A0\u8F7D\u66F4\u591A",
1024
- emptyPreview: "\u9009\u62E9\u4E00\u4E2A\u6587\u4EF6\u67E5\u770B\u8BE6\u60C5"
1025
- };
1227
+ var SIDEBAR_GROUP_PAGE_SIZE = 5;
1026
1228
  function autoFitPanelWidth(groupEl, contentEl, panel, trailingPx) {
1027
1229
  if (!groupEl || !contentEl || !panel) {
1028
1230
  return;
@@ -1051,8 +1253,10 @@ function autoFitPanelWidth(groupEl, contentEl, panel, trailingPx) {
1051
1253
  function ReferenceSourcePicker({
1052
1254
  aggregator,
1053
1255
  copy,
1256
+ initialTarget,
1054
1257
  onClose,
1055
1258
  onConfirm,
1259
+ onConfirmBundles,
1056
1260
  open,
1057
1261
  workspaceId
1058
1262
  }) {
@@ -1061,9 +1265,23 @@ function ReferenceSourcePicker({
1061
1265
  aggregator,
1062
1266
  workspaceId,
1063
1267
  open,
1268
+ workspaceRootGroupLabel: copy.t("referencePicker.workspaceRootGroup"),
1269
+ initialTarget,
1064
1270
  onClose,
1065
- onConfirm
1271
+ onConfirm,
1272
+ onConfirmBundles
1066
1273
  });
1274
+ const activeFilterSet = new Set(view.activeFilters);
1275
+ const toggleFilter = (id) => {
1276
+ const next = new Set(activeFilterSet);
1277
+ if (next.has(id)) {
1278
+ next.delete(id);
1279
+ } else {
1280
+ next.add(id);
1281
+ }
1282
+ view.setFilters([...next]);
1283
+ };
1284
+ const clearFilters = () => view.setFilters([]);
1067
1285
  const layoutRef = useRef2(null);
1068
1286
  const sidebarContentRef = useRef2(null);
1069
1287
  const middleContentRef = useRef2(null);
@@ -1092,7 +1310,7 @@ function ReferenceSourcePicker({
1092
1310
  style: { zIndex: "var(--z-panel)" },
1093
1311
  onClick: onClose,
1094
1312
  children: /* @__PURE__ */ jsxs4(
1095
- Card2,
1313
+ Card,
1096
1314
  {
1097
1315
  "aria-labelledby": titleId,
1098
1316
  "aria-modal": "true",
@@ -1100,38 +1318,45 @@ function ReferenceSourcePicker({
1100
1318
  role: "dialog",
1101
1319
  onClick: (event) => event.stopPropagation(),
1102
1320
  children: [
1103
- /* @__PURE__ */ jsx4(CardHeader2, { className: "gap-3 px-4 pt-4 pb-4 sm:px-6", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-start justify-between gap-4", children: [
1104
- /* @__PURE__ */ jsx4(CardTitle2, { id: titleId, children: copy.t("referencePicker.title") }),
1321
+ /* @__PURE__ */ jsx4(CardHeader, { className: "gap-3 px-4 pt-4 pb-4 sm:px-6", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-start justify-between gap-4", children: [
1322
+ /* @__PURE__ */ jsx4(CardTitle, { id: titleId, children: copy.t("referencePicker.title") }),
1105
1323
  /* @__PURE__ */ jsx4(
1106
- Button4,
1324
+ Button3,
1107
1325
  {
1108
1326
  "aria-label": copy.t("actions.cancel"),
1109
1327
  size: "icon-sm",
1110
1328
  type: "button",
1111
1329
  variant: "ghost",
1112
1330
  onClick: onClose,
1113
- children: /* @__PURE__ */ jsx4(CloseIcon2, { size: 16 })
1331
+ children: /* @__PURE__ */ jsx4(CloseIcon, { size: 16 })
1114
1332
  }
1115
1333
  )
1116
1334
  ] }) }),
1117
- /* @__PURE__ */ jsx4(CardContent2, { className: "flex min-h-0 flex-1 overflow-hidden border-t border-[var(--line-1)] p-0", children: /* @__PURE__ */ jsx4("div", { ref: layoutRef, className: "flex min-h-0 flex-1", children: /* @__PURE__ */ jsxs4(
1335
+ /* @__PURE__ */ jsx4(CardContent, { className: "flex min-h-0 flex-1 overflow-hidden border-t border-[var(--line-1)] p-0", children: /* @__PURE__ */ jsx4("div", { ref: layoutRef, className: "flex min-h-0 min-w-0 flex-1", children: /* @__PURE__ */ jsxs4(
1118
1336
  ResizablePanelGroup,
1119
1337
  {
1120
- className: "min-h-0 flex-1",
1338
+ className: "min-h-0 min-w-0 flex-1",
1121
1339
  orientation: "horizontal",
1122
- defaultLayout: { sidebar: 2, middle: 5, preview: 3 },
1340
+ defaultLayout: { sidebar: 2.5, middle: 4.5, preview: 3 },
1123
1341
  children: [
1124
1342
  /* @__PURE__ */ jsx4(
1125
1343
  ResizablePanel,
1126
1344
  {
1127
1345
  id: "sidebar",
1128
- className: "min-h-0 border-r border-[var(--line-1)]",
1129
- defaultSize: 20,
1130
- minSize: "180px",
1346
+ className: "min-h-0 min-w-0",
1347
+ defaultSize: 15,
1348
+ minSize: "150px",
1131
1349
  panelRef: (handle) => {
1132
1350
  sidebarPanelRef.current = handle;
1133
1351
  },
1134
- children: /* @__PURE__ */ jsx4(SourceSidebar, { contentRef: sidebarContentRef, view })
1352
+ children: /* @__PURE__ */ jsx4(
1353
+ SourceSidebar,
1354
+ {
1355
+ contentRef: sidebarContentRef,
1356
+ copy,
1357
+ view
1358
+ }
1359
+ )
1135
1360
  }
1136
1361
  ),
1137
1362
  /* @__PURE__ */ jsx4(
@@ -1139,6 +1364,7 @@ function ReferenceSourcePicker({
1139
1364
  {
1140
1365
  disableDoubleClick: true,
1141
1366
  withHandle: true,
1367
+ className: "after:bg-[var(--line-1)]",
1142
1368
  onDoubleClick: fitSidebar
1143
1369
  }
1144
1370
  ),
@@ -1146,73 +1372,105 @@ function ReferenceSourcePicker({
1146
1372
  ResizablePanel,
1147
1373
  {
1148
1374
  id: "middle",
1149
- className: "min-h-0",
1375
+ className: "min-h-0 min-w-0",
1150
1376
  defaultSize: 50,
1151
1377
  minSize: "260px",
1152
1378
  panelRef: (handle) => {
1153
1379
  middlePanelRef.current = handle;
1154
1380
  },
1155
1381
  children: /* @__PURE__ */ jsxs4("div", { className: "flex h-full min-h-0 flex-col", children: [
1156
- /* @__PURE__ */ jsx4("div", { className: "flex items-center gap-2 border-b border-[var(--line-1)] p-3", children: /* @__PURE__ */ jsxs4("div", { className: "relative flex-1", children: [
1157
- /* @__PURE__ */ jsx4(SearchIcon2, { className: "pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-[var(--text-tertiary)]" }),
1158
- /* @__PURE__ */ jsx4(
1159
- Input2,
1382
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 border-b border-[var(--line-1)] p-3", children: [
1383
+ /* @__PURE__ */ jsxs4("div", { className: "relative flex-1", children: [
1384
+ /* @__PURE__ */ jsx4(SearchIcon2, { className: "pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-[var(--text-tertiary)]" }),
1385
+ /* @__PURE__ */ jsx4(
1386
+ Input2,
1387
+ {
1388
+ className: "pl-9",
1389
+ placeholder: copy.t(
1390
+ "referencePicker.searchPlaceholder"
1391
+ ),
1392
+ value: view.searchQuery,
1393
+ onChange: (event) => view.setSearchQuery(event.target.value)
1394
+ }
1395
+ )
1396
+ ] }),
1397
+ view.capabilities?.filterable && view.filterCategories.length > 0 ? /* @__PURE__ */ jsx4(
1398
+ FilterCategoryFilter,
1160
1399
  {
1161
- className: "pl-9",
1162
- placeholder: copy.t(
1163
- "referencePicker.searchPlaceholder"
1164
- ),
1165
- value: view.searchQuery,
1166
- onChange: (event) => view.setSearchQuery(event.target.value)
1400
+ categories: view.filterCategories,
1401
+ copy,
1402
+ selected: activeFilterSet,
1403
+ onClear: clearFilters,
1404
+ onToggle: toggleFilter
1167
1405
  }
1168
- )
1169
- ] }) }),
1170
- /* @__PURE__ */ jsx4(ScrollArea2, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxs4(
1171
- "div",
1406
+ ) : null
1407
+ ] }),
1408
+ /* @__PURE__ */ jsx4(
1409
+ ScrollArea2,
1172
1410
  {
1173
- ref: middleContentRef,
1174
- className: "flex flex-col gap-[2px] p-3",
1175
- children: [
1176
- view.isLoading ? /* @__PURE__ */ jsx4(Feedback, { children: /* @__PURE__ */ jsx4(Spinner2, { size: 16 }) }) : view.isSearch ? (
1177
- // 搜索:扁平结果
1178
- view.searchResults.length === 0 ? /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.emptyDirectory") }) : view.searchResults.map((node) => /* @__PURE__ */ jsx4(
1179
- SearchResultRow,
1180
- {
1181
- focused: isFocused(view.focusedNode, node),
1182
- node,
1183
- selected: view.isSelected(node),
1184
- onFocus: view.setFocusedNode,
1185
- onToggle: view.toggleSelection
1186
- },
1187
- nodeRefKey(node.ref)
1188
- ))
1189
- ) : !hasSelectedGroup ? /* @__PURE__ */ jsx4(Feedback, { children: L.selectGroupHint }) : view.currentEntries.length === 0 ? /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.emptyDirectory") }) : (
1190
- // 浏览:就地递归展开树(复刻 agent 引用面板文件树交互)
1191
- view.currentEntries.map((node) => /* @__PURE__ */ jsx4(
1192
- TreeNodeRow,
1193
- {
1194
- copy,
1195
- depth: 0,
1196
- node,
1197
- view
1198
- },
1199
- nodeRefKey(node.ref)
1200
- ))
1201
- ),
1202
- view.hasMore && hasSelectedGroup && !view.isSearch ? /* @__PURE__ */ jsx4(
1203
- Button4,
1204
- {
1205
- className: "mt-1 w-full",
1206
- size: "sm",
1207
- type: "button",
1208
- variant: "ghost",
1209
- onClick: view.loadMore,
1210
- children: L.loadMore
1411
+ className: "min-h-0 flex-1",
1412
+ viewportProps: {
1413
+ // 拉到底部(距底 <120px)自动加载更多 —— 查询态走增长式分页,
1414
+ // 浏览态走 cursor 续页。已在加载/无更多时由 loadMore 内部 no-op。
1415
+ onScroll: (event) => {
1416
+ const el = event.currentTarget;
1417
+ if (view.hasMore && !view.isLoading && !view.isLoadingMore && el.scrollHeight - el.scrollTop - el.clientHeight < 120) {
1418
+ view.loadMore();
1211
1419
  }
1212
- ) : null
1213
- ]
1420
+ }
1421
+ },
1422
+ children: /* @__PURE__ */ jsxs4(
1423
+ "div",
1424
+ {
1425
+ ref: middleContentRef,
1426
+ className: "flex flex-col gap-[2px] p-3",
1427
+ children: [
1428
+ view.isLoading ? /* @__PURE__ */ jsx4(Feedback, { children: /* @__PURE__ */ jsx4(Spinner2, { size: 16 }) }) : view.isQuery ? (
1429
+ // 查询态(关键词或筛选):扁平结果
1430
+ view.searchResults.length === 0 ? /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.emptySearch") }) : view.searchResults.map((node) => /* @__PURE__ */ jsx4(
1431
+ SearchResultRow,
1432
+ {
1433
+ focused: isFocused(view.focusedNode, node),
1434
+ node,
1435
+ selected: view.isSelected(node),
1436
+ onFocus: view.setFocusedNode,
1437
+ onToggle: view.toggleSelection
1438
+ },
1439
+ nodeRefKey(node.ref)
1440
+ ))
1441
+ ) : !hasSelectedGroup ? /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.selectGroupHint") }) : view.currentEntries.length === 0 ? /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.emptyDirectory") }) : (
1442
+ // 浏览:就地递归展开树(复刻 agent 引用面板文件树交互)
1443
+ view.currentEntries.map((node) => /* @__PURE__ */ jsx4(
1444
+ TreeNodeRow,
1445
+ {
1446
+ copy,
1447
+ depth: 0,
1448
+ node,
1449
+ view
1450
+ },
1451
+ nodeRefKey(node.ref)
1452
+ ))
1453
+ ),
1454
+ view.hasMore && (view.isQuery || hasSelectedGroup) ? /* @__PURE__ */ jsxs4(
1455
+ Button3,
1456
+ {
1457
+ className: "mt-1 w-full",
1458
+ disabled: view.isLoadingMore,
1459
+ size: "sm",
1460
+ type: "button",
1461
+ variant: "ghost",
1462
+ onClick: view.loadMore,
1463
+ children: [
1464
+ view.isLoadingMore ? /* @__PURE__ */ jsx4(Spinner2, { className: "text-current", size: 14 }) : null,
1465
+ copy.t("referencePicker.loadMore")
1466
+ ]
1467
+ }
1468
+ ) : null
1469
+ ]
1470
+ }
1471
+ )
1214
1472
  }
1215
- ) })
1473
+ )
1216
1474
  ] })
1217
1475
  }
1218
1476
  ),
@@ -1221,6 +1479,7 @@ function ReferenceSourcePicker({
1221
1479
  {
1222
1480
  disableDoubleClick: true,
1223
1481
  withHandle: true,
1482
+ className: "after:bg-[var(--line-1)]",
1224
1483
  onDoubleClick: fitMiddle
1225
1484
  }
1226
1485
  ),
@@ -1228,17 +1487,16 @@ function ReferenceSourcePicker({
1228
1487
  ResizablePanel,
1229
1488
  {
1230
1489
  id: "preview",
1231
- className: "min-h-0 border-l border-[var(--line-1)]",
1490
+ className: "min-h-0 min-w-0",
1232
1491
  defaultSize: 30,
1233
1492
  minSize: "200px",
1234
1493
  children: /* @__PURE__ */ jsx4(
1235
1494
  PreviewInfoPane,
1236
1495
  {
1496
+ copy,
1237
1497
  node: view.focusedNode,
1238
- sourceLabel: view.activeTabLabel,
1239
- hierarchy: view.breadcrumb,
1240
- onReference: view.toggleSelection,
1241
- referenced: view.focusedNode ? view.isSelected(view.focusedNode) : false
1498
+ previewState: view.previewState,
1499
+ sourceLabel: view.activeTabLabel
1242
1500
  }
1243
1501
  )
1244
1502
  }
@@ -1269,52 +1527,73 @@ function ReferenceSourcePicker({
1269
1527
  if (typeof document === "undefined") {
1270
1528
  return dialog;
1271
1529
  }
1272
- return createPortal2(dialog, document.body);
1530
+ return createPortal(dialog, document.body);
1531
+ }
1532
+ function GroupFallbackIcon({
1533
+ icon,
1534
+ className
1535
+ }) {
1536
+ if (icon === "issue") {
1537
+ return /* @__PURE__ */ jsx4(IssueIcon, { className });
1538
+ }
1539
+ return /* @__PURE__ */ jsx4(FolderFilledIcon3, { className });
1273
1540
  }
1274
1541
  function SourceSidebar({
1542
+ copy,
1275
1543
  view,
1276
1544
  contentRef
1277
1545
  }) {
1546
+ const selectedGroupRef = useRef2(null);
1547
+ useEffect3(() => {
1548
+ selectedGroupRef.current?.scrollIntoView({ block: "nearest" });
1549
+ }, [view.selectedGroupKey]);
1550
+ const [shownBySource, setShownBySource] = useState4(
1551
+ {}
1552
+ );
1553
+ const loadMoreGroups = (sourceId) => {
1554
+ const groups = view.sidebarGroupsBySource[sourceId] ?? [];
1555
+ const limit = shownBySource[sourceId] ?? SIDEBAR_GROUP_PAGE_SIZE;
1556
+ const visibleCount = Math.max(groups.length, limit);
1557
+ if (groups.length > limit) {
1558
+ setShownBySource((prev) => ({ ...prev, [sourceId]: visibleCount }));
1559
+ return;
1560
+ }
1561
+ if (view.sidebarHasMoreBySource[sourceId] ?? false) {
1562
+ view.loadMoreSidebarGroups(sourceId);
1563
+ }
1564
+ };
1278
1565
  return /* @__PURE__ */ jsx4(ScrollArea2, { className: "h-full min-h-0 w-full", children: /* @__PURE__ */ jsxs4("div", { ref: contentRef, className: "flex flex-col gap-0.5 p-2", children: [
1279
- /* @__PURE__ */ jsx4("p", { className: "px-2 py-1 text-[11px] font-semibold text-[var(--text-tertiary)]", children: L.sourceColumn }),
1566
+ /* @__PURE__ */ jsx4("p", { className: "px-2 py-1 text-[11px] font-semibold text-[var(--text-tertiary)]", children: copy.t("referencePicker.sourceColumn") }),
1280
1567
  view.tabs.map((tab) => {
1281
- const active = tab.sourceId === view.activeSourceId;
1282
- const expanded = view.isSourceExpanded(tab.sourceId);
1283
1568
  const groups = view.sidebarGroupsBySource[tab.sourceId] ?? [];
1569
+ const limit = shownBySource[tab.sourceId] ?? SIDEBAR_GROUP_PAGE_SIZE;
1570
+ const selectedIndex = groups.findIndex(
1571
+ (group) => nodeRefKey(group.ref) === view.selectedGroupKey
1572
+ );
1573
+ const effectiveLimit = selectedIndex >= limit ? selectedIndex + 1 : limit;
1574
+ const visibleGroups = groups.slice(0, effectiveLimit);
1575
+ const loadingMore = view.sidebarLoadingMoreBySource[tab.sourceId] ?? false;
1576
+ const hasMore = groups.length > effectiveLimit || (view.sidebarHasMoreBySource[tab.sourceId] ?? false);
1284
1577
  return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-0.5", children: [
1285
- /* @__PURE__ */ jsxs4(
1286
- "button",
1578
+ /* @__PURE__ */ jsx4(
1579
+ "p",
1287
1580
  {
1288
- "aria-expanded": expanded,
1289
- className: cn3(
1290
- "flex items-center gap-1.5 rounded-[6px] px-2 py-1.5 text-left text-[13px] font-semibold transition-colors hover:bg-transparency-block",
1291
- active ? "text-[var(--text-primary)]" : "text-[var(--text-secondary)]"
1292
- ),
1293
- type: "button",
1294
- onClick: () => view.toggleSourceExpanded(tab.sourceId),
1295
- children: [
1296
- /* @__PURE__ */ jsx4(
1297
- ArrowRightIcon2,
1298
- {
1299
- className: cn3(
1300
- "size-3 shrink-0 text-[var(--text-tertiary)] transition-transform",
1301
- expanded && "rotate-90"
1302
- )
1303
- }
1304
- ),
1305
- /* @__PURE__ */ jsx4("span", { className: "truncate", "data-autofit-label": true, children: tab.label })
1306
- ]
1581
+ className: "px-2 pt-1.5 pb-0.5 text-[11px] font-semibold text-[var(--text-tertiary)]",
1582
+ "data-autofit-label": true,
1583
+ children: tab.label
1307
1584
  }
1308
1585
  ),
1309
- expanded ? /* @__PURE__ */ jsx4("div", { className: "flex flex-col gap-0.5", children: groups.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-2 py-1.5 pl-7 text-[12px] text-[var(--text-tertiary)]", children: view.isLoadingTabs ? "\u2026" : "" }) : groups.map((group) => {
1586
+ groups.length === 0 ? view.isLoadingTabs ? /* @__PURE__ */ jsx4("p", { className: "px-2 py-1 text-[12px] text-[var(--text-tertiary)]", children: "\u2026" }) : null : visibleGroups.map((group) => {
1310
1587
  const key = nodeRefKey(group.ref);
1311
1588
  const selected = key === view.selectedGroupKey;
1312
1589
  return /* @__PURE__ */ jsxs4(
1313
1590
  "button",
1314
1591
  {
1592
+ ref: selected ? selectedGroupRef : void 0,
1593
+ "aria-current": selected ? "true" : void 0,
1315
1594
  className: cn3(
1316
- "flex items-center gap-2 rounded-[6px] py-1.5 pr-2 pl-7 text-left text-[13px] transition-colors hover:bg-transparency-block",
1317
- selected ? "bg-transparency-block text-[var(--text-primary)]" : "text-[var(--text-secondary)]"
1595
+ "flex items-center gap-2 rounded-[6px] px-2 py-1.5 text-left text-[13px] transition-colors",
1596
+ selected ? "bg-primary/10 font-medium text-[var(--text-primary)] hover:bg-primary/15" : "text-[var(--text-secondary)] hover:bg-[var(--transparency-hover)] hover:text-[var(--text-primary)]"
1318
1597
  ),
1319
1598
  type: "button",
1320
1599
  onClick: () => view.selectGroup(group),
@@ -1326,21 +1605,52 @@ function SourceSidebar({
1326
1605
  className: "size-4 shrink-0 rounded-[3px] object-cover",
1327
1606
  src: group.iconUrl
1328
1607
  }
1329
- ) : /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-4 shrink-0 text-[var(--rich-text-folder)]" }),
1330
- /* @__PURE__ */ jsx4(
1608
+ ) : /* @__PURE__ */ jsx4(
1609
+ GroupFallbackIcon,
1610
+ {
1611
+ className: "size-4 shrink-0 text-[var(--rich-text-folder)]",
1612
+ icon: tab.icon
1613
+ }
1614
+ ),
1615
+ /* @__PURE__ */ jsx4(FullTextTooltip, { content: group.displayName, children: /* @__PURE__ */ jsx4(
1331
1616
  "span",
1332
1617
  {
1333
1618
  className: "min-w-0 flex-1 truncate",
1334
1619
  "data-autofit-label": true,
1335
1620
  children: group.displayName
1336
1621
  }
1337
- ),
1622
+ ) }),
1338
1623
  group.childCount != null ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-[11px] text-[var(--text-tertiary)]", children: group.childCount }) : null
1339
1624
  ]
1340
1625
  },
1341
1626
  key
1342
1627
  );
1343
- }) }) : null
1628
+ }),
1629
+ hasMore ? /* @__PURE__ */ jsxs4(
1630
+ "button",
1631
+ {
1632
+ className: "flex items-center gap-1.5 rounded-[6px] px-2 py-1.5 text-left text-[12px] text-[var(--text-secondary)] transition-colors hover:bg-[var(--transparency-hover)] hover:text-[var(--text-primary)] disabled:opacity-60",
1633
+ disabled: loadingMore,
1634
+ type: "button",
1635
+ onClick: () => loadMoreGroups(tab.sourceId),
1636
+ children: [
1637
+ loadingMore ? /* @__PURE__ */ jsx4(
1638
+ Spinner2,
1639
+ {
1640
+ className: "text-[var(--text-secondary)]",
1641
+ size: 12
1642
+ }
1643
+ ) : /* @__PURE__ */ jsx4(
1644
+ ChevronDownIcon,
1645
+ {
1646
+ className: "shrink-0 text-[var(--text-secondary)]",
1647
+ size: 12
1648
+ }
1649
+ ),
1650
+ /* @__PURE__ */ jsx4("span", { children: copy.t("referencePicker.loadMoreGroups") })
1651
+ ]
1652
+ }
1653
+ ) : null
1344
1654
  ] }, tab.sourceId);
1345
1655
  })
1346
1656
  ] }) });
@@ -1353,77 +1663,215 @@ function SearchResultRow({
1353
1663
  onToggle
1354
1664
  }) {
1355
1665
  const isFolder = node.kind === "folder";
1666
+ const contextLabel = node.contextLabel ?? node.ref.nodeId;
1356
1667
  return /* @__PURE__ */ jsxs4(
1357
1668
  "div",
1358
1669
  {
1359
1670
  className: cn3(
1360
- "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-[6px] border py-2.5 pr-1 pl-3 transition-colors",
1671
+ "grid cursor-pointer grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-[6px] border py-2.5 pr-1 pl-3 transition-colors",
1361
1672
  focused || selected ? "border-border bg-transparency-block" : "border-transparent bg-transparent hover:border-border/70 hover:bg-transparency-block"
1362
1673
  ),
1674
+ onClick: () => onFocus(node),
1363
1675
  children: [
1364
- /* @__PURE__ */ jsxs4(
1365
- "button",
1366
- {
1367
- className: "flex min-w-0 items-center gap-3 text-left",
1368
- type: "button",
1369
- onClick: () => onFocus(node),
1370
- children: [
1371
- /* @__PURE__ */ jsx4("span", { className: "grid size-9 shrink-0 place-items-center rounded-lg bg-[var(--transparency-block)] text-[var(--text-tertiary)]", children: isFolder ? /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-4 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx4(FileIcon3, { className: "size-4 text-[var(--text-tertiary)]" }) }),
1372
- /* @__PURE__ */ jsxs4("span", { className: "min-w-0", children: [
1373
- /* @__PURE__ */ jsx4("span", { className: "block truncate text-[13px] font-medium text-[var(--text-primary)]", children: node.displayName }),
1374
- /* @__PURE__ */ jsx4("span", { className: "block truncate text-[11px] text-[var(--text-secondary)]", children: node.ref.nodeId })
1375
- ] })
1376
- ]
1377
- }
1378
- ),
1676
+ /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-center gap-3 text-left", children: [
1677
+ /* @__PURE__ */ jsx4("span", { className: "grid size-9 shrink-0 place-items-center rounded-lg bg-[var(--transparency-block)] text-[var(--text-tertiary)]", children: isFolder ? /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-4 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx4(FileIcon3, { className: "size-4 text-[var(--text-tertiary)]" }) }),
1678
+ /* @__PURE__ */ jsxs4("span", { className: "min-w-0", children: [
1679
+ /* @__PURE__ */ jsx4(FullTextTooltip, { content: node.displayName, children: /* @__PURE__ */ jsx4("span", { className: "block truncate text-[13px] font-medium text-[var(--text-primary)]", children: node.displayName }) }),
1680
+ /* @__PURE__ */ jsx4(FullTextTooltip, { content: contextLabel, children: /* @__PURE__ */ jsx4("span", { className: "block truncate text-[11px] text-[var(--text-secondary)]", children: contextLabel }) })
1681
+ ] })
1682
+ ] }),
1379
1683
  /* @__PURE__ */ jsx4(
1380
- Button4,
1684
+ Button3,
1381
1685
  {
1382
1686
  "aria-label": node.displayName,
1383
1687
  "aria-pressed": selected,
1384
1688
  size: "icon-sm",
1385
1689
  type: "button",
1386
1690
  variant: "ghost",
1387
- onClick: () => {
1691
+ onClick: (event) => {
1692
+ event.stopPropagation();
1388
1693
  onFocus(node);
1389
1694
  onToggle(node);
1390
1695
  },
1391
- children: selected ? /* @__PURE__ */ jsx4(CheckIcon2, { size: 14 }) : /* @__PURE__ */ jsx4(AddIcon, { className: "text-[var(--text-secondary)]", size: 16 })
1696
+ children: selected ? /* @__PURE__ */ jsx4(CheckIcon2, { size: 14 }) : /* @__PURE__ */ jsx4(AddLinedIcon2, { className: "text-[var(--text-secondary)]", size: 16 })
1392
1697
  }
1393
1698
  )
1394
1699
  ]
1395
1700
  }
1396
1701
  );
1397
1702
  }
1703
+ function toPreviewSurfaceState(node, previewState, copy) {
1704
+ if (!("node" in previewState) || nodeRefKey(previewState.node.ref) !== nodeRefKey(node.ref)) {
1705
+ return node.kind === "folder" ? { entry: node, status: "directory" } : { entry: node, status: "loading" };
1706
+ }
1707
+ switch (previewState.status) {
1708
+ case "directory":
1709
+ return { entry: node, status: "directory" };
1710
+ case "loading":
1711
+ return { entry: node, status: "loading" };
1712
+ case "image":
1713
+ return {
1714
+ entry: node,
1715
+ objectUrl: previewState.objectUrl,
1716
+ status: "image"
1717
+ };
1718
+ case "video":
1719
+ return {
1720
+ entry: node,
1721
+ objectUrl: previewState.objectUrl,
1722
+ status: "video"
1723
+ };
1724
+ case "text":
1725
+ return { content: previewState.content, entry: node, status: "text" };
1726
+ case "html":
1727
+ return { content: previewState.content, entry: node, status: "html" };
1728
+ case "readonly":
1729
+ return {
1730
+ entry: node,
1731
+ message: previewState.reason === "binary" ? copy.t("referencePicker.previewBinary") : previewState.reason === "file_too_large" || previewState.reason === "text_too_large" ? copy.t("referencePicker.previewTooLarge") : copy.t("referencePicker.previewUnsupported"),
1732
+ status: "readonly"
1733
+ };
1734
+ case "error":
1735
+ return {
1736
+ entry: node,
1737
+ message: copy.t("referencePicker.previewError"),
1738
+ status: "error"
1739
+ };
1740
+ case "unsupported":
1741
+ return {
1742
+ entry: node,
1743
+ message: copy.t("referencePicker.previewUnsupported"),
1744
+ status: "unsupported"
1745
+ };
1746
+ }
1747
+ }
1748
+ function sourceBadgeClassName(sourceId) {
1749
+ return SOURCE_BADGE_CLASSES[sourceId];
1750
+ }
1751
+ var SOURCE_BADGE_CLASSES = {
1752
+ "workspace-file": "bg-[color-mix(in_srgb,var(--rich-text-mention-file)_12%,transparent)] text-[var(--rich-text-mention-file)]",
1753
+ "app-artifact": "bg-[color-mix(in_srgb,var(--rich-text-mention-app)_12%,transparent)] text-[var(--rich-text-mention-app)]",
1754
+ "issue-file": "bg-[color-mix(in_srgb,var(--rich-text-mention-issue)_12%,transparent)] text-[var(--rich-text-mention-issue)]"
1755
+ };
1756
+ function FullTextTooltip({
1757
+ children,
1758
+ content
1759
+ }) {
1760
+ return /* @__PURE__ */ jsxs4(Tooltip2, { delayDuration: 300, children: [
1761
+ /* @__PURE__ */ jsx4(TooltipTrigger2, { asChild: true, children }),
1762
+ /* @__PURE__ */ jsx4(
1763
+ TooltipContent2,
1764
+ {
1765
+ className: "max-w-[min(520px,calc(100vw-32px))] whitespace-normal text-left [overflow-wrap:anywhere]",
1766
+ side: "top",
1767
+ style: {
1768
+ maxWidth: "min(520px, calc(100vw - 32px))",
1769
+ overflowWrap: "anywhere",
1770
+ whiteSpace: "normal",
1771
+ backgroundColor: "var(--background-fronted)",
1772
+ border: "1px solid var(--border-1)",
1773
+ borderRadius: 6,
1774
+ boxShadow: "var(--shadow-soft)",
1775
+ color: "var(--text-primary)",
1776
+ padding: "4px 8px"
1777
+ },
1778
+ children: content
1779
+ }
1780
+ )
1781
+ ] });
1782
+ }
1398
1783
  function PreviewInfoPane({
1784
+ copy,
1399
1785
  node,
1400
- sourceLabel,
1401
- hierarchy,
1402
- referenced,
1403
- onReference
1786
+ previewState,
1787
+ sourceLabel
1404
1788
  }) {
1405
1789
  return /* @__PURE__ */ jsx4("aside", { className: "flex h-full min-h-0 w-full flex-col bg-[var(--background-fronted)]", children: node ? /* @__PURE__ */ jsxs4("div", { className: "flex min-h-0 flex-1 flex-col gap-4 p-4", children: [
1406
- /* @__PURE__ */ jsx4("div", { className: "grid aspect-[3/2] w-full place-items-center rounded-[8px] border border-[var(--line-2,var(--border-2))] bg-[var(--transparency-block)]", children: /* @__PURE__ */ jsx4(FileIcon3, { className: "size-9 text-[var(--text-tertiary)]" }) }),
1407
- /* @__PURE__ */ jsx4("p", { className: "truncate text-[15px] font-semibold", children: node.displayName }),
1408
- /* @__PURE__ */ jsxs4("dl", { className: "space-y-2 text-[13px]", children: [
1409
- /* @__PURE__ */ jsx4(InfoRow, { label: L.previewSource, children: /* @__PURE__ */ jsx4(Badge2, { variant: "secondary", children: sourceLabel }) }),
1410
- node.mtimeMs != null ? /* @__PURE__ */ jsx4(InfoRow, { label: L.previewModified, children: formatDateTime(node.mtimeMs) }) : null,
1411
- node.sizeBytes != null ? /* @__PURE__ */ jsx4(InfoRow, { label: L.previewSize, children: formatBytes(node.sizeBytes) }) : null
1790
+ /* @__PURE__ */ jsx4(
1791
+ WorkspaceFilePreviewSurface,
1792
+ {
1793
+ directoryMessage: copy.t("referencePicker.previewFolder"),
1794
+ emptyMessage: copy.t("referencePicker.emptyPreview"),
1795
+ frameClassName: "flex aspect-[3/2] w-full flex-col items-center justify-center overflow-hidden rounded-[8px] border border-[var(--line-2,var(--border-2))] bg-[var(--transparency-block)] p-0 text-center",
1796
+ imageAlt: (entry) => entry.displayName,
1797
+ htmlFrameClassName: "items-stretch justify-stretch bg-white",
1798
+ htmlTitle: (entry) => entry.displayName,
1799
+ imageFrameClassName: "p-3",
1800
+ loadingIndicator: /* @__PURE__ */ jsx4(Spinner2, { size: 16 }),
1801
+ loadingMessage: copy.t("referencePicker.previewLoading"),
1802
+ messageClassName: "mx-auto max-w-[24ch] text-[13px] leading-5 text-[var(--text-secondary)] [overflow-wrap:anywhere]",
1803
+ renderIcon: (entry) => entry.kind === "folder" ? /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-9 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx4(FileIcon3, { className: "size-9 text-[var(--text-tertiary)]" }),
1804
+ state: toPreviewSurfaceState(node, previewState, copy),
1805
+ textClassName: "h-full w-full overflow-auto p-3 text-left text-[11px] leading-5 whitespace-pre-wrap break-words text-[var(--text-primary)]",
1806
+ textFrameClassName: "items-stretch justify-stretch"
1807
+ }
1808
+ ),
1809
+ /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
1810
+ /* @__PURE__ */ jsx4("p", { className: "truncate text-[15px] font-semibold", children: node.displayName }),
1811
+ /* @__PURE__ */ jsx4(ReferencePathText, { node })
1412
1812
  ] }),
1413
- hierarchy.length > 0 ? /* @__PURE__ */ jsxs4("div", { className: "space-y-1.5", children: [
1414
- /* @__PURE__ */ jsx4("p", { className: "text-[11px] font-semibold text-[var(--text-tertiary)]", children: L.previewHierarchy }),
1415
- /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-1", children: hierarchy.map((crumb) => /* @__PURE__ */ jsx4(Badge2, { variant: "secondary", children: crumb.displayName }, nodeRefKey(crumb.ref))) })
1416
- ] }) : null,
1417
- /* @__PURE__ */ jsx4("div", { className: "mt-auto flex justify-end", children: /* @__PURE__ */ jsx4(
1418
- Button4,
1813
+ /* @__PURE__ */ jsxs4("dl", { className: "space-y-2 text-[13px]", children: [
1814
+ /* @__PURE__ */ jsx4(InfoRow, { label: copy.t("referencePicker.previewSource"), children: /* @__PURE__ */ jsx4(
1815
+ Badge2,
1816
+ {
1817
+ variant: "secondary",
1818
+ className: sourceBadgeClassName(node.ref.sourceId),
1819
+ children: sourceLabel
1820
+ }
1821
+ ) }),
1822
+ node.mtimeMs != null ? /* @__PURE__ */ jsx4(InfoRow, { label: copy.t("referencePicker.previewModified"), children: formatDateTime(node.mtimeMs) }) : null,
1823
+ node.sizeBytes != null ? /* @__PURE__ */ jsx4(InfoRow, { label: copy.t("referencePicker.previewSize"), children: formatBytes(node.sizeBytes) }) : null
1824
+ ] })
1825
+ ] }) : /* @__PURE__ */ jsx4(Feedback, { children: copy.t("referencePicker.emptyPreview") }) });
1826
+ }
1827
+ function ReferencePathText({ node }) {
1828
+ const pathText = getReferenceNodePathText(node);
1829
+ const lastSlashIndex = pathText.lastIndexOf("/");
1830
+ if (lastSlashIndex <= 0 || lastSlashIndex === pathText.length - 1) {
1831
+ return /* @__PURE__ */ jsx4(
1832
+ "p",
1419
1833
  {
1420
- type: "button",
1421
- variant: referenced ? "secondary" : void 0,
1422
- onClick: () => onReference(node),
1423
- children: L.reference
1834
+ className: "truncate text-[12px] leading-5 text-[var(--text-tertiary)]",
1835
+ title: pathText,
1836
+ children: pathText
1424
1837
  }
1425
- ) })
1426
- ] }) : /* @__PURE__ */ jsx4(Feedback, { children: L.emptyPreview }) });
1838
+ );
1839
+ }
1840
+ return /* @__PURE__ */ jsxs4(
1841
+ "p",
1842
+ {
1843
+ className: "flex min-w-0 items-center text-[12px] leading-5 text-[var(--text-tertiary)]",
1844
+ title: pathText,
1845
+ children: [
1846
+ /* @__PURE__ */ jsx4("span", { className: "min-w-0 truncate", children: pathText.slice(0, lastSlashIndex + 1) }),
1847
+ /* @__PURE__ */ jsx4("span", { className: "max-w-[65%] shrink-0 truncate", children: pathText.slice(lastSlashIndex + 1) })
1848
+ ]
1849
+ }
1850
+ );
1851
+ }
1852
+ function getReferenceNodePathText(node) {
1853
+ const decodedPath = decodeReferenceListFileNodeId(node.ref.nodeId);
1854
+ if (decodedPath) {
1855
+ return decodedPath;
1856
+ }
1857
+ return node.contextLabel?.trim() || node.ref.nodeId;
1858
+ }
1859
+ function decodeReferenceListFileNodeId(nodeId) {
1860
+ if (!nodeId.startsWith("f:")) {
1861
+ return null;
1862
+ }
1863
+ try {
1864
+ const normalized = nodeId.slice(2).replace(/-/g, "+").replace(/_/g, "/");
1865
+ const padded = normalized.padEnd(
1866
+ normalized.length + (4 - normalized.length % 4) % 4,
1867
+ "="
1868
+ );
1869
+ const binary = atob(padded);
1870
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
1871
+ return new TextDecoder().decode(bytes);
1872
+ } catch {
1873
+ return null;
1874
+ }
1427
1875
  }
1428
1876
  function InfoRow({
1429
1877
  label,
@@ -1444,19 +1892,72 @@ function Footer({
1444
1892
  onClose,
1445
1893
  onConfirm
1446
1894
  }) {
1895
+ const selectionTooltipId = useId2();
1896
+ const [selectionTooltipOpen, setSelectionTooltipOpen] = useState4(false);
1897
+ const selectionTooltipLabel = selection.map((node) => node.displayName).join("\n");
1447
1898
  return /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-3 border-t border-[var(--line-1)] px-4 py-3 sm:px-6", children: [
1448
1899
  /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-center gap-2", children: [
1449
- /* @__PURE__ */ jsx4("span", { className: "text-[13px] text-[var(--text-secondary)]", children: countLabel }),
1450
- selection.slice(0, 2).map((node) => /* @__PURE__ */ jsx4(Badge2, { variant: "secondary", children: /* @__PURE__ */ jsx4("span", { className: "truncate", children: node.displayName }) }, nodeRefKey(node.ref))),
1451
- selection.length > 2 ? /* @__PURE__ */ jsxs4(Badge2, { variant: "secondary", children: [
1452
- "+",
1453
- selection.length - 2
1454
- ] }) : null
1900
+ /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-[13px] text-[var(--text-secondary)]", children: countLabel }),
1901
+ selection.slice(0, 2).map((node) => /* @__PURE__ */ jsx4(
1902
+ Badge2,
1903
+ {
1904
+ className: "min-w-0 max-w-[12rem]",
1905
+ variant: "secondary",
1906
+ children: /* @__PURE__ */ jsx4(FullTextTooltip, { content: node.displayName, children: /* @__PURE__ */ jsx4("span", { className: "truncate", children: node.displayName }) })
1907
+ },
1908
+ nodeRefKey(node.ref)
1909
+ )),
1910
+ selection.length > 2 ? /* @__PURE__ */ jsxs4(
1911
+ "span",
1912
+ {
1913
+ className: "relative inline-flex shrink-0",
1914
+ onBlur: () => setSelectionTooltipOpen(false),
1915
+ onFocus: () => setSelectionTooltipOpen(true),
1916
+ onMouseEnter: () => setSelectionTooltipOpen(true),
1917
+ onMouseLeave: () => setSelectionTooltipOpen(false),
1918
+ children: [
1919
+ /* @__PURE__ */ jsx4(
1920
+ Badge2,
1921
+ {
1922
+ asChild: true,
1923
+ className: "shrink-0 cursor-default",
1924
+ variant: "secondary",
1925
+ children: /* @__PURE__ */ jsxs4(
1926
+ "button",
1927
+ {
1928
+ "aria-describedby": selectionTooltipId,
1929
+ "aria-label": selectionTooltipLabel,
1930
+ type: "button",
1931
+ children: [
1932
+ "+",
1933
+ selection.length - 2
1934
+ ]
1935
+ }
1936
+ )
1937
+ }
1938
+ ),
1939
+ /* @__PURE__ */ jsx4(
1940
+ "span",
1941
+ {
1942
+ "aria-hidden": !selectionTooltipOpen,
1943
+ className: "pointer-events-none absolute bottom-[calc(100%+8px)] left-0 z-[var(--z-tooltip,100700)] max-h-[min(20rem,calc(100vh-96px))] w-max max-w-[min(28rem,calc(100vw-32px))] overflow-auto whitespace-pre-line rounded-md border border-[var(--border-1)] bg-[var(--background-fronted)] px-2 py-1 text-left text-[13px] leading-[1.3] text-[var(--text-primary)] shadow-soft transition-opacity duration-100 [overflow-wrap:anywhere]",
1944
+ id: selectionTooltipId,
1945
+ role: "tooltip",
1946
+ style: {
1947
+ opacity: selectionTooltipOpen ? 1 : 0,
1948
+ visibility: selectionTooltipOpen ? "visible" : "hidden"
1949
+ },
1950
+ children: selectionTooltipLabel
1951
+ }
1952
+ )
1953
+ ]
1954
+ }
1955
+ ) : null
1455
1956
  ] }),
1456
1957
  /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
1457
- /* @__PURE__ */ jsx4(Button4, { type: "button", variant: "secondary", onClick: onClose, children: cancelLabel }),
1958
+ /* @__PURE__ */ jsx4(Button3, { type: "button", variant: "secondary", onClick: onClose, children: cancelLabel }),
1458
1959
  /* @__PURE__ */ jsxs4(
1459
- Button4,
1960
+ Button3,
1460
1961
  {
1461
1962
  disabled: disabled || loading,
1462
1963
  type: "button",
@@ -1473,6 +1974,112 @@ function Footer({
1473
1974
  function Feedback({ children }) {
1474
1975
  return /* @__PURE__ */ jsx4("div", { className: "grid min-h-0 flex-1 place-items-center px-4 py-8 text-center text-[13px] text-[var(--text-secondary)]", children });
1475
1976
  }
1977
+ function FilterCategoryFilter({
1978
+ categories,
1979
+ copy,
1980
+ selected,
1981
+ onClear,
1982
+ onToggle
1983
+ }) {
1984
+ const [open, setOpen] = useState4(false);
1985
+ const containerRef = useRef2(null);
1986
+ useEffect3(() => {
1987
+ if (!open) {
1988
+ return;
1989
+ }
1990
+ const onPointerDown = (event) => {
1991
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
1992
+ setOpen(false);
1993
+ }
1994
+ };
1995
+ const onKeyDown = (event) => {
1996
+ if (event.key === "Escape") {
1997
+ setOpen(false);
1998
+ }
1999
+ };
2000
+ document.addEventListener("pointerdown", onPointerDown, true);
2001
+ document.addEventListener("keydown", onKeyDown);
2002
+ return () => {
2003
+ document.removeEventListener("pointerdown", onPointerDown, true);
2004
+ document.removeEventListener("keydown", onKeyDown);
2005
+ };
2006
+ }, [open]);
2007
+ const selectedLabels = categories.filter((category) => selected.has(category.id)).map((category) => copy.t(category.labelKey));
2008
+ const count = selectedLabels.length;
2009
+ const labelText = count > 0 ? selectedLabels.join(copy.t("referencePicker.fileTypeSeparator")) : copy.t("referencePicker.fileTypeAll");
2010
+ return (
2011
+ // shrink-0:不被搜索框挤压;固定宽度让控件不随选中文案变宽。
2012
+ /* @__PURE__ */ jsxs4("div", { ref: containerRef, className: "relative w-[124px] shrink-0", children: [
2013
+ /* @__PURE__ */ jsxs4(
2014
+ Button3,
2015
+ {
2016
+ "aria-expanded": open,
2017
+ "aria-haspopup": "menu",
2018
+ className: "h-8 w-full justify-between gap-1.5 border-0 px-2.5",
2019
+ size: "default",
2020
+ type: "button",
2021
+ variant: "secondary",
2022
+ onClick: () => setOpen((current) => !current),
2023
+ children: [
2024
+ /* @__PURE__ */ jsx4("span", { className: "min-w-0 flex-1 truncate text-left", title: labelText, children: labelText }),
2025
+ count > 1 ? /* @__PURE__ */ jsx4(Badge2, { className: "shrink-0 px-1.5", variant: "secondary", children: count }) : null,
2026
+ count > 0 ? (
2027
+ // 有选中时,右侧箭头位让位给「清除」:role=button 的 span(避免 button 嵌
2028
+ // button 的非法结构),stopPropagation 让点击只清空筛选、不触发触发器的开合。
2029
+ /* @__PURE__ */ jsx4(
2030
+ "span",
2031
+ {
2032
+ "aria-label": copy.t("referencePicker.clearFilter"),
2033
+ className: "grid size-4 shrink-0 cursor-pointer place-items-center rounded-full text-[var(--text-tertiary)] transition-colors hover:bg-[var(--transparency-hover)] hover:text-[var(--text-primary)]",
2034
+ role: "button",
2035
+ tabIndex: 0,
2036
+ onClick: (event) => {
2037
+ event.stopPropagation();
2038
+ onClear();
2039
+ },
2040
+ onKeyDown: (event) => {
2041
+ if (event.key === "Enter" || event.key === " ") {
2042
+ event.preventDefault();
2043
+ event.stopPropagation();
2044
+ onClear();
2045
+ }
2046
+ },
2047
+ children: /* @__PURE__ */ jsx4(CloseIcon, { size: 12 })
2048
+ }
2049
+ )
2050
+ ) : /* @__PURE__ */ jsx4(ChevronDownIcon, { className: "size-3.5 shrink-0 text-[var(--text-tertiary)]" })
2051
+ ]
2052
+ }
2053
+ ),
2054
+ open ? /* @__PURE__ */ jsx4(
2055
+ "div",
2056
+ {
2057
+ className: "absolute top-[calc(100%+4px)] right-0 min-w-40 overflow-hidden rounded-[8px] border border-[var(--line-1)] bg-[var(--background-fronted)] p-1 shadow-panel",
2058
+ role: "menu",
2059
+ style: { zIndex: "var(--z-panel-popover)" },
2060
+ children: categories.map((category) => {
2061
+ const checked = selected.has(category.id);
2062
+ return /* @__PURE__ */ jsxs4(
2063
+ "button",
2064
+ {
2065
+ "aria-checked": checked,
2066
+ className: "flex w-full items-center gap-2 rounded-[6px] px-2 py-1.5 text-left text-[13px] text-[var(--text-primary)] transition-colors hover:bg-[var(--transparency-hover)]",
2067
+ role: "menuitemcheckbox",
2068
+ type: "button",
2069
+ onClick: () => onToggle(category.id),
2070
+ children: [
2071
+ /* @__PURE__ */ jsx4("span", { className: "grid size-4 shrink-0 place-items-center", children: checked ? /* @__PURE__ */ jsx4(CheckIcon2, { className: "size-3.5 text-[var(--tutti-purple)]" }) : null }),
2072
+ /* @__PURE__ */ jsx4("span", { className: "flex-1", children: copy.t(category.labelKey) })
2073
+ ]
2074
+ },
2075
+ category.id
2076
+ );
2077
+ })
2078
+ }
2079
+ ) : null
2080
+ ] })
2081
+ );
2082
+ }
1476
2083
  var TREE_INDENT = 28;
1477
2084
  var TREE_COLLAPSE_DURATION_MS = 200;
1478
2085
  function isFocused(focused, node) {
@@ -1491,7 +2098,7 @@ function TreeNodeRow({
1491
2098
  const childEntries = view.sortNodes(childState?.entries ?? []);
1492
2099
  const selected = view.isSelected(node);
1493
2100
  const focused = isFocused(view.focusedNode, node);
1494
- const [shouldRenderChildContent, setShouldRenderChildContent] = useState3(expanded);
2101
+ const [shouldRenderChildContent, setShouldRenderChildContent] = useState4(expanded);
1495
2102
  useEffect3(() => {
1496
2103
  if (expanded) {
1497
2104
  setShouldRenderChildContent(true);
@@ -1535,10 +2142,16 @@ function TreeNodeRow({
1535
2142
  "div",
1536
2143
  {
1537
2144
  className: cn3(
1538
- "flex items-center gap-2 rounded-[6px] py-1.5 pr-1 transition-colors",
2145
+ "flex cursor-pointer items-center gap-2 rounded-[6px] py-1.5 pr-1 transition-colors",
1539
2146
  focused || selected ? "bg-transparency-block" : "hover:bg-transparency-block"
1540
2147
  ),
1541
2148
  style: { paddingLeft: `${depth * TREE_INDENT + 8}px` },
2149
+ onClick: () => {
2150
+ view.setFocusedNode(node);
2151
+ if (isFolder) {
2152
+ view.toggleNode(node);
2153
+ }
2154
+ },
1542
2155
  children: [
1543
2156
  isFolder ? /* @__PURE__ */ jsx4(
1544
2157
  "button",
@@ -1546,7 +2159,11 @@ function TreeNodeRow({
1546
2159
  "aria-label": node.displayName,
1547
2160
  className: "grid size-5 shrink-0 place-items-center rounded-sm text-[var(--text-secondary)] hover:bg-[var(--transparency-hover)]",
1548
2161
  type: "button",
1549
- onClick: () => view.toggleNode(node),
2162
+ onClick: (event) => {
2163
+ event.stopPropagation();
2164
+ view.setFocusedNode(node);
2165
+ view.toggleNode(node);
2166
+ },
1550
2167
  children: /* @__PURE__ */ jsx4(
1551
2168
  ArrowRightIcon2,
1552
2169
  {
@@ -1558,32 +2175,17 @@ function TreeNodeRow({
1558
2175
  )
1559
2176
  }
1560
2177
  ) : null,
1561
- /* @__PURE__ */ jsxs4(
1562
- "button",
2178
+ isFolder ? /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-4 shrink-0 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx4(FileIcon3, { className: "size-4 shrink-0 text-[var(--text-tertiary)]" }),
2179
+ /* @__PURE__ */ jsx4(FullTextTooltip, { content: node.displayName, children: /* @__PURE__ */ jsx4(
2180
+ "span",
1563
2181
  {
1564
- className: "flex min-w-0 flex-1 items-center gap-2 text-left",
1565
- type: "button",
1566
- onClick: () => {
1567
- view.setFocusedNode(node);
1568
- if (isFolder) {
1569
- view.toggleNode(node);
1570
- }
1571
- },
1572
- children: [
1573
- isFolder ? /* @__PURE__ */ jsx4(FolderFilledIcon3, { className: "size-4 shrink-0 text-[var(--rich-text-folder)]" }) : /* @__PURE__ */ jsx4(FileIcon3, { className: "size-4 shrink-0 text-[var(--text-tertiary)]" }),
1574
- /* @__PURE__ */ jsx4(
1575
- "span",
1576
- {
1577
- className: "truncate text-[13px] text-[var(--text-primary)]",
1578
- "data-autofit-label": true,
1579
- children: node.displayName
1580
- }
1581
- )
1582
- ]
2182
+ className: "min-w-0 flex-1 truncate text-[13px] text-[var(--text-primary)]",
2183
+ "data-autofit-label": true,
2184
+ children: node.displayName
1583
2185
  }
1584
- ),
2186
+ ) }),
1585
2187
  /* @__PURE__ */ jsx4(
1586
- Button4,
2188
+ Button3,
1587
2189
  {
1588
2190
  "aria-label": node.displayName,
1589
2191
  "aria-pressed": selected,
@@ -1591,11 +2193,12 @@ function TreeNodeRow({
1591
2193
  size: "icon-sm",
1592
2194
  type: "button",
1593
2195
  variant: "ghost",
1594
- onClick: () => {
2196
+ onClick: (event) => {
2197
+ event.stopPropagation();
1595
2198
  view.setFocusedNode(node);
1596
2199
  view.toggleSelection(node);
1597
2200
  },
1598
- children: selected ? /* @__PURE__ */ jsx4(CheckIcon2, { size: 14 }) : /* @__PURE__ */ jsx4(AddIcon, { className: "text-[var(--text-secondary)]", size: 16 })
2201
+ children: selected ? /* @__PURE__ */ jsx4(CheckIcon2, { size: 14 }) : /* @__PURE__ */ jsx4(AddLinedIcon2, { className: "text-[var(--text-secondary)]", size: 16 })
1599
2202
  }
1600
2203
  )
1601
2204
  ]