@brftech/filex-core 0.5.0 → 0.7.0

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 (49) hide show
  1. package/dist/{ArchiveViewer-DKW-HyXT.js → ArchiveViewer-w6GDJJt9.js} +2 -2
  2. package/dist/{ArchiveViewer-DKW-HyXT.js.map → ArchiveViewer-w6GDJJt9.js.map} +1 -1
  3. package/dist/{CsvViewer-D2HmP2yr.js → CsvViewer-Ci5-mke8.js} +2 -2
  4. package/dist/{CsvViewer-D2HmP2yr.js.map → CsvViewer-Ci5-mke8.js.map} +1 -1
  5. package/dist/{DrawioViewer-DPNvOFCs.js → DrawioViewer-D2iMuQfD.js} +2 -2
  6. package/dist/{DrawioViewer-DPNvOFCs.js.map → DrawioViewer-D2iMuQfD.js.map} +1 -1
  7. package/dist/{EpubViewer-ByqjNuNL.js → EpubViewer-D4ydsa9X.js} +2 -2
  8. package/dist/{EpubViewer-ByqjNuNL.js.map → EpubViewer-D4ydsa9X.js.map} +1 -1
  9. package/dist/{IpynbViewer-2iONhbuJ.js → IpynbViewer-CWBwiFKX.js} +2 -2
  10. package/dist/{IpynbViewer-2iONhbuJ.js.map → IpynbViewer-CWBwiFKX.js.map} +1 -1
  11. package/dist/{MermaidViewer-CmiU7hIT.js → MermaidViewer-CDhuqQli.js} +2 -2
  12. package/dist/{MermaidViewer-CmiU7hIT.js.map → MermaidViewer-CDhuqQli.js.map} +1 -1
  13. package/dist/{PsdViewer-Inhl2yN9.js → PsdViewer-C_TkFb2H.js} +2 -2
  14. package/dist/{PsdViewer-Inhl2yN9.js.map → PsdViewer-C_TkFb2H.js.map} +1 -1
  15. package/dist/{TiffViewer-C2AFFEze.js → TiffViewer-BmWCXR7e.js} +2 -2
  16. package/dist/{TiffViewer-C2AFFEze.js.map → TiffViewer-BmWCXR7e.js.map} +1 -1
  17. package/dist/{Viewer3D-YdW14q_n.js → Viewer3D-5kPT1QsY.js} +2 -2
  18. package/dist/{Viewer3D-YdW14q_n.js.map → Viewer3D-5kPT1QsY.js.map} +1 -1
  19. package/dist/filex-core.js +66 -47
  20. package/dist/filex-core.umd.cjs +41 -40
  21. package/dist/filex-core.umd.cjs.map +1 -1
  22. package/dist/index-nc0-oI_l.js +11723 -0
  23. package/dist/index-nc0-oI_l.js.map +1 -0
  24. package/dist/index.d.ts +364 -1
  25. package/dist/style.css +1 -1
  26. package/package.json +1 -1
  27. package/src/FileExplorer.vue +756 -23
  28. package/src/components/CommandPalette.vue +12 -1
  29. package/src/components/EncryptedFolderModal.vue +125 -0
  30. package/src/components/GalleryView.vue +215 -0
  31. package/src/components/GridView.vue +2 -0
  32. package/src/components/InspectorPanel.vue +170 -0
  33. package/src/components/ListView.vue +2 -0
  34. package/src/components/SecondaryPane.vue +411 -0
  35. package/src/components/TabBar.vue +174 -0
  36. package/src/components/Toolbar.vue +23 -5
  37. package/src/composables/useFileApi.ts +60 -0
  38. package/src/composables/useKeyboardShortcuts.ts +19 -0
  39. package/src/composables/useTabs.ts +222 -0
  40. package/src/index.ts +27 -0
  41. package/src/lib/e2ecrypto.ts +299 -0
  42. package/src/locales/en.ts +67 -0
  43. package/src/locales/tr.ts +67 -0
  44. package/src/modals/NewFolderModal.vue +18 -0
  45. package/src/modals/PreviewModal.vue +7 -2
  46. package/src/styles/base.css +631 -0
  47. package/src/types/FileNode.ts +4 -1
  48. package/dist/index-eARevpz0.js +0 -9952
  49. package/dist/index-eARevpz0.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -12,6 +12,46 @@ declare type __VLS_Props = {
12
12
  config: ExplorerConfig;
13
13
  };
14
14
 
15
+ declare type __VLS_Props_10 = {
16
+ tabs: TabItem[];
17
+ activeId: string;
18
+ locale: LocaleCode;
19
+ /** false → the split toggle is hidden (narrow embeds). */
20
+ splitEnabled?: boolean;
21
+ /** true → the split toggle renders pressed (active tab has a pane). */
22
+ splitActive?: boolean;
23
+ };
24
+
25
+ declare type __VLS_Props_11 = {
26
+ api: FileApi;
27
+ /** Opening location, in the host's `currentPath` form. */
28
+ initialPath: string;
29
+ locale: LocaleCode;
30
+ /** Host's own qualify(): user path → wire `<adapter>://<rel>`. */
31
+ qualify: (p: string) => string;
32
+ /** Host's wire → user-path converter (multi-storage aware). */
33
+ toUser: (wire: string) => string;
34
+ /** Host's rootFloor clamp — keeps the pane inside the confine. */
35
+ clamp: (p: string) => string;
36
+ /** Label for the root crumb (adapter name / storage list). */
37
+ rootLabel: string;
38
+ /** rootFloor (user-path form, '' when unconfined) — trims crumbs. */
39
+ floor?: string;
40
+ /** Multi-storage mode: '' is the virtual drives root (no fetch). */
41
+ multiRoot?: boolean;
42
+ /** Synthesized storage rows for the virtual drives root. */
43
+ virtualRows?: () => FileNode[];
44
+ /** Active-panel highlight (keyboard target). */
45
+ active?: boolean;
46
+ };
47
+
48
+ declare type __VLS_Props_12 = {
49
+ open: boolean;
50
+ locale: LocaleCode;
51
+ /** True while the parent is creating the folder + uploading the marker. */
52
+ busy?: boolean;
53
+ };
54
+
15
55
  declare type __VLS_Props_2 = {
16
56
  open: boolean;
17
57
  locale: LocaleCode;
@@ -30,6 +70,7 @@ declare type __VLS_Props_2 = {
30
70
  pdfSaveUrl?: string | null;
31
71
  /** Standalone full-screen viewer route — `?path=…&storage=…&type=…`. */
32
72
  viewerBaseUrl?: string | null;
73
+ newTabEnabled?: boolean;
33
74
  /** Drop the dialog chrome (backdrop tint, header bar, footer actions)
34
75
  * so the viewer fills the full viewport. Used by the standalone
35
76
  * /files/edit route where the browser tab IS the container — a
@@ -216,16 +257,122 @@ declare interface ContextAction {
216
257
  divider?: boolean;
217
258
  }
218
259
 
260
+ /**
261
+ * Tiny per-explorer key ring: encrypted-folder root (wire path) → KEK.
262
+ * Lives ONLY in memory — "Kilitle" drops the entry, a reload drops all.
263
+ */
264
+ export declare function createKeyRing(): {
265
+ get(root: string): CryptoKey | undefined;
266
+ set(root: string, kek: CryptoKey): void;
267
+ /** Drop one folder's key ("Kilitle"). */
268
+ lock(root: string): void;
269
+ has(root: string): boolean;
270
+ clear(): void;
271
+ };
272
+
273
+ /** Create a fresh folder marker for `password` (also returns the derived KEK). */
274
+ export declare function createMarker(password: string, iterations?: number): Promise<{
275
+ marker: E2eMarker;
276
+ kek: CryptoKey;
277
+ }>;
278
+
279
+ /**
280
+ * Decrypt a 'filexe2e' blob with the folder KEK. Throws E2eDecryptError on
281
+ * a wrong key / tampered data, and a plain Error when the header is not an
282
+ * e2e file at all.
283
+ */
284
+ export declare function decryptFile(kek: CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer>;
285
+
219
286
  export declare const DEFAULT_THEME_ID = "default";
220
287
 
221
288
  declare type Density = 'comfortable' | 'compact';
222
289
 
290
+ /**
291
+ * Derive the folder KEK from a password. Returns a NON-extractable
292
+ * AES-256-GCM CryptoKey — it can encrypt/decrypt but never be exported,
293
+ * so even a same-origin script can't read the raw key material back.
294
+ */
295
+ export declare function deriveKek(password: string, salt: Uint8Array, iterations: number): Promise<CryptoKey>;
296
+
297
+ export declare const E2E_DEFAULT_ITERATIONS = 600000;
298
+
299
+ export declare const E2E_MAGIC = "filexe2e";
300
+
301
+ /**
302
+ * e2ecrypto — client-side crypto for E2E-encrypted folders (wiring:e2).
303
+ *
304
+ * WebCrypto ONLY — zero dependencies. Design doc: docs/E2E-ENCRYPTION.md.
305
+ *
306
+ * Scheme (v1):
307
+ * folder password ─PBKDF2-SHA256(600k iter, per-folder 16B salt)─▶ KEK (AES-256-GCM)
308
+ * per-file random 32B DEK (AES-256-GCM) encrypts the content one-shot;
309
+ * the DEK is wrapped with the KEK and stored in the file's own header.
310
+ *
311
+ * File layout ('filexe2e' magic, fixed 97-byte header):
312
+ * [0..8) magic "filexe2e"
313
+ * [8] version 0x01
314
+ * [9..21) wrapIV (12B) — GCM IV of the DEK wrap
315
+ * [21..69) wrappedDEK (48B = 32B DEK + 16B GCM tag)
316
+ * [69..81) dataIV (12B) — GCM IV of the content
317
+ * [81..97) reserved (zeros; v2 chunking/metadata)
318
+ * [97..) ciphertext (content + 16B GCM tag)
319
+ *
320
+ * Folder marker `.filex-e2e.json` at the encrypted-folder root:
321
+ * { v:1, salt:<b64 16B>, iter:600000, verify:<b64 12B IV || GCM('filex-e2e-verify-v1')> }
322
+ *
323
+ * The KEK NEVER leaves memory — no storage of any kind. Password loss is
324
+ * data loss by design (no recovery path exists anywhere).
325
+ */
326
+ export declare const E2E_MARKER_NAME = ".filex-e2e.json";
327
+
328
+ /** MVP single-shot in-memory ceiling — larger uploads are refused with a warning. */
329
+ export declare const E2E_MAX_FILE_BYTES: number;
330
+
331
+ export declare const E2E_MIN_PASSWORD_LEN = 8;
332
+
333
+ export declare const E2E_VERSION = 1;
334
+
335
+ /** Thrown on wrong password / corrupted ciphertext (GCM tag mismatch). */
336
+ export declare class E2eDecryptError extends Error {
337
+ constructor(msg?: string);
338
+ }
339
+
340
+ export declare type E2eKeyRing = ReturnType<typeof createKeyRing>;
341
+
342
+ export declare interface E2eMarker {
343
+ v: number;
344
+ salt: string;
345
+ iter: number;
346
+ verify: string;
347
+ }
348
+
223
349
  /** Current combo for an action ('' = unbound). */
224
350
  export declare function effectiveCombo(id: string): string;
225
351
 
226
352
  /** English UI strings. */
227
353
  export declare const en: Record<string, string>;
228
354
 
355
+ export declare const EncryptedFolderModal: DefineComponent<__VLS_Props_12, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
356
+ close: () => any;
357
+ submit: (payload: {
358
+ name: string;
359
+ password: string;
360
+ }) => any;
361
+ }, string, PublicProps, Readonly<__VLS_Props_12> & Readonly<{
362
+ onClose?: (() => any) | undefined;
363
+ onSubmit?: ((payload: {
364
+ name: string;
365
+ password: string;
366
+ }) => any) | undefined;
367
+ }>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
368
+
369
+ /**
370
+ * Encrypt `content` under the folder KEK: mints a fresh DEK, encrypts the
371
+ * content one-shot, wraps the DEK with the KEK and prepends the fixed
372
+ * 'filexe2e' header. Throws when content exceeds E2E_MAX_FILE_BYTES.
373
+ */
374
+ export declare function encryptFile(kek: CryptoKey, content: ArrayBuffer): Promise<ArrayBuffer>;
375
+
229
376
  /**
230
377
  * Resolved endpoint map. `useFileApi` derives this once on construction
231
378
  * — components never need to think about config/apiBase precedence.
@@ -648,6 +795,82 @@ onRefresh?: (() => any) | undefined;
648
795
  }>, {
649
796
  focusSearch: () => Promise<void>;
650
797
  }, {}, {}, {}, {}> | null;
798
+ splitPaneRef: CreateComponentPublicInstanceWithMixins<Readonly<{
799
+ api: FileApi;
800
+ initialPath: string;
801
+ locale: LocaleCode;
802
+ qualify: (p: string) => string;
803
+ toUser: (wire: string) => string;
804
+ clamp: (p: string) => string;
805
+ rootLabel: string;
806
+ floor?: string;
807
+ multiRoot?: boolean;
808
+ virtualRows?: () => FileNode[];
809
+ active?: boolean;
810
+ }> & Readonly<{
811
+ onClose?: (() => any) | undefined;
812
+ onNavigate?: ((path: string) => any) | undefined;
813
+ onActivate?: (() => any) | undefined;
814
+ "onOpen-tab"?: ((path: string) => any) | undefined;
815
+ onTransfer?: ((p: {
816
+ sources: string[];
817
+ targetWire: string;
818
+ originWire?: string;
819
+ }) => any) | undefined;
820
+ }>, {
821
+ reload: () => Promise<void>;
822
+ goUp: () => void;
823
+ selectAll: () => void;
824
+ openSelected: () => void;
825
+ selectedNodes: () => FileNode[];
826
+ getPath: () => string;
827
+ }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
828
+ close: () => any;
829
+ navigate: (path: string) => any;
830
+ activate: () => any;
831
+ "open-tab": (path: string) => any;
832
+ transfer: (p: {
833
+ sources: string[];
834
+ targetWire: string;
835
+ originWire?: string;
836
+ }) => any;
837
+ }, PublicProps, {}, false, {}, {}, GlobalComponents, GlobalDirectives, string, {}, HTMLElement, ComponentProvideOptions, {
838
+ P: {};
839
+ B: {};
840
+ D: {};
841
+ C: {};
842
+ M: {};
843
+ Defaults: {};
844
+ }, Readonly<{
845
+ api: FileApi;
846
+ initialPath: string;
847
+ locale: LocaleCode;
848
+ qualify: (p: string) => string;
849
+ toUser: (wire: string) => string;
850
+ clamp: (p: string) => string;
851
+ rootLabel: string;
852
+ floor?: string;
853
+ multiRoot?: boolean;
854
+ virtualRows?: () => FileNode[];
855
+ active?: boolean;
856
+ }> & Readonly<{
857
+ onClose?: (() => any) | undefined;
858
+ onNavigate?: ((path: string) => any) | undefined;
859
+ onActivate?: (() => any) | undefined;
860
+ "onOpen-tab"?: ((path: string) => any) | undefined;
861
+ onTransfer?: ((p: {
862
+ sources: string[];
863
+ targetWire: string;
864
+ originWire?: string;
865
+ }) => any) | undefined;
866
+ }>, {
867
+ reload: () => Promise<void>;
868
+ goUp: () => void;
869
+ selectAll: () => void;
870
+ openSelected: () => void;
871
+ selectedNodes: () => FileNode[];
872
+ getPath: () => string;
873
+ }, {}, {}, {}, {}> | null;
651
874
  ctxRef: CreateComponentPublicInstanceWithMixins<Readonly<{
652
875
  locale: LocaleCode;
653
876
  actions: ContextAction[];
@@ -734,6 +957,7 @@ export declare interface FileNode {
734
957
  * projectFileNodes emits it when a storage has RBAC on). '' / undefined =
735
958
  * ACL not enforced. Used to gate edit/manage affordances client-side. */
736
959
  perm?: 'none' | 'viewer' | 'editor' | 'owner';
960
+ e2e?: boolean;
737
961
  /** Generic — any additional fields the backend wants to inline. */
738
962
  [k: string]: unknown;
739
963
  }
@@ -759,6 +983,8 @@ export declare function getHighlight(): unknown | null;
759
983
  /** Synchronous cached lookup — null if Monaco not yet ready or absent. */
760
984
  export declare function getMonaco(): unknown | null;
761
985
 
986
+ declare function getPath(): string;
987
+
762
988
  /**
763
989
  * One hit from the dedicated files-search endpoint. The backend returns raw
764
990
  * node rows (`{results: [...]}`), so `path` is the IN-STORAGE relative path
@@ -785,6 +1011,8 @@ export declare interface GlobalSearchHit {
785
1011
 
786
1012
  export declare type GlobalSearchScope = 'name' | 'content' | 'all';
787
1013
 
1014
+ declare function goUp(): void;
1015
+
788
1016
  /** A single ACL grant row (RBAC permissions panel). */
789
1017
  declare interface Grant {
790
1018
  id: number;
@@ -797,6 +1025,9 @@ declare interface Grant {
797
1025
  inherited?: boolean;
798
1026
  }
799
1027
 
1028
+ /** True when the buffer starts with the 'filexe2e' magic. */
1029
+ export declare function hasMagic(buf: ArrayBuffer | Uint8Array): boolean;
1030
+
800
1031
  declare interface InviteResponse {
801
1032
  mode: 'granted' | 'user_created' | 'shared';
802
1033
  user_id?: number;
@@ -822,6 +1053,8 @@ export declare interface ManagerResponse {
822
1053
  /** RBAC effective level for the current user on this directory ('' when ACL
823
1054
  * is not enforced on the storage). Gates the folder-level write actions. */
824
1055
  perm?: 'none' | 'viewer' | 'editor' | 'owner';
1056
+ e2e?: boolean;
1057
+ e2e_root?: string;
825
1058
  files: FileNode[];
826
1059
  }
827
1060
 
@@ -830,6 +1063,20 @@ export declare function matchedInContent(matched: unknown): boolean;
830
1063
 
831
1064
  export declare const messages: Record<LocaleCode, Record<string, string>>;
832
1065
 
1066
+ /** Mirrors backend `model.NodeComment` (GET /api/files/comments?node_id=…). */
1067
+ declare interface NodeComment {
1068
+ id: number;
1069
+ node_id: number;
1070
+ user_id: number;
1071
+ body: string;
1072
+ created_at: string;
1073
+ updated_at?: string;
1074
+ /** Joined author display name (email fallback), filled by the backend. */
1075
+ author_name?: string;
1076
+ /** Whether the CURRENT caller may delete this row (author or admin). */
1077
+ can_delete?: boolean;
1078
+ }
1079
+
833
1080
  /** Mirrors backend `model.NodeVersion` (GET /api/files/versions?node_id=…). */
834
1081
  declare interface NodeVersion {
835
1082
  id: number;
@@ -841,6 +1088,8 @@ declare interface NodeVersion {
841
1088
  created_at: string;
842
1089
  }
843
1090
 
1091
+ declare function openSelected(): void;
1092
+
844
1093
  export declare interface Operation {
845
1094
  /** `${group}:${id}` — unique across publishers. */
846
1095
  key: string;
@@ -924,6 +1173,9 @@ export declare type OperationsStore = ReturnType<typeof useOperations>;
924
1173
 
925
1174
  export declare type OperationStatus = 'running' | 'done' | 'error' | 'aborted';
926
1175
 
1176
+ /** Parse marker JSON text; returns null when the shape is not a v1 marker. */
1177
+ export declare function parseMarker(text: string): E2eMarker | null;
1178
+
927
1179
  export declare interface PendingOp {
928
1180
  id: number;
929
1181
  op_type: 'copy' | 'move' | 'delete';
@@ -1030,6 +1282,8 @@ declare interface RecentNode {
1030
1282
  last_opened?: string;
1031
1283
  }
1032
1284
 
1285
+ declare function reload(): Promise<void>;
1286
+
1033
1287
  export declare function resetAllShortcuts(): void;
1034
1288
 
1035
1289
  export declare function resetShortcut(id: string): void;
@@ -1055,6 +1309,39 @@ export declare function resolveEndpoints(config: ExplorerConfig): EndpointMap;
1055
1309
  /** Matched-in values the search contract allows on a hit. */
1056
1310
  export declare type SearchMatched = 'name' | 'content' | 'both';
1057
1311
 
1312
+ export declare const SecondaryPane: DefineComponent<__VLS_Props_11, {
1313
+ reload: typeof reload;
1314
+ goUp: typeof goUp;
1315
+ selectAll: typeof selectAll;
1316
+ openSelected: typeof openSelected;
1317
+ selectedNodes: typeof selectedNodes;
1318
+ getPath: typeof getPath;
1319
+ }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
1320
+ close: () => any;
1321
+ navigate: (path: string) => any;
1322
+ activate: () => any;
1323
+ "open-tab": (path: string) => any;
1324
+ transfer: (p: {
1325
+ sources: string[];
1326
+ targetWire: string;
1327
+ originWire?: string;
1328
+ }) => any;
1329
+ }, string, PublicProps, Readonly<__VLS_Props_11> & Readonly<{
1330
+ onClose?: (() => any) | undefined;
1331
+ onNavigate?: ((path: string) => any) | undefined;
1332
+ onActivate?: (() => any) | undefined;
1333
+ "onOpen-tab"?: ((path: string) => any) | undefined;
1334
+ onTransfer?: ((p: {
1335
+ sources: string[];
1336
+ targetWire: string;
1337
+ originWire?: string;
1338
+ }) => any) | undefined;
1339
+ }>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, HTMLElement>;
1340
+
1341
+ declare function selectAll(): void;
1342
+
1343
+ declare function selectedNodes(): FileNode[];
1344
+
1058
1345
  declare type SelectionMode_2 = 'none' | 'single-file' | 'single-dir' | 'multi';
1059
1346
 
1060
1347
  /** Set (or clear back to default) a user override. '' unbinds. */
@@ -1120,6 +1407,10 @@ export declare interface ShortcutHandlers {
1120
1407
  onShowHelp?: () => void;
1121
1408
  onToggleInspector?: () => void;
1122
1409
  onQuickLook?: () => void;
1410
+ onTabNew?: () => void;
1411
+ onTabClose?: () => void;
1412
+ onTabNext?: () => void;
1413
+ onTabPrev?: () => void;
1123
1414
  hasSelection?: () => boolean;
1124
1415
  }
1125
1416
 
@@ -1178,6 +1469,42 @@ onChange?: ((value: boolean) => any) | undefined;
1178
1469
  */
1179
1470
  export declare function syncThemeStyle(id: string): void;
1180
1471
 
1472
+ export declare const TabBar: DefineComponent<__VLS_Props_10, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
1473
+ close: (id: string) => any;
1474
+ select: (id: string) => any;
1475
+ new: () => any;
1476
+ reorder: (from: number, to: number) => any;
1477
+ "toggle-split": () => any;
1478
+ }, string, PublicProps, Readonly<__VLS_Props_10> & Readonly<{
1479
+ onClose?: ((id: string) => any) | undefined;
1480
+ onSelect?: ((id: string) => any) | undefined;
1481
+ onNew?: (() => any) | undefined;
1482
+ onReorder?: ((from: number, to: number) => any) | undefined;
1483
+ "onToggle-split"?: (() => any) | undefined;
1484
+ }>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, HTMLDivElement>;
1485
+
1486
+ declare interface TabItem {
1487
+ id: string;
1488
+ label: string;
1489
+ /** true → the tab carries a split pane (small accent dot). */
1490
+ split: boolean;
1491
+ }
1492
+
1493
+ export declare type TabsApi = ReturnType<typeof useTabs>;
1494
+
1495
+ /** Per-tab split state — the secondary pane's own location. */
1496
+ export declare interface TabSplit {
1497
+ path: string;
1498
+ }
1499
+
1500
+ export declare interface TabState {
1501
+ id: string;
1502
+ /** Location snapshot in `currentPath` form (see module docs). */
1503
+ path: string;
1504
+ viewMode: ViewMode;
1505
+ split: TabSplit | null;
1506
+ }
1507
+
1181
1508
  export declare const TagPicker: DefineComponent<__VLS_Props_4, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
1182
1509
  error: (message: string) => any;
1183
1510
  change: (tags: string[]) => any;
@@ -1361,6 +1688,9 @@ export declare function useFileApi(config: ExplorerConfig): {
1361
1688
  listVersions: (nodeId: number) => Promise<NodeVersion[]>;
1362
1689
  restoreVersion: (nodeId: number, versionId: number, snapshotCurrent?: boolean) => Promise<void>;
1363
1690
  snapshotVersion: (nodeId: number) => Promise<void>;
1691
+ listComments: (nodeId: number) => Promise<NodeComment[]>;
1692
+ addComment: (nodeId: number, body: string) => Promise<NodeComment>;
1693
+ deleteComment: (id: number) => Promise<void>;
1364
1694
  listPermissions: (path: string) => Promise<PermissionsResponse>;
1365
1695
  resolveEmail: (email: string) => Promise<ResolveEmailResponse>;
1366
1696
  searchUsers: (q: string) => Promise<UserSearchResponse>;
@@ -1580,6 +1910,32 @@ export declare function useSelection(items: () => FileNode[]): {
1580
1910
  /** Reactive, override-aware view of the registry (registry order). */
1581
1911
  export declare function useShortcutList(): ComputedRef<ShortcutView[]>;
1582
1912
 
1913
+ export declare function useTabs(opts: UseTabsOptions): {
1914
+ tabs: Ref<TabState[], TabState[]>;
1915
+ activeId: Ref<string, string>;
1916
+ activeTab: ComputedRef<TabState | null>;
1917
+ activeIndex: ComputedRef<number>;
1918
+ hasMultiple: ComputedRef<boolean>;
1919
+ restore: () => boolean;
1920
+ seed: (path: string, viewMode: ViewMode) => void;
1921
+ syncActive: (patch: Partial<Pick<TabState, "path" | "viewMode">>) => void;
1922
+ openTab: (path: string, o: {
1923
+ viewMode: ViewMode;
1924
+ background?: boolean;
1925
+ split?: TabSplit | null;
1926
+ }) => TabState;
1927
+ closeTab: (id: string) => TabState | null;
1928
+ activate: (id: string) => TabState | null;
1929
+ step: (delta: number) => TabState | null;
1930
+ move: (from: number, to: number) => void;
1931
+ setSplit: (split: TabSplit | null) => void;
1932
+ };
1933
+
1934
+ declare interface UseTabsOptions {
1935
+ /** localStorage key; null disables persistence entirely. */
1936
+ storageKey: string | null;
1937
+ }
1938
+
1583
1939
  /** Reactive handle for components: `{ themeId, setTheme }`. */
1584
1940
  export declare function useThemeState(): {
1585
1941
  themeId: Ref<string>;
@@ -1590,6 +1946,13 @@ export declare function useUploadChunked(config: ExplorerConfig, api: FileApi):
1590
1946
  uploadFile: (opts: UploadOptions) => Promise<UploadFinalizeResponse>;
1591
1947
  };
1592
1948
 
1593
- export declare type ViewMode = 'list' | 'grid';
1949
+ /**
1950
+ * Check `password` against a folder marker. Resolves to the derived KEK on
1951
+ * success, or `null` on a wrong password (GCM tag mismatch on the verify
1952
+ * blob). Never talks to any server.
1953
+ */
1954
+ export declare function verifyPassword(marker: E2eMarker, password: string): Promise<CryptoKey | null>;
1955
+
1956
+ export declare type ViewMode = 'list' | 'grid' | 'gallery';
1594
1957
 
1595
1958
  export { }