@tutti-os/workspace-file-reference 0.0.1

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.
@@ -0,0 +1,901 @@
1
+ import {
2
+ uniqueWorkspaceFileReferences
3
+ } from "./chunk-PAR2R2G5.js";
4
+
5
+ // src/react/internal/reference/WorkspaceFileReferencePickerState.ts
6
+ var workspaceFileReferenceDefaultExpandedDepth = 4;
7
+ function normalizeDirectoryPath(path) {
8
+ if (!path || path === "/") {
9
+ return "/";
10
+ }
11
+ return path.replace(/\/+$/, "") || "/";
12
+ }
13
+ function collectVisibleTreeEntries(entries, directoryStateByPath, expandedFolderPaths) {
14
+ const collected = [];
15
+ for (const entry of entries) {
16
+ collected.push(entry);
17
+ if (entry.kind !== "folder") {
18
+ continue;
19
+ }
20
+ const folderKey = normalizeDirectoryPath(entry.path);
21
+ if (!expandedFolderPaths[folderKey]) {
22
+ continue;
23
+ }
24
+ const childEntries = directoryStateByPath[folderKey]?.entries ?? [];
25
+ if (childEntries.length === 0) {
26
+ continue;
27
+ }
28
+ collected.push(
29
+ ...collectVisibleTreeEntries(
30
+ childEntries,
31
+ directoryStateByPath,
32
+ expandedFolderPaths
33
+ )
34
+ );
35
+ }
36
+ return collected;
37
+ }
38
+ function mergeExpandedFolderPaths(current, prefetched) {
39
+ return {
40
+ ...prefetched,
41
+ ...current
42
+ };
43
+ }
44
+ function mergePrefetchedDirectoryState(current, prefetched) {
45
+ return {
46
+ ...current,
47
+ ...prefetched
48
+ };
49
+ }
50
+ function createWorkspaceFileReferenceDirectoryStateFromSnapshot(snapshot) {
51
+ const stateByPath = {};
52
+ addReferenceDirectoryStateFromSnapshot(stateByPath, snapshot.directory);
53
+ return stateByPath;
54
+ }
55
+ function addReferenceDirectoryStateFromSnapshot(stateByPath, directory) {
56
+ const normalizedPath = normalizeDirectoryPath(directory.directoryPath);
57
+ stateByPath[normalizedPath] = {
58
+ displayPath: directory.directoryPath,
59
+ entries: directory.entries.map((entry) => ({
60
+ displayName: entry.displayName,
61
+ kind: entry.kind,
62
+ path: entry.path
63
+ })),
64
+ loaded: true,
65
+ loading: false,
66
+ prefetchReason: directory.prefetchReason,
67
+ prefetchState: directory.prefetchState
68
+ };
69
+ for (const entry of directory.entries) {
70
+ if (entry.kind !== "folder") {
71
+ continue;
72
+ }
73
+ const folderKey = normalizeDirectoryPath(entry.path);
74
+ if (entry.prefetchedDirectory) {
75
+ addReferenceDirectoryStateFromSnapshot(
76
+ stateByPath,
77
+ entry.prefetchedDirectory
78
+ );
79
+ continue;
80
+ }
81
+ if (entry.prefetchState) {
82
+ stateByPath[folderKey] = {
83
+ displayPath: entry.path,
84
+ entries: [],
85
+ loaded: false,
86
+ loading: false,
87
+ prefetchReason: entry.prefetchReason,
88
+ prefetchState: entry.prefetchState
89
+ };
90
+ }
91
+ }
92
+ }
93
+ async function prefetchReferenceTree(input) {
94
+ const { depth = 1, listDirectory, maxDepth, path } = input;
95
+ const listing = await listDirectory(path);
96
+ if (!listing) {
97
+ return {
98
+ directoryStateByPath: {},
99
+ expandedFolderPaths: {}
100
+ };
101
+ }
102
+ const directoryStateByPath = {
103
+ [listing.normalizedPath]: {
104
+ displayPath: listing.displayPath,
105
+ entries: listing.entries,
106
+ loaded: true,
107
+ loading: false
108
+ }
109
+ };
110
+ const expandedFolderPaths = {
111
+ [listing.normalizedPath]: true
112
+ };
113
+ if (depth >= maxDepth) {
114
+ return {
115
+ directoryStateByPath,
116
+ expandedFolderPaths
117
+ };
118
+ }
119
+ for (const entry of listing.entries) {
120
+ if (entry.kind !== "folder") {
121
+ continue;
122
+ }
123
+ try {
124
+ const childTree = await prefetchReferenceTree({
125
+ depth: depth + 1,
126
+ listDirectory,
127
+ maxDepth,
128
+ path: entry.path
129
+ });
130
+ Object.assign(directoryStateByPath, childTree.directoryStateByPath);
131
+ Object.assign(expandedFolderPaths, childTree.expandedFolderPaths);
132
+ } catch {
133
+ }
134
+ }
135
+ return {
136
+ directoryStateByPath,
137
+ expandedFolderPaths
138
+ };
139
+ }
140
+
141
+ // src/react/internal/reference/useWorkspaceFileReferencePickerView.ts
142
+ import {
143
+ useCallback,
144
+ useEffect,
145
+ useEffectEvent,
146
+ useMemo,
147
+ useState
148
+ } from "react";
149
+ import { useSnapshot } from "valtio";
150
+
151
+ // src/react/internal/reference/WorkspaceFileReferencePickerController.ts
152
+ import { proxy } from "valtio/vanilla";
153
+ import {
154
+ createWorkspaceFilePreviewLoadedState,
155
+ resolveWorkspaceFilePreviewReadiness
156
+ } from "@tutti-os/workspace-file-preview";
157
+ var defaultDirectoryPath = "/";
158
+ var defaultSearchDebounceMs = 180;
159
+ function createWorkspaceFileReferencePickerController(input) {
160
+ const searchDebounceMs = input.searchDebounceMs ?? defaultSearchDebounceMs;
161
+ let browseSequence = 0;
162
+ let previewObjectUrl = null;
163
+ let previewSequence = 0;
164
+ let retained = false;
165
+ let searchAbortController = null;
166
+ let searchSequence = 0;
167
+ let searchTimer = null;
168
+ let snapshot = {
169
+ browseError: null,
170
+ browseRootPath: null,
171
+ directoryStateByPath: {},
172
+ expandedFolderPaths: {},
173
+ initialPathRevealed: false,
174
+ isBrowseLoading: false,
175
+ isSearchLoading: false,
176
+ mode: "browse",
177
+ previewState: { status: "empty" },
178
+ searchEntries: [],
179
+ searchError: null,
180
+ searchQuery: ""
181
+ };
182
+ const store = proxy(snapshot);
183
+ const setSnapshot = (update) => {
184
+ const next = typeof update === "function" ? update(snapshot) : { ...snapshot, ...update };
185
+ if (next === snapshot) {
186
+ return;
187
+ }
188
+ snapshot = next;
189
+ Object.assign(store, next);
190
+ };
191
+ const loadDirectoryListing = async (path) => {
192
+ if (!input.fileAdapter?.listDirectory) {
193
+ return null;
194
+ }
195
+ const listing = await input.fileAdapter.listDirectory({
196
+ path: path ?? void 0,
197
+ workspaceId: input.workspaceId
198
+ });
199
+ const displayPath = listing.directoryPath || listing.rootPath || path || defaultDirectoryPath;
200
+ const normalizedPath = normalizeDirectoryPath(displayPath);
201
+ return {
202
+ displayPath,
203
+ entries: uniqueWorkspaceFileReferences(listing.entries),
204
+ normalizedPath
205
+ };
206
+ };
207
+ const resolveMode = (query) => query.trim().length > 0 && input.fileAdapter?.searchReferences ? "search" : "browse";
208
+ const clearSearchTimer = () => {
209
+ if (searchTimer === null) {
210
+ return;
211
+ }
212
+ clearTimeout(searchTimer);
213
+ searchTimer = null;
214
+ };
215
+ const cancelCurrentSearch = () => {
216
+ clearSearchTimer();
217
+ searchSequence += 1;
218
+ searchAbortController?.abort();
219
+ searchAbortController = null;
220
+ };
221
+ const cancelCurrentBrowse = () => {
222
+ browseSequence += 1;
223
+ };
224
+ const cancelCurrentPreview = () => {
225
+ previewSequence += 1;
226
+ if (!previewObjectUrl) {
227
+ return;
228
+ }
229
+ URL.revokeObjectURL(previewObjectUrl);
230
+ previewObjectUrl = null;
231
+ };
232
+ const clearSearchResults = () => {
233
+ cancelCurrentSearch();
234
+ setSnapshot({
235
+ isSearchLoading: false,
236
+ searchEntries: [],
237
+ searchError: null
238
+ });
239
+ };
240
+ const runSearch = async (query) => {
241
+ if (!retained || !input.fileAdapter?.searchReferences) {
242
+ return;
243
+ }
244
+ const sequence = ++searchSequence;
245
+ searchAbortController?.abort();
246
+ const abortController = new AbortController();
247
+ searchAbortController = abortController;
248
+ setSnapshot({
249
+ isSearchLoading: true,
250
+ searchError: null
251
+ });
252
+ try {
253
+ const refs = await input.fileAdapter.searchReferences({
254
+ query,
255
+ signal: abortController.signal,
256
+ workspaceId: input.workspaceId
257
+ });
258
+ if (!retained || sequence !== searchSequence) {
259
+ return;
260
+ }
261
+ setSnapshot({
262
+ isSearchLoading: false,
263
+ searchEntries: uniqueWorkspaceFileReferences(refs),
264
+ searchError: null
265
+ });
266
+ } catch (error) {
267
+ if (isAbortError(error) || sequence !== searchSequence || !retained) {
268
+ return;
269
+ }
270
+ setSnapshot({
271
+ isSearchLoading: false,
272
+ searchEntries: [],
273
+ searchError: normalizeControllerError(
274
+ error,
275
+ "Workspace file reference search failed"
276
+ )
277
+ });
278
+ } finally {
279
+ if (sequence === searchSequence) {
280
+ searchAbortController = null;
281
+ }
282
+ }
283
+ };
284
+ const scheduleSearch = () => {
285
+ clearSearchTimer();
286
+ const query = snapshot.searchQuery.trim();
287
+ if (!retained || !input.fileAdapter?.searchReferences || !query) {
288
+ clearSearchResults();
289
+ return;
290
+ }
291
+ if (searchDebounceMs <= 0) {
292
+ void runSearch(query);
293
+ return;
294
+ }
295
+ searchTimer = setTimeout(() => {
296
+ searchTimer = null;
297
+ void runSearch(query);
298
+ }, searchDebounceMs);
299
+ };
300
+ const loadBrowseRoot = async () => {
301
+ if (!retained || snapshot.mode !== "browse" || !(input.fileAdapter?.listDirectory || input.fileAdapter?.loadReferenceTree)) {
302
+ return;
303
+ }
304
+ const activeBrowseRootPath = snapshot.browseRootPath;
305
+ const normalizedRoot = activeBrowseRootPath ? normalizeDirectoryPath(activeBrowseRootPath) : null;
306
+ if (normalizedRoot && snapshot.directoryStateByPath[normalizedRoot]?.loaded) {
307
+ return;
308
+ }
309
+ const sequence = ++browseSequence;
310
+ setSnapshot({
311
+ browseError: null,
312
+ isBrowseLoading: true
313
+ });
314
+ try {
315
+ if (input.fileAdapter.loadReferenceTree) {
316
+ const treeSnapshot = await input.fileAdapter.loadReferenceTree({
317
+ path: activeBrowseRootPath ?? void 0,
318
+ prefetchBudgetMs: 500,
319
+ prefetchDepth: 4,
320
+ workspaceId: input.workspaceId
321
+ });
322
+ if (!retained || sequence !== browseSequence) {
323
+ return;
324
+ }
325
+ setSnapshot({
326
+ browseRootPath: normalizeDirectoryPath(
327
+ treeSnapshot.directory.directoryPath
328
+ ),
329
+ directoryStateByPath: createWorkspaceFileReferenceDirectoryStateFromSnapshot(
330
+ treeSnapshot
331
+ ),
332
+ isBrowseLoading: false
333
+ });
334
+ return;
335
+ }
336
+ const listing = await loadDirectoryListing(activeBrowseRootPath);
337
+ if (!retained || sequence !== browseSequence || !listing) {
338
+ return;
339
+ }
340
+ setSnapshot((current) => ({
341
+ ...current,
342
+ browseRootPath: listing.normalizedPath,
343
+ directoryStateByPath: {
344
+ ...current.directoryStateByPath,
345
+ [listing.normalizedPath]: {
346
+ displayPath: listing.displayPath,
347
+ entries: listing.entries,
348
+ loaded: true,
349
+ loading: false
350
+ }
351
+ },
352
+ isBrowseLoading: false
353
+ }));
354
+ } catch (error) {
355
+ if (!retained || sequence !== browseSequence) {
356
+ return;
357
+ }
358
+ setSnapshot({
359
+ browseError: normalizeControllerError(
360
+ error,
361
+ "Workspace file reference browse failed"
362
+ ),
363
+ isBrowseLoading: false
364
+ });
365
+ }
366
+ };
367
+ const loadFolderChildren = async (folder) => {
368
+ const folderKey = normalizeDirectoryPath(folder.path);
369
+ if (!retained || snapshot.directoryStateByPath[folderKey]?.loaded || snapshot.directoryStateByPath[folderKey]?.loading) {
370
+ return;
371
+ }
372
+ const sequence = ++browseSequence;
373
+ setSnapshot((current) => ({
374
+ ...current,
375
+ directoryStateByPath: {
376
+ ...current.directoryStateByPath,
377
+ [folderKey]: {
378
+ displayPath: folder.path,
379
+ entries: current.directoryStateByPath[folderKey]?.entries ?? [],
380
+ loaded: current.directoryStateByPath[folderKey]?.loaded ?? false,
381
+ loading: true
382
+ }
383
+ }
384
+ }));
385
+ try {
386
+ const listing = await loadDirectoryListing(folderKey);
387
+ if (!retained || sequence !== browseSequence || !listing) {
388
+ return;
389
+ }
390
+ setSnapshot((current) => ({
391
+ ...current,
392
+ directoryStateByPath: {
393
+ ...current.directoryStateByPath,
394
+ [folderKey]: {
395
+ displayPath: listing.displayPath,
396
+ entries: listing.entries,
397
+ loaded: true,
398
+ loading: false
399
+ }
400
+ }
401
+ }));
402
+ } catch {
403
+ if (!retained || sequence !== browseSequence) {
404
+ return;
405
+ }
406
+ setSnapshot((current) => ({
407
+ ...current,
408
+ directoryStateByPath: {
409
+ ...current.directoryStateByPath,
410
+ [folderKey]: {
411
+ displayPath: current.directoryStateByPath[folderKey]?.displayPath ?? folder.path,
412
+ entries: current.directoryStateByPath[folderKey]?.entries ?? [],
413
+ loaded: current.directoryStateByPath[folderKey]?.loaded ?? false,
414
+ loading: false
415
+ }
416
+ }
417
+ }));
418
+ }
419
+ };
420
+ const loadReferencePreview = async (reference, target, sequence) => {
421
+ try {
422
+ const preview = await input.fileAdapter?.readReferencePreview?.({
423
+ reference,
424
+ workspaceId: input.workspaceId
425
+ });
426
+ if (!retained || sequence !== previewSequence) {
427
+ return;
428
+ }
429
+ if (!preview) {
430
+ setSnapshot({
431
+ previewState: { reference, status: "unsupported" }
432
+ });
433
+ return;
434
+ }
435
+ const nextState = createReferencePreviewState(reference, target, preview);
436
+ if (!retained || sequence !== previewSequence) {
437
+ if (nextState.status === "image") {
438
+ URL.revokeObjectURL(nextState.objectUrl);
439
+ }
440
+ return;
441
+ }
442
+ if (nextState.status === "image") {
443
+ previewObjectUrl = nextState.objectUrl;
444
+ }
445
+ setSnapshot({
446
+ previewState: nextState
447
+ });
448
+ } catch {
449
+ if (!retained || sequence !== previewSequence) {
450
+ return;
451
+ }
452
+ setSnapshot({
453
+ previewState: { reference, status: "error" }
454
+ });
455
+ }
456
+ };
457
+ const setPreviewReference = (reference) => {
458
+ cancelCurrentPreview();
459
+ if (!retained || !reference) {
460
+ setSnapshot({
461
+ previewState: { status: "empty" }
462
+ });
463
+ return;
464
+ }
465
+ const readiness = resolveWorkspaceFilePreviewReadiness(reference);
466
+ if (readiness.status === "directory") {
467
+ setSnapshot({
468
+ previewState: { reference, status: "directory" }
469
+ });
470
+ return;
471
+ }
472
+ if (readiness.status === "unsupported") {
473
+ setSnapshot({
474
+ previewState: { reference, status: "unsupported" }
475
+ });
476
+ return;
477
+ }
478
+ if (readiness.status === "readonly") {
479
+ setSnapshot({
480
+ previewState: {
481
+ maxSizeBytes: readiness.maxSizeBytes,
482
+ reason: readiness.reason,
483
+ reference,
484
+ status: "readonly"
485
+ }
486
+ });
487
+ return;
488
+ }
489
+ if (!input.fileAdapter?.readReferencePreview) {
490
+ setSnapshot({
491
+ previewState: { reference, status: "unavailable" }
492
+ });
493
+ return;
494
+ }
495
+ const sequence = ++previewSequence;
496
+ setSnapshot({
497
+ previewState: { reference, status: "loading" }
498
+ });
499
+ void loadReferencePreview(reference, readiness.target, sequence);
500
+ };
501
+ return {
502
+ close() {
503
+ retained = false;
504
+ cancelCurrentBrowse();
505
+ cancelCurrentPreview();
506
+ cancelCurrentSearch();
507
+ setSnapshot({
508
+ isBrowseLoading: false,
509
+ isSearchLoading: false,
510
+ previewState: { status: "empty" }
511
+ });
512
+ },
513
+ getSnapshot() {
514
+ return snapshot;
515
+ },
516
+ loadBrowseRoot() {
517
+ void loadBrowseRoot();
518
+ },
519
+ open() {
520
+ if (retained) {
521
+ return;
522
+ }
523
+ retained = true;
524
+ if (snapshot.mode === "search") {
525
+ scheduleSearch();
526
+ return;
527
+ }
528
+ void loadBrowseRoot();
529
+ },
530
+ async revealInitialPath(initialDirectoryPath) {
531
+ if (!retained || snapshot.mode !== "browse" || snapshot.initialPathRevealed || !snapshot.browseRootPath) {
532
+ return null;
533
+ }
534
+ const rootPath = normalizeDirectoryPath(snapshot.browseRootPath);
535
+ if (!isPathInsideOrEqual(initialDirectoryPath, rootPath)) {
536
+ setSnapshot({
537
+ initialPathRevealed: true
538
+ });
539
+ return null;
540
+ }
541
+ const sequence = ++browseSequence;
542
+ const loadedDirectories = {};
543
+ const expandedDirectories = {};
544
+ const directoryState = (path) => loadedDirectories[path] ?? snapshot.directoryStateByPath[path];
545
+ const directoryPaths = directoryChainBetween(
546
+ rootPath,
547
+ initialDirectoryPath
548
+ );
549
+ for (const directoryPath of directoryPaths) {
550
+ expandedDirectories[directoryPath] = true;
551
+ }
552
+ const listings = await Promise.all(
553
+ directoryPaths.filter((directoryPath) => !directoryState(directoryPath)?.loaded).map(async (directoryPath) => {
554
+ try {
555
+ return await loadDirectoryListing(directoryPath);
556
+ } catch {
557
+ return null;
558
+ }
559
+ })
560
+ );
561
+ for (const listing of listings) {
562
+ if (!listing) {
563
+ continue;
564
+ }
565
+ loadedDirectories[listing.normalizedPath] = {
566
+ displayPath: listing.displayPath,
567
+ entries: listing.entries,
568
+ loaded: true,
569
+ loading: false
570
+ };
571
+ }
572
+ if (!retained || sequence !== browseSequence) {
573
+ return null;
574
+ }
575
+ setSnapshot((current) => ({
576
+ ...current,
577
+ directoryStateByPath: {
578
+ ...current.directoryStateByPath,
579
+ ...loadedDirectories
580
+ },
581
+ expandedFolderPaths: {
582
+ ...current.expandedFolderPaths,
583
+ ...expandedDirectories
584
+ },
585
+ initialPathRevealed: true
586
+ }));
587
+ return initialDirectoryPath;
588
+ },
589
+ reset() {
590
+ cancelCurrentBrowse();
591
+ cancelCurrentPreview();
592
+ cancelCurrentSearch();
593
+ setSnapshot({
594
+ browseError: null,
595
+ browseRootPath: null,
596
+ directoryStateByPath: {},
597
+ expandedFolderPaths: {},
598
+ initialPathRevealed: false,
599
+ isBrowseLoading: false,
600
+ isSearchLoading: false,
601
+ mode: "browse",
602
+ previewState: { status: "empty" },
603
+ searchEntries: [],
604
+ searchError: null,
605
+ searchQuery: ""
606
+ });
607
+ },
608
+ setPreviewReference(reference) {
609
+ setPreviewReference(reference);
610
+ },
611
+ setSearchQuery(query) {
612
+ if (query === snapshot.searchQuery) {
613
+ return;
614
+ }
615
+ const nextMode = resolveMode(query);
616
+ setSnapshot({
617
+ mode: nextMode,
618
+ searchQuery: query,
619
+ ...nextMode === "browse" ? { isSearchLoading: false } : {}
620
+ });
621
+ if (nextMode === "search") {
622
+ cancelCurrentBrowse();
623
+ scheduleSearch();
624
+ return;
625
+ }
626
+ clearSearchResults();
627
+ void loadBrowseRoot();
628
+ },
629
+ get store() {
630
+ return store;
631
+ },
632
+ toggleFolder(entry) {
633
+ const folderKey = normalizeDirectoryPath(entry.path);
634
+ const childState = snapshot.directoryStateByPath[folderKey];
635
+ const nextExpanded = !(snapshot.expandedFolderPaths[folderKey] ?? false);
636
+ setSnapshot((current) => ({
637
+ ...current,
638
+ expandedFolderPaths: {
639
+ ...current.expandedFolderPaths,
640
+ [folderKey]: nextExpanded
641
+ }
642
+ }));
643
+ if (nextExpanded && !childState?.loaded && !childState?.loading) {
644
+ void loadFolderChildren(entry);
645
+ }
646
+ }
647
+ };
648
+ }
649
+ function isAbortError(error) {
650
+ return error instanceof Error && error.name === "AbortError";
651
+ }
652
+ function normalizeControllerError(error, fallbackMessage) {
653
+ return error instanceof Error ? error : new Error(fallbackMessage);
654
+ }
655
+ function createReferencePreviewState(reference, target, preview) {
656
+ const loadedState = createWorkspaceFilePreviewLoadedState({
657
+ bytes: preview.bytes,
658
+ contentType: preview.contentType,
659
+ entry: reference,
660
+ target: {
661
+ ...target,
662
+ fileKind: preview.kind
663
+ }
664
+ });
665
+ if (loadedState.status === "image") {
666
+ return {
667
+ objectUrl: URL.createObjectURL(
668
+ new Blob([loadedState.bytes], {
669
+ type: loadedState.contentType
670
+ })
671
+ ),
672
+ reference,
673
+ status: "image"
674
+ };
675
+ }
676
+ if (loadedState.status === "text") {
677
+ return {
678
+ content: loadedState.content,
679
+ reference,
680
+ status: "text"
681
+ };
682
+ }
683
+ return {
684
+ maxSizeBytes: loadedState.maxSizeBytes,
685
+ reason: loadedState.reason,
686
+ reference,
687
+ status: "readonly"
688
+ };
689
+ }
690
+ function isPathInsideOrEqual(path, root) {
691
+ const normalizedPath = normalizeDirectoryPath(path);
692
+ const normalizedRoot = normalizeDirectoryPath(root);
693
+ if (normalizedRoot === "/") {
694
+ return normalizedPath.startsWith("/");
695
+ }
696
+ return normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot}/`);
697
+ }
698
+ function directoryChainBetween(root, target) {
699
+ const normalizedRoot = normalizeDirectoryPath(root);
700
+ let current = normalizeDirectoryPath(target);
701
+ const chain = [];
702
+ while (current && current !== normalizedRoot) {
703
+ chain.unshift(current);
704
+ const parent = dirnameDirectoryPath(current);
705
+ if (parent === current) {
706
+ break;
707
+ }
708
+ current = parent;
709
+ }
710
+ return chain;
711
+ }
712
+ function dirnameDirectoryPath(path) {
713
+ const normalized = normalizeDirectoryPath(path);
714
+ if (normalized === "/") {
715
+ return "/";
716
+ }
717
+ const index = normalized.lastIndexOf("/");
718
+ if (index <= 0) {
719
+ return "/";
720
+ }
721
+ return normalized.slice(0, index);
722
+ }
723
+
724
+ // src/react/internal/reference/useWorkspaceFileReferencePickerView.ts
725
+ function useWorkspaceFileReferencePickerView({
726
+ fileAdapter,
727
+ initialPath,
728
+ onClose,
729
+ onConfirm,
730
+ open,
731
+ workspaceId
732
+ }) {
733
+ const readPickerSnapshot = useSnapshot;
734
+ const trimmedInitialPath = initialPath?.trim() ?? "";
735
+ const initialDirectoryPath = trimmedInitialPath ? normalizeDirectoryPath(trimmedInitialPath) : null;
736
+ const [focusedPath, setFocusedPath] = useState(null);
737
+ const [selectedRefs, setSelectedRefs] = useState(
738
+ []
739
+ );
740
+ const pickerController = useMemo(
741
+ () => createWorkspaceFileReferencePickerController({
742
+ fileAdapter,
743
+ workspaceId
744
+ }),
745
+ [fileAdapter, workspaceId]
746
+ );
747
+ const pickerSnapshot = readPickerSnapshot(pickerController.store);
748
+ const {
749
+ browseRootPath,
750
+ directoryStateByPath,
751
+ expandedFolderPaths,
752
+ isBrowseLoading,
753
+ isSearchLoading,
754
+ mode,
755
+ previewState,
756
+ searchEntries,
757
+ searchQuery
758
+ } = pickerSnapshot;
759
+ const isLoading = mode === "search" ? isSearchLoading : isBrowseLoading;
760
+ const finalizeRequestedReferences = useEffectEvent(
761
+ (refs) => {
762
+ onConfirm(uniqueWorkspaceFileReferences(refs));
763
+ onClose();
764
+ }
765
+ );
766
+ const setSearchQuery = useCallback(
767
+ (query) => {
768
+ pickerController.setSearchQuery(query);
769
+ },
770
+ [pickerController]
771
+ );
772
+ useEffect(() => {
773
+ if (!open) {
774
+ return;
775
+ }
776
+ pickerController.reset();
777
+ pickerController.open();
778
+ setFocusedPath(null);
779
+ setSelectedRefs([]);
780
+ return () => {
781
+ pickerController.close();
782
+ };
783
+ }, [initialDirectoryPath, open, pickerController]);
784
+ useEffect(() => {
785
+ if (!open || mode !== "browse") {
786
+ return;
787
+ }
788
+ pickerController.loadBrowseRoot();
789
+ }, [mode, open, pickerController]);
790
+ useEffect(() => {
791
+ if (!open || !fileAdapter || mode !== "browse") {
792
+ return;
793
+ }
794
+ if (fileAdapter.listDirectory || fileAdapter.loadReferenceTree || !fileAdapter.requestReferences) {
795
+ return;
796
+ }
797
+ let canceled = false;
798
+ void fileAdapter.requestReferences({ workspaceId }).then((refs) => {
799
+ if (!canceled) {
800
+ finalizeRequestedReferences(refs);
801
+ }
802
+ });
803
+ return () => {
804
+ canceled = true;
805
+ };
806
+ }, [fileAdapter, finalizeRequestedReferences, mode, open, workspaceId]);
807
+ useEffect(() => {
808
+ if (!open || mode !== "browse" || pickerSnapshot.initialPathRevealed || !initialDirectoryPath || !browseRootPath) {
809
+ return;
810
+ }
811
+ let canceled = false;
812
+ const reveal = async () => {
813
+ const focusedInitialPath = await pickerController.revealInitialPath(initialDirectoryPath);
814
+ if (!canceled && focusedInitialPath) {
815
+ setFocusedPath(focusedInitialPath);
816
+ }
817
+ };
818
+ void reveal();
819
+ return () => {
820
+ canceled = true;
821
+ };
822
+ }, [
823
+ browseRootPath,
824
+ initialDirectoryPath,
825
+ mode,
826
+ open,
827
+ pickerController,
828
+ pickerSnapshot.initialPathRevealed
829
+ ]);
830
+ const browseRootEntries = useMemo(
831
+ () => browseRootPath ? directoryStateByPath[normalizeDirectoryPath(browseRootPath)]?.entries ?? [] : [],
832
+ [browseRootPath, directoryStateByPath]
833
+ );
834
+ const visibleEntries = useMemo(
835
+ () => mode === "search" ? searchEntries : collectVisibleTreeEntries(
836
+ browseRootEntries,
837
+ directoryStateByPath,
838
+ expandedFolderPaths
839
+ ),
840
+ [
841
+ browseRootEntries,
842
+ directoryStateByPath,
843
+ expandedFolderPaths,
844
+ mode,
845
+ searchEntries
846
+ ]
847
+ );
848
+ useEffect(() => {
849
+ if (!open) {
850
+ return;
851
+ }
852
+ setFocusedPath((current) => {
853
+ if (current && visibleEntries.some((entry) => entry.path === current)) {
854
+ return current;
855
+ }
856
+ return visibleEntries[0]?.path ?? null;
857
+ });
858
+ }, [open, visibleEntries]);
859
+ const focusedEntry = visibleEntries.find((entry) => entry.path === focusedPath) ?? selectedRefs.find((entry) => entry.path === focusedPath) ?? null;
860
+ useEffect(() => {
861
+ pickerController.setPreviewReference(open ? focusedEntry : null);
862
+ }, [focusedEntry, open, pickerController]);
863
+ const toggleRef = (ref) => {
864
+ setSelectedRefs((current) => {
865
+ const existing = current.some((item) => item.path === ref.path);
866
+ return existing ? current.filter((item) => item.path !== ref.path) : uniqueWorkspaceFileReferences([...current, ref]);
867
+ });
868
+ };
869
+ const toggleFolder = (entry) => {
870
+ pickerController.toggleFolder(entry);
871
+ };
872
+ return {
873
+ browseRootEntries,
874
+ directoryStateByPath,
875
+ expandedFolderPaths,
876
+ focusedEntry,
877
+ focusedPath,
878
+ isLoading,
879
+ mode,
880
+ previewState,
881
+ searchQuery,
882
+ selectedRefs,
883
+ visibleEntries,
884
+ setFocusedPath,
885
+ setSearchQuery,
886
+ toggleFolder,
887
+ toggleRef
888
+ };
889
+ }
890
+
891
+ export {
892
+ workspaceFileReferenceDefaultExpandedDepth,
893
+ normalizeDirectoryPath,
894
+ collectVisibleTreeEntries,
895
+ mergeExpandedFolderPaths,
896
+ mergePrefetchedDirectoryState,
897
+ createWorkspaceFileReferenceDirectoryStateFromSnapshot,
898
+ prefetchReferenceTree,
899
+ useWorkspaceFileReferencePickerView
900
+ };
901
+ //# sourceMappingURL=chunk-LS7P3J6Z.js.map