@brftech/filex-core 0.31.0 → 0.32.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.
- package/README.md +26 -2
- package/dist/filex-core.js +9289 -8103
- package/dist/filex-core.js.map +1 -1
- package/dist/filex-core.umd.cjs +49 -49
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/index.d.ts +186 -8
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/FileExplorer.vue +510 -12
- package/src/components/CommandPalette.vue +18 -2
- package/src/components/E2eRecoveryUnlockModal.vue +29 -4
- package/src/components/FilterBar.vue +244 -0
- package/src/components/GalleryView.vue +11 -0
- package/src/components/GridView.vue +46 -3
- package/src/components/InspectorPanel.vue +231 -8
- package/src/components/ListView.vue +10 -1
- package/src/components/SecondaryPane.vue +15 -1
- package/src/components/SideNav.vue +161 -1
- package/src/components/Toolbar.vue +235 -49
- package/src/components/ViewSwitcher.vue +81 -0
- package/src/composables/useFileApi.ts +45 -0
- package/src/index.ts +6 -0
- package/src/lib/e2ecrypto.ts +191 -3
- package/src/lib/fileFilters.ts +143 -0
- package/src/lib/listing.ts +47 -0
- package/src/locales/en.ts +83 -0
- package/src/locales/tr.ts +83 -0
- package/src/modals/PermissionsModal.vue +18 -2
- package/src/styles/base.css +491 -0
- package/src/types/ExplorerConfig.ts +17 -1
package/dist/index.d.ts
CHANGED
|
@@ -54,6 +54,13 @@ declare type __VLS_Props_11 = {
|
|
|
54
54
|
/** ui-fix — mirror the main panel's virtual `.trash` row at storage root
|
|
55
55
|
* so both split panes list identical rows (no row-offset). Defaults on. */
|
|
56
56
|
trashVisible?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* The navigation panel is already offering Trash, so neither pane draws the
|
|
59
|
+
* virtual row. Passed down rather than worked out here: the panel belongs to
|
|
60
|
+
* the host explorer, and a pane that guessed at it is how the two halves of a
|
|
61
|
+
* split end up listing different rows.
|
|
62
|
+
*/
|
|
63
|
+
navOffersTrash?: boolean;
|
|
57
64
|
};
|
|
58
65
|
|
|
59
66
|
declare type __VLS_Props_12 = {
|
|
@@ -85,9 +92,15 @@ declare type __VLS_Props_14 = {
|
|
|
85
92
|
locale: LocaleCode;
|
|
86
93
|
/** The folder has a user recovery key slot (v2 markers created since 0.31). */
|
|
87
94
|
hasRecovery: boolean;
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Whether the escrow door applies to THIS folder — see escrowAvailability.
|
|
97
|
+
* 'predates' and 'other-key' are not "no escrow tab"; they are two
|
|
98
|
+
* different facts the dialog has to state, because an admin who knows the
|
|
99
|
+
* installation has escrow reads a missing tab as a bug and tries the key
|
|
100
|
+
* anyway.
|
|
101
|
+
*/
|
|
102
|
+
escrowState: EscrowAvailability;
|
|
103
|
+
/** Short id of the escrow key THIS FOLDER was sealed to, when it has one. */
|
|
91
104
|
escrowKid?: string | null;
|
|
92
105
|
busy?: boolean;
|
|
93
106
|
/** Set by the parent after a failed attempt. */
|
|
@@ -287,6 +300,40 @@ declare type __VLS_Props_9 = {
|
|
|
287
300
|
narrow?: boolean;
|
|
288
301
|
};
|
|
289
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Give an EXISTING v2 folder an escrow slot, in place, using the folder
|
|
305
|
+
* password its owner has just typed.
|
|
306
|
+
*
|
|
307
|
+
* ── Why this exists ─────────────────────────────────────────────────
|
|
308
|
+
*
|
|
309
|
+
* Escrow used to be all-or-nothing at install time, and then adoptable but
|
|
310
|
+
* never retroactive: on any installation that had been running for a while,
|
|
311
|
+
* escrow covered only folders nobody had created yet. On a real deployment
|
|
312
|
+
* the folders that matter already exist, so "new folders only" means escrow
|
|
313
|
+
* covers nothing anyone cares about.
|
|
314
|
+
*
|
|
315
|
+
* The server still cannot do this, and that has not changed: adding a slot
|
|
316
|
+
* needs the folder master key, which needs a credential the server has never
|
|
317
|
+
* held. What CAN do it is the browser, at the one moment the password is in
|
|
318
|
+
* memory — exactly where `upgradeMarkerV1` already lives. Same moment, same
|
|
319
|
+
* shape, different slot.
|
|
320
|
+
*
|
|
321
|
+
* ⚠⚠ Accepting hands the operator of this installation a second, permanent
|
|
322
|
+
* way into this folder. It is the folder's owner who decides, from inside,
|
|
323
|
+
* with the password; no configuration change and no admin action can do it
|
|
324
|
+
* for them. The caller MUST say that in those words before calling this —
|
|
325
|
+
* see `e2e.escrowoffer.*` in the locales.
|
|
326
|
+
*
|
|
327
|
+
* ⚠ v2 only. A v1 marker has no slots at all; the path for those is
|
|
328
|
+
* `upgradeMarkerV1`, which already seals an escrow slot when the
|
|
329
|
+
* installation has a key and already discloses it. Two doors into the same
|
|
330
|
+
* room would be two chances to get the disclosure wrong.
|
|
331
|
+
*
|
|
332
|
+
* ⚠ No file is re-encrypted, moved or rewritten. Only `.filex-e2e.json`
|
|
333
|
+
* changes, and only by gaining `esc` (and losing `esc_declined`).
|
|
334
|
+
*/
|
|
335
|
+
export declare function addEscrowSlot(marker: E2eMarker, password: string, escrowPublicKey: string): Promise<E2eMarker>;
|
|
336
|
+
|
|
290
337
|
/**
|
|
291
338
|
* The self-service API-token surface (`/api/tokens`).
|
|
292
339
|
*
|
|
@@ -589,6 +636,19 @@ export declare function createMarker(password: string, iterations?: number): Pro
|
|
|
589
636
|
kek: CryptoKey;
|
|
590
637
|
}>;
|
|
591
638
|
|
|
639
|
+
/**
|
|
640
|
+
* Record that this folder's owner was offered an escrow slot and declined.
|
|
641
|
+
*
|
|
642
|
+
* A refusal is a decision, not a delay: without this the offer would come
|
|
643
|
+
* back on every single unlock, which is how people learn to click past
|
|
644
|
+
* security dialogs without reading them. Nothing about the folder's keys
|
|
645
|
+
* changes — the only effect is that filex stops asking.
|
|
646
|
+
*
|
|
647
|
+
* Reversible by `addEscrowSlot`, which is the way back for somebody who
|
|
648
|
+
* says no today and changes their mind next month.
|
|
649
|
+
*/
|
|
650
|
+
export declare function declineEscrowSlot(marker: E2eMarker, when: string): E2eMarker;
|
|
651
|
+
|
|
592
652
|
/**
|
|
593
653
|
* Decrypt a 'filexe2e' blob with the folder master key. Throws
|
|
594
654
|
* E2eDecryptError on a wrong key / tampered data, and a plain Error when the
|
|
@@ -661,10 +721,19 @@ export declare const E2E_MAGIC = "filexe2e";
|
|
|
661
721
|
* - No key, password or recovery key is ever stored, logged or sent to a
|
|
662
722
|
* server. The FMK lives in an in-memory key ring and dies with the tab.
|
|
663
723
|
* - `deriveKek` imports non-extractable. Raw KEK bytes are produced ONLY
|
|
664
|
-
* by `deriveKekBits`, only
|
|
665
|
-
*
|
|
724
|
+
* by `deriveKekBits`, only for a marker whose FMK *is* the KEK
|
|
725
|
+
* (`upgradeMarkerV1`, and `addEscrowSlot` on a folder it produced), and
|
|
726
|
+
* only long enough to wrap them into a slot.
|
|
666
727
|
* - A folder created while escrow was off carries no escrow slot, so the
|
|
667
|
-
* escrow key cannot open it
|
|
728
|
+
* escrow key cannot open it, and nothing the OPERATOR does changes
|
|
729
|
+
* that — not enabling escrow, not adopting it, not any admin action or
|
|
730
|
+
* future version. That is arithmetic, not policy: adding a slot needs
|
|
731
|
+
* the folder master key, and the server has never held a credential
|
|
732
|
+
* that produces one.
|
|
733
|
+
* - The folder's OWNER can, from inside, with the password:
|
|
734
|
+
* `addEscrowSlot`. That is the only door, it opens from one side only,
|
|
735
|
+
* and it is the reason `escrowAvailability` says "not as things stand"
|
|
736
|
+
* rather than "never".
|
|
668
737
|
*
|
|
669
738
|
* File layout ('filexe2e' magic, fixed 97-byte header) — UNCHANGED in v2:
|
|
670
739
|
* [0..8) magic "filexe2e"
|
|
@@ -722,6 +791,23 @@ export declare interface E2eMarker {
|
|
|
722
791
|
rk?: E2eRecoverySlot;
|
|
723
792
|
/** v2 only, optional: the operator escrow slot. */
|
|
724
793
|
esc?: E2eEscrowSlot;
|
|
794
|
+
/**
|
|
795
|
+
* v2 only, optional: an ISO timestamp recording that this folder's owner
|
|
796
|
+
* was OFFERED an escrow slot and said no.
|
|
797
|
+
*
|
|
798
|
+
* It lives in the marker rather than in browser storage because the unit
|
|
799
|
+
* of the decision is the FOLDER, not the device: the same person opening
|
|
800
|
+
* the folder from their phone must not be asked again, and a decision
|
|
801
|
+
* that vanished when someone cleared their site data would be no decision
|
|
802
|
+
* at all. It travels with the folder through a move, a backup and a
|
|
803
|
+
* restore, for the same reason the key slots do.
|
|
804
|
+
*
|
|
805
|
+
* It holds no key material and hides nothing from the operator — it is a
|
|
806
|
+
* record of an answer, and its only effect is that filex stops asking.
|
|
807
|
+
* `addEscrowSlot` clears it, so a decline is reversible by the one person
|
|
808
|
+
* who can reverse it.
|
|
809
|
+
*/
|
|
810
|
+
esc_declined?: string;
|
|
725
811
|
}
|
|
726
812
|
|
|
727
813
|
/** User-recovery-key slot: HKDF salt + the FMK wrapped under the derived key. */
|
|
@@ -823,6 +909,41 @@ export declare function ensureHighlight(): Promise<unknown | null>;
|
|
|
823
909
|
*/
|
|
824
910
|
export declare function ensureMonaco(): Promise<unknown | null>;
|
|
825
911
|
|
|
912
|
+
/**
|
|
913
|
+
* Why the escrow door is, or is not, on offer for this folder.
|
|
914
|
+
*
|
|
915
|
+
* 'off' this installation has no escrow key at all.
|
|
916
|
+
* 'available' the folder is sealed to THIS installation's escrow key.
|
|
917
|
+
* 'predates' the installation has an escrow key, and this folder has no
|
|
918
|
+
* escrow slot: it was created before escrow existed here.
|
|
919
|
+
* 'other-key' the folder carries an escrow slot sealed to a DIFFERENT
|
|
920
|
+
* key id — it came from another installation, via a restore
|
|
921
|
+
* or a copied data directory.
|
|
922
|
+
*
|
|
923
|
+
* ⚠ 'predates' exists because escrow can be ADOPTED by an installation
|
|
924
|
+
* that already has folders (FILEX_INSTALLATION_E2E_ESCROW_ADOPT), and
|
|
925
|
+
* adoption is not retroactive: the folder's master key was wrapped to its
|
|
926
|
+
* recovery paths when the folder was created. Before this distinction
|
|
927
|
+
* existed the dialog simply showed no Escrow tab, which is true but says
|
|
928
|
+
* nothing — an admin who knows escrow is on reads a missing tab as a bug,
|
|
929
|
+
* tries the key anyway, and learns the real answer from a failure. The UI
|
|
930
|
+
* has to say it instead.
|
|
931
|
+
*
|
|
932
|
+
* ⚠⚠ 'predates' means "not as things stand", NOT "never". The folder's
|
|
933
|
+
* owner can add a slot from inside with the password (`addEscrowSlot`,
|
|
934
|
+
* offered at unlock). Any wording built on this state has to leave that
|
|
935
|
+
* door visible, or it tells an operator their escrow key can never reach a
|
|
936
|
+
* folder whose owner could hand it over this afternoon.
|
|
937
|
+
*
|
|
938
|
+
* ⚠ 'other-key' was a quieter lie: the dialog labelled the escrow field
|
|
939
|
+
* with the INSTALLATION's key id whatever the folder's slot said, so a
|
|
940
|
+
* folder restored from another install looked openable by the key the
|
|
941
|
+
* operator has, and was not.
|
|
942
|
+
*/
|
|
943
|
+
export declare type EscrowAvailability = 'off' | 'available' | 'predates' | 'other-key';
|
|
944
|
+
|
|
945
|
+
export declare function escrowAvailability(m: E2eMarker | null, installationKid: string | null | undefined): EscrowAvailability;
|
|
946
|
+
|
|
826
947
|
/**
|
|
827
948
|
* Stable short name for an escrow key: first 8 bytes of SHA-256(SPKI), hex.
|
|
828
949
|
* Written into every escrow slot so a marker says WHICH key opens it, and so
|
|
@@ -830,6 +951,27 @@ export declare function ensureMonaco(): Promise<unknown | null>;
|
|
|
830
951
|
*/
|
|
831
952
|
export declare function escrowKeyId(spkiB64: string): Promise<string>;
|
|
832
953
|
|
|
954
|
+
/**
|
|
955
|
+
* Whether this folder's owner should be offered an escrow slot, and whether
|
|
956
|
+
* they have already answered.
|
|
957
|
+
*
|
|
958
|
+
* 'n/a' nothing to offer: the installation has no escrow key, the
|
|
959
|
+
* folder already has a slot, or the marker is v1 (whose path
|
|
960
|
+
* is `upgradeMarkerV1`).
|
|
961
|
+
* 'offer' the offer applies and no answer has been recorded.
|
|
962
|
+
* 'declined' the offer applies and the owner said no. Do not ask again;
|
|
963
|
+
* leave a way back.
|
|
964
|
+
*
|
|
965
|
+
* ⚠ This deliberately does NOT look at whether the folder is unlocked. That
|
|
966
|
+
* is the caller's business, and it matters: the offer may only be shown
|
|
967
|
+
* after an unlock actually succeeded, because accepting needs the password
|
|
968
|
+
* and because asking someone who cannot open the folder to give away a key
|
|
969
|
+
* to it is asking the wrong person.
|
|
970
|
+
*/
|
|
971
|
+
export declare type EscrowOfferState = 'n/a' | 'offer' | 'declined';
|
|
972
|
+
|
|
973
|
+
export declare function escrowOfferState(m: E2eMarker | null, installationKid: string | null | undefined): EscrowOfferState;
|
|
974
|
+
|
|
833
975
|
export declare interface ExplorerConfig {
|
|
834
976
|
/**
|
|
835
977
|
* Modern shorthand: URL prefix for the standard /api/files/* layout.
|
|
@@ -988,6 +1130,22 @@ export declare interface ExplorerConfig {
|
|
|
988
1130
|
* navigation panel starts expanded, the tab strip
|
|
989
1131
|
* and split pane are off, the gallery view mode and
|
|
990
1132
|
* the host's "How to connect" surface are hidden.
|
|
1133
|
+
* 'drive' — everything `simple` does, plus the shell an end
|
|
1134
|
+
* user already knows: one primary "New" menu, one
|
|
1135
|
+
* search field in the header (with its ⌘K/Ctrl+K
|
|
1136
|
+
* escalation into the command palette), a filter row
|
|
1137
|
+
* under the breadcrumb, Folders and Files as
|
|
1138
|
+
* labelled sections in grid view, the details panel
|
|
1139
|
+
* split into Details / Activity, and a storage line
|
|
1140
|
+
* under the navigation.
|
|
1141
|
+
*
|
|
1142
|
+
* ⚠ `drive` is a SUPERSET of `simple`, not a sibling: everything `simple`
|
|
1143
|
+
* turns off stays off, and the code asks `simpleUi` for those questions so a
|
|
1144
|
+
* later change to `simple` cannot silently miss `drive`. It is a third value
|
|
1145
|
+
* rather than a second boolean because "which chrome" is ONE question with
|
|
1146
|
+
* three answers — a `driveShell: true` next to `uiProfile: 'standard'` would
|
|
1147
|
+
* be a combination nobody can describe, and keeping that question single is
|
|
1148
|
+
* why `uiProfile` was a preset to begin with.
|
|
991
1149
|
*
|
|
992
1150
|
* Why it exists (GitHub #14): the reporter's users are not in IT and read
|
|
993
1151
|
* split panes, tabs and mount instructions as a file manager they would have
|
|
@@ -999,7 +1157,7 @@ export declare interface ExplorerConfig {
|
|
|
999
1157
|
* rest of the chrome differ. A viewer's own collapse choice, once made,
|
|
1000
1158
|
* outranks the profile — it is a per-viewer preference, not a policy.
|
|
1001
1159
|
*/
|
|
1002
|
-
uiProfile?: 'standard' | 'simple';
|
|
1160
|
+
uiProfile?: 'standard' | 'simple' | 'drive';
|
|
1003
1161
|
/**
|
|
1004
1162
|
* Render the navigation panel (Upload · Recent / Starred / Shared with me /
|
|
1005
1163
|
* Trash · the storage list). Collapsible to an icon rail by the viewer, whose
|
|
@@ -1324,6 +1482,8 @@ inspectorOpen?: boolean;
|
|
|
1324
1482
|
navOpen?: boolean;
|
|
1325
1483
|
navEnabled?: boolean;
|
|
1326
1484
|
viewModes?: ViewMode[];
|
|
1485
|
+
shell?: "classic" | "drive";
|
|
1486
|
+
scopeLabel?: string;
|
|
1327
1487
|
}> & Readonly<{
|
|
1328
1488
|
onAction?: ((key: string) => any) | undefined;
|
|
1329
1489
|
onUpload?: (() => any) | undefined;
|
|
@@ -1338,6 +1498,7 @@ onRefresh?: (() => any) | undefined;
|
|
|
1338
1498
|
"onToggle-nav"?: (() => any) | undefined;
|
|
1339
1499
|
"onOpen-theme"?: (() => any) | undefined;
|
|
1340
1500
|
"onOpen-shortcut-settings"?: (() => any) | undefined;
|
|
1501
|
+
"onOpen-palette"?: ((query: string) => any) | undefined;
|
|
1341
1502
|
}>, {
|
|
1342
1503
|
focusSearch: () => Promise<void>;
|
|
1343
1504
|
}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
|
|
@@ -1354,6 +1515,7 @@ refresh: () => any;
|
|
|
1354
1515
|
"toggle-nav": () => any;
|
|
1355
1516
|
"open-theme": () => any;
|
|
1356
1517
|
"open-shortcut-settings": () => any;
|
|
1518
|
+
"open-palette": (query: string) => any;
|
|
1357
1519
|
}, PublicProps, {}, false, {}, {}, GlobalComponents, GlobalDirectives, string, {
|
|
1358
1520
|
primaryEl: HTMLDivElement;
|
|
1359
1521
|
wideMoreBtnEl: HTMLButtonElement;
|
|
@@ -1463,6 +1625,8 @@ inspectorOpen?: boolean;
|
|
|
1463
1625
|
navOpen?: boolean;
|
|
1464
1626
|
navEnabled?: boolean;
|
|
1465
1627
|
viewModes?: ViewMode[];
|
|
1628
|
+
shell?: "classic" | "drive";
|
|
1629
|
+
scopeLabel?: string;
|
|
1466
1630
|
}> & Readonly<{
|
|
1467
1631
|
onAction?: ((key: string) => any) | undefined;
|
|
1468
1632
|
onUpload?: (() => any) | undefined;
|
|
@@ -1477,6 +1641,7 @@ onRefresh?: (() => any) | undefined;
|
|
|
1477
1641
|
"onToggle-nav"?: (() => any) | undefined;
|
|
1478
1642
|
"onOpen-theme"?: (() => any) | undefined;
|
|
1479
1643
|
"onOpen-shortcut-settings"?: (() => any) | undefined;
|
|
1644
|
+
"onOpen-palette"?: ((query: string) => any) | undefined;
|
|
1480
1645
|
}>, {
|
|
1481
1646
|
focusSearch: () => Promise<void>;
|
|
1482
1647
|
}, {}, {}, {}, {}> | null;
|
|
@@ -1496,6 +1661,7 @@ viewMode?: ViewMode;
|
|
|
1496
1661
|
thumbSrc?: (n: FileNode) => string | null;
|
|
1497
1662
|
keepBadgeFor?: (n: FileNode) => "kept" | "syncing" | "cloud" | "partial" | null;
|
|
1498
1663
|
trashVisible?: boolean;
|
|
1664
|
+
navOffersTrash?: boolean;
|
|
1499
1665
|
}> & Readonly<{
|
|
1500
1666
|
onTransfer?: ((p: {
|
|
1501
1667
|
sources: string[];
|
|
@@ -1550,6 +1716,7 @@ viewMode?: ViewMode;
|
|
|
1550
1716
|
thumbSrc?: (n: FileNode) => string | null;
|
|
1551
1717
|
keepBadgeFor?: (n: FileNode) => "kept" | "syncing" | "cloud" | "partial" | null;
|
|
1552
1718
|
trashVisible?: boolean;
|
|
1719
|
+
navOffersTrash?: boolean;
|
|
1553
1720
|
}> & Readonly<{
|
|
1554
1721
|
onTransfer?: ((p: {
|
|
1555
1722
|
sources: string[];
|
|
@@ -2213,6 +2380,14 @@ onNav?: ((delta: number) => any) | undefined;
|
|
|
2213
2380
|
"onOpen-full"?: (() => any) | undefined;
|
|
2214
2381
|
}>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
|
|
2215
2382
|
|
|
2383
|
+
/** surucu:d1 — `GET /api/files/quota/me` (quota.Snapshot). */
|
|
2384
|
+
declare interface QuotaSnapshot {
|
|
2385
|
+
used_bytes: number;
|
|
2386
|
+
quota_bytes: number;
|
|
2387
|
+
percent_used: number;
|
|
2388
|
+
unlimited: boolean;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2216
2391
|
export declare const RecentlyOpened: DefineComponent<__VLS_Props_5, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
|
|
2217
2392
|
error: (message: string) => any;
|
|
2218
2393
|
open: (node: RecentNode) => any;
|
|
@@ -3008,6 +3183,7 @@ export declare function useFileApi(config: ExplorerConfig): {
|
|
|
3008
3183
|
limit?: number;
|
|
3009
3184
|
scope?: GlobalSearchScope;
|
|
3010
3185
|
}) => Promise<GlobalSearchHit[]>;
|
|
3186
|
+
quotaMe: () => Promise<QuotaSnapshot | null>;
|
|
3011
3187
|
subfolders: (path: string) => Promise<{
|
|
3012
3188
|
folders: FileNode[];
|
|
3013
3189
|
}>;
|
|
@@ -3038,7 +3214,9 @@ export declare function useFileApi(config: ExplorerConfig): {
|
|
|
3038
3214
|
uploadMultipart: (path: string, files: File[], onProgress?: (p: number) => void) => Promise<ManagerResponse>;
|
|
3039
3215
|
downloadUrl: (path: string) => string;
|
|
3040
3216
|
previewUrl: (path: string) => string;
|
|
3041
|
-
fetchBlob: (path: string
|
|
3217
|
+
fetchBlob: (path: string, opts?: {
|
|
3218
|
+
fresh?: boolean;
|
|
3219
|
+
}) => Promise<{
|
|
3042
3220
|
url: string;
|
|
3043
3221
|
blob: Blob;
|
|
3044
3222
|
mime: string;
|