@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
|
@@ -29,6 +29,9 @@ const props = defineProps<{
|
|
|
29
29
|
t?: (key: string) => string;
|
|
30
30
|
authHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
31
31
|
authCredentials?: RequestCredentials;
|
|
32
|
+
/** Could this person set up draw.io (`capabilities.caller_admin`)? Picks
|
|
33
|
+
* which sentence the "not configured" pane says — see `bootIfReady`. */
|
|
34
|
+
canConfigure?: boolean;
|
|
32
35
|
}>();
|
|
33
36
|
|
|
34
37
|
const iframeRef = ref<HTMLIFrameElement | null>(null);
|
|
@@ -76,7 +79,11 @@ async function loadXml(): Promise<void> {
|
|
|
76
79
|
credentials: props.authCredentials,
|
|
77
80
|
});
|
|
78
81
|
} catch (err) {
|
|
79
|
-
|
|
82
|
+
/* ⚠ A sentence, never `${status} ${statusText} — <body>` (which is what
|
|
83
|
+
fetchViewerText's error carries). The detail goes to the console. */
|
|
84
|
+
console.warn('[filex] diagram could not be read', err);
|
|
85
|
+
error.value = tt('viewer.drawio.load_failed', 'This diagram could not be read. Download it to open it elsewhere.');
|
|
86
|
+
disabledPane = false;
|
|
80
87
|
status.value = 'error';
|
|
81
88
|
}
|
|
82
89
|
}
|
|
@@ -100,14 +107,19 @@ async function persist(xml: string): Promise<void> {
|
|
|
100
107
|
body: JSON.stringify({ path: props.filePath, content: xml }),
|
|
101
108
|
});
|
|
102
109
|
if (!res.ok) {
|
|
103
|
-
|
|
110
|
+
/* ⚠ Never the HTTP layer on screen ("503 Service Unavailable" is what
|
|
111
|
+
this used to say when a save could not take its version snapshot). */
|
|
112
|
+
console.warn('[filex] diagram save failed', res.status, res.statusText);
|
|
113
|
+
throw new Error('save');
|
|
104
114
|
}
|
|
105
115
|
status.value = 'saved';
|
|
106
116
|
setTimeout(() => {
|
|
107
117
|
if (status.value === 'saved') status.value = 'ready';
|
|
108
118
|
}, 2500);
|
|
109
119
|
} catch (err) {
|
|
110
|
-
|
|
120
|
+
console.warn('[filex] diagram save failed', err);
|
|
121
|
+
error.value = tt('viewer.drawio.save_failed', 'The diagram could not be saved. Your changes are still in the editor — try again.');
|
|
122
|
+
disabledPane = false;
|
|
111
123
|
status.value = 'error';
|
|
112
124
|
}
|
|
113
125
|
}
|
|
@@ -152,15 +164,29 @@ function onMessage(ev: MessageEvent): void {
|
|
|
152
164
|
}
|
|
153
165
|
}
|
|
154
166
|
|
|
167
|
+
/** Is the pane on screen the "not configured" one (so a later answer that
|
|
168
|
+
* draw.io IS there can clear it)? A flag, not a comparison with the text:
|
|
169
|
+
* the text now depends on who is looking. */
|
|
170
|
+
let disabledPane = false;
|
|
171
|
+
|
|
155
172
|
function bootIfReady(): void {
|
|
156
173
|
if (!drawioBase.value) {
|
|
157
|
-
|
|
174
|
+
/* The owner's rule for a missing service (2026-09-21): whoever can fix
|
|
175
|
+
it is told where; everybody else is told what they can do instead.
|
|
176
|
+
⚠ The old sentence named an ENVIRONMENT VARIABLE to every reader —
|
|
177
|
+
"operator must wire FILEX_DRAWIO_URL" — which is a riddle to a person
|
|
178
|
+
who only wanted to look at a diagram. */
|
|
179
|
+
error.value = props.canConfigure
|
|
180
|
+
? tt('viewer.drawio.disabled_admin', 'draw.io is not set up on this server. Set it up in the admin panel under External services.')
|
|
181
|
+
: tt('viewer.drawio.disabled', 'Diagrams cannot be opened here: this server has no draw.io. Download the file to open it elsewhere.');
|
|
182
|
+
disabledPane = true;
|
|
158
183
|
status.value = 'error';
|
|
159
184
|
return;
|
|
160
185
|
}
|
|
161
|
-
// Clear a previous "disabled"
|
|
162
|
-
//
|
|
163
|
-
if (
|
|
186
|
+
// Clear a previous "disabled" pane in case the prop just became available
|
|
187
|
+
// (capability probe finished after mount).
|
|
188
|
+
if (disabledPane) {
|
|
189
|
+
disabledPane = false;
|
|
164
190
|
error.value = null;
|
|
165
191
|
status.value = 'loading';
|
|
166
192
|
}
|
|
@@ -204,7 +230,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
204
230
|
ref="iframeRef"
|
|
205
231
|
:src="iframeSrc"
|
|
206
232
|
class="filex-viewer-drawio__frame"
|
|
207
|
-
title="
|
|
233
|
+
title="draw.io"
|
|
208
234
|
/>
|
|
209
235
|
</div>
|
|
210
236
|
</template>
|
|
@@ -240,7 +266,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
240
266
|
.filex-viewer-drawio__status[data-state="saved"] { color: #059669; }
|
|
241
267
|
.filex-viewer-drawio__readonly {
|
|
242
268
|
font-style: italic;
|
|
243
|
-
margin-
|
|
269
|
+
margin-inline-start: auto;
|
|
244
270
|
}
|
|
245
271
|
.filex-viewer-drawio__frame {
|
|
246
272
|
flex: 1;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { requestFailure } from '../lib/errorWords';
|
|
2
3
|
/**
|
|
3
4
|
* EpubViewer — flowable EPUB reader via `epubjs`.
|
|
4
5
|
*
|
|
@@ -74,9 +75,7 @@ async function load(): Promise<void> {
|
|
|
74
75
|
try {
|
|
75
76
|
mod = await import(/* @vite-ignore */ 'epubjs');
|
|
76
77
|
} catch {
|
|
77
|
-
error.value =
|
|
78
|
-
? props.t('viewer.peer_not_installed')
|
|
79
|
-
: 'EPUB viewer requires `epubjs` — install or use download.';
|
|
78
|
+
error.value = tt('viewer.peer_not_installed', 'This kind of file cannot be shown here. Download it to open it on your device.');
|
|
80
79
|
loading.value = false;
|
|
81
80
|
return;
|
|
82
81
|
}
|
|
@@ -92,7 +91,7 @@ async function load(): Promise<void> {
|
|
|
92
91
|
headers,
|
|
93
92
|
credentials: props.authCredentials || 'same-origin',
|
|
94
93
|
});
|
|
95
|
-
if (!res.ok) throw
|
|
94
|
+
if (!res.ok) throw requestFailure(res.status, await res.text().catch(() => ''), undefined);
|
|
96
95
|
source = await res.arrayBuffer();
|
|
97
96
|
}
|
|
98
97
|
book = Epub(source);
|
|
@@ -242,7 +241,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
242
241
|
.filex-viewer-epub__nav {
|
|
243
242
|
position: absolute;
|
|
244
243
|
bottom: 12px;
|
|
245
|
-
left: 50%;
|
|
244
|
+
left: 50%; /* rtl-physical: centred by translate(-50%) — the same place in both directions */
|
|
246
245
|
transform: translateX(-50%);
|
|
247
246
|
display: flex;
|
|
248
247
|
gap: 8px;
|
|
@@ -291,7 +290,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
291
290
|
}
|
|
292
291
|
.filex-viewer-epub__toc {
|
|
293
292
|
width: 260px;
|
|
294
|
-
border-
|
|
293
|
+
border-inline-end: 1px solid var(--fe-border, #e2e6ed);
|
|
295
294
|
overflow-y: auto;
|
|
296
295
|
padding: 12px 0;
|
|
297
296
|
background: var(--fe-bg-elev, #f7f8fa);
|
|
@@ -304,7 +303,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
304
303
|
.filex-viewer-epub__toc-link {
|
|
305
304
|
display: block;
|
|
306
305
|
width: 100%;
|
|
307
|
-
text-align:
|
|
306
|
+
text-align: start;
|
|
308
307
|
background: transparent;
|
|
309
308
|
border: 0;
|
|
310
309
|
padding: 6px 14px;
|
|
@@ -316,7 +315,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
316
315
|
background: var(--fe-bg-hover, #edf0f5);
|
|
317
316
|
}
|
|
318
317
|
.filex-viewer-epub__toc-link.is-child {
|
|
319
|
-
padding-
|
|
318
|
+
padding-inline-start: 28px;
|
|
320
319
|
color: var(--fe-text-muted, #5a6475);
|
|
321
320
|
font-size: 12px;
|
|
322
321
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { sayFailure } from '../lib/errorWords';
|
|
2
3
|
/**
|
|
3
4
|
* IpynbViewer — Jupyter notebook (.ipynb JSON) renderer.
|
|
4
5
|
*
|
|
@@ -84,7 +85,7 @@ async function load(): Promise<void> {
|
|
|
84
85
|
credentials: props.authCredentials,
|
|
85
86
|
});
|
|
86
87
|
} catch (err) {
|
|
87
|
-
error.value = err
|
|
88
|
+
error.value = sayFailure(err, tt('viewer.failed_to_load', 'Failed to load file'), { t: props.t }).text;
|
|
88
89
|
loading.value = false;
|
|
89
90
|
return;
|
|
90
91
|
}
|
|
@@ -263,6 +264,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
263
264
|
<div
|
|
264
265
|
v-if="renderedMarkdown.get(idx)"
|
|
265
266
|
class="filex-viewer-ipynb__md"
|
|
267
|
+
dir="auto"
|
|
266
268
|
v-html="renderedMarkdown.get(idx)"
|
|
267
269
|
/>
|
|
268
270
|
<pre v-else class="filex-viewer-ipynb__md-raw">{{ joinSource(cell.source) }}</pre>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { sayFailure } from '../lib/errorWords';
|
|
2
3
|
/**
|
|
3
4
|
* MermaidViewer — render a `.mmd` / `.mermaid` source file.
|
|
4
5
|
*
|
|
@@ -60,9 +61,7 @@ async function load(): Promise<void> {
|
|
|
60
61
|
const mermaid = await ensureMermaid();
|
|
61
62
|
if (myToken !== renderToken) return;
|
|
62
63
|
if (!mermaid) {
|
|
63
|
-
error.value =
|
|
64
|
-
? props.t('viewer.peer_not_installed')
|
|
65
|
-
: 'Mermaid viewer requires `mermaid` — install or use download.';
|
|
64
|
+
error.value = tt('viewer.peer_not_installed', 'This kind of file cannot be shown here. Download it to open it on your device.');
|
|
66
65
|
loading.value = false;
|
|
67
66
|
return;
|
|
68
67
|
}
|
|
@@ -75,7 +74,7 @@ async function load(): Promise<void> {
|
|
|
75
74
|
credentials: props.authCredentials,
|
|
76
75
|
});
|
|
77
76
|
} catch (err) {
|
|
78
|
-
error.value = err
|
|
77
|
+
error.value = sayFailure(err, tt('viewer.failed_to_load', 'Failed to load file'), { t: props.t }).text;
|
|
79
78
|
loading.value = false;
|
|
80
79
|
return;
|
|
81
80
|
}
|
|
@@ -95,7 +94,7 @@ async function load(): Promise<void> {
|
|
|
95
94
|
}
|
|
96
95
|
}
|
|
97
96
|
} catch (err) {
|
|
98
|
-
error.value = err
|
|
97
|
+
error.value = sayFailure(err, tt('viewer.failed_to_load', 'Failed to load file'), { t: props.t }).text;
|
|
99
98
|
} finally {
|
|
100
99
|
loading.value = false;
|
|
101
100
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { requestFailure, sayFailure } from '../lib/errorWords';
|
|
2
3
|
/**
|
|
3
4
|
* PdfViewer — rich PDF preview/edit via `pdfjs-dist`.
|
|
4
5
|
*
|
|
@@ -23,6 +24,7 @@ import { computed, onBeforeUnmount, onMounted, ref, watch, nextTick } from 'vue'
|
|
|
23
24
|
import { actionIconSvg } from '../lib/actionIcons'; /* ikon:emoji */
|
|
24
25
|
import { fileIconTile } from '../lib/fileIcons'; /* ikon:emoji */
|
|
25
26
|
import { fetchViewerArrayBuffer } from '../composables/useViewerFetch';
|
|
27
|
+
import { loadPdfjs } from '../lib/pdfjsLoader';
|
|
26
28
|
|
|
27
29
|
const props = defineProps<{
|
|
28
30
|
url: string;
|
|
@@ -60,21 +62,10 @@ let renderToken = 0;
|
|
|
60
62
|
|
|
61
63
|
async function ensurePdfjs(): Promise<any | null> {
|
|
62
64
|
if (pdfjs) return pdfjs;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
pdfjs = mod.default ?? mod;
|
|
68
|
-
if (pdfjs.GlobalWorkerOptions) {
|
|
69
|
-
const version = pdfjs.version || '4.0.379';
|
|
70
|
-
pdfjs.GlobalWorkerOptions.workerSrc =
|
|
71
|
-
props.pdfWorkerUrl ||
|
|
72
|
-
`https://cdn.jsdelivr.net/npm/pdfjs-dist@${version}/legacy/build/pdf.worker.min.js`;
|
|
73
|
-
}
|
|
74
|
-
return pdfjs;
|
|
75
|
-
} catch {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
65
|
+
// One loader for every pdf.js consumer in the package (lib/pdfjsLoader):
|
|
66
|
+
// the legacy build, the host's worker URL or the pinned CDN copy.
|
|
67
|
+
pdfjs = await loadPdfjs(props.pdfWorkerUrl);
|
|
68
|
+
return pdfjs;
|
|
78
69
|
}
|
|
79
70
|
|
|
80
71
|
// useNativeViewer flips the renderer to <embed type="application/pdf">
|
|
@@ -311,14 +302,14 @@ async function saveAnnotations(): Promise<void> {
|
|
|
311
302
|
credentials: props.authCredentials || 'same-origin',
|
|
312
303
|
body: JSON.stringify({ path: props.filePath, base64 }),
|
|
313
304
|
});
|
|
314
|
-
if (!res.ok) throw
|
|
305
|
+
if (!res.ok) throw requestFailure(res.status, await res.text().catch(() => ''), undefined);
|
|
315
306
|
saveStatus.value = 'saved';
|
|
316
307
|
setTimeout(() => {
|
|
317
308
|
if (saveStatus.value === 'saved') saveStatus.value = 'idle';
|
|
318
309
|
}, 2500);
|
|
319
310
|
} catch (err) {
|
|
320
311
|
saveStatus.value = 'error';
|
|
321
|
-
error.value = err
|
|
312
|
+
error.value = sayFailure(err, tt('err.save_failed', 'Your changes could not be saved.'), { t: props.t }).text;
|
|
322
313
|
}
|
|
323
314
|
}
|
|
324
315
|
|
|
@@ -74,9 +74,7 @@ async function load(): Promise<void> {
|
|
|
74
74
|
const lib = await ensureAgPsd();
|
|
75
75
|
if (myToken !== renderToken) return;
|
|
76
76
|
if (!lib) {
|
|
77
|
-
error.value =
|
|
78
|
-
? props.t('viewer.peer_not_installed')
|
|
79
|
-
: 'PSD viewer requires `ag-psd` — install or use download.';
|
|
77
|
+
error.value = tt('viewer.peer_not_installed', 'This kind of file cannot be shown here. Download it to open it on your device.');
|
|
80
78
|
loading.value = false;
|
|
81
79
|
return;
|
|
82
80
|
}
|
|
@@ -222,7 +220,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
222
220
|
}
|
|
223
221
|
.filex-viewer-psd__layers {
|
|
224
222
|
width: 240px;
|
|
225
|
-
border-
|
|
223
|
+
border-inline-end: 1px solid var(--fe-border, #e2e6ed);
|
|
226
224
|
overflow-y: auto;
|
|
227
225
|
padding: 8px 0;
|
|
228
226
|
background: var(--fe-bg-elev, #f7f8fa);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { sayFailure } from '../lib/errorWords';
|
|
2
3
|
/**
|
|
3
4
|
* TiffViewer — multi-page TIFF preview via `utif`.
|
|
4
5
|
*
|
|
@@ -56,9 +57,7 @@ async function load(): Promise<void> {
|
|
|
56
57
|
const lib = await ensureUtif();
|
|
57
58
|
if (myToken !== renderToken) return;
|
|
58
59
|
if (!lib) {
|
|
59
|
-
error.value =
|
|
60
|
-
? props.t('viewer.peer_not_installed')
|
|
61
|
-
: 'TIFF viewer requires `utif` — install or use download.';
|
|
60
|
+
error.value = tt('viewer.peer_not_installed', 'This kind of file cannot be shown here. Download it to open it on your device.');
|
|
62
61
|
loading.value = false;
|
|
63
62
|
return;
|
|
64
63
|
}
|
|
@@ -77,7 +76,7 @@ async function load(): Promise<void> {
|
|
|
77
76
|
}
|
|
78
77
|
paint();
|
|
79
78
|
} catch (err) {
|
|
80
|
-
error.value = err
|
|
79
|
+
error.value = sayFailure(err, tt('viewer.failed_to_load', 'Failed to load file'), { t: props.t }).text;
|
|
81
80
|
} finally {
|
|
82
81
|
loading.value = false;
|
|
83
82
|
}
|
|
@@ -209,7 +208,7 @@ const typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ex
|
|
|
209
208
|
.filex-viewer-tiff__pager {
|
|
210
209
|
position: absolute;
|
|
211
210
|
bottom: 12px;
|
|
212
|
-
left: 50%;
|
|
211
|
+
left: 50%; /* rtl-physical: centred by translate(-50%) — the same place in both directions */
|
|
213
212
|
transform: translateX(-50%);
|
|
214
213
|
display: flex;
|
|
215
214
|
align-items: center;
|
package/src/viewers/Viewer3D.vue
CHANGED
|
@@ -55,7 +55,7 @@ async function load(): Promise<void> {
|
|
|
55
55
|
} catch {
|
|
56
56
|
error.value = props.t
|
|
57
57
|
? props.t('viewer.peer_not_installed')
|
|
58
|
-
: '
|
|
58
|
+
: 'This kind of file cannot be shown here. Download it to open it on your device.';
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 BRF Tech
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { defineComponent as b, ref as v, onMounted as g, watch as L, computed as h, openBlock as s, createElementBlock as o, createElementVNode as i, toDisplayString as r, unref as H, Fragment as M, renderList as z, createTextVNode as C } from "vue";
|
|
2
|
-
import { f as x, a as S } from "./index-Dx0IbBcI.js";
|
|
3
|
-
const $ = { class: "filex-viewer-archive" }, B = {
|
|
4
|
-
key: 0,
|
|
5
|
-
class: "filex-viewer-fallback"
|
|
6
|
-
}, F = ["innerHTML"], P = {
|
|
7
|
-
key: 1,
|
|
8
|
-
class: "filex-viewer-fallback"
|
|
9
|
-
}, E = ["innerHTML"], N = {
|
|
10
|
-
key: 2,
|
|
11
|
-
class: "filex-viewer-fallback"
|
|
12
|
-
}, I = ["innerHTML"], O = {
|
|
13
|
-
key: 3,
|
|
14
|
-
class: "filex-viewer-archive__pane"
|
|
15
|
-
}, V = { class: "filex-viewer-archive__summary" }, j = { class: "filex-viewer-archive__table" }, A = { class: "filex-viewer-archive__size" }, U = ["data-dir"], D = ["innerHTML"], G = { class: "filex-viewer-archive__size" }, q = /* @__PURE__ */ b({
|
|
16
|
-
__name: "ArchiveViewer",
|
|
17
|
-
props: {
|
|
18
|
-
url: {},
|
|
19
|
-
filePath: {},
|
|
20
|
-
ext: {},
|
|
21
|
-
t: { type: Function },
|
|
22
|
-
authHeaders: { type: Function },
|
|
23
|
-
authCredentials: {},
|
|
24
|
-
archiveListUrl: {}
|
|
25
|
-
},
|
|
26
|
-
setup(m) {
|
|
27
|
-
const t = m, c = v([]), d = v(!0), u = v(null);
|
|
28
|
-
function l(e, a, n) {
|
|
29
|
-
return t.t ? t.t(e, n) : a;
|
|
30
|
-
}
|
|
31
|
-
function _(e) {
|
|
32
|
-
return e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${(e / 1024).toFixed(1)} KB` : e < 1024 * 1024 * 1024 ? `${(e / (1024 * 1024)).toFixed(1)} MB` : `${(e / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
33
|
-
}
|
|
34
|
-
async function f() {
|
|
35
|
-
if (d.value = !0, u.value = null, c.value = [], !t.filePath) {
|
|
36
|
-
u.value = l("viewer.archive.error", "Could not read archive contents."), d.value = !1;
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
try {
|
|
40
|
-
const e = await fetch(t.archiveListUrl || "/api/files/archive/list", {
|
|
41
|
-
method: "POST",
|
|
42
|
-
credentials: t.authCredentials || "same-origin",
|
|
43
|
-
headers: {
|
|
44
|
-
"Content-Type": "application/json",
|
|
45
|
-
...t.authHeaders ? await t.authHeaders() : {}
|
|
46
|
-
},
|
|
47
|
-
body: JSON.stringify({ path: t.filePath })
|
|
48
|
-
});
|
|
49
|
-
if (!e.ok)
|
|
50
|
-
throw new Error(`${e.status} ${e.statusText}`);
|
|
51
|
-
const a = await e.json();
|
|
52
|
-
c.value = a.entries ?? [];
|
|
53
|
-
} catch (e) {
|
|
54
|
-
u.value = e instanceof Error ? e.message : l("viewer.archive.error", "Could not read archive contents.");
|
|
55
|
-
} finally {
|
|
56
|
-
d.value = !1;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
g(f), L(() => t.filePath, f);
|
|
60
|
-
const y = h(() => c.value.reduce((e, a) => e + (a.size || 0), 0)), p = h(() => c.value.filter((e) => !e.is_dir).length), w = h(() => x({ type: "file", extension: t.ext }));
|
|
61
|
-
function k(e) {
|
|
62
|
-
const a = e.name || "", n = a.lastIndexOf(".");
|
|
63
|
-
return x({
|
|
64
|
-
type: e.is_dir ? "dir" : "file",
|
|
65
|
-
extension: n > 0 ? a.slice(n + 1) : "",
|
|
66
|
-
basename: a
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return (e, a) => (s(), o("div", $, [
|
|
70
|
-
u.value ? (s(), o("div", B, [
|
|
71
|
-
i("span", {
|
|
72
|
-
class: "filex-viewer-fallback__icon",
|
|
73
|
-
"aria-hidden": "true",
|
|
74
|
-
innerHTML: w.value
|
|
75
|
-
}, null, 8, F),
|
|
76
|
-
i("p", null, r(u.value), 1)
|
|
77
|
-
])) : d.value ? (s(), o("div", P, [
|
|
78
|
-
i("span", {
|
|
79
|
-
class: "filex-viewer-fallback__icon filex-viewer-fallback__icon--spin",
|
|
80
|
-
"aria-hidden": "true",
|
|
81
|
-
innerHTML: H(S)("progress")
|
|
82
|
-
}, null, 8, E),
|
|
83
|
-
i("p", null, r(l("viewer.loading", "Loading…")), 1)
|
|
84
|
-
])) : c.value.length === 0 ? (s(), o("div", N, [
|
|
85
|
-
i("span", {
|
|
86
|
-
class: "filex-viewer-fallback__icon",
|
|
87
|
-
"aria-hidden": "true",
|
|
88
|
-
innerHTML: w.value
|
|
89
|
-
}, null, 8, I),
|
|
90
|
-
i("p", null, r(l("viewer.archive.empty", "Archive is empty.")), 1)
|
|
91
|
-
])) : (s(), o("div", O, [
|
|
92
|
-
i("div", V, r(l("viewer.archive.entries", `${p.value} files`, { n: p.value })) + " · " + r(_(y.value)), 1),
|
|
93
|
-
i("table", j, [
|
|
94
|
-
i("thead", null, [
|
|
95
|
-
i("tr", null, [
|
|
96
|
-
i("th", null, r(l("viewer.name", "Name")), 1),
|
|
97
|
-
i("th", A, r(l("viewer.size", "Size")), 1)
|
|
98
|
-
])
|
|
99
|
-
]),
|
|
100
|
-
i("tbody", null, [
|
|
101
|
-
(s(!0), o(M, null, z(c.value, (n, T) => (s(), o("tr", {
|
|
102
|
-
key: T,
|
|
103
|
-
"data-dir": n.is_dir ? "1" : "0"
|
|
104
|
-
}, [
|
|
105
|
-
i("td", null, [
|
|
106
|
-
i("span", {
|
|
107
|
-
class: "filex-viewer-archive__icon",
|
|
108
|
-
"aria-hidden": "true",
|
|
109
|
-
innerHTML: k(n)
|
|
110
|
-
}, null, 8, D),
|
|
111
|
-
C(" " + r(n.name), 1)
|
|
112
|
-
]),
|
|
113
|
-
i("td", G, r(n.is_dir ? "" : _(n.size)), 1)
|
|
114
|
-
], 8, U))), 128))
|
|
115
|
-
])
|
|
116
|
-
])
|
|
117
|
-
]))
|
|
118
|
-
]));
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
export {
|
|
122
|
-
q as default
|
|
123
|
-
};
|
|
124
|
-
//# sourceMappingURL=ArchiveViewer-CLNevfPE.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ArchiveViewer-CLNevfPE.js","sources":["../src/viewers/ArchiveViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * ArchiveViewer — minimal zip / archive contents preview.\n *\n * Hits the configured archive-list endpoint (`archiveListUrl`, default\n * `POST /api/files/archive/list`) with the adapter-qualified path\n * and renders the member list as a flat table (name, size, mtime).\n * Read-only — extraction is exposed elsewhere (context menu / actions\n * panel). The viewer's job is just \"what's inside?\" so the user can\n * decide whether to extract or download.\n */\nimport { computed, onMounted, ref, watch } from 'vue';\nimport { actionIconSvg } from '../lib/actionIcons'; /* ikon:emoji */\nimport { fileIconTile } from '../lib/fileIcons'; /* ikon:emoji */\n\ninterface ArchiveEntry {\n name: string;\n size: number;\n mtime?: string;\n is_dir?: boolean;\n}\n\nconst props = defineProps<{\n url: string;\n filePath?: string;\n ext: string;\n t?: (key: string, vars?: Record<string, string | number>) => string;\n authHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n authCredentials?: RequestCredentials;\n /** ⚠ Where to ask. This used to be the literal '/api/files/archive/list',\n * which is the ONE thing an embed cannot assume: with `apiBase` pointing at\n * another origin the request went to the host page instead and 404'd, while\n * every other call in the package honoured the configured base. The default\n * keeps a same-origin install working unchanged. */\n archiveListUrl?: string;\n}>();\n\nconst entries = ref<ArchiveEntry[]>([]);\nconst loading = ref(true);\nconst error = ref<string | null>(null);\n\n/** ⚠ `vars` go THROUGH `t()`, not into a `.replace()` afterwards: `t()` is what\n * picks the singular (`viewer.archive.entries_one`) from the count, and it can\n * only do that if it is told the count. Same shape CsvViewer's helper has. */\nfunction tt(key: string, fallback: string, vars?: Record<string, string | number>): string {\n return props.t ? props.t(key, vars) : fallback;\n}\n\nfunction fmtSize(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;\n return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\nasync function load(): Promise<void> {\n loading.value = true;\n error.value = null;\n entries.value = [];\n // The viewer is mounted with the resolved file path. Hit the API\n // endpoint with the same adapter-qualified path the preview URL\n // points at — the backend resolves storage + relative path itself.\n if (!props.filePath) {\n error.value = tt('viewer.archive.error', 'Could not read archive contents.');\n loading.value = false;\n return;\n }\n try {\n const res = await fetch(props.archiveListUrl || '/api/files/archive/list', {\n method: 'POST',\n credentials: props.authCredentials || 'same-origin',\n headers: {\n 'Content-Type': 'application/json',\n ...(props.authHeaders ? await props.authHeaders() : {}),\n },\n body: JSON.stringify({ path: props.filePath }),\n });\n if (!res.ok) {\n throw new Error(`${res.status} ${res.statusText}`);\n }\n const body = (await res.json()) as { entries?: ArchiveEntry[] };\n entries.value = body.entries ?? [];\n } catch (err) {\n error.value = err instanceof Error ? err.message : tt('viewer.archive.error', 'Could not read archive contents.');\n } finally {\n loading.value = false;\n }\n}\n\nonMounted(load);\nwatch(() => props.filePath, load);\n\nconst totalSize = computed(() => entries.value.reduce((sum, e) => sum + (e.size || 0), 0));\nconst fileCount = computed(() => entries.value.filter((e) => !e.is_dir).length);\n\n/* === ikon:emoji — the fallback screen's mark ==========================\n * Every viewer opened its \"cannot show this\" / \"still loading\" screen with a\n * 48px colour emoji, one per format, each from whatever emoji font the OS\n * shipped. The format mark is `lib/fileIcons`'s tile — the SAME tile the row\n * the person just clicked is wearing, so the fallback is recognisably about\n * that file — and \"loading\" is the stroked ring, spun by CSS, because no\n * still picture can say \"still going\". */\nconst typeTile = computed(() => fileIconTile({ type: 'file', extension: props.ext }));\n\n/** A row inside the archive, drawn the way the listing draws the same kind. */\nfunction entryTile(e: ArchiveEntry): string {\n const name = e.name || '';\n const dot = name.lastIndexOf('.');\n return fileIconTile({\n type: e.is_dir ? 'dir' : 'file',\n extension: dot > 0 ? name.slice(dot + 1) : '',\n basename: name,\n });\n}\n</script>\n\n<template>\n <div class=\"filex-viewer-archive\">\n <div v-if=\"error\" class=\"filex-viewer-fallback\">\n <!-- eslint-disable-next-line vue/no-v-html -- static markup from lib/fileIcons + lib/actionIcons -->\n <span class=\"filex-viewer-fallback__icon\" aria-hidden=\"true\" v-html=\"typeTile\"></span>\n <p>{{ error }}</p>\n </div>\n <div v-else-if=\"loading\" class=\"filex-viewer-fallback\">\n <!-- eslint-disable-next-line vue/no-v-html -- static markup from lib/fileIcons + lib/actionIcons -->\n <span\n class=\"filex-viewer-fallback__icon filex-viewer-fallback__icon--spin\"\n aria-hidden=\"true\"\n v-html=\"actionIconSvg('progress')\"\n ></span>\n <p>{{ tt('viewer.loading', 'Loading…') }}</p>\n </div>\n <div v-else-if=\"entries.length === 0\" class=\"filex-viewer-fallback\">\n <!-- eslint-disable-next-line vue/no-v-html -- static markup from lib/fileIcons + lib/actionIcons -->\n <span class=\"filex-viewer-fallback__icon\" aria-hidden=\"true\" v-html=\"typeTile\"></span>\n <p>{{ tt('viewer.archive.empty', 'Archive is empty.') }}</p>\n </div>\n <div v-else class=\"filex-viewer-archive__pane\">\n <div class=\"filex-viewer-archive__summary\">\n <!-- ⚠ The count is IN the message (\"{n} files\"). A `{{ fileCount }}`\n printed in front of it read \"3 3 files\". -->\n {{ tt('viewer.archive.entries', `${fileCount} files`, { n: fileCount }) }}\n · {{ fmtSize(totalSize) }}\n </div>\n <table class=\"filex-viewer-archive__table\">\n <thead>\n <tr>\n <th>{{ tt('viewer.name', 'Name') }}</th>\n <th class=\"filex-viewer-archive__size\">{{ tt('viewer.size', 'Size') }}</th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(e, i) in entries\" :key=\"i\" :data-dir=\"e.is_dir ? '1' : '0'\">\n <td>\n <!-- ikon:emoji — the same tile the listing draws for the same\n kind of file, so an archive's contents and the folder it\n came from read as one product. -->\n <!-- eslint-disable-next-line vue/no-v-html -- static markup from lib/fileIcons -->\n <span class=\"filex-viewer-archive__icon\" aria-hidden=\"true\" v-html=\"entryTile(e)\"></span>\n {{ e.name }}\n </td>\n <td class=\"filex-viewer-archive__size\">{{ e.is_dir ? '' : fmtSize(e.size) }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style>\n/* ⚠ NOT `scoped`, deliberately. Vue's scoped styles compile to\n `.cls[data-v-HASH]`, and in the web-component build the hash baked into\n this CSS does not match the one Vue stamps onto the DOM — so every rule\n here silently stopped applying. Measured in the desktop app: the share\n dialog had `position: static`, no background and no radius, i.e. raw\n unstyled HTML, in EVERY embedded surface.\n Safe to drop: every selector below is prefixed (fx-/fe-/filex-), so\n there is nothing here that can leak into a host page. */\n.filex-viewer-archive {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 70vh;\n background: var(--fe-bg, #fff);\n color: var(--fe-text, #1a1e27);\n}\n.filex-viewer-archive__pane {\n flex: 1;\n overflow: auto;\n padding: 16px 20px;\n}\n.filex-viewer-archive__summary {\n font-size: 12px;\n color: var(--fe-text-muted, #5a6475);\n margin-bottom: 12px;\n font-variant-numeric: tabular-nums;\n}\n.filex-viewer-archive__table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n font-family: var(--fe-font-mono, monospace);\n}\n.filex-viewer-archive__table th,\n.filex-viewer-archive__table td {\n padding: 6px 12px;\n border-bottom: 1px solid var(--fe-border, #e2e6ed);\n text-align: left;\n}\n.filex-viewer-archive__table th {\n background: var(--fe-bg-elev, #f7f8fa);\n font-weight: 600;\n position: sticky;\n top: 0;\n}\n.filex-viewer-archive__size {\n text-align: right;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n color: var(--fe-text-muted, #5a6475);\n}\n.filex-viewer-archive__icon {\n margin-right: 6px;\n}\n.filex-viewer-archive__table tr[data-dir=\"1\"] {\n color: var(--fe-text-muted, #5a6475);\n}\n.filex-viewer-fallback {\n text-align: center;\n padding: 32px;\n color: var(--fe-text-muted, #5a6475);\n}\n.filex-viewer-fallback__icon {\n font-size: 48px;\n display: block;\n margin-bottom: 12px;\n}\n</style>\n"],"names":["props","__props","entries","ref","loading","error","tt","key","fallback","vars","fmtSize","n","load","res","body","err","onMounted","watch","totalSize","computed","sum","e","fileCount","typeTile","fileIconTile","entryTile","name","dot","_openBlock","_createElementBlock","_hoisted_1","_hoisted_2","_createElementVNode","_hoisted_4","_unref","actionIconSvg","_hoisted_6","_hoisted_8","_hoisted_9","_toDisplayString","_hoisted_10","_hoisted_11","_Fragment","_renderList","i","_hoisted_14"],"mappings":"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"}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { defineComponent as B, ref as v, onMounted as N, watch as b, computed as f, openBlock as s, createElementBlock as r, createElementVNode as n, toDisplayString as d, unref as W, Fragment as k, renderList as L, createCommentVNode as j } from "vue";
|
|
2
|
-
import { f as q, a as A } from "./index-Dx0IbBcI.js";
|
|
3
|
-
import { f as G } from "./useViewerFetch-czqbd2Lj.js";
|
|
4
|
-
const O = { class: "filex-viewer-csv" }, Z = { class: "filex-viewer-csv__pane" }, z = {
|
|
5
|
-
key: 0,
|
|
6
|
-
class: "filex-viewer-fallback"
|
|
7
|
-
}, J = ["innerHTML"], K = {
|
|
8
|
-
key: 1,
|
|
9
|
-
class: "filex-viewer-fallback"
|
|
10
|
-
}, Q = ["innerHTML"], U = {
|
|
11
|
-
key: 2,
|
|
12
|
-
class: "filex-viewer-csv__table"
|
|
13
|
-
}, X = { class: "filex-viewer-csv__rownum" }, Y = {
|
|
14
|
-
key: 0,
|
|
15
|
-
class: "filex-viewer-csv__pager"
|
|
16
|
-
}, ee = ["disabled"], te = { class: "filex-viewer-csv__pageno" }, le = ["disabled"], C = 100, D = 1e3, re = /* @__PURE__ */ B({
|
|
17
|
-
__name: "CsvViewer",
|
|
18
|
-
props: {
|
|
19
|
-
url: {},
|
|
20
|
-
ext: {},
|
|
21
|
-
t: { type: Function },
|
|
22
|
-
authHeaders: { type: Function },
|
|
23
|
-
authCredentials: {}
|
|
24
|
-
},
|
|
25
|
-
setup(P) {
|
|
26
|
-
const i = P, o = v([]), _ = v(null), h = v(!0), T = v(!0), M = v(""), u = v(1), m = v(","), w = v("auto");
|
|
27
|
-
let H = 0;
|
|
28
|
-
function R(l) {
|
|
29
|
-
const e = l.split(/\r?\n/).slice(0, 5).join(`
|
|
30
|
-
`), t = (e.match(/\t/g) || []).length, a = (e.match(/;/g) || []).length, c = (e.match(/,/g) || []).length;
|
|
31
|
-
return t > c && t > a ? " " : a > c && a > t ? ";" : ",";
|
|
32
|
-
}
|
|
33
|
-
async function V(l, e) {
|
|
34
|
-
try {
|
|
35
|
-
const t = await import(
|
|
36
|
-
/* @vite-ignore */
|
|
37
|
-
"./papaparse.min-VB1HBwYX.js"
|
|
38
|
-
).then((y) => y.p);
|
|
39
|
-
return (t.default ?? t).parse(l, {
|
|
40
|
-
delimiter: e,
|
|
41
|
-
skipEmptyLines: !0,
|
|
42
|
-
header: !1
|
|
43
|
-
}).data.slice(0, D);
|
|
44
|
-
} catch {
|
|
45
|
-
return l.split(/\r?\n/).filter((t) => t.length > 0).slice(0, D).map((t) => t.split(e));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
async function g() {
|
|
49
|
-
var a;
|
|
50
|
-
h.value = !0, _.value = null, o.value = [], u.value = 1;
|
|
51
|
-
const l = ++H;
|
|
52
|
-
let e;
|
|
53
|
-
try {
|
|
54
|
-
e = await G({
|
|
55
|
-
url: i.url,
|
|
56
|
-
headers: await ((a = i.authHeaders) == null ? void 0 : a.call(i)) ?? {},
|
|
57
|
-
credentials: i.authCredentials
|
|
58
|
-
});
|
|
59
|
-
} catch (c) {
|
|
60
|
-
_.value = c instanceof Error ? c.message : "fetch failed", h.value = !1;
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (l !== H) return;
|
|
64
|
-
i.ext === "tsv" ? m.value = " " : m.value = R(e.slice(0, 4096));
|
|
65
|
-
const t = w.value === "auto" ? m.value : w.value;
|
|
66
|
-
o.value = await V(e, t), h.value = !1;
|
|
67
|
-
}
|
|
68
|
-
N(g), b(() => i.url, g), b(() => w.value, () => {
|
|
69
|
-
o.value.length !== 0 && g();
|
|
70
|
-
});
|
|
71
|
-
const $ = f(() => !T.value || o.value.length === 0 ? o.value.length === 0 ? [] : o.value[0].map((l, e) => `Col ${e + 1}`) : o.value[0]), E = f(() => T.value ? o.value.slice(1) : o.value), p = f(() => {
|
|
72
|
-
const l = M.value.trim().toLowerCase();
|
|
73
|
-
return l ? E.value.filter(
|
|
74
|
-
(e) => e.some((t) => (t || "").toLowerCase().includes(l))
|
|
75
|
-
) : E.value;
|
|
76
|
-
}), x = f(
|
|
77
|
-
() => Math.max(1, Math.ceil(p.value.length / C))
|
|
78
|
-
), F = f(() => {
|
|
79
|
-
const l = (u.value - 1) * C;
|
|
80
|
-
return p.value.slice(l, l + C);
|
|
81
|
-
});
|
|
82
|
-
b(M, () => {
|
|
83
|
-
u.value = 1;
|
|
84
|
-
});
|
|
85
|
-
function I(l, e, t) {
|
|
86
|
-
return i.t ? i.t(l, t) : e;
|
|
87
|
-
}
|
|
88
|
-
const S = f(() => q({ type: "file", extension: i.ext }));
|
|
89
|
-
return (l, e) => (s(), r("div", O, [
|
|
90
|
-
n("div", Z, [
|
|
91
|
-
_.value ? (s(), r("div", z, [
|
|
92
|
-
n("span", {
|
|
93
|
-
class: "filex-viewer-fallback__icon",
|
|
94
|
-
"aria-hidden": "true",
|
|
95
|
-
innerHTML: S.value
|
|
96
|
-
}, null, 8, J),
|
|
97
|
-
n("p", null, d(_.value), 1)
|
|
98
|
-
])) : h.value ? (s(), r("div", K, [
|
|
99
|
-
n("span", {
|
|
100
|
-
class: "filex-viewer-fallback__icon filex-viewer-fallback__icon--spin",
|
|
101
|
-
"aria-hidden": "true",
|
|
102
|
-
innerHTML: W(A)("progress")
|
|
103
|
-
}, null, 8, Q),
|
|
104
|
-
n("p", null, d(I("viewer.loading", "Loading…")), 1)
|
|
105
|
-
])) : (s(), r("table", U, [
|
|
106
|
-
n("thead", null, [
|
|
107
|
-
n("tr", null, [
|
|
108
|
-
e[2] || (e[2] = n("th", { class: "filex-viewer-csv__rownum" }, "#", -1)),
|
|
109
|
-
(s(!0), r(k, null, L($.value, (t, a) => (s(), r("th", { key: a }, d(t), 1))), 128))
|
|
110
|
-
])
|
|
111
|
-
]),
|
|
112
|
-
n("tbody", null, [
|
|
113
|
-
(s(!0), r(k, null, L(F.value, (t, a) => (s(), r("tr", { key: a }, [
|
|
114
|
-
n("td", X, d((u.value - 1) * 100 + a + 1), 1),
|
|
115
|
-
(s(!0), r(k, null, L(t, (c, y) => (s(), r("td", { key: y }, d(c), 1))), 128))
|
|
116
|
-
]))), 128))
|
|
117
|
-
])
|
|
118
|
-
]))
|
|
119
|
-
]),
|
|
120
|
-
!h.value && !_.value && x.value > 1 ? (s(), r("div", Y, [
|
|
121
|
-
n("button", {
|
|
122
|
-
type: "button",
|
|
123
|
-
class: "filex-viewer-btn",
|
|
124
|
-
disabled: u.value <= 1,
|
|
125
|
-
onClick: e[0] || (e[0] = (t) => u.value--)
|
|
126
|
-
}, "‹", 8, ee),
|
|
127
|
-
n("span", te, d(u.value) + " / " + d(x.value) + " (" + d(I("viewer.csv_rows", `${p.value.length} rows`, { n: p.value.length })) + ")", 1),
|
|
128
|
-
n("button", {
|
|
129
|
-
type: "button",
|
|
130
|
-
class: "filex-viewer-btn",
|
|
131
|
-
disabled: u.value >= x.value,
|
|
132
|
-
onClick: e[1] || (e[1] = (t) => u.value++)
|
|
133
|
-
}, "›", 8, le)
|
|
134
|
-
])) : j("", !0)
|
|
135
|
-
]));
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
export {
|
|
139
|
-
re as default
|
|
140
|
-
};
|
|
141
|
-
//# sourceMappingURL=CsvViewer-K8XA3z7N.js.map
|