vitepress-plugin-filelist 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -3
- package/dist/client/browser/index.d.ts +28 -1
- package/dist/client/browser/index.js +459 -119
- package/dist/client/ssr/index.d.ts +28 -1
- package/dist/client/ssr/index.js +278 -21
- package/dist/client/style.css +101 -5
- package/package.json +8 -2
|
@@ -179,12 +179,29 @@ function previewKind(name) {
|
|
|
179
179
|
function totalSize(items) {
|
|
180
180
|
return items.reduce((sum, item) => sum + (Number.isFinite(item.size) && (item.size ?? 0) > 0 ? item.size : 0), 0);
|
|
181
181
|
}
|
|
182
|
-
|
|
182
|
+
const collators = /* @__PURE__ */ new Map();
|
|
183
|
+
/**
|
|
184
|
+
* Name order for a UI locale: pinyin for Chinese, alphabetical for English,
|
|
185
|
+
* with numbers in natural order (“2” before “10”) either way.
|
|
186
|
+
*/
|
|
187
|
+
function collatorFor(locale) {
|
|
188
|
+
const tag = locale.toLowerCase().startsWith("zh") ? "zh-Hans-CN" : "en";
|
|
189
|
+
let collator = collators.get(tag);
|
|
190
|
+
if (!collator) {
|
|
191
|
+
collator = new Intl.Collator(tag, {
|
|
192
|
+
numeric: true,
|
|
193
|
+
sensitivity: "base"
|
|
194
|
+
});
|
|
195
|
+
collators.set(tag, collator);
|
|
196
|
+
}
|
|
197
|
+
return collator;
|
|
198
|
+
}
|
|
199
|
+
function compareNodes(a, b, key, ascending = true, locale = "zh-CN") {
|
|
183
200
|
if (a.is_dir !== b.is_dir) return a.is_dir ? -1 : 1;
|
|
184
201
|
let result = 0;
|
|
185
202
|
if (key === "size") result = a.size - b.size;
|
|
186
203
|
else if (key === "modified") result = new Date(a.modified).getTime() - new Date(b.modified).getTime();
|
|
187
|
-
else result = a.name.
|
|
204
|
+
else result = collatorFor(locale).compare(a.name, b.name) || (a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
188
205
|
return ascending ? result : -result;
|
|
189
206
|
}
|
|
190
207
|
/**
|
|
@@ -899,6 +916,22 @@ const fileListLocales = {
|
|
|
899
916
|
forgetPassword: "忘记",
|
|
900
917
|
unlockFolder: "输入 {name} 的密码",
|
|
901
918
|
networkHint: "可能被跨域拦截:请检查 OpenList 的 cors.allow_origins,或 OpenList-Worker 的 ALLOW_URLS。",
|
|
919
|
+
deepHint: "只筛选了已加载的内容",
|
|
920
|
+
deepSearch: "深度搜索",
|
|
921
|
+
deepRunning: "正在深度搜索 · 已查找 {count} 个文件夹",
|
|
922
|
+
deepStop: "停止",
|
|
923
|
+
deepDone: "已搜索全部 {count} 个文件夹",
|
|
924
|
+
deepCapped: "已查找 {count} 个文件夹,还有未搜索的子目录",
|
|
925
|
+
deepContinue: "继续",
|
|
926
|
+
deepSkipped: "跳过 {count} 个加密文件夹",
|
|
927
|
+
deepFailed: "{count} 个文件夹读取失败",
|
|
928
|
+
viewMode: "显示方式",
|
|
929
|
+
viewSource: "源码",
|
|
930
|
+
viewRendered: "渲染",
|
|
931
|
+
htmlSandboxNote: "已禁用脚本;网页引用的相对路径资源不会加载",
|
|
932
|
+
imageOmitted: "[图片:{alt}]",
|
|
933
|
+
batchFailedTitle: "打包失败",
|
|
934
|
+
batchFailedHint: "可以减少勾选的文件后重试。",
|
|
902
935
|
downloadStarted: "已交给浏览器下载:{name}",
|
|
903
936
|
batchProgress: "{done} / {total} · {files} / {count} 个文件",
|
|
904
937
|
batchDone: "已打包 {name}({count} 个文件,{size})",
|
|
@@ -977,6 +1010,22 @@ const fileListLocales = {
|
|
|
977
1010
|
forgetPassword: "Forget",
|
|
978
1011
|
unlockFolder: "Enter the password for {name}",
|
|
979
1012
|
networkHint: "Possibly blocked by CORS: check cors.allow_origins in OpenList, or ALLOW_URLS in OpenList-Worker.",
|
|
1013
|
+
deepHint: "Only loaded folders are filtered",
|
|
1014
|
+
deepSearch: "Deep search",
|
|
1015
|
+
deepRunning: "Searching · {count} folders checked",
|
|
1016
|
+
deepStop: "Stop",
|
|
1017
|
+
deepDone: "Searched all {count} folders",
|
|
1018
|
+
deepCapped: "{count} folders checked; some sub-folders remain",
|
|
1019
|
+
deepContinue: "Continue",
|
|
1020
|
+
deepSkipped: "{count} locked folders skipped",
|
|
1021
|
+
deepFailed: "{count} folders could not be read",
|
|
1022
|
+
viewMode: "View",
|
|
1023
|
+
viewSource: "Source",
|
|
1024
|
+
viewRendered: "Rendered",
|
|
1025
|
+
htmlSandboxNote: "Scripts are disabled; resources at relative paths do not load",
|
|
1026
|
+
imageOmitted: "[Image: {alt}]",
|
|
1027
|
+
batchFailedTitle: "Could not create the ZIP",
|
|
1028
|
+
batchFailedHint: "Try again with fewer files selected.",
|
|
980
1029
|
downloadStarted: "Downloading {name} in your browser",
|
|
981
1030
|
batchProgress: "{done} / {total} · {files} / {count} files",
|
|
982
1031
|
batchDone: "Saved {name} ({count} files, {size})",
|
|
@@ -1003,6 +1052,45 @@ function resolveLabels(locale = "zh-CN", overrides) {
|
|
|
1003
1052
|
...overrides
|
|
1004
1053
|
};
|
|
1005
1054
|
}
|
|
1055
|
+
// src/client/markdown.ts
|
|
1056
|
+
/**
|
|
1057
|
+
* Markdown → safe HTML for the preview dialog. Loaded on demand, so pages that
|
|
1058
|
+
* never render Markdown do not download markdown-it or DOMPurify.
|
|
1059
|
+
*
|
|
1060
|
+
* - Raw HTML in the file is allowed, then sanitised (no scripts, handlers, iframes...)
|
|
1061
|
+
* - Only absolute http(s) / mailto links stay clickable, opening in a new tab;
|
|
1062
|
+
* relative links (to other files in the share) become plain text
|
|
1063
|
+
* - Images with a relative source are replaced by their alt text, because the
|
|
1064
|
+
* share's relative paths do not resolve on the documentation site
|
|
1065
|
+
*/
|
|
1066
|
+
async function renderMarkdown(source, imageLabel) {
|
|
1067
|
+
const [{ default: MarkdownIt }, { default: DOMPurify }] = await Promise.all([import("markdown-it"), import("dompurify")]);
|
|
1068
|
+
const probe = DOMPurify.isSupported ? DOMPurify.sanitize("<img src=\"x\" onerror=\"1\"><script>1<\/script>") : "<script";
|
|
1069
|
+
if (/<script|onerror/i.test(probe)) throw new Error("HTML sanitiser unavailable");
|
|
1070
|
+
const html = new MarkdownIt({
|
|
1071
|
+
html: true,
|
|
1072
|
+
linkify: true
|
|
1073
|
+
}).render(source);
|
|
1074
|
+
const template = document.createElement("template");
|
|
1075
|
+
template.innerHTML = DOMPurify.sanitize(html);
|
|
1076
|
+
const fragment = template.content;
|
|
1077
|
+
for (const link of fragment.querySelectorAll("a")) {
|
|
1078
|
+
const href = link.getAttribute("href") ?? "";
|
|
1079
|
+
if (/^(https?:|mailto:)/i.test(href)) {
|
|
1080
|
+
link.setAttribute("target", "_blank");
|
|
1081
|
+
link.setAttribute("rel", "noopener noreferrer");
|
|
1082
|
+
} else link.replaceWith(...link.childNodes);
|
|
1083
|
+
}
|
|
1084
|
+
for (const image of fragment.querySelectorAll("img")) {
|
|
1085
|
+
const src = image.getAttribute("src") ?? "";
|
|
1086
|
+
if (/^(https?:|data:image\/)/i.test(src)) continue;
|
|
1087
|
+
const placeholder = document.createElement("span");
|
|
1088
|
+
placeholder.className = "fl-md-image";
|
|
1089
|
+
placeholder.textContent = imageLabel(image.getAttribute("alt") || src.split("/").pop() || "");
|
|
1090
|
+
image.replaceWith(placeholder);
|
|
1091
|
+
}
|
|
1092
|
+
return template.innerHTML;
|
|
1093
|
+
}
|
|
1006
1094
|
// src/client/PreviewDialog.vue
|
|
1007
1095
|
const _hoisted_1$1 = ["aria-label"];
|
|
1008
1096
|
const _hoisted_2$1 = {
|
|
@@ -1027,33 +1115,40 @@ const _hoisted_13$1 = [
|
|
|
1027
1115
|
"aria-label",
|
|
1028
1116
|
"title"
|
|
1029
1117
|
];
|
|
1030
|
-
const _hoisted_14$1 =
|
|
1031
|
-
const _hoisted_15$1 = ["aria-
|
|
1032
|
-
const _hoisted_16$1 =
|
|
1033
|
-
const _hoisted_17$1 = ["
|
|
1034
|
-
const _hoisted_18$1 =
|
|
1118
|
+
const _hoisted_14$1 = ["aria-label"];
|
|
1119
|
+
const _hoisted_15$1 = ["aria-pressed", "onClick"];
|
|
1120
|
+
const _hoisted_16$1 = { class: "fl-preview-tools fl-preview-actions" };
|
|
1121
|
+
const _hoisted_17$1 = ["aria-label", "title"];
|
|
1122
|
+
const _hoisted_18$1 = ["aria-label", "title"];
|
|
1123
|
+
const _hoisted_19$1 = ["data-mode"];
|
|
1124
|
+
const _hoisted_20$1 = {
|
|
1035
1125
|
key: 0,
|
|
1036
1126
|
class: "fl-preview-status",
|
|
1037
1127
|
role: "status"
|
|
1038
1128
|
};
|
|
1039
|
-
const
|
|
1040
|
-
const
|
|
1041
|
-
const
|
|
1042
|
-
const
|
|
1043
|
-
key: 4,
|
|
1044
|
-
class: "fl-preview-text"
|
|
1045
|
-
};
|
|
1046
|
-
const _hoisted_23$1 = {
|
|
1129
|
+
const _hoisted_21$1 = ["src", "alt"];
|
|
1130
|
+
const _hoisted_22$1 = ["src"];
|
|
1131
|
+
const _hoisted_23$1 = ["src"];
|
|
1132
|
+
const _hoisted_24$1 = {
|
|
1047
1133
|
key: 0,
|
|
1048
1134
|
class: "fl-preview-note"
|
|
1049
1135
|
};
|
|
1050
|
-
const
|
|
1051
|
-
const
|
|
1136
|
+
const _hoisted_25$1 = ["innerHTML"];
|
|
1137
|
+
const _hoisted_26$1 = {
|
|
1138
|
+
key: 1,
|
|
1139
|
+
class: "fl-preview-status",
|
|
1140
|
+
role: "status"
|
|
1141
|
+
};
|
|
1142
|
+
const _hoisted_27$1 = { class: "fl-preview-note" };
|
|
1143
|
+
const _hoisted_28$1 = ["srcdoc", "title"];
|
|
1144
|
+
const _hoisted_29$1 = { key: 3 };
|
|
1145
|
+
const _hoisted_30$1 = ["src", "title"];
|
|
1146
|
+
const _hoisted_31$1 = {
|
|
1052
1147
|
key: 6,
|
|
1053
1148
|
class: "fl-preview-info"
|
|
1054
1149
|
};
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1150
|
+
const _hoisted_32$1 = ["data-kind"];
|
|
1151
|
+
const _hoisted_33$1 = { class: "fl-preview-message" };
|
|
1057
1152
|
const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
1058
1153
|
__name: "PreviewDialog",
|
|
1059
1154
|
props: {
|
|
@@ -1086,7 +1181,9 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1086
1181
|
/**
|
|
1087
1182
|
* File preview in a native <dialog>, without leaving the page.
|
|
1088
1183
|
*
|
|
1089
|
-
* image / video / audio: native elements; text: the first `maxTextBytes
|
|
1184
|
+
* image / video / audio: native elements; text: the first `maxTextBytes`,
|
|
1185
|
+
* with Markdown and HTML switchable between source (default) and rendered
|
|
1186
|
+
* (Markdown sanitised in VitePress' doc style, HTML in a script-less sandbox);
|
|
1090
1187
|
* through a Range request; pdf: the first matching OpenList `iframe_previews`
|
|
1091
1188
|
* viewer, else the browser's reader through a Blob. Anything else (Office
|
|
1092
1189
|
* included), failures, oversized PDFs and demo mode: file info + download.
|
|
@@ -1105,8 +1202,18 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1105
1202
|
const blobUrl = ref("");
|
|
1106
1203
|
const rotation = ref(0);
|
|
1107
1204
|
const actualSize = ref(false);
|
|
1205
|
+
/** Markdown / HTML: show the source or render it. Kept while the page is open. */
|
|
1206
|
+
const view = ref("source");
|
|
1207
|
+
const renderedHtml = ref("");
|
|
1108
1208
|
let controller;
|
|
1109
1209
|
let loadSeq = 0;
|
|
1210
|
+
/** Text files that can also be rendered. */
|
|
1211
|
+
const renderAs = computed(() => {
|
|
1212
|
+
const name = props.file?.name ?? "";
|
|
1213
|
+
if (/\.(md|markdown)$/i.test(name)) return "markdown";
|
|
1214
|
+
if (/\.html?$/i.test(name)) return "html";
|
|
1215
|
+
return null;
|
|
1216
|
+
});
|
|
1110
1217
|
const tooLarge = computed(() => kind.value === "pdf" && !props.viewer && (props.file?.size ?? 0) > props.maxPdfBytes);
|
|
1111
1218
|
const mode = computed(() => {
|
|
1112
1219
|
if (props.demo) return "demo";
|
|
@@ -1116,6 +1223,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1116
1223
|
return kind.value;
|
|
1117
1224
|
});
|
|
1118
1225
|
const busy = computed(() => !props.demo && kind.value !== "none" && !props.error && !bodyFailed.value && (props.resolving || loadingBody.value || !props.src && !tooLarge.value));
|
|
1226
|
+
const canRender = computed(() => !!renderAs.value && mode.value === "text" && !busy.value);
|
|
1119
1227
|
const infoMessage = computed(() => {
|
|
1120
1228
|
const file = props.file;
|
|
1121
1229
|
if (!file) return "";
|
|
@@ -1136,6 +1244,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1136
1244
|
loadingBody.value = false;
|
|
1137
1245
|
bodyFailed.value = false;
|
|
1138
1246
|
textContent.value = "";
|
|
1247
|
+
renderedHtml.value = "";
|
|
1139
1248
|
truncated.value = false;
|
|
1140
1249
|
rotation.value = 0;
|
|
1141
1250
|
actualSize.value = false;
|
|
@@ -1217,6 +1326,20 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1217
1326
|
props.error,
|
|
1218
1327
|
props.demo
|
|
1219
1328
|
], () => void loadBody(), { immediate: true });
|
|
1329
|
+
watch(() => [
|
|
1330
|
+
view.value,
|
|
1331
|
+
renderAs.value,
|
|
1332
|
+
textContent.value
|
|
1333
|
+
], async ([current, as, text]) => {
|
|
1334
|
+
if (current !== "rendered" || as !== "markdown" || !text) return;
|
|
1335
|
+
const seq = loadSeq;
|
|
1336
|
+
try {
|
|
1337
|
+
const html = await renderMarkdown(text, (alt) => t("imageOmitted", { alt }));
|
|
1338
|
+
if (seq === loadSeq) renderedHtml.value = html;
|
|
1339
|
+
} catch {
|
|
1340
|
+
if (seq === loadSeq) view.value = "source";
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1220
1343
|
watch(() => props.open, (open) => {
|
|
1221
1344
|
const el = dialog.value;
|
|
1222
1345
|
if (!el) return;
|
|
@@ -1266,49 +1389,66 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1266
1389
|
"aria-hidden": "true"
|
|
1267
1390
|
}, toDisplayString(unref(extOf)(__props.file.name) || "FILE"), 9, _hoisted_4$1),
|
|
1268
1391
|
createElementVNode("div", _hoisted_5$1, [createElementVNode("span", _hoisted_6$1, toDisplayString(__props.file.name), 1), createElementVNode("span", _hoisted_7$1, toDisplayString(unref(formatSize)(__props.file.size)) + " · " + toDisplayString(unref(formatDate)(__props.file.modified)), 1)]),
|
|
1269
|
-
__props.total > 1 || mode.value === "image" && !busy.value ? (openBlock(), createElementBlock("div", _hoisted_8$1, [
|
|
1270
|
-
|
|
1271
|
-
|
|
1392
|
+
__props.total > 1 || mode.value === "image" && !busy.value || canRender.value ? (openBlock(), createElementBlock("div", _hoisted_8$1, [
|
|
1393
|
+
__props.total > 1 ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
1394
|
+
createElementVNode("button", {
|
|
1395
|
+
class: "fl-preview-tool",
|
|
1396
|
+
type: "button",
|
|
1397
|
+
"aria-label": t("previous"),
|
|
1398
|
+
title: t("previous"),
|
|
1399
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("prev"))
|
|
1400
|
+
}, [createVNode(_sfc_main$2, {
|
|
1401
|
+
name: "previous",
|
|
1402
|
+
size: 18
|
|
1403
|
+
})], 8, _hoisted_9$1),
|
|
1404
|
+
createElementVNode("span", _hoisted_10$1, toDisplayString(__props.position) + " / " + toDisplayString(__props.total), 1),
|
|
1405
|
+
createElementVNode("button", {
|
|
1406
|
+
class: "fl-preview-tool",
|
|
1407
|
+
type: "button",
|
|
1408
|
+
"aria-label": t("next"),
|
|
1409
|
+
title: t("next"),
|
|
1410
|
+
onClick: _cache[1] || (_cache[1] = ($event) => emit("next"))
|
|
1411
|
+
}, [createVNode(_sfc_main$2, {
|
|
1412
|
+
name: "next",
|
|
1413
|
+
size: 18
|
|
1414
|
+
})], 8, _hoisted_11$1)
|
|
1415
|
+
], 64)) : createCommentVNode("v-if", true),
|
|
1416
|
+
mode.value === "image" && !busy.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createElementVNode("button", {
|
|
1417
|
+
class: "fl-preview-tool fl-preview-rotate",
|
|
1272
1418
|
type: "button",
|
|
1273
|
-
"aria-label": t("
|
|
1274
|
-
title: t("
|
|
1275
|
-
onClick: _cache[
|
|
1419
|
+
"aria-label": t("rotate"),
|
|
1420
|
+
title: t("rotate"),
|
|
1421
|
+
onClick: _cache[2] || (_cache[2] = ($event) => rotation.value = (rotation.value + 90) % 360)
|
|
1276
1422
|
}, [createVNode(_sfc_main$2, {
|
|
1277
|
-
name: "
|
|
1278
|
-
size:
|
|
1279
|
-
})], 8,
|
|
1280
|
-
|
|
1281
|
-
createElementVNode("button", {
|
|
1282
|
-
class: "fl-preview-tool",
|
|
1423
|
+
name: "refresh",
|
|
1424
|
+
size: 17
|
|
1425
|
+
})], 8, _hoisted_12$1), createElementVNode("button", {
|
|
1426
|
+
class: "fl-preview-tool fl-preview-zoom",
|
|
1283
1427
|
type: "button",
|
|
1284
|
-
"aria-
|
|
1285
|
-
|
|
1286
|
-
|
|
1428
|
+
"aria-pressed": actualSize.value,
|
|
1429
|
+
"aria-label": t("actualSize"),
|
|
1430
|
+
title: t("actualSize"),
|
|
1431
|
+
onClick: _cache[3] || (_cache[3] = ($event) => actualSize.value = !actualSize.value)
|
|
1287
1432
|
}, [createVNode(_sfc_main$2, {
|
|
1288
|
-
name: "
|
|
1289
|
-
size:
|
|
1290
|
-
})], 8,
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
}, [createVNode(_sfc_main$2, {
|
|
1308
|
-
name: actualSize.value ? "zoomOut" : "zoomIn",
|
|
1309
|
-
size: 17
|
|
1310
|
-
}, null, 8, ["name"])], 8, _hoisted_13$1)], 64)) : createCommentVNode("v-if", true)])) : createCommentVNode("v-if", true),
|
|
1311
|
-
createElementVNode("div", _hoisted_14$1, [!__props.demo ? (openBlock(), createElementBlock("button", {
|
|
1433
|
+
name: actualSize.value ? "zoomOut" : "zoomIn",
|
|
1434
|
+
size: 17
|
|
1435
|
+
}, null, 8, ["name"])], 8, _hoisted_13$1)], 64)) : createCommentVNode("v-if", true),
|
|
1436
|
+
canRender.value ? (openBlock(), createElementBlock("span", {
|
|
1437
|
+
key: 2,
|
|
1438
|
+
class: "fl-preview-view",
|
|
1439
|
+
role: "group",
|
|
1440
|
+
"aria-label": t("viewMode")
|
|
1441
|
+
}, [(openBlock(), createElementBlock(Fragment, null, renderList(["source", "rendered"], (option) => {
|
|
1442
|
+
return createElementVNode("button", {
|
|
1443
|
+
key: option,
|
|
1444
|
+
class: "fl-preview-view-btn",
|
|
1445
|
+
type: "button",
|
|
1446
|
+
"aria-pressed": view.value === option,
|
|
1447
|
+
onClick: ($event) => view.value = option
|
|
1448
|
+
}, toDisplayString(t(option === "source" ? "viewSource" : "viewRendered")), 9, _hoisted_15$1);
|
|
1449
|
+
}), 64))], 8, _hoisted_14$1)) : createCommentVNode("v-if", true)
|
|
1450
|
+
])) : createCommentVNode("v-if", true),
|
|
1451
|
+
createElementVNode("div", _hoisted_16$1, [!__props.demo ? (openBlock(), createElementBlock("button", {
|
|
1312
1452
|
key: 0,
|
|
1313
1453
|
class: "fl-preview-tool fl-preview-download",
|
|
1314
1454
|
type: "button",
|
|
@@ -1318,7 +1458,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1318
1458
|
}, [createVNode(_sfc_main$2, {
|
|
1319
1459
|
name: "download",
|
|
1320
1460
|
size: 17
|
|
1321
|
-
})], 8,
|
|
1461
|
+
})], 8, _hoisted_17$1)) : createCommentVNode("v-if", true), createElementVNode("button", {
|
|
1322
1462
|
class: "fl-preview-tool fl-preview-close",
|
|
1323
1463
|
type: "button",
|
|
1324
1464
|
autofocus: "",
|
|
@@ -1328,18 +1468,18 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1328
1468
|
}, [createVNode(_sfc_main$2, {
|
|
1329
1469
|
name: "close",
|
|
1330
1470
|
size: 18
|
|
1331
|
-
})], 8,
|
|
1471
|
+
})], 8, _hoisted_18$1)])
|
|
1332
1472
|
]), createElementVNode("div", {
|
|
1333
1473
|
class: "fl-preview-body",
|
|
1334
1474
|
"data-mode": busy.value ? "loading" : mode.value
|
|
1335
|
-
}, [busy.value ? (openBlock(), createElementBlock("div",
|
|
1475
|
+
}, [busy.value ? (openBlock(), createElementBlock("div", _hoisted_20$1, toDisplayString(t("loading")), 1)) : mode.value === "image" ? (openBlock(), createElementBlock("img", {
|
|
1336
1476
|
key: 1,
|
|
1337
1477
|
class: normalizeClass(["fl-preview-image", { "fl-preview-actual": actualSize.value }]),
|
|
1338
1478
|
src: __props.src,
|
|
1339
1479
|
alt: __props.file.name,
|
|
1340
1480
|
style: normalizeStyle({ transform: rotation.value ? `rotate(${rotation.value}deg)` : void 0 }),
|
|
1341
1481
|
onError: markFailed
|
|
1342
|
-
}, null, 46,
|
|
1482
|
+
}, null, 46, _hoisted_21$1)) : mode.value === "video" ? (openBlock(), createElementBlock("video", {
|
|
1343
1483
|
key: 2,
|
|
1344
1484
|
class: "fl-preview-video",
|
|
1345
1485
|
src: __props.src,
|
|
@@ -1347,25 +1487,42 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1347
1487
|
playsinline: "",
|
|
1348
1488
|
preload: "metadata",
|
|
1349
1489
|
onError: markFailed
|
|
1350
|
-
}, null, 40,
|
|
1490
|
+
}, null, 40, _hoisted_22$1)) : mode.value === "audio" ? (openBlock(), createElementBlock("audio", {
|
|
1351
1491
|
key: 3,
|
|
1352
1492
|
class: "fl-preview-audio",
|
|
1353
1493
|
src: __props.src,
|
|
1354
1494
|
controls: "",
|
|
1355
1495
|
preload: "metadata",
|
|
1356
1496
|
onError: markFailed
|
|
1357
|
-
}, null, 40,
|
|
1497
|
+
}, null, 40, _hoisted_23$1)) : mode.value === "text" ? (openBlock(), createElementBlock("div", {
|
|
1498
|
+
key: 4,
|
|
1499
|
+
class: normalizeClass(["fl-preview-text", { "fl-preview-rendered": renderAs.value && view.value === "rendered" }])
|
|
1500
|
+
}, [truncated.value ? (openBlock(), createElementBlock("p", _hoisted_24$1, toDisplayString(t("textTruncated", { size: unref(formatSize)(__props.maxTextBytes) })), 1)) : createCommentVNode("v-if", true), renderAs.value === "markdown" && view.value === "rendered" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" sanitised by renderMarkdown (DOMPurify) "), renderedHtml.value ? (openBlock(), createElementBlock("div", {
|
|
1501
|
+
key: 0,
|
|
1502
|
+
class: "vp-doc fl-md",
|
|
1503
|
+
innerHTML: renderedHtml.value
|
|
1504
|
+
}, null, 8, _hoisted_25$1)) : (openBlock(), createElementBlock("div", _hoisted_26$1, toDisplayString(t("loading")), 1))], 64)) : renderAs.value === "html" && view.value === "rendered" ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [
|
|
1505
|
+
createElementVNode("p", _hoisted_27$1, toDisplayString(t("htmlSandboxNote")), 1),
|
|
1506
|
+
createCommentVNode(" empty sandbox: no scripts, no forms, no popups, an opaque origin "),
|
|
1507
|
+
createElementVNode("iframe", {
|
|
1508
|
+
class: "fl-preview-html",
|
|
1509
|
+
sandbox: "",
|
|
1510
|
+
referrerpolicy: "no-referrer",
|
|
1511
|
+
srcdoc: textContent.value,
|
|
1512
|
+
title: __props.file.name
|
|
1513
|
+
}, null, 8, _hoisted_28$1)
|
|
1514
|
+
], 64)) : (openBlock(), createElementBlock("pre", _hoisted_29$1, toDisplayString(textContent.value), 1))], 2)) : mode.value === "pdf" ? (openBlock(), createElementBlock("iframe", {
|
|
1358
1515
|
key: 5,
|
|
1359
1516
|
class: "fl-preview-frame",
|
|
1360
1517
|
src: __props.viewer || blobUrl.value,
|
|
1361
1518
|
title: __props.file.name
|
|
1362
|
-
}, null, 8,
|
|
1519
|
+
}, null, 8, _hoisted_30$1)) : (openBlock(), createElementBlock("div", _hoisted_31$1, [
|
|
1363
1520
|
createElementVNode("span", {
|
|
1364
1521
|
class: "fl-type fl-preview-badge",
|
|
1365
1522
|
"data-kind": unref(fileKind)(__props.file.name),
|
|
1366
1523
|
"aria-hidden": "true"
|
|
1367
|
-
}, toDisplayString(unref(extOf)(__props.file.name) || "FILE"), 9,
|
|
1368
|
-
createElementVNode("p",
|
|
1524
|
+
}, toDisplayString(unref(extOf)(__props.file.name) || "FILE"), 9, _hoisted_32$1),
|
|
1525
|
+
createElementVNode("p", _hoisted_33$1, toDisplayString(infoMessage.value), 1),
|
|
1369
1526
|
!__props.demo ? (openBlock(), createElementBlock("button", {
|
|
1370
1527
|
key: 0,
|
|
1371
1528
|
class: "fl-btn fl-btn-primary",
|
|
@@ -1375,7 +1532,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1375
1532
|
name: "download",
|
|
1376
1533
|
size: 15
|
|
1377
1534
|
}), createTextVNode(" " + toDisplayString(t("download")), 1)])) : createCommentVNode("v-if", true)
|
|
1378
|
-
]))], 8,
|
|
1535
|
+
]))], 8, _hoisted_19$1)])) : createCommentVNode("v-if", true)], 40, _hoisted_1$1);
|
|
1379
1536
|
};
|
|
1380
1537
|
}
|
|
1381
1538
|
});
|
|
@@ -1402,9 +1559,9 @@ function toNodes(items, parent, password) {
|
|
|
1402
1559
|
zipPath: [...parent.zipPath, item.name]
|
|
1403
1560
|
}));
|
|
1404
1561
|
}
|
|
1405
|
-
function sortTree(nodes, key, ascending) {
|
|
1406
|
-
nodes.sort((a, b) => compareNodes(a, b, key, ascending));
|
|
1407
|
-
for (const node of nodes) if (node.children.length) sortTree(node.children, key, ascending);
|
|
1562
|
+
function sortTree(nodes, key, ascending, locale) {
|
|
1563
|
+
nodes.sort((a, b) => compareNodes(a, b, key, ascending, locale));
|
|
1564
|
+
for (const node of nodes) if (node.children.length) sortTree(node.children, key, ascending, locale);
|
|
1408
1565
|
}
|
|
1409
1566
|
/** Loaded files at or under a node. */
|
|
1410
1567
|
function descendantFiles(node) {
|
|
@@ -1720,24 +1877,28 @@ const _hoisted_52 = {
|
|
|
1720
1877
|
const _hoisted_53 = ["data-kind"];
|
|
1721
1878
|
const _hoisted_54 = { class: "fl-label" };
|
|
1722
1879
|
const _hoisted_55 = ["onClick"];
|
|
1723
|
-
const _hoisted_56 = {
|
|
1724
|
-
|
|
1880
|
+
const _hoisted_56 = {
|
|
1881
|
+
key: 0,
|
|
1882
|
+
class: "fl-hit"
|
|
1883
|
+
};
|
|
1884
|
+
const _hoisted_57 = { class: "fl-meta" };
|
|
1885
|
+
const _hoisted_58 = {
|
|
1725
1886
|
key: 5,
|
|
1726
1887
|
class: "fl-count"
|
|
1727
1888
|
};
|
|
1728
|
-
const
|
|
1729
|
-
const
|
|
1889
|
+
const _hoisted_59 = ["id"];
|
|
1890
|
+
const _hoisted_60 = [
|
|
1730
1891
|
"aria-label",
|
|
1731
1892
|
"title",
|
|
1732
1893
|
"onClick"
|
|
1733
1894
|
];
|
|
1734
|
-
const
|
|
1895
|
+
const _hoisted_61 = {
|
|
1735
1896
|
key: 8,
|
|
1736
1897
|
class: "fl-loading",
|
|
1737
1898
|
"aria-hidden": "true"
|
|
1738
1899
|
};
|
|
1739
|
-
const
|
|
1740
|
-
const
|
|
1900
|
+
const _hoisted_62 = ["onSubmit", "onKeydown"];
|
|
1901
|
+
const _hoisted_63 = [
|
|
1741
1902
|
"onUpdate:modelValue",
|
|
1742
1903
|
"data-unlock",
|
|
1743
1904
|
"placeholder",
|
|
@@ -1745,40 +1906,49 @@ const _hoisted_62 = [
|
|
|
1745
1906
|
"aria-invalid",
|
|
1746
1907
|
"aria-describedby"
|
|
1747
1908
|
];
|
|
1748
|
-
const
|
|
1909
|
+
const _hoisted_64 = [
|
|
1749
1910
|
"aria-pressed",
|
|
1750
1911
|
"aria-label",
|
|
1751
1912
|
"title",
|
|
1752
1913
|
"onClick"
|
|
1753
1914
|
];
|
|
1754
|
-
const
|
|
1755
|
-
const
|
|
1915
|
+
const _hoisted_65 = ["disabled"];
|
|
1916
|
+
const _hoisted_66 = [
|
|
1756
1917
|
"aria-label",
|
|
1757
1918
|
"title",
|
|
1758
1919
|
"onClick"
|
|
1759
1920
|
];
|
|
1760
|
-
const
|
|
1761
|
-
const
|
|
1762
|
-
const
|
|
1763
|
-
const
|
|
1764
|
-
const
|
|
1921
|
+
const _hoisted_67 = { class: "fl-cell fl-cell-size" };
|
|
1922
|
+
const _hoisted_68 = { class: "fl-cell fl-cell-date" };
|
|
1923
|
+
const _hoisted_69 = { class: "fl-cell fl-cell-op" };
|
|
1924
|
+
const _hoisted_70 = ["aria-label", "onClick"];
|
|
1925
|
+
const _hoisted_71 = [
|
|
1765
1926
|
"aria-label",
|
|
1766
1927
|
"aria-expanded",
|
|
1767
1928
|
"onClick"
|
|
1768
1929
|
];
|
|
1769
|
-
const
|
|
1930
|
+
const _hoisted_72 = [
|
|
1770
1931
|
"aria-label",
|
|
1771
1932
|
"title",
|
|
1772
1933
|
"onClick"
|
|
1773
1934
|
];
|
|
1774
|
-
const
|
|
1935
|
+
const _hoisted_73 = [
|
|
1775
1936
|
"aria-label",
|
|
1776
1937
|
"title",
|
|
1777
1938
|
"onClick"
|
|
1778
1939
|
];
|
|
1779
|
-
const _hoisted_73 = { class: "fl-foot" };
|
|
1780
1940
|
const _hoisted_74 = {
|
|
1781
1941
|
key: 4,
|
|
1942
|
+
class: "fl-row fl-deep",
|
|
1943
|
+
role: "status"
|
|
1944
|
+
};
|
|
1945
|
+
const _hoisted_75 = { class: "fl-cell fl-more-cell" };
|
|
1946
|
+
const _hoisted_76 = { class: "fl-more-count" };
|
|
1947
|
+
const _hoisted_77 = { class: "fl-more-count" };
|
|
1948
|
+
const _hoisted_78 = { class: "fl-more-count" };
|
|
1949
|
+
const _hoisted_79 = { class: "fl-foot" };
|
|
1950
|
+
const _hoisted_80 = {
|
|
1951
|
+
key: 5,
|
|
1782
1952
|
class: "fl-toast",
|
|
1783
1953
|
role: "status"
|
|
1784
1954
|
};
|
|
@@ -1802,6 +1972,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1802
1972
|
default: true
|
|
1803
1973
|
},
|
|
1804
1974
|
pageSize: { default: 200 },
|
|
1975
|
+
deepSearchLimit: { default: 500 },
|
|
1805
1976
|
zipWarnSize: {},
|
|
1806
1977
|
zipMaxSize: {},
|
|
1807
1978
|
locale: { default: "zh-CN" },
|
|
@@ -1866,7 +2037,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1866
2037
|
return isNetworkError(error) ? `${message} ${t("corsHint")}` : message;
|
|
1867
2038
|
}
|
|
1868
2039
|
function sortNodes(nodes) {
|
|
1869
|
-
sortTree(nodes, sortKey.value, sortAsc.value);
|
|
2040
|
+
sortTree(nodes, sortKey.value, sortAsc.value, props.locale);
|
|
1870
2041
|
}
|
|
1871
2042
|
function focusUnlock(path) {
|
|
1872
2043
|
nextTick(() => {
|
|
@@ -1971,7 +2142,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1971
2142
|
* Load a folder's children. A password error locks the folder instead of
|
|
1972
2143
|
* toasting; `prompt` also shows its same-row input (and focuses it).
|
|
1973
2144
|
*/
|
|
1974
|
-
async function loadChildren(node, prompt = false) {
|
|
2145
|
+
async function loadChildren(node, prompt = false, quiet = false) {
|
|
1975
2146
|
if (node.loaded) return true;
|
|
1976
2147
|
if (node.lock) {
|
|
1977
2148
|
if (prompt) startPrompt(node, prompt === "focus");
|
|
@@ -1987,7 +2158,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1987
2158
|
node.expanded = false;
|
|
1988
2159
|
node.lock = newLock(false, passwords.dropStale(rememberedAt) ? "stale" : "");
|
|
1989
2160
|
if (prompt) startPrompt(node, prompt === "focus");
|
|
1990
|
-
} else showToast(failureMessage("folderFailed", error));
|
|
2161
|
+
} else if (!quiet) showToast(failureMessage("folderFailed", error));
|
|
1991
2162
|
return false;
|
|
1992
2163
|
} finally {
|
|
1993
2164
|
node.loading = false;
|
|
@@ -2074,6 +2245,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2074
2245
|
}
|
|
2075
2246
|
/** First load, or a retry after an error: shows the loading state. */
|
|
2076
2247
|
async function loadRoot() {
|
|
2248
|
+
stopDeepSearch();
|
|
2077
2249
|
refreshSeq += 1;
|
|
2078
2250
|
refreshing.value = false;
|
|
2079
2251
|
loadingRoot.value = true;
|
|
@@ -2097,6 +2269,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2097
2269
|
*/
|
|
2098
2270
|
async function refresh() {
|
|
2099
2271
|
if (refreshing.value || loadingRoot.value) return;
|
|
2272
|
+
stopDeepSearch();
|
|
2100
2273
|
if (!roots.value.length || errorMsg.value || rootLock.value) {
|
|
2101
2274
|
await loadRoot();
|
|
2102
2275
|
return;
|
|
@@ -2196,6 +2369,124 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2196
2369
|
return out;
|
|
2197
2370
|
});
|
|
2198
2371
|
const visible = computed(() => rows.value.filter((row) => !isMore(row)));
|
|
2372
|
+
/** A name split around the filter keyword, for highlighting. */
|
|
2373
|
+
function nameParts(name) {
|
|
2374
|
+
const query = keyword.value.trim().toLowerCase();
|
|
2375
|
+
if (!query) return [{
|
|
2376
|
+
text: name,
|
|
2377
|
+
hit: false
|
|
2378
|
+
}];
|
|
2379
|
+
const parts = [];
|
|
2380
|
+
const lower = name.toLowerCase();
|
|
2381
|
+
let from = 0;
|
|
2382
|
+
for (let at = lower.indexOf(query); at !== -1; at = lower.indexOf(query, from)) {
|
|
2383
|
+
if (at > from) parts.push({
|
|
2384
|
+
text: name.slice(from, at),
|
|
2385
|
+
hit: false
|
|
2386
|
+
});
|
|
2387
|
+
parts.push({
|
|
2388
|
+
text: name.slice(at, at + query.length),
|
|
2389
|
+
hit: true
|
|
2390
|
+
});
|
|
2391
|
+
from = at + query.length;
|
|
2392
|
+
}
|
|
2393
|
+
if (from < name.length) parts.push({
|
|
2394
|
+
text: name.slice(from),
|
|
2395
|
+
hit: false
|
|
2396
|
+
});
|
|
2397
|
+
return parts;
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* The filter only sees loaded folders. Deep search loads the rest under the
|
|
2401
|
+
* mounted paths (four at a time, `deepSearchLimit` per run) so matches appear
|
|
2402
|
+
* in place, in their folders, like any other row. Locked folders are skipped.
|
|
2403
|
+
*/
|
|
2404
|
+
const deep = reactive({
|
|
2405
|
+
running: false,
|
|
2406
|
+
searched: 0,
|
|
2407
|
+
skipped: 0,
|
|
2408
|
+
failed: 0,
|
|
2409
|
+
capped: false,
|
|
2410
|
+
done: false
|
|
2411
|
+
});
|
|
2412
|
+
let deepSeq = 0;
|
|
2413
|
+
/** Folders that are known but not loaded yet (locked ones excluded). */
|
|
2414
|
+
function unloadedFolders() {
|
|
2415
|
+
const out = [];
|
|
2416
|
+
const walk = (nodes) => {
|
|
2417
|
+
for (const node of nodes) {
|
|
2418
|
+
if (!node.is_dir || node.lock) continue;
|
|
2419
|
+
if (!node.loaded) out.push(node);
|
|
2420
|
+
else walk(node.children);
|
|
2421
|
+
}
|
|
2422
|
+
};
|
|
2423
|
+
walk(roots.value);
|
|
2424
|
+
return out;
|
|
2425
|
+
}
|
|
2426
|
+
const hasUnloaded = computed(() => unloadedFolders().length > 0);
|
|
2427
|
+
const deepRow = computed(() => {
|
|
2428
|
+
if (!keyword.value.trim()) return null;
|
|
2429
|
+
if (deep.running) return "running";
|
|
2430
|
+
if (deep.capped) return "capped";
|
|
2431
|
+
if (hasUnloaded.value) return "idle";
|
|
2432
|
+
return deep.done ? "done" : null;
|
|
2433
|
+
});
|
|
2434
|
+
async function deepSearch() {
|
|
2435
|
+
const seq = ++deepSeq;
|
|
2436
|
+
const limit = Math.max(1, Math.floor(props.deepSearchLimit ?? 500));
|
|
2437
|
+
const runStart = deep.searched;
|
|
2438
|
+
Object.assign(deep, {
|
|
2439
|
+
running: true,
|
|
2440
|
+
capped: false,
|
|
2441
|
+
done: false,
|
|
2442
|
+
skipped: 0,
|
|
2443
|
+
failed: 0
|
|
2444
|
+
});
|
|
2445
|
+
let wave = unloadedFolders();
|
|
2446
|
+
while (wave.length && seq === deepSeq) {
|
|
2447
|
+
const next = [];
|
|
2448
|
+
const queue = [...wave];
|
|
2449
|
+
const worker = async () => {
|
|
2450
|
+
for (let node = queue.shift(); node && seq === deepSeq; node = queue.shift()) {
|
|
2451
|
+
if (deep.searched - runStart >= limit) return;
|
|
2452
|
+
deep.searched += 1;
|
|
2453
|
+
if (!await loadChildren(node, false, true)) {
|
|
2454
|
+
if (node.lock) deep.skipped += 1;
|
|
2455
|
+
else deep.failed += 1;
|
|
2456
|
+
continue;
|
|
2457
|
+
}
|
|
2458
|
+
for (const child of node.children) if (child.is_dir && !child.loaded && !child.lock) next.push(child);
|
|
2459
|
+
}
|
|
2460
|
+
};
|
|
2461
|
+
await Promise.all(Array.from({ length: Math.min(4, queue.length) }, worker));
|
|
2462
|
+
if (deep.searched - runStart >= limit) break;
|
|
2463
|
+
wave = next;
|
|
2464
|
+
}
|
|
2465
|
+
if (seq !== deepSeq) return;
|
|
2466
|
+
deep.running = false;
|
|
2467
|
+
deep.capped = hasUnloaded.value && deep.searched - runStart >= limit;
|
|
2468
|
+
deep.done = !deep.capped;
|
|
2469
|
+
}
|
|
2470
|
+
function stopDeepSearch() {
|
|
2471
|
+
deepSeq += 1;
|
|
2472
|
+
deep.running = false;
|
|
2473
|
+
}
|
|
2474
|
+
watch(keyword, (value) => {
|
|
2475
|
+
if (value.trim()) return;
|
|
2476
|
+
stopDeepSearch();
|
|
2477
|
+
Object.assign(deep, {
|
|
2478
|
+
searched: 0,
|
|
2479
|
+
skipped: 0,
|
|
2480
|
+
failed: 0,
|
|
2481
|
+
capped: false,
|
|
2482
|
+
done: false
|
|
2483
|
+
});
|
|
2484
|
+
});
|
|
2485
|
+
const deepSummary = computed(() => [
|
|
2486
|
+
deepRow.value === "capped" ? t("deepCapped", { count: deep.searched }) : t("deepDone", { count: deep.searched }),
|
|
2487
|
+
deep.skipped ? t("deepSkipped", { count: deep.skipped }) : "",
|
|
2488
|
+
deep.failed ? t("deepFailed", { count: deep.failed }) : ""
|
|
2489
|
+
].filter(Boolean).join(" · "));
|
|
2199
2490
|
const stats = computed(() => folderStats(roots.value, selected.value));
|
|
2200
2491
|
const noStats = {
|
|
2201
2492
|
files: 0,
|
|
@@ -2298,9 +2589,28 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2298
2589
|
max: bytes(props.zipMaxSize, touch ? 1024 : 2048)
|
|
2299
2590
|
};
|
|
2300
2591
|
}
|
|
2592
|
+
/** A ZIP question or notice: over the warning size, over the limit, or failed. */
|
|
2301
2593
|
const zipNotice = ref(null);
|
|
2302
2594
|
/** Only desktop browsers get “use Chrome or Edge”; on iPhone every browser is Safari. */
|
|
2303
2595
|
const zipTip = computed(() => typeof window !== "undefined" && !window.matchMedia?.("(pointer: coarse)").matches ? t("zipStreamTip") : "");
|
|
2596
|
+
const zipDialog = computed(() => {
|
|
2597
|
+
const notice = zipNotice.value;
|
|
2598
|
+
if (!notice) return null;
|
|
2599
|
+
if (notice.kind === "failed") return {
|
|
2600
|
+
title: t("batchFailedTitle"),
|
|
2601
|
+
text: notice.message,
|
|
2602
|
+
tip: t("batchFailedHint"),
|
|
2603
|
+
cancel: t("gotIt")
|
|
2604
|
+
};
|
|
2605
|
+
const warn = notice.kind === "warn";
|
|
2606
|
+
return {
|
|
2607
|
+
title: t(warn ? "zipWarnTitle" : "zipBlockTitle", { size: formatSize(notice.size) }),
|
|
2608
|
+
text: t(warn ? "zipWarnText" : "zipBlockText", { limit: formatSize(notice.limit) }),
|
|
2609
|
+
tip: zipTip.value,
|
|
2610
|
+
confirm: warn ? t("zipContinue") : void 0,
|
|
2611
|
+
cancel: warn ? t("cancel") : t("gotIt")
|
|
2612
|
+
};
|
|
2613
|
+
});
|
|
2304
2614
|
function zipName() {
|
|
2305
2615
|
return zipFileName(props.title || t("defaultZip"));
|
|
2306
2616
|
}
|
|
@@ -2369,7 +2679,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2369
2679
|
}));
|
|
2370
2680
|
selected.value = /* @__PURE__ */ new Set();
|
|
2371
2681
|
} catch (error) {
|
|
2372
|
-
|
|
2682
|
+
if (controller.signal.aborted) showToast(t("batchCanceled"));
|
|
2683
|
+
else zipNotice.value = {
|
|
2684
|
+
kind: "failed",
|
|
2685
|
+
message: errorText(error)
|
|
2686
|
+
};
|
|
2373
2687
|
} finally {
|
|
2374
2688
|
zipAbort = null;
|
|
2375
2689
|
batchBusy.value = false;
|
|
@@ -2709,14 +3023,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2709
3023
|
createElementVNode("span", _hoisted_54, [createElementVNode("span", {
|
|
2710
3024
|
class: normalizeClass(["fl-name", { "fl-name-dir": node.is_dir }]),
|
|
2711
3025
|
onClick: ($event) => node.lock || node.is_dir ? toggleExpand(node) : download(node)
|
|
2712
|
-
},
|
|
2713
|
-
|
|
3026
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(nameParts(node.name), (part, at) => {
|
|
3027
|
+
return openBlock(), createElementBlock(Fragment, { key: at }, [part.hit ? (openBlock(), createElementBlock("mark", _hoisted_56, toDisplayString(part.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
3028
|
+
}), 128))], 10, _hoisted_55), createElementVNode("span", _hoisted_57, [!node.is_dir ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(unref(formatSize)(node.size)) + " · " + toDisplayString(unref(formatDate)(node.modified)), 1)], 64)) : node.loaded ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(t("folderItems", { count: statsOf(node).files })) + " · " + toDisplayString(unref(formatSize)(statsOf(node).bytes)), 1)], 64)) : createCommentVNode("v-if", true)])]),
|
|
3029
|
+
node.is_dir && node.loaded ? (openBlock(), createElementBlock("span", _hoisted_58, toDisplayString(t("folderItems", { count: statsOf(node).files })), 1)) : createCommentVNode("v-if", true),
|
|
2714
3030
|
node.lock?.prompting && node.lock.error ? (openBlock(), createElementBlock("span", {
|
|
2715
3031
|
key: 6,
|
|
2716
3032
|
id: `${unref(uid)}-error-${index}`,
|
|
2717
3033
|
class: "fl-unlock-message",
|
|
2718
3034
|
role: "alert"
|
|
2719
|
-
}, toDisplayString(unlockErrorText(node.lock.error)), 9,
|
|
3035
|
+
}, toDisplayString(unlockErrorText(node.lock.error)), 9, _hoisted_59)) : createCommentVNode("v-if", true),
|
|
2720
3036
|
!node.lock && unref(remembered).has(node.path) ? (openBlock(), createElementBlock("button", {
|
|
2721
3037
|
key: 7,
|
|
2722
3038
|
class: "fl-remembered",
|
|
@@ -2727,8 +3043,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2727
3043
|
}, [createVNode(_sfc_main$2, {
|
|
2728
3044
|
name: "key",
|
|
2729
3045
|
size: 11
|
|
2730
|
-
}), createTextVNode(" " + toDisplayString(t("remembered")) + " · " + toDisplayString(t("forgetPassword")), 1)], 8,
|
|
2731
|
-
node.loading ? (openBlock(), createElementBlock("span",
|
|
3046
|
+
}), createTextVNode(" " + toDisplayString(t("remembered")) + " · " + toDisplayString(t("forgetPassword")), 1)], 8, _hoisted_60)) : createCommentVNode("v-if", true),
|
|
3047
|
+
node.loading ? (openBlock(), createElementBlock("span", _hoisted_61, "…")) : createCommentVNode("v-if", true)
|
|
2732
3048
|
], 8, _hoisted_48),
|
|
2733
3049
|
node.lock?.prompting ? (openBlock(), createElementBlock("form", {
|
|
2734
3050
|
key: 0,
|
|
@@ -2750,7 +3066,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2750
3066
|
"aria-label": t("passwordFor", { name: node.name }),
|
|
2751
3067
|
"aria-invalid": !!node.lock.error,
|
|
2752
3068
|
"aria-describedby": node.lock.error ? `${unref(uid)}-error-${index}` : void 0
|
|
2753
|
-
}, null, 8,
|
|
3069
|
+
}, null, 8, _hoisted_63), [[vModelText, node.lock.input]]),
|
|
2754
3070
|
canRemember.value ? (openBlock(), createElementBlock("button", {
|
|
2755
3071
|
key: 0,
|
|
2756
3072
|
class: "fl-unlock-remember",
|
|
@@ -2759,12 +3075,12 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2759
3075
|
"aria-label": t("rememberPassword"),
|
|
2760
3076
|
title: t("rememberPassword"),
|
|
2761
3077
|
onClick: ($event) => node.lock.remember = !node.lock.remember
|
|
2762
|
-
}, [createVNode(_sfc_main$2, { name: "key" })], 8,
|
|
3078
|
+
}, [createVNode(_sfc_main$2, { name: "key" })], 8, _hoisted_64)) : createCommentVNode("v-if", true),
|
|
2763
3079
|
createElementVNode("button", {
|
|
2764
3080
|
class: "fl-unlock-submit",
|
|
2765
3081
|
type: "submit",
|
|
2766
3082
|
disabled: node.lock.busy || !node.lock.input
|
|
2767
|
-
}, toDisplayString(t("open")), 9,
|
|
3083
|
+
}, toDisplayString(t("open")), 9, _hoisted_65),
|
|
2768
3084
|
createElementVNode("button", {
|
|
2769
3085
|
class: "fl-unlock-cancel",
|
|
2770
3086
|
type: "button",
|
|
@@ -2774,11 +3090,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2774
3090
|
}, [createVNode(_sfc_main$2, {
|
|
2775
3091
|
name: "close",
|
|
2776
3092
|
size: 14
|
|
2777
|
-
})], 8,
|
|
2778
|
-
], 40,
|
|
2779
|
-
createElementVNode("span",
|
|
2780
|
-
createElementVNode("span",
|
|
2781
|
-
createElementVNode("span",
|
|
3093
|
+
})], 8, _hoisted_66)
|
|
3094
|
+
], 40, _hoisted_62)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
3095
|
+
createElementVNode("span", _hoisted_67, toDisplayString(node.is_dir ? "—" : unref(formatSize)(node.size)), 1),
|
|
3096
|
+
createElementVNode("span", _hoisted_68, toDisplayString(unref(formatDate)(node.modified)), 1),
|
|
3097
|
+
createElementVNode("span", _hoisted_69, [node.lock ? (openBlock(), createElementBlock("button", {
|
|
2782
3098
|
key: 0,
|
|
2783
3099
|
class: "fl-action fl-arrow-end fl-lock",
|
|
2784
3100
|
type: "button",
|
|
@@ -2787,7 +3103,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2787
3103
|
}, [createVNode(_sfc_main$2, {
|
|
2788
3104
|
name: "lock",
|
|
2789
3105
|
size: 18
|
|
2790
|
-
})], 8,
|
|
3106
|
+
})], 8, _hoisted_70)) : node.is_dir ? (openBlock(), createElementBlock("button", {
|
|
2791
3107
|
key: 1,
|
|
2792
3108
|
class: normalizeClass(["fl-action fl-arrow-end", { "fl-arrow-open": node.expanded }]),
|
|
2793
3109
|
type: "button",
|
|
@@ -2797,32 +3113,56 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2797
3113
|
}, [createVNode(_sfc_main$2, {
|
|
2798
3114
|
name: "next",
|
|
2799
3115
|
size: 18
|
|
2800
|
-
})], 10,
|
|
3116
|
+
})], 10, _hoisted_71)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [canPreview(node) ? (openBlock(), createElementBlock("button", {
|
|
2801
3117
|
key: 0,
|
|
2802
3118
|
class: "fl-action fl-preview-btn",
|
|
2803
3119
|
type: "button",
|
|
2804
3120
|
"aria-label": `${t("preview")} ${node.name}`,
|
|
2805
3121
|
title: t("preview"),
|
|
2806
3122
|
onClick: ($event) => preview(node)
|
|
2807
|
-
}, [createVNode(_sfc_main$2, { name: "eye" })], 8,
|
|
3123
|
+
}, [createVNode(_sfc_main$2, { name: "eye" })], 8, _hoisted_72)) : createCommentVNode("v-if", true), createElementVNode("button", {
|
|
2808
3124
|
class: "fl-action fl-download",
|
|
2809
3125
|
type: "button",
|
|
2810
3126
|
"aria-label": `${t("download")} ${node.name}`,
|
|
2811
3127
|
title: t("download"),
|
|
2812
3128
|
onClick: ($event) => download(node)
|
|
2813
|
-
}, [createVNode(_sfc_main$2, { name: "download" })], 8,
|
|
3129
|
+
}, [createVNode(_sfc_main$2, { name: "download" })], 8, _hoisted_73)], 64))])
|
|
2814
3130
|
], 64))
|
|
2815
3131
|
], 6))], 64);
|
|
2816
3132
|
}), 128)),
|
|
2817
|
-
|
|
2818
|
-
|
|
3133
|
+
deepRow.value ? (openBlock(), createElementBlock("div", _hoisted_74, [_cache[14] || (_cache[14] = createElementVNode("span", { class: "fl-cell fl-cell-check" }, null, -1)), createElementVNode("span", _hoisted_75, [_cache[13] || (_cache[13] = createElementVNode("span", {
|
|
3134
|
+
class: "fl-indent",
|
|
3135
|
+
"aria-hidden": "true"
|
|
3136
|
+
}, null, -1)), deepRow.value === "running" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
3137
|
+
createElementVNode("span", _hoisted_76, toDisplayString(t("deepRunning", { count: deep.searched })), 1),
|
|
3138
|
+
createElementVNode("button", {
|
|
3139
|
+
class: "fl-more-btn fl-more-all",
|
|
3140
|
+
type: "button",
|
|
3141
|
+
onClick: stopDeepSearch
|
|
3142
|
+
}, toDisplayString(t("deepStop")), 1),
|
|
3143
|
+
_cache[12] || (_cache[12] = createElementVNode("span", {
|
|
3144
|
+
class: "fl-deep-bar",
|
|
3145
|
+
"aria-hidden": "true"
|
|
3146
|
+
}, null, -1))
|
|
3147
|
+
], 64)) : deepRow.value === "idle" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createElementVNode("span", _hoisted_77, toDisplayString(deep.searched ? deepSummary.value : t("deepHint")), 1), createElementVNode("button", {
|
|
3148
|
+
class: "fl-more-btn",
|
|
3149
|
+
type: "button",
|
|
3150
|
+
onClick: deepSearch
|
|
3151
|
+
}, toDisplayString(t("deepSearch")), 1)], 64)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [createElementVNode("span", _hoisted_78, toDisplayString(deepSummary.value), 1), deepRow.value === "capped" ? (openBlock(), createElementBlock("button", {
|
|
3152
|
+
key: 0,
|
|
3153
|
+
class: "fl-more-btn",
|
|
3154
|
+
type: "button",
|
|
3155
|
+
onClick: deepSearch
|
|
3156
|
+
}, toDisplayString(t("deepContinue")), 1)) : createCommentVNode("v-if", true)], 64))])])) : createCommentVNode("v-if", true),
|
|
3157
|
+
createElementVNode("div", _hoisted_79, [createElementVNode("span", null, toDisplayString(t("loaded", { count: allFiles.value.length })) + " · " + toDisplayString(t("totalSize", { size: unref(formatSize)(unref(totalSize)(allFiles.value)) })), 1)]),
|
|
3158
|
+
toast.value ? (openBlock(), createElementBlock("div", _hoisted_80, toDisplayString(toast.value), 1)) : createCommentVNode("v-if", true),
|
|
2819
3159
|
createVNode(_sfc_main$3, {
|
|
2820
|
-
open: !!
|
|
2821
|
-
title:
|
|
2822
|
-
text:
|
|
2823
|
-
tip:
|
|
2824
|
-
"confirm-label":
|
|
2825
|
-
"cancel-label":
|
|
3160
|
+
open: !!zipDialog.value,
|
|
3161
|
+
title: zipDialog.value?.title ?? "",
|
|
3162
|
+
text: zipDialog.value?.text ?? "",
|
|
3163
|
+
tip: zipDialog.value?.tip,
|
|
3164
|
+
"confirm-label": zipDialog.value?.confirm,
|
|
3165
|
+
"cancel-label": zipDialog.value?.cancel ?? "",
|
|
2826
3166
|
onConfirm: confirmZip,
|
|
2827
3167
|
onClose: _cache[4] || (_cache[4] = ($event) => zipNotice.value = null)
|
|
2828
3168
|
}, null, 8, [
|