@brftech/filex-core 0.6.0 → 0.7.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.
Files changed (47) hide show
  1. package/dist/{ArchiveViewer-EGgNqLPE.js → ArchiveViewer-Qi4L2jj9.js} +2 -2
  2. package/dist/{ArchiveViewer-EGgNqLPE.js.map → ArchiveViewer-Qi4L2jj9.js.map} +1 -1
  3. package/dist/{CsvViewer-BvriJ42M.js → CsvViewer-IOIrJDsJ.js} +2 -2
  4. package/dist/{CsvViewer-BvriJ42M.js.map → CsvViewer-IOIrJDsJ.js.map} +1 -1
  5. package/dist/{DrawioViewer-B_bSPsWX.js → DrawioViewer-D0KtUQBO.js} +2 -2
  6. package/dist/{DrawioViewer-B_bSPsWX.js.map → DrawioViewer-D0KtUQBO.js.map} +1 -1
  7. package/dist/{EpubViewer-hl68h4gG.js → EpubViewer-DJOXiOi4.js} +2 -2
  8. package/dist/{EpubViewer-hl68h4gG.js.map → EpubViewer-DJOXiOi4.js.map} +1 -1
  9. package/dist/{IpynbViewer-lhwOjXK7.js → IpynbViewer-D16jXVJc.js} +2 -2
  10. package/dist/{IpynbViewer-lhwOjXK7.js.map → IpynbViewer-D16jXVJc.js.map} +1 -1
  11. package/dist/{MermaidViewer-H2Z6AYXg.js → MermaidViewer-BnSaVg1L.js} +2 -2
  12. package/dist/{MermaidViewer-H2Z6AYXg.js.map → MermaidViewer-BnSaVg1L.js.map} +1 -1
  13. package/dist/{PsdViewer-Bsk-11yH.js → PsdViewer-BUerXaQA.js} +2 -2
  14. package/dist/{PsdViewer-Bsk-11yH.js.map → PsdViewer-BUerXaQA.js.map} +1 -1
  15. package/dist/{TiffViewer-D-j9vycG.js → TiffViewer-D0Qn3IV-.js} +2 -2
  16. package/dist/{TiffViewer-D-j9vycG.js.map → TiffViewer-D0Qn3IV-.js.map} +1 -1
  17. package/dist/{Viewer3D-Bcr70rTe.js → Viewer3D-BtRK-k1B.js} +2 -2
  18. package/dist/{Viewer3D-Bcr70rTe.js.map → Viewer3D-BtRK-k1B.js.map} +1 -1
  19. package/dist/filex-core.js +66 -50
  20. package/dist/filex-core.umd.cjs +35 -35
  21. package/dist/filex-core.umd.cjs.map +1 -1
  22. package/dist/index-CGNE1bSv.js +11802 -0
  23. package/dist/index-CGNE1bSv.js.map +1 -0
  24. package/dist/index.d.ts +184 -0
  25. package/dist/style.css +1 -1
  26. package/package.json +1 -1
  27. package/src/FileExplorer.vue +416 -15
  28. package/src/components/EncryptedFolderModal.vue +125 -0
  29. package/src/components/GalleryView.vue +1 -0
  30. package/src/components/GridView.vue +1 -0
  31. package/src/components/ListView.vue +1 -0
  32. package/src/components/SecondaryPane.vue +72 -78
  33. package/src/components/Toolbar.vue +112 -3
  34. package/src/composables/useFileApi.ts +7 -0
  35. package/src/composables/useKeyboardShortcuts.ts +6 -0
  36. package/src/composables/useTabs.ts +11 -2
  37. package/src/index.ts +21 -0
  38. package/src/lib/e2ecrypto.ts +299 -0
  39. package/src/locales/en.ts +34 -0
  40. package/src/locales/tr.ts +34 -0
  41. package/src/modals/NewFolderModal.vue +18 -0
  42. package/src/modals/PreviewModal.vue +7 -2
  43. package/src/styles/base.css +126 -1
  44. package/src/styles/variables.css +2 -2
  45. package/src/types/FileNode.ts +3 -0
  46. package/dist/index-C_ZSnk_I.js +0 -11109
  47. package/dist/index-C_ZSnk_I.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -43,6 +43,18 @@ declare type __VLS_Props_11 = {
43
43
  virtualRows?: () => FileNode[];
44
44
  /** Active-panel highlight (keyboard target). */
45
45
  active?: boolean;
46
+ /** ui-fix — the pane renders the SAME view components as the main
47
+ * panel (list/grid/gallery) instead of its own flat list. */
48
+ viewMode?: ViewMode;
49
+ /** ui-fix — authenticated thumb resolver, forwarded to grid/gallery. */
50
+ thumbSrc?: (n: FileNode) => string | null;
51
+ };
52
+
53
+ declare type __VLS_Props_12 = {
54
+ open: boolean;
55
+ locale: LocaleCode;
56
+ /** True while the parent is creating the folder + uploading the marker. */
57
+ busy?: boolean;
46
58
  };
47
59
 
48
60
  declare type __VLS_Props_2 = {
@@ -63,6 +75,7 @@ declare type __VLS_Props_2 = {
63
75
  pdfSaveUrl?: string | null;
64
76
  /** Standalone full-screen viewer route — `?path=…&storage=…&type=…`. */
65
77
  viewerBaseUrl?: string | null;
78
+ newTabEnabled?: boolean;
66
79
  /** Drop the dialog chrome (backdrop tint, header bar, footer actions)
67
80
  * so the viewer fills the full viewport. Used by the standalone
68
81
  * /files/edit route where the browser tab IS the container — a
@@ -249,16 +262,122 @@ declare interface ContextAction {
249
262
  divider?: boolean;
250
263
  }
251
264
 
265
+ /**
266
+ * Tiny per-explorer key ring: encrypted-folder root (wire path) → KEK.
267
+ * Lives ONLY in memory — "Kilitle" drops the entry, a reload drops all.
268
+ */
269
+ export declare function createKeyRing(): {
270
+ get(root: string): CryptoKey | undefined;
271
+ set(root: string, kek: CryptoKey): void;
272
+ /** Drop one folder's key ("Kilitle"). */
273
+ lock(root: string): void;
274
+ has(root: string): boolean;
275
+ clear(): void;
276
+ };
277
+
278
+ /** Create a fresh folder marker for `password` (also returns the derived KEK). */
279
+ export declare function createMarker(password: string, iterations?: number): Promise<{
280
+ marker: E2eMarker;
281
+ kek: CryptoKey;
282
+ }>;
283
+
284
+ /**
285
+ * Decrypt a 'filexe2e' blob with the folder KEK. Throws E2eDecryptError on
286
+ * a wrong key / tampered data, and a plain Error when the header is not an
287
+ * e2e file at all.
288
+ */
289
+ export declare function decryptFile(kek: CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer>;
290
+
252
291
  export declare const DEFAULT_THEME_ID = "default";
253
292
 
254
293
  declare type Density = 'comfortable' | 'compact';
255
294
 
295
+ /**
296
+ * Derive the folder KEK from a password. Returns a NON-extractable
297
+ * AES-256-GCM CryptoKey — it can encrypt/decrypt but never be exported,
298
+ * so even a same-origin script can't read the raw key material back.
299
+ */
300
+ export declare function deriveKek(password: string, salt: Uint8Array, iterations: number): Promise<CryptoKey>;
301
+
302
+ export declare const E2E_DEFAULT_ITERATIONS = 600000;
303
+
304
+ export declare const E2E_MAGIC = "filexe2e";
305
+
306
+ /**
307
+ * e2ecrypto — client-side crypto for E2E-encrypted folders (wiring:e2).
308
+ *
309
+ * WebCrypto ONLY — zero dependencies. Design doc: docs/E2E-ENCRYPTION.md.
310
+ *
311
+ * Scheme (v1):
312
+ * folder password ─PBKDF2-SHA256(600k iter, per-folder 16B salt)─▶ KEK (AES-256-GCM)
313
+ * per-file random 32B DEK (AES-256-GCM) encrypts the content one-shot;
314
+ * the DEK is wrapped with the KEK and stored in the file's own header.
315
+ *
316
+ * File layout ('filexe2e' magic, fixed 97-byte header):
317
+ * [0..8) magic "filexe2e"
318
+ * [8] version 0x01
319
+ * [9..21) wrapIV (12B) — GCM IV of the DEK wrap
320
+ * [21..69) wrappedDEK (48B = 32B DEK + 16B GCM tag)
321
+ * [69..81) dataIV (12B) — GCM IV of the content
322
+ * [81..97) reserved (zeros; v2 chunking/metadata)
323
+ * [97..) ciphertext (content + 16B GCM tag)
324
+ *
325
+ * Folder marker `.filex-e2e.json` at the encrypted-folder root:
326
+ * { v:1, salt:<b64 16B>, iter:600000, verify:<b64 12B IV || GCM('filex-e2e-verify-v1')> }
327
+ *
328
+ * The KEK NEVER leaves memory — no storage of any kind. Password loss is
329
+ * data loss by design (no recovery path exists anywhere).
330
+ */
331
+ export declare const E2E_MARKER_NAME = ".filex-e2e.json";
332
+
333
+ /** MVP single-shot in-memory ceiling — larger uploads are refused with a warning. */
334
+ export declare const E2E_MAX_FILE_BYTES: number;
335
+
336
+ export declare const E2E_MIN_PASSWORD_LEN = 8;
337
+
338
+ export declare const E2E_VERSION = 1;
339
+
340
+ /** Thrown on wrong password / corrupted ciphertext (GCM tag mismatch). */
341
+ export declare class E2eDecryptError extends Error {
342
+ constructor(msg?: string);
343
+ }
344
+
345
+ export declare type E2eKeyRing = ReturnType<typeof createKeyRing>;
346
+
347
+ export declare interface E2eMarker {
348
+ v: number;
349
+ salt: string;
350
+ iter: number;
351
+ verify: string;
352
+ }
353
+
256
354
  /** Current combo for an action ('' = unbound). */
257
355
  export declare function effectiveCombo(id: string): string;
258
356
 
259
357
  /** English UI strings. */
260
358
  export declare const en: Record<string, string>;
261
359
 
360
+ export declare const EncryptedFolderModal: DefineComponent<__VLS_Props_12, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
361
+ close: () => any;
362
+ submit: (payload: {
363
+ name: string;
364
+ password: string;
365
+ }) => any;
366
+ }, string, PublicProps, Readonly<__VLS_Props_12> & Readonly<{
367
+ onClose?: (() => any) | undefined;
368
+ onSubmit?: ((payload: {
369
+ name: string;
370
+ password: string;
371
+ }) => any) | undefined;
372
+ }>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
373
+
374
+ /**
375
+ * Encrypt `content` under the folder KEK: mints a fresh DEK, encrypts the
376
+ * content one-shot, wraps the DEK with the KEK and prepends the fixed
377
+ * 'filexe2e' header. Throws when content exceeds E2E_MAX_FILE_BYTES.
378
+ */
379
+ export declare function encryptFile(kek: CryptoKey, content: ArrayBuffer): Promise<ArrayBuffer>;
380
+
262
381
  /**
263
382
  * Resolved endpoint map. `useFileApi` derives this once on construction
264
383
  * — components never need to think about config/apiBase precedence.
@@ -602,6 +721,9 @@ refresh: () => any;
602
721
  "open-theme": () => any;
603
722
  "open-shortcut-settings": () => any;
604
723
  }, PublicProps, {}, false, {}, {}, GlobalComponents, GlobalDirectives, string, {
724
+ primaryEl: HTMLDivElement;
725
+ wideMoreBtnEl: HTMLButtonElement;
726
+ measureEl: HTMLDivElement;
605
727
  searchEl: HTMLInputElement;
606
728
  moreBtnEl: HTMLButtonElement;
607
729
  moreRef: CreateComponentPublicInstanceWithMixins<Readonly<{
@@ -643,6 +765,45 @@ clientY: number;
643
765
  }, nodes: FileNode[]) => Promise<void>;
644
766
  hide: () => void;
645
767
  }, {}, {}, {}, {}> | null;
768
+ wideMoreRef: CreateComponentPublicInstanceWithMixins<Readonly<{
769
+ locale: LocaleCode;
770
+ actions: ContextAction[];
771
+ theme?: ThemeMode;
772
+ sheet?: boolean;
773
+ }> & Readonly<{
774
+ onSelect?: ((action: ContextAction, target: FileNode[]) => any) | undefined;
775
+ }>, {
776
+ show: (ev: {
777
+ clientX: number;
778
+ clientY: number;
779
+ }, nodes: FileNode[]) => Promise<void>;
780
+ hide: () => void;
781
+ }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
782
+ select: (action: ContextAction, target: FileNode[]) => any;
783
+ }, PublicProps, {}, false, {}, {}, GlobalComponents, GlobalDirectives, string, {
784
+ sheetEl: HTMLDivElement;
785
+ menuEl: HTMLDivElement;
786
+ }, any, ComponentProvideOptions, {
787
+ P: {};
788
+ B: {};
789
+ D: {};
790
+ C: {};
791
+ M: {};
792
+ Defaults: {};
793
+ }, Readonly<{
794
+ locale: LocaleCode;
795
+ actions: ContextAction[];
796
+ theme?: ThemeMode;
797
+ sheet?: boolean;
798
+ }> & Readonly<{
799
+ onSelect?: ((action: ContextAction, target: FileNode[]) => any) | undefined;
800
+ }>, {
801
+ show: (ev: {
802
+ clientX: number;
803
+ clientY: number;
804
+ }, nodes: FileNode[]) => Promise<void>;
805
+ hide: () => void;
806
+ }, {}, {}, {}, {}> | null;
646
807
  }, HTMLDivElement, ComponentProvideOptions, {
647
808
  P: {};
648
809
  B: {};
@@ -693,6 +854,8 @@ floor?: string;
693
854
  multiRoot?: boolean;
694
855
  virtualRows?: () => FileNode[];
695
856
  active?: boolean;
857
+ viewMode?: ViewMode;
858
+ thumbSrc?: (n: FileNode) => string | null;
696
859
  }> & Readonly<{
697
860
  onClose?: (() => any) | undefined;
698
861
  onNavigate?: ((path: string) => any) | undefined;
@@ -739,6 +902,8 @@ floor?: string;
739
902
  multiRoot?: boolean;
740
903
  virtualRows?: () => FileNode[];
741
904
  active?: boolean;
905
+ viewMode?: ViewMode;
906
+ thumbSrc?: (n: FileNode) => string | null;
742
907
  }> & Readonly<{
743
908
  onClose?: (() => any) | undefined;
744
909
  onNavigate?: ((path: string) => any) | undefined;
@@ -843,6 +1008,7 @@ export declare interface FileNode {
843
1008
  * projectFileNodes emits it when a storage has RBAC on). '' / undefined =
844
1009
  * ACL not enforced. Used to gate edit/manage affordances client-side. */
845
1010
  perm?: 'none' | 'viewer' | 'editor' | 'owner';
1011
+ e2e?: boolean;
846
1012
  /** Generic — any additional fields the backend wants to inline. */
847
1013
  [k: string]: unknown;
848
1014
  }
@@ -910,6 +1076,9 @@ declare interface Grant {
910
1076
  inherited?: boolean;
911
1077
  }
912
1078
 
1079
+ /** True when the buffer starts with the 'filexe2e' magic. */
1080
+ export declare function hasMagic(buf: ArrayBuffer | Uint8Array): boolean;
1081
+
913
1082
  declare interface InviteResponse {
914
1083
  mode: 'granted' | 'user_created' | 'shared';
915
1084
  user_id?: number;
@@ -935,6 +1104,8 @@ export declare interface ManagerResponse {
935
1104
  /** RBAC effective level for the current user on this directory ('' when ACL
936
1105
  * is not enforced on the storage). Gates the folder-level write actions. */
937
1106
  perm?: 'none' | 'viewer' | 'editor' | 'owner';
1107
+ e2e?: boolean;
1108
+ e2e_root?: string;
938
1109
  files: FileNode[];
939
1110
  }
940
1111
 
@@ -1053,6 +1224,9 @@ export declare type OperationsStore = ReturnType<typeof useOperations>;
1053
1224
 
1054
1225
  export declare type OperationStatus = 'running' | 'done' | 'error' | 'aborted';
1055
1226
 
1227
+ /** Parse marker JSON text; returns null when the shape is not a v1 marker. */
1228
+ export declare function parseMarker(text: string): E2eMarker | null;
1229
+
1056
1230
  export declare interface PendingOp {
1057
1231
  id: number;
1058
1232
  op_type: 'copy' | 'move' | 'delete';
@@ -1372,6 +1546,9 @@ export declare type TabsApi = ReturnType<typeof useTabs>;
1372
1546
  /** Per-tab split state — the secondary pane's own location. */
1373
1547
  export declare interface TabSplit {
1374
1548
  path: string;
1549
+ /** ui-fix — the pane's OWN view mode (list/grid/gallery); undefined
1550
+ * inherits the main panel's mode at split time. */
1551
+ viewMode?: ViewMode;
1375
1552
  }
1376
1553
 
1377
1554
  export declare interface TabState {
@@ -1823,6 +2000,13 @@ export declare function useUploadChunked(config: ExplorerConfig, api: FileApi):
1823
2000
  uploadFile: (opts: UploadOptions) => Promise<UploadFinalizeResponse>;
1824
2001
  };
1825
2002
 
2003
+ /**
2004
+ * Check `password` against a folder marker. Resolves to the derived KEK on
2005
+ * success, or `null` on a wrong password (GCM tag mismatch on the verify
2006
+ * blob). Never talks to any server.
2007
+ */
2008
+ export declare function verifyPassword(marker: E2eMarker, password: string): Promise<CryptoKey | null>;
2009
+
1826
2010
  export declare type ViewMode = 'list' | 'grid' | 'gallery';
1827
2011
 
1828
2012
  export { }