@brftech/filex-core 0.42.1 → 0.43.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 +14 -2
- package/dist/ArchiveViewer-BqU761Wk.js +149 -0
- package/dist/ArchiveViewer-BqU761Wk.js.map +1 -0
- package/dist/CsvViewer-ByHVK5ga.js +172 -0
- package/dist/CsvViewer-ByHVK5ga.js.map +1 -0
- package/dist/{DrawioViewer-BuhZOAx6.js → DrawioViewer-Bgrj8-zG.js} +45 -44
- package/dist/DrawioViewer-Bgrj8-zG.js.map +1 -0
- package/dist/{EpubViewer-DS3mb3k8.js → EpubViewer-DJwUKlHW.js} +50 -50
- package/dist/EpubViewer-DJwUKlHW.js.map +1 -0
- package/dist/IpynbViewer-Bz0eJ4NS.js +184 -0
- package/dist/IpynbViewer-Bz0eJ4NS.js.map +1 -0
- package/dist/MermaidViewer-CEDTD_pp.js +136 -0
- package/dist/MermaidViewer-CEDTD_pp.js.map +1 -0
- package/dist/PsdViewer-CZcxUpZO.js +121 -0
- package/dist/PsdViewer-CZcxUpZO.js.map +1 -0
- package/dist/TiffViewer-BwI62CbQ.js +141 -0
- package/dist/TiffViewer-BwI62CbQ.js.map +1 -0
- package/dist/{Viewer3D-CIrOn63I.js → Viewer3D-DoeZcuMm.js} +16 -16
- package/dist/Viewer3D-DoeZcuMm.js.map +1 -0
- package/dist/filex-core.js +475 -219
- package/dist/filex-core.umd.cjs +165 -108
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/fonts/caveat-latin-ext.woff2 +0 -0
- package/dist/fonts/caveat-latin.woff2 +0 -0
- package/dist/fonts/dancing-script-latin-ext.woff2 +0 -0
- package/dist/fonts/dancing-script-latin.woff2 +0 -0
- package/dist/fonts/homemade-apple-latin.woff2 +0 -0
- package/dist/fonts/source-serif-4-latin-ext.woff2 +0 -0
- package/dist/fonts/source-serif-4-latin.woff2 +0 -0
- package/dist/index-DuWnGuv5.js +35901 -0
- package/dist/index-DuWnGuv5.js.map +1 -0
- package/dist/index.d.ts +5241 -553
- package/dist/pdf-CaPYsOUo.js +21302 -0
- package/dist/pdf-CaPYsOUo.js.map +1 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/FileExplorer.vue +1207 -343
- package/src/assets/fonts/LICENSE-Apache-2.0.txt +202 -0
- package/src/assets/fonts/LICENSE-OFL-1.1.txt +93 -0
- package/src/assets/fonts/README.md +58 -0
- package/src/assets/fonts/caveat-latin-ext.woff2 +0 -0
- package/src/assets/fonts/caveat-latin.woff2 +0 -0
- package/src/assets/fonts/dancing-script-latin-ext.woff2 +0 -0
- package/src/assets/fonts/dancing-script-latin.woff2 +0 -0
- package/src/assets/fonts/homemade-apple-latin.woff2 +0 -0
- package/src/assets/fonts/source-serif-4-latin-ext.woff2 +0 -0
- package/src/assets/fonts/source-serif-4-latin.woff2 +0 -0
- package/src/components/AdvancedSearch.vue +49 -0
- package/src/components/Breadcrumb.vue +5 -5
- package/src/components/ChoiceButtons.vue +152 -0
- package/src/components/CommandPalette.vue +5 -5
- package/src/components/ConnectionGuideView.vue +1 -1
- package/src/components/ConnectionNotice.vue +59 -0
- package/src/components/ConnectionsPanel.vue +30 -568
- package/src/components/ContextMenu.vue +31 -5
- package/src/components/DataTable.vue +1331 -0
- package/src/components/FilePane.vue +3 -0
- package/src/components/FilterBar.vue +12 -5
- package/src/components/GalleryView.vue +50 -2
- package/src/components/GridView.vue +51 -3
- package/src/components/HomeView.vue +18 -6
- package/src/components/InspectorPanel.vue +134 -17
- package/src/components/ListView.vue +366 -900
- package/src/components/LogoMark.vue +57 -0
- package/src/components/NFSExportsPanel.vue +76 -28
- package/src/components/OnboardingTour.vue +19 -7
- package/src/components/OperationsCenter.vue +77 -1
- package/src/components/PendingOpsTray.vue +59 -5
- package/src/components/PresenceBar.vue +7 -7
- package/src/components/ProductVersion.vue +31 -0
- package/src/components/QuickLook.vue +10 -14
- package/src/components/RecentlyOpened.vue +47 -17
- package/src/components/RecoveryKeyModal.vue +1 -1
- package/src/components/RowActions.vue +134 -0
- package/src/components/S3KeysPanel.vue +98 -56
- package/src/components/SSHKeysPanel.vue +79 -26
- package/src/components/SideNav.vue +260 -96
- package/src/components/StarButton.vue +1 -0
- package/src/components/StorageFields.vue +74 -6
- package/src/components/StorageTags.vue +62 -0
- package/src/components/TabBar.vue +1 -1
- package/src/components/TagKindIcon.vue +56 -0
- package/src/components/TagPicker.vue +226 -40
- package/src/components/ThemePalette.vue +4 -4
- package/src/components/TimeZonePicker.vue +29 -10
- package/src/components/TokensPanel.vue +82 -27
- package/src/components/Toolbar.vue +28 -6
- package/src/components/plugin/PluginConfirmModal.vue +54 -0
- package/src/components/plugin/PluginInspectorSection.vue +149 -0
- package/src/components/plugin/PluginPageView.vue +339 -0
- package/src/components/plugin/PluginViewModal.vue +159 -0
- package/src/components/plugin/SurfaceConversation.vue +76 -0
- package/src/components/plugin/SurfaceFooterButtons.vue +73 -0
- package/src/components/plugin/SurfaceRenderer.vue +299 -0
- package/src/components/plugin/SurfaceSections.vue +91 -0
- package/src/components/plugin/nodes/PdfFieldEditor.vue +509 -0
- package/src/components/plugin/nodes/SurfaceFileChooser.vue +94 -0
- package/src/components/plugin/nodes/SurfaceForm.vue +82 -0
- package/src/components/plugin/nodes/SurfaceList.vue +127 -0
- package/src/components/plugin/nodes/SurfacePdfFields.vue +1285 -0
- package/src/components/plugin/nodes/SurfacePeoplePicker.vue +213 -0
- package/src/components/plugin/nodes/SurfacePinInput.vue +57 -0
- package/src/components/plugin/nodes/SurfacePreview.vue +119 -0
- package/src/components/plugin/nodes/SurfaceProgress.vue +48 -0
- package/src/components/plugin/nodes/SurfaceSignaturePad.vue +472 -0
- package/src/components/plugin/nodes/SurfaceSteps.vue +53 -0
- package/src/components/public/PublicLanguagePicker.vue +58 -0
- package/src/components/public/PublicLinkPage.vue +286 -0
- package/src/components/public/PublicLinkPreview.vue +85 -0
- package/src/components/public/PublicPinGate.vue +108 -0
- package/src/components/public/PublicRequestBody.vue +204 -0
- package/src/components/public/PublicShareBody.vue +188 -0
- package/src/components/public/PublicShell.vue +332 -0
- package/src/composables/useConnections.ts +8 -2
- package/src/composables/useFileApi.ts +183 -44
- package/src/composables/useLocale.ts +277 -41
- package/src/composables/useNFSExports.ts +8 -10
- package/src/composables/useOperations.ts +44 -3
- package/src/composables/usePendingOps.ts +59 -8
- package/src/composables/usePluginActions.ts +121 -0
- package/src/composables/usePluginSurface.ts +223 -0
- package/src/composables/usePublicBranding.ts +172 -0
- package/src/composables/usePublicLink.ts +573 -0
- package/src/composables/usePublicPage.ts +61 -0
- package/src/composables/useS3Keys.ts +17 -12
- package/src/composables/useSSHKeys.ts +8 -10
- package/src/composables/useTokens.ts +8 -12
- package/src/composables/useUploadChunked.ts +18 -8
- package/src/composables/useViewerFetch.ts +6 -3
- package/src/index.ts +404 -8
- package/src/lib/actionIcons.ts +46 -1
- package/src/lib/advSearch.ts +14 -7
- package/src/lib/appLock.ts +130 -0
- package/src/lib/connection.ts +110 -0
- package/src/lib/destinationTree.ts +19 -3
- package/src/lib/direction.ts +353 -0
- package/src/lib/dragGhost.ts +6 -0
- package/src/lib/dropLimits.ts +69 -0
- package/src/lib/errorWords.ts +394 -0
- package/src/lib/externalReach.ts +5 -1
- package/src/lib/fileFilters.ts +7 -2
- package/src/lib/fileIcons.ts +4 -0
- package/src/lib/internalPaths.ts +77 -0
- package/src/lib/listInput.ts +64 -0
- package/src/lib/listing.ts +58 -23
- package/src/lib/modalStack.ts +26 -0
- package/src/lib/nodeRow.ts +121 -0
- package/src/lib/pdfFieldRules.ts +126 -0
- package/src/lib/pdfFields.ts +589 -0
- package/src/lib/pdfFieldsGeom.ts +203 -0
- package/src/lib/pdfjsLoader.ts +51 -0
- package/src/lib/personName.ts +47 -0
- package/src/lib/pluginApplies.ts +123 -0
- package/src/lib/pluginLabel.ts +72 -0
- package/src/lib/pluginMenu.ts +253 -0
- package/src/lib/pluginPage.ts +58 -0
- package/src/lib/plural.ts +181 -0
- package/src/lib/popupLayer.ts +103 -0
- package/src/lib/prefs.ts +563 -0
- package/src/lib/productVersion.ts +35 -0
- package/src/lib/publicLayout.ts +39 -0
- package/src/lib/serviceGate.ts +104 -0
- package/src/lib/shareTtl.ts +25 -11
- package/src/lib/signFonts.ts +85 -0
- package/src/lib/signaturePad.ts +111 -0
- package/src/lib/sortOrder.ts +100 -38
- package/src/lib/star.ts +7 -1
- package/src/lib/surfaceCell.ts +41 -0
- package/src/lib/surfaceConditions.ts +156 -0
- package/src/lib/surfaceOpen.ts +106 -0
- package/src/lib/surfaceSteps.ts +68 -0
- package/src/lib/surfaceValues.ts +191 -0
- package/src/lib/symlink.ts +150 -0
- package/src/lib/tableColumns.ts +493 -0
- package/src/lib/tableEnv.ts +34 -0
- package/src/lib/tablePrefs.ts +84 -0
- package/src/lib/tags.ts +121 -12
- package/src/lib/themes.ts +286 -18
- package/src/lib/tour.ts +152 -0
- package/src/lib/trashEmpty.ts +111 -0
- package/src/lib/trashTimeLeft.ts +23 -0
- package/src/lib/uiLocales.ts +460 -0
- package/src/lib/viewPrefs.ts +696 -660
- package/src/lib/viewPrefsHttp.ts +78 -0
- package/src/locales/en.ts +522 -119
- package/src/locales/resolve.ts +17 -8
- package/src/locales/tr.ts +539 -155
- package/src/modals/ConvertModal.vue +62 -11
- package/src/modals/DestinationPickerModal.vue +59 -16
- package/src/modals/Modal.vue +113 -23
- package/src/modals/NewDocumentModal.vue +16 -6
- package/src/modals/NewFolderModal.vue +8 -0
- package/src/modals/PermissionsModal.vue +272 -163
- package/src/modals/PreviewModal.vue +119 -20
- package/src/modals/RenameModal.vue +37 -1
- package/src/styles/base.css +2895 -241
- package/src/styles/sign-fonts.css +118 -0
- package/src/styles/variables.css +60 -0
- package/src/types/Connections.ts +17 -0
- package/src/types/ExplorerConfig.ts +117 -1
- package/src/types/FileNode.ts +58 -0
- package/src/types/Plugins.ts +494 -0
- package/src/types/Public.ts +212 -0
- package/src/viewers/ArchiveViewer.vue +93 -48
- package/src/viewers/CsvViewer.vue +99 -53
- package/src/viewers/DrawioViewer.vue +35 -9
- package/src/viewers/EpubViewer.vue +7 -8
- package/src/viewers/IpynbViewer.vue +3 -1
- package/src/viewers/MermaidViewer.vue +4 -5
- package/src/viewers/PdfViewer.vue +8 -17
- package/src/viewers/PsdViewer.vue +2 -4
- package/src/viewers/TiffViewer.vue +4 -5
- package/src/viewers/Viewer3D.vue +1 -1
- package/LICENSE +0 -21
- package/dist/ArchiveViewer-CLNevfPE.js +0 -124
- package/dist/ArchiveViewer-CLNevfPE.js.map +0 -1
- package/dist/CsvViewer-K8XA3z7N.js +0 -141
- package/dist/CsvViewer-K8XA3z7N.js.map +0 -1
- package/dist/DrawioViewer-BuhZOAx6.js.map +0 -1
- package/dist/EpubViewer-DS3mb3k8.js.map +0 -1
- package/dist/IpynbViewer-BTk7p4Dn.js +0 -184
- package/dist/IpynbViewer-BTk7p4Dn.js.map +0 -1
- package/dist/MermaidViewer-DqucH_io.js +0 -137
- package/dist/MermaidViewer-DqucH_io.js.map +0 -1
- package/dist/PsdViewer-CwiVENmq.js +0 -122
- package/dist/PsdViewer-CwiVENmq.js.map +0 -1
- package/dist/TiffViewer-CpewSVmQ.js +0 -142
- package/dist/TiffViewer-CpewSVmQ.js.map +0 -1
- package/dist/Viewer3D-CIrOn63I.js.map +0 -1
- package/dist/index-Dx0IbBcI.js +0 -26831
- package/dist/index-Dx0IbBcI.js.map +0 -1
- package/dist/useViewerFetch-czqbd2Lj.js +0 -25
- package/dist/useViewerFetch-czqbd2Lj.js.map +0 -1
|
@@ -23,6 +23,13 @@
|
|
|
23
23
|
|
|
24
24
|
import type { ExplorerConfig, AuthConfig, EndpointMap } from '../types/ExplorerConfig';
|
|
25
25
|
import { resolveLocale } from '../locales/resolve';
|
|
26
|
+
import { listingAddress } from '../lib/internalPaths';
|
|
27
|
+
import { localeTag } from './useLocale';
|
|
28
|
+
import { networkFailure, requestFailure } from '../lib/errorWords';
|
|
29
|
+
// ⚠ The same folding rule the web app's axios layer applies, applied by the
|
|
30
|
+
// request layer an EMBED uses — one notice for a connection that is down, and
|
|
31
|
+
// no surface-specific copy of the decision (lib/connection).
|
|
32
|
+
import { kindOfMethod, noteRequestFailed, noteRequestSucceeded } from '../lib/connection';
|
|
26
33
|
import type {
|
|
27
34
|
FileNode,
|
|
28
35
|
ShareInfo,
|
|
@@ -31,6 +38,13 @@ import type {
|
|
|
31
38
|
ArchiveEntry,
|
|
32
39
|
TrashEntry,
|
|
33
40
|
} from '../types/FileNode';
|
|
41
|
+
import type {
|
|
42
|
+
PluginActionsResponse,
|
|
43
|
+
PluginRunResult,
|
|
44
|
+
PluginSurface,
|
|
45
|
+
PluginUsersResponse,
|
|
46
|
+
PluginViewEventBody,
|
|
47
|
+
} from '../types/Plugins';
|
|
34
48
|
|
|
35
49
|
/** Server-side PendingOp DTO (mirror of Modules\FishApp\Models\PendingOp::toApiArray). */
|
|
36
50
|
export interface PendingOpDto {
|
|
@@ -75,6 +89,11 @@ export interface ManagerResponse {
|
|
|
75
89
|
e2e?: boolean;
|
|
76
90
|
e2e_root?: string;
|
|
77
91
|
files: FileNode[];
|
|
92
|
+
/** `action=search` only: more rows matched than came back — the index
|
|
93
|
+
* filled its page, or the index-less fallback filled its window — so the
|
|
94
|
+
* list is not the whole answer. Absent on other actions and on servers
|
|
95
|
+
* older than the flag. */
|
|
96
|
+
truncated?: boolean;
|
|
78
97
|
}
|
|
79
98
|
|
|
80
99
|
/** A single ACL grant row (RBAC permissions panel). */
|
|
@@ -86,6 +105,8 @@ export interface Grant {
|
|
|
86
105
|
level: 'viewer' | 'editor' | 'owner';
|
|
87
106
|
user_email?: string;
|
|
88
107
|
user_display_name?: string;
|
|
108
|
+
/** The person as every screen names them (server model.PersonLabel). */
|
|
109
|
+
user_name?: string;
|
|
89
110
|
inherited?: boolean;
|
|
90
111
|
}
|
|
91
112
|
|
|
@@ -107,13 +128,14 @@ export interface PermissionsResponse {
|
|
|
107
128
|
|
|
108
129
|
export interface ResolveEmailResponse {
|
|
109
130
|
found: boolean;
|
|
110
|
-
user?: { id: number; email: string; display_name: string; role: string };
|
|
131
|
+
user?: { id: number; email: string; display_name: string; username?: string; role: string };
|
|
111
132
|
}
|
|
112
133
|
|
|
113
134
|
export interface UserSuggestion {
|
|
114
135
|
id: number;
|
|
115
136
|
email: string;
|
|
116
137
|
display_name: string;
|
|
138
|
+
username?: string;
|
|
117
139
|
role: string;
|
|
118
140
|
}
|
|
119
141
|
|
|
@@ -249,6 +271,13 @@ export function resolveEndpoints(config: ExplorerConfig): EndpointMap {
|
|
|
249
271
|
/* wiring:e2 — escrow proof-of-possession, then the owner is told. */
|
|
250
272
|
e2eEscrowChallenge: derive(config.e2eEscrowChallenge, '/api/files/e2e/escrow/challenge'),
|
|
251
273
|
e2eEscrowUsed: derive(config.e2eEscrowUsed, '/api/files/e2e/escrow/used'),
|
|
274
|
+
/* App plugins — docs/APP-PLUGINS-API.md. */
|
|
275
|
+
pluginActions: derive(config.pluginActions, '/api/files/plugins/actions'),
|
|
276
|
+
pluginActionRun: derive(config.pluginActionRun, '/api/files/plugins/actions/{plugin}/{action}/run'),
|
|
277
|
+
pluginView: derive(config.pluginView, '/api/files/plugins/views/{plugin}/{view}'),
|
|
278
|
+
pluginViewEvent: derive(config.pluginViewEvent, '/api/files/plugins/views/{plugin}/{view}/event'),
|
|
279
|
+
pluginUsers: derive(config.pluginUsers, '/api/files/plugins/users'),
|
|
280
|
+
opsCancel: derive(config.opsCancel, '/api/files/ops/{id}/cancel'),
|
|
252
281
|
};
|
|
253
282
|
}
|
|
254
283
|
|
|
@@ -346,47 +375,52 @@ export function useFileApi(config: ExplorerConfig) {
|
|
|
346
375
|
return authConf.kind === 'csrf' ? 'include' : 'same-origin';
|
|
347
376
|
}
|
|
348
377
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
401: ['Oturum gerekli, tekrar giriş yapın', 'Sign-in required'],
|
|
358
|
-
403: ['Bu işlem için yetkiniz yok', 'You are not allowed to do this'],
|
|
359
|
-
404: ['Bulunamadı', 'Not found'],
|
|
360
|
-
409: ['Zaten var / çakışma', 'Already exists / conflict'],
|
|
361
|
-
413: ['Dosya çok büyük', 'File too large'],
|
|
362
|
-
415: ['Bu dosya türü desteklenmiyor', 'Unsupported file type'],
|
|
363
|
-
422: ['Geçersiz veri', 'Invalid data'],
|
|
364
|
-
429: ['Çok fazla istek, biraz bekleyin', 'Too many requests'],
|
|
365
|
-
500: ['Sunucu hatası', 'Server error'],
|
|
366
|
-
501: ['Bu işlem desteklenmiyor', 'Not supported'],
|
|
367
|
-
503: ['Servis şu an kullanılamıyor', 'Service unavailable'],
|
|
368
|
-
};
|
|
369
|
-
const e = m[status];
|
|
370
|
-
if (e) return tr ? e[0] : e[1];
|
|
371
|
-
return tr ? `Hata (${status})` : `Error (${status})`;
|
|
372
|
-
}
|
|
378
|
+
/* ⚠⚠ Every refusal this client meets is said by lib/errorWords — ONE table
|
|
379
|
+
of status words and refusal codes for every screen (a read-only drive,
|
|
380
|
+
a missing encryption key, a quota, the statuses). It used to live here
|
|
381
|
+
as an inline table and was copied, differently, into each component that
|
|
382
|
+
called `fetch` itself; those printed "save failed: 500 {…}" and
|
|
383
|
+
"Config fetch 503: {…}". The raw body rides along as `.detail`, for an
|
|
384
|
+
administrator's second line and for `lockedRefusal`, never the sentence. */
|
|
385
|
+
const lang = () => resolveLocale(config.locale);
|
|
373
386
|
|
|
374
387
|
async function jsonFetch<T>(url: string, init: RequestInit = {}): Promise<T> {
|
|
375
388
|
const headers = {
|
|
389
|
+
// ⚠⚠ The language on SCREEN, not the one the browser was installed in.
|
|
390
|
+
// Everything the server writes for a person — a plugin's surface, the
|
|
391
|
+
// labels inside it, the message on a queued job — is rendered with the
|
|
392
|
+
// request's `Accept-Language`. Chrome sends `en-US` whatever filex is
|
|
393
|
+
// set to, so a Turkish window asked a signing wizard for its screens
|
|
394
|
+
// and got "Identity" and an English help line in the middle of Turkish
|
|
395
|
+
// ones. The window's own language is the only right answer here.
|
|
396
|
+
'Accept-Language': localeTag(resolveLocale(config.locale)),
|
|
376
397
|
...(await authHeaders()),
|
|
377
398
|
...((init.headers as Record<string, string> | undefined) ?? {}),
|
|
378
399
|
};
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
400
|
+
let res: Response;
|
|
401
|
+
try {
|
|
402
|
+
res = await fetch(url, {
|
|
403
|
+
...init,
|
|
404
|
+
headers,
|
|
405
|
+
credentials: credentialsMode(),
|
|
406
|
+
});
|
|
407
|
+
} catch (e) {
|
|
408
|
+
// An abort is the caller's own decision — handed back untouched, callers
|
|
409
|
+
// branch on `name === 'AbortError'`.
|
|
410
|
+
if ((e as Error)?.name === 'AbortError') throw e;
|
|
411
|
+
// No answer at all: said as a network failure, not as the browser's
|
|
412
|
+
// "TypeError: Failed to fetch".
|
|
413
|
+
// ⚠ Told to the shared notice as well, so an embedded explorer folds a
|
|
414
|
+
// storm of failed listings and thumbnails the same way the panel does.
|
|
415
|
+
noteRequestFailed(kindOfMethod(init.method));
|
|
416
|
+
throw networkFailure(lang(), e);
|
|
417
|
+
}
|
|
418
|
+
// Any answer — a 404 and a 500 included — means the connection is not
|
|
419
|
+
// what is wrong, and the shared notice must not keep saying it is.
|
|
420
|
+
noteRequestSucceeded();
|
|
384
421
|
if (!res.ok) {
|
|
385
422
|
const text = await res.text().catch(() => '');
|
|
386
|
-
|
|
387
|
-
err.status = res.status;
|
|
388
|
-
err.detail = text.slice(0, 300);
|
|
389
|
-
throw err;
|
|
423
|
+
throw requestFailure(res.status, text, lang());
|
|
390
424
|
}
|
|
391
425
|
// ⚠⚠ A 204 carries NO BODY, and several endpoints answer with one (every
|
|
392
426
|
// delete does). Parsing it throws "Unexpected end of JSON input" AFTER the
|
|
@@ -454,12 +488,22 @@ export function useFileApi(config: ExplorerConfig) {
|
|
|
454
488
|
return `${endpoints.manager}${sep}${qs({ q: action, action, ...params })}`;
|
|
455
489
|
}
|
|
456
490
|
|
|
491
|
+
// ⚠⚠ A listing of one of filex's own directories is answered with the
|
|
492
|
+
// storage's root instead (lib/internalPaths → listingAddress). The server
|
|
493
|
+
// refuses the trash/versions/thumbs trees outright, but it has to keep
|
|
494
|
+
// serving `.filex-open` by exact path — every desktop release since 0.29.0
|
|
495
|
+
// reads its working copies that way — so the explorer is the only place
|
|
496
|
+
// that can tell "a person navigated here" from "the desktop is checking
|
|
497
|
+
// its copy". The explorer arrives wherever the response's `dirname` says,
|
|
498
|
+
// so a stale hash, an old tab or a notification from before this release
|
|
499
|
+
// lands in the storage instead of in the machinery (measured 2026-09-21:
|
|
500
|
+
// `#docs/.filex-open` opened a folder of working copies).
|
|
457
501
|
async function index(path: string): Promise<ManagerResponse> {
|
|
458
|
-
return jsonFetch<ManagerResponse>(managerUrl('index', { path }));
|
|
502
|
+
return jsonFetch<ManagerResponse>(managerUrl('index', { path: listingAddress(path) }));
|
|
459
503
|
}
|
|
460
504
|
|
|
461
505
|
async function search(path: string, filter: string): Promise<ManagerResponse> {
|
|
462
|
-
return jsonFetch<ManagerResponse>(managerUrl('search', { path, filter }));
|
|
506
|
+
return jsonFetch<ManagerResponse>(managerUrl('search', { path: listingAddress(path), filter }));
|
|
463
507
|
}
|
|
464
508
|
|
|
465
509
|
/* === bul:s3 — global "search everywhere" ===
|
|
@@ -675,6 +719,7 @@ export function useFileApi(config: ExplorerConfig) {
|
|
|
675
719
|
}
|
|
676
720
|
};
|
|
677
721
|
xhr.onload = () => {
|
|
722
|
+
noteRequestSucceeded();
|
|
678
723
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
679
724
|
try {
|
|
680
725
|
resolve(JSON.parse(xhr.responseText));
|
|
@@ -682,10 +727,16 @@ export function useFileApi(config: ExplorerConfig) {
|
|
|
682
727
|
reject(e);
|
|
683
728
|
}
|
|
684
729
|
} else {
|
|
685
|
-
reject(
|
|
730
|
+
reject(requestFailure(xhr.status, xhr.responseText || '', lang()));
|
|
686
731
|
}
|
|
687
732
|
};
|
|
688
|
-
|
|
733
|
+
// ⚠ `action`, not `background`: an upload is something the person
|
|
734
|
+
// started and is waiting on, so it is NEVER folded into the shared
|
|
735
|
+
// notice — it says for itself that it did not go.
|
|
736
|
+
xhr.onerror = () => {
|
|
737
|
+
noteRequestFailed('action');
|
|
738
|
+
reject(networkFailure(lang()));
|
|
739
|
+
};
|
|
689
740
|
xhr.send(fd);
|
|
690
741
|
});
|
|
691
742
|
}
|
|
@@ -729,9 +780,7 @@ export function useFileApi(config: ExplorerConfig) {
|
|
|
729
780
|
});
|
|
730
781
|
if (!res.ok) {
|
|
731
782
|
const text = await res.text().catch(() => '');
|
|
732
|
-
throw
|
|
733
|
-
`${res.status} ${res.statusText}${text ? ' — ' + text.slice(0, 200) : ''}`,
|
|
734
|
-
);
|
|
783
|
+
throw requestFailure(res.status, text, lang());
|
|
735
784
|
}
|
|
736
785
|
const blob = await res.blob();
|
|
737
786
|
const mime = blob.type || res.headers.get('content-type') || '';
|
|
@@ -752,9 +801,7 @@ export function useFileApi(config: ExplorerConfig) {
|
|
|
752
801
|
});
|
|
753
802
|
if (!res.ok) {
|
|
754
803
|
const text = await res.text().catch(() => '');
|
|
755
|
-
throw
|
|
756
|
-
`${res.status} ${res.statusText}${text ? ' — ' + text.slice(0, 200) : ''}`,
|
|
757
|
-
);
|
|
804
|
+
throw requestFailure(res.status, text, lang());
|
|
758
805
|
}
|
|
759
806
|
return res.arrayBuffer();
|
|
760
807
|
}
|
|
@@ -784,6 +831,91 @@ export function useFileApi(config: ExplorerConfig) {
|
|
|
784
831
|
return jsonFetch<Capabilities>(endpoints.capabilities);
|
|
785
832
|
}
|
|
786
833
|
|
|
834
|
+
/* ── App plugins (docs/APP-PLUGINS-API.md) ─────────────────────────── */
|
|
835
|
+
|
|
836
|
+
/** Fill `{name}` placeholders of an endpoint template. */
|
|
837
|
+
function fillTemplate(tpl: string, vars: Record<string, string | number>): string {
|
|
838
|
+
return Object.entries(vars).reduce(
|
|
839
|
+
(acc, [k, v]) => acc.replaceAll(`{${k}}`, encodeURIComponent(String(v))),
|
|
840
|
+
tpl,
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/** `GET /api/files/plugins/actions` — what applies to the caller. */
|
|
845
|
+
async function pluginActions(): Promise<PluginActionsResponse> {
|
|
846
|
+
if (!endpoints.pluginActions) return { actions: [], views: [] };
|
|
847
|
+
const res = await jsonFetch<Partial<PluginActionsResponse>>(endpoints.pluginActions);
|
|
848
|
+
return { actions: res?.actions ?? [], views: res?.views ?? [] };
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* `POST …/actions/{plugin}/{action}/run`. `paths` are adapter-qualified
|
|
853
|
+
* wire paths (`docs://reports/nda.pdf`) — the same form copy/move send.
|
|
854
|
+
* Answers `{op}` (queued) or `{surface}` (the action opens a view first).
|
|
855
|
+
*/
|
|
856
|
+
async function pluginActionRun(
|
|
857
|
+
plugin: string,
|
|
858
|
+
action: string,
|
|
859
|
+
body: { paths: string[]; params?: Record<string, unknown> },
|
|
860
|
+
): Promise<PluginRunResult> {
|
|
861
|
+
if (!endpoints.pluginActionRun) throw new Error('pluginActionRun endpoint not configured');
|
|
862
|
+
return jsonFetch<PluginRunResult>(fillTemplate(endpoints.pluginActionRun, { plugin, action }), {
|
|
863
|
+
method: 'POST',
|
|
864
|
+
headers: { 'Content-Type': 'application/json' },
|
|
865
|
+
body: JSON.stringify(body),
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/** `GET …/views/{plugin}/{view}?path=` — the initial surface (event `open`). */
|
|
870
|
+
/**
|
|
871
|
+
* `GET …/views/{plugin}/{view}` — a view's opening surface. `section` opens
|
|
872
|
+
* a home page at one of its sections (`surface.sections`): the frame keeps
|
|
873
|
+
* it in its own address, so Back and a link land where they were.
|
|
874
|
+
*/
|
|
875
|
+
async function pluginView(
|
|
876
|
+
plugin: string,
|
|
877
|
+
view: string,
|
|
878
|
+
path?: string,
|
|
879
|
+
section?: string,
|
|
880
|
+
): Promise<{ surface: PluginSurface }> {
|
|
881
|
+
if (!endpoints.pluginView) throw new Error('pluginView endpoint not configured');
|
|
882
|
+
const url = fillTemplate(endpoints.pluginView, { plugin, view });
|
|
883
|
+
const q = [
|
|
884
|
+
path ? `path=${encodeURIComponent(path)}` : '',
|
|
885
|
+
section ? `section=${encodeURIComponent(section)}` : '',
|
|
886
|
+
].filter(Boolean);
|
|
887
|
+
return jsonFetch<{ surface: PluginSurface }>(url + (q.length ? `?${q.join('&')}` : ''));
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/** `POST …/views/{plugin}/{view}/event` — answers a surface, or `{op}` when it enqueued a job. */
|
|
891
|
+
async function pluginViewEvent(plugin: string, view: string, body: PluginViewEventBody): Promise<PluginRunResult> {
|
|
892
|
+
if (!endpoints.pluginViewEvent) throw new Error('pluginViewEvent endpoint not configured');
|
|
893
|
+
return jsonFetch<PluginRunResult>(fillTemplate(endpoints.pluginViewEvent, { plugin, view }), {
|
|
894
|
+
method: 'POST',
|
|
895
|
+
headers: { 'Content-Type': 'application/json' },
|
|
896
|
+
body: JSON.stringify(body),
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* `GET …/plugins/users?plugin=<name>&q=` — the people-picker's lookup of
|
|
902
|
+
* internal users. Answers only for a plugin that holds `users:lookup`;
|
|
903
|
+
* 403 (and 404 on an older server) mean "free e-mail entry only" and the
|
|
904
|
+
* caller reads the status off the thrown error.
|
|
905
|
+
*/
|
|
906
|
+
async function pluginUsers(plugin: string, q: string): Promise<PluginUsersResponse> {
|
|
907
|
+
if (!endpoints.pluginUsers) throw Object.assign(new Error('pluginUsers endpoint not configured'), { status: 404 });
|
|
908
|
+
const url = `${endpoints.pluginUsers}?plugin=${encodeURIComponent(plugin)}&q=${encodeURIComponent(q)}`;
|
|
909
|
+
const res = await jsonFetch<Partial<PluginUsersResponse>>(url);
|
|
910
|
+
return { users: res?.users ?? [] };
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/** `POST /api/files/ops/{id}/cancel`. */
|
|
914
|
+
async function opsCancel(id: number): Promise<void> {
|
|
915
|
+
if (!endpoints.opsCancel) throw new Error('opsCancel endpoint not configured');
|
|
916
|
+
await jsonFetch<unknown>(fillTemplate(endpoints.opsCancel, { id }), { method: 'POST' });
|
|
917
|
+
}
|
|
918
|
+
|
|
787
919
|
/* wiring:e2 — escrow use is announced, not merely performed.
|
|
788
920
|
*
|
|
789
921
|
* Ask the server for a nonce sealed to the escrow public key; only the
|
|
@@ -1003,6 +1135,13 @@ export function useFileApi(config: ExplorerConfig) {
|
|
|
1003
1135
|
/* wiring:e2 */
|
|
1004
1136
|
e2eEscrowChallenge,
|
|
1005
1137
|
e2eEscrowUsed,
|
|
1138
|
+
/* App plugins */
|
|
1139
|
+
pluginActions,
|
|
1140
|
+
pluginActionRun,
|
|
1141
|
+
pluginView,
|
|
1142
|
+
pluginViewEvent,
|
|
1143
|
+
pluginUsers,
|
|
1144
|
+
opsCancel,
|
|
1006
1145
|
createShare,
|
|
1007
1146
|
listShares,
|
|
1008
1147
|
revokeShare,
|