@brftech/filex-core 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/{ArchiveViewer-xGV6PJ2d.js → ArchiveViewer-BRFvhWwL.js} +2 -2
  2. package/dist/{ArchiveViewer-xGV6PJ2d.js.map → ArchiveViewer-BRFvhWwL.js.map} +1 -1
  3. package/dist/{CsvViewer-C_VfZCbP.js → CsvViewer-1cGGKeGi.js} +2 -2
  4. package/dist/{CsvViewer-C_VfZCbP.js.map → CsvViewer-1cGGKeGi.js.map} +1 -1
  5. package/dist/{DrawioViewer--Wx7CUSa.js → DrawioViewer-C0oThT59.js} +2 -2
  6. package/dist/{DrawioViewer--Wx7CUSa.js.map → DrawioViewer-C0oThT59.js.map} +1 -1
  7. package/dist/{EpubViewer-CIlKN5rU.js → EpubViewer-DCPOD2S-.js} +2 -2
  8. package/dist/{EpubViewer-CIlKN5rU.js.map → EpubViewer-DCPOD2S-.js.map} +1 -1
  9. package/dist/{IpynbViewer-D9nID_-7.js → IpynbViewer-mv4xiv3H.js} +2 -2
  10. package/dist/{IpynbViewer-D9nID_-7.js.map → IpynbViewer-mv4xiv3H.js.map} +1 -1
  11. package/dist/{MermaidViewer-BnW75WoJ.js → MermaidViewer-BBNVghTz.js} +2 -2
  12. package/dist/{MermaidViewer-BnW75WoJ.js.map → MermaidViewer-BBNVghTz.js.map} +1 -1
  13. package/dist/{PsdViewer-yaU0b1GT.js → PsdViewer-B8ygWw-s.js} +2 -2
  14. package/dist/{PsdViewer-yaU0b1GT.js.map → PsdViewer-B8ygWw-s.js.map} +1 -1
  15. package/dist/{TiffViewer-BsLGsy10.js → TiffViewer-BhHjf3s5.js} +2 -2
  16. package/dist/{TiffViewer-BsLGsy10.js.map → TiffViewer-BhHjf3s5.js.map} +1 -1
  17. package/dist/{Viewer3D-DrVf_Uyz.js → Viewer3D-BhOJpzyG.js} +2 -2
  18. package/dist/{Viewer3D-DrVf_Uyz.js.map → Viewer3D-BhOJpzyG.js.map} +1 -1
  19. package/dist/filex-core.js +1 -1
  20. package/dist/filex-core.umd.cjs +31 -31
  21. package/dist/filex-core.umd.cjs.map +1 -1
  22. package/dist/{index-BrUiOc9C.js → index-BjiUhk9K.js} +3090 -3060
  23. package/dist/index-BjiUhk9K.js.map +1 -0
  24. package/dist/index.d.ts +10 -0
  25. package/package.json +1 -1
  26. package/src/FileExplorer.vue +23 -49
  27. package/src/components/SecondaryPane.vue +21 -9
  28. package/src/lib/listing.ts +105 -0
  29. package/dist/index-BrUiOc9C.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -48,6 +48,9 @@ declare type __VLS_Props_11 = {
48
48
  viewMode?: ViewMode;
49
49
  /** ui-fix — authenticated thumb resolver, forwarded to grid/gallery. */
50
50
  thumbSrc?: (n: FileNode) => string | null;
51
+ /** ui-fix — mirror the main panel's virtual `.trash` row at storage root
52
+ * so both split panes list identical rows (no row-offset). Defaults on. */
53
+ trashVisible?: boolean;
51
54
  };
52
55
 
53
56
  declare type __VLS_Props_12 = {
@@ -856,9 +859,11 @@ virtualRows?: () => FileNode[];
856
859
  active?: boolean;
857
860
  viewMode?: ViewMode;
858
861
  thumbSrc?: (n: FileNode) => string | null;
862
+ trashVisible?: boolean;
859
863
  }> & Readonly<{
860
864
  onClose?: (() => any) | undefined;
861
865
  onNavigate?: ((path: string) => any) | undefined;
866
+ "onOpen-trash"?: (() => any) | undefined;
862
867
  onActivate?: (() => any) | undefined;
863
868
  "onOpen-tab"?: ((path: string) => any) | undefined;
864
869
  onTransfer?: ((p: {
@@ -877,6 +882,7 @@ getPath: () => string;
877
882
  }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
878
883
  close: () => any;
879
884
  navigate: (path: string) => any;
885
+ "open-trash": () => any;
880
886
  activate: () => any;
881
887
  "open-tab": (path: string) => any;
882
888
  transfer: (p: {
@@ -906,9 +912,11 @@ virtualRows?: () => FileNode[];
906
912
  active?: boolean;
907
913
  viewMode?: ViewMode;
908
914
  thumbSrc?: (n: FileNode) => string | null;
915
+ trashVisible?: boolean;
909
916
  }> & Readonly<{
910
917
  onClose?: (() => any) | undefined;
911
918
  onNavigate?: ((path: string) => any) | undefined;
919
+ "onOpen-trash"?: (() => any) | undefined;
912
920
  onActivate?: (() => any) | undefined;
913
921
  "onOpen-tab"?: ((path: string) => any) | undefined;
914
922
  onTransfer?: ((p: {
@@ -1373,6 +1381,7 @@ getPath: typeof getPath;
1373
1381
  }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
1374
1382
  close: () => any;
1375
1383
  navigate: (path: string) => any;
1384
+ "open-trash": () => any;
1376
1385
  activate: () => any;
1377
1386
  "open-tab": (path: string) => any;
1378
1387
  transfer: (p: {
@@ -1384,6 +1393,7 @@ context: (node: FileNode | null, ev: MouseEvent) => any;
1384
1393
  }, string, PublicProps, Readonly<__VLS_Props_11> & Readonly<{
1385
1394
  onClose?: (() => any) | undefined;
1386
1395
  onNavigate?: ((path: string) => any) | undefined;
1396
+ "onOpen-trash"?: (() => any) | undefined;
1387
1397
  onActivate?: (() => any) | undefined;
1388
1398
  "onOpen-tab"?: ((path: string) => any) | undefined;
1389
1399
  onTransfer?: ((p: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brftech/filex-core",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "filex core — Vue 3 source of truth for the filex file manager (FileExplorer SFC + composables + types)",
5
5
  "type": "module",
6
6
  "main": "./dist/filex-core.umd.cjs",
@@ -86,6 +86,15 @@ import {
86
86
  } from './lib/e2ecrypto';
87
87
  /* /wiring:e2 */
88
88
 
89
+ /* ui-fix — listing helpers shared with SecondaryPane (single source: the
90
+ internal-entry filter + virtual `.trash` row must be identical in both
91
+ panes or split view shows mismatched rows). */
92
+ import {
93
+ filterInternalEntries,
94
+ injectTrashRow,
95
+ hydrateTrashRow as hydrateTrashRowShared,
96
+ } from './lib/listing';
97
+
89
98
  import NewFolderModal from './modals/NewFolderModal.vue';
90
99
  import RenameModal from './modals/RenameModal.vue';
91
100
  import DeleteConfirmModal from './modals/DeleteConfirmModal.vue';
@@ -707,33 +716,11 @@ async function load(path?: string) {
707
716
  subtree; '' resets on every plain folder. Drives the lock screen. */
708
717
  e2eRoot.value = typeof resp.e2e_root === 'string' ? resp.e2e_root : '';
709
718
  /* /wiring:e2 */
710
- files.value = (resp.files || []).filter((f) => {
711
- if (f.path.includes('.thumbs')) return false;
712
- if (f.path.includes('.versions') || f.basename === '.versions') return false;
713
- if (f.basename === '.trash') return false;
714
- if (f.basename === '.keepdir') return false;
715
- if (f.basename === E2E_MARKER_NAME) return false; /* wiring:e2 — marker gizli */
716
- return true;
717
- });
718
- // Inject virtual `.trash` entry at root only.
719
- const dirRel = stripAdapter(resp.dirname);
720
- const inRoot = dirRel === 'fileman' || dirRel === '';
721
- const isTrashListing = dirRel.startsWith('fileman/.trash');
722
- const trashEntryEnabled = props.config.trashVisible !== false;
723
- if (!isTrashListing && inRoot && trashEntryEnabled) {
724
- files.value.unshift({
725
- type: 'dir',
726
- path: `${resp.adapter}://fileman/.trash`,
727
- basename: '.trash',
728
- extension: '',
729
- storage: resp.adapter,
730
- visibility: 'private',
731
- size: 0,
732
- file_size: 0,
733
- mime_type: 'inode/directory',
734
- extra_metadata: {},
735
- } as unknown as FileNode);
736
- void hydrateTrashRow(resp.adapter);
719
+ files.value = filterInternalEntries(resp.files);
720
+ // Inject virtual `.trash` entry at root only — shared helper so the
721
+ // split-view secondary pane shows the exact same row (no row-offset).
722
+ if (injectTrashRow(files.value, resp.adapter, resp.dirname, props.config.trashVisible !== false)) {
723
+ void hydrateTrashRowShared(files.value, resp.adapter, api);
737
724
  }
738
725
  // currentPath is the user-facing form: `s3-test/example` in
739
726
  // multi-storage mode, the bare relative path otherwise.
@@ -784,28 +771,7 @@ function leaveNotFound() {
784
771
  void load('');
785
772
  }
786
773
 
787
- // hydrateTrashRow the virtual `.trash` row is synthesized with no size/date;
788
- // fill both from the backend trash listing (total bytes + newest deletion) so
789
- // the row reads like a real folder instead of "— / —". Best-effort and
790
- // non-blocking: the row appears immediately and updates when the listing lands.
791
- async function hydrateTrashRow(storage: string) {
792
- try {
793
- const { entries } = await api.listTrash(storage);
794
- const row = files.value.find((f) => f.basename === '.trash');
795
- if (!row) return;
796
- let total = 0;
797
- let newest = 0;
798
- for (const e of entries) {
799
- total += e.size || 0;
800
- const ts = Date.parse(e.deleted_at);
801
- if (!Number.isNaN(ts) && ts > newest) newest = ts;
802
- }
803
- row.size = total;
804
- if (newest > 0) row.last_modified = newest;
805
- } catch {
806
- /* keep the bare row */
807
- }
808
- }
774
+ // (hydrateTrashRow moved to lib/listing.ts shared with SecondaryPane.)
809
775
 
810
776
  // loadTrash — show the backend trash (soft-deleted nodes) as a flat listing.
811
777
  // Entered by opening the virtual `.trash` row. Each row keeps its node `id`
@@ -2616,6 +2582,12 @@ function closeSplit() {
2616
2582
  function onPaneNavigate(p: string) {
2617
2583
  tabsApi.setSplit({ ...(activeSplit.value ?? {}), path: p });
2618
2584
  }
2585
+ /* ui-fix — çöp kutusu satırı yan panelde açılınca: trash görünümü (geri
2586
+ * yükleme aksiyonlarıyla) ana panele aittir → ana paneli aktif edip aç. */
2587
+ function onPaneOpenTrash() {
2588
+ activePane.value = 'main';
2589
+ void loadTrash();
2590
+ }
2619
2591
  /* ui-fix — pane'in KENDİ görünüm modu: split açılırken ana panelinkini
2620
2592
  * devralır, sonrasında bağımsız. Toolbar'ın görünüm değiştiricisi ve palet
2621
2593
  * toggle'ı AKTİF panele yazar (Burak: "B tıklıyken ikon değiştir dersem
@@ -3349,12 +3321,14 @@ async function submitEncryptedFolder(payload: { name: string; password: string }
3349
3321
  :active="paneIsActive"
3350
3322
  :view-mode="paneViewMode /* ui-fix */"
3351
3323
  :thumb-src="thumbs.src /* ui-fix */"
3324
+ :trash-visible="config.trashVisible !== false /* ui-fix — çöp satırı simetrisi */"
3352
3325
  @navigate="onPaneNavigate"
3353
3326
  @activate="activePane = 'split'"
3354
3327
  @close="closeSplit"
3355
3328
  @open-tab="(p: string) => tabsApi.openTab(p, { viewMode: viewMode, background: true })"
3356
3329
  @transfer="onPaneTransfer"
3357
3330
  @context="onPaneContext /* ui-fix — yan panel sağ-tık menüsü */"
3331
+ @open-trash="onPaneOpenTrash /* ui-fix — çöp kutusu ana panelde açılır */"
3358
3332
  />
3359
3333
  <!-- /wiring:d1 -->
3360
3334
 
@@ -21,6 +21,7 @@ import type { FileApi } from '../composables/useFileApi';
21
21
  import type { FileNode, ViewMode } from '../types/FileNode';
22
22
  import type { LocaleCode } from '../types/ExplorerConfig';
23
23
  import { useLocale } from '../composables/useLocale';
24
+ import { filterInternalEntries, injectTrashRow, hydrateTrashRow } from '../lib/listing';
24
25
  import ListView from './ListView.vue';
25
26
  import GridView from './GridView.vue';
26
27
  import GalleryView from './GalleryView.vue';
@@ -57,6 +58,9 @@ const props = defineProps<{
57
58
  viewMode?: ViewMode;
58
59
  /** ui-fix — authenticated thumb resolver, forwarded to grid/gallery. */
59
60
  thumbSrc?: (n: FileNode) => string | null;
61
+ /** ui-fix — mirror the main panel's virtual `.trash` row at storage root
62
+ * so both split panes list identical rows (no row-offset). Defaults on. */
63
+ trashVisible?: boolean;
60
64
  }>();
61
65
 
62
66
  const emit = defineEmits<{
@@ -68,6 +72,9 @@ const emit = defineEmits<{
68
72
  /* ui-fix — yan panelde sağ-tık: menü ana bileşende (FileExplorer) açılır.
69
73
  * node=null → boş alana sağ-tık (seçimsiz, yalnız Yapıştır). */
70
74
  (e: 'context', node: FileNode | null, ev: MouseEvent): void;
75
+ /* ui-fix — çöp kutusu satırı açıldığında: trash görünümü ana panele aittir
76
+ * (geri yükleme aksiyonlarıyla), host loadTrash() ile açar. */
77
+ (e: 'open-trash'): void;
71
78
  }>();
72
79
 
73
80
  const { t } = useLocale(() => props.locale);
@@ -101,14 +108,12 @@ async function loadPane(target?: string): Promise<void> {
101
108
  return;
102
109
  }
103
110
  const resp = await props.api.index(props.qualify(requested));
104
- // Same internal-entry filter as the main listing.
105
- files.value = (resp.files || []).filter((f) => {
106
- if (f.path.includes('.thumbs')) return false;
107
- if (f.path.includes('.versions') || f.basename === '.versions') return false;
108
- if (f.basename === '.trash') return false;
109
- if (f.basename === '.keepdir') return false;
110
- return true;
111
- });
111
+ // Same internal-entry filter + virtual `.trash` row as the main panel
112
+ // (shared helpers → both split panes list identical rows, no offset).
113
+ files.value = filterInternalEntries(resp.files);
114
+ if (injectTrashRow(files.value, resp.adapter, resp.dirname, props.trashVisible !== false)) {
115
+ void hydrateTrashRow(files.value, resp.adapter, props.api);
116
+ }
112
117
  path.value = props.toUser(resp.dirname);
113
118
  selected.value = new Set();
114
119
  emit('navigate', path.value);
@@ -204,6 +209,13 @@ function onViewDropInto(target: FileNode, ev: DragEvent) {
204
209
 
205
210
  function onRowDbl(n: FileNode) {
206
211
  if (n.type !== 'dir') return;
212
+ // The virtual `.trash` row opens the backend trash listing (restore
213
+ // actions) — that view lives in the main panel; let the host open it.
214
+ if (n.basename === '.trash') {
215
+ emit('activate');
216
+ emit('open-trash');
217
+ return;
218
+ }
207
219
  void loadPane(isStorageRow(n) ? n.path : props.toUser(n.path));
208
220
  }
209
221
 
@@ -217,7 +229,7 @@ function clearSelection() {
217
229
 
218
230
  function onRowDragStart(n: FileNode, ev: DragEvent) {
219
231
  if (!ev.dataTransfer) return;
220
- if (isStorageRow(n) || atVirtualRoot.value) {
232
+ if (isStorageRow(n) || atVirtualRoot.value || n.basename === '.trash') {
221
233
  ev.preventDefault();
222
234
  return;
223
235
  }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * listing.ts — shared listing helpers used by BOTH the main panel
3
+ * (FileExplorer.load) and the split-view secondary panel
4
+ * (SecondaryPane.loadPane). SINGLE SOURCE so the two panes can never
5
+ * drift apart: the internal-entry filter and the virtual `.trash` row
6
+ * must be identical in both, otherwise split view shows mismatched rows
7
+ * (the trash row missing on one side → visible row-offset).
8
+ */
9
+ import type { FileNode } from '../types/FileNode';
10
+ import { E2E_MARKER_NAME } from './e2ecrypto';
11
+
12
+ /** Adapter-strip: `s3-test://fileman/x` → `fileman/x`. */
13
+ export function stripAdapter(p: string): string {
14
+ const idx = p.indexOf('://');
15
+ return idx === -1 ? p : p.slice(idx + 3);
16
+ }
17
+
18
+ /**
19
+ * Hide system/internal entries the user must never see as files:
20
+ * thumbnails, version history, the soft-delete store, keepdir markers
21
+ * and the E2E marker. Shared by both panes' listing filters.
22
+ */
23
+ export function filterInternalEntries(files: FileNode[]): FileNode[] {
24
+ return (files || []).filter((f) => {
25
+ if (f.path.includes('.thumbs')) return false;
26
+ if (f.path.includes('.versions') || f.basename === '.versions') return false;
27
+ if (f.basename === '.trash') return false;
28
+ if (f.basename === '.keepdir') return false;
29
+ if (f.basename === E2E_MARKER_NAME) return false;
30
+ return true;
31
+ });
32
+ }
33
+
34
+ /** True when `dirname` (adapter-qualified) is the storage root, where the
35
+ * virtual trash row belongs. */
36
+ export function isStorageRootDir(dirname: string): boolean {
37
+ const rel = stripAdapter(dirname);
38
+ return rel === 'fileman' || rel === '';
39
+ }
40
+
41
+ /** True when the listing IS the trash view (don't inject the row into itself). */
42
+ export function isTrashListing(dirname: string): boolean {
43
+ return stripAdapter(dirname).startsWith('fileman/.trash');
44
+ }
45
+
46
+ /** The synthetic `.trash` row shown at storage root — rendered as
47
+ * "Çöp Kutusu" / "Trash" via the `.trash` basename → locale mapping. */
48
+ export function makeTrashRow(adapter: string): FileNode {
49
+ return {
50
+ type: 'dir',
51
+ path: `${adapter}://fileman/.trash`,
52
+ basename: '.trash',
53
+ extension: '',
54
+ storage: adapter,
55
+ visibility: 'private',
56
+ size: 0,
57
+ file_size: 0,
58
+ mime_type: 'inode/directory',
59
+ extra_metadata: {},
60
+ } as unknown as FileNode;
61
+ }
62
+
63
+ /**
64
+ * Inject the virtual `.trash` row at the front of a root listing when
65
+ * enabled. Returns true if a row was added (so the caller can hydrate
66
+ * it). Mutates `files` in place. Single source for both panes.
67
+ */
68
+ export function injectTrashRow(
69
+ files: FileNode[],
70
+ adapter: string,
71
+ dirname: string,
72
+ trashVisible: boolean,
73
+ ): boolean {
74
+ if (!trashVisible) return false;
75
+ if (isTrashListing(dirname)) return false;
76
+ if (!isStorageRootDir(dirname)) return false;
77
+ files.unshift(makeTrashRow(adapter));
78
+ return true;
79
+ }
80
+
81
+ /** Best-effort fill of the trash row's size (total bytes) + date (newest
82
+ * deletion) from the backend trash listing, so it reads like a real
83
+ * folder instead of "— / —". Non-blocking; mutates the row in place. */
84
+ export async function hydrateTrashRow(
85
+ files: FileNode[],
86
+ storage: string,
87
+ api: { listTrash: (s?: string) => Promise<{ entries: Array<{ size?: number; deleted_at: string }> }> },
88
+ ): Promise<void> {
89
+ try {
90
+ const { entries } = await api.listTrash(storage);
91
+ const row = files.find((f) => f.basename === '.trash');
92
+ if (!row) return;
93
+ let total = 0;
94
+ let newest = 0;
95
+ for (const e of entries) {
96
+ total += e.size || 0;
97
+ const ts = Date.parse(e.deleted_at);
98
+ if (!Number.isNaN(ts) && ts > newest) newest = ts;
99
+ }
100
+ row.size = total;
101
+ if (newest > 0) row.last_modified = newest;
102
+ } catch {
103
+ /* keep the bare row */
104
+ }
105
+ }