vitepress-plugin-filelist 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -2
- package/dist/client/browser/index.d.ts +35 -1
- package/dist/client/browser/index.js +622 -214
- package/dist/client/ssr/index.d.ts +35 -1
- package/dist/client/ssr/index.js +362 -23
- package/dist/client/style.css +175 -1
- 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
|
+
deepLocked: "未搜索(需要密码)",
|
|
929
|
+
clearFilter: "清除筛选",
|
|
930
|
+
viewMode: "显示方式",
|
|
931
|
+
viewSource: "源码",
|
|
932
|
+
viewRendered: "渲染",
|
|
933
|
+
htmlSandboxNote: "已禁用脚本;网页引用的相对路径资源不会加载",
|
|
934
|
+
imageOmitted: "[图片:{alt}]",
|
|
902
935
|
batchFailedTitle: "打包失败",
|
|
903
936
|
batchFailedHint: "可以减少勾选的文件后重试。",
|
|
904
937
|
downloadStarted: "已交给浏览器下载:{name}",
|
|
@@ -979,6 +1012,22 @@ const fileListLocales = {
|
|
|
979
1012
|
forgetPassword: "Forget",
|
|
980
1013
|
unlockFolder: "Enter the password for {name}",
|
|
981
1014
|
networkHint: "Possibly blocked by CORS: check cors.allow_origins in OpenList, or ALLOW_URLS in OpenList-Worker.",
|
|
1015
|
+
deepHint: "Only loaded folders are filtered. ",
|
|
1016
|
+
deepSearch: "Try deep search",
|
|
1017
|
+
deepRunning: "Searching · {count} folders checked",
|
|
1018
|
+
deepStop: "Stop",
|
|
1019
|
+
deepDone: "Searched all {count} folders",
|
|
1020
|
+
deepCapped: "{count} folders checked; some sub-folders remain",
|
|
1021
|
+
deepContinue: "Continue",
|
|
1022
|
+
deepSkipped: "{count} locked folders skipped",
|
|
1023
|
+
deepFailed: "{count} folders could not be read",
|
|
1024
|
+
deepLocked: "Not searched (password needed)",
|
|
1025
|
+
clearFilter: "Clear filter",
|
|
1026
|
+
viewMode: "View",
|
|
1027
|
+
viewSource: "Source",
|
|
1028
|
+
viewRendered: "Rendered",
|
|
1029
|
+
htmlSandboxNote: "Scripts are disabled; resources at relative paths do not load",
|
|
1030
|
+
imageOmitted: "[Image: {alt}]",
|
|
982
1031
|
batchFailedTitle: "Could not create the ZIP",
|
|
983
1032
|
batchFailedHint: "Try again with fewer files selected.",
|
|
984
1033
|
downloadStarted: "Downloading {name} in your browser",
|
|
@@ -1007,6 +1056,45 @@ function resolveLabels(locale = "zh-CN", overrides) {
|
|
|
1007
1056
|
...overrides
|
|
1008
1057
|
};
|
|
1009
1058
|
}
|
|
1059
|
+
// src/client/markdown.ts
|
|
1060
|
+
/**
|
|
1061
|
+
* Markdown → safe HTML for the preview dialog. Loaded on demand, so pages that
|
|
1062
|
+
* never render Markdown do not download markdown-it or DOMPurify.
|
|
1063
|
+
*
|
|
1064
|
+
* - Raw HTML in the file is allowed, then sanitised (no scripts, handlers, iframes...)
|
|
1065
|
+
* - Only absolute http(s) / mailto links stay clickable, opening in a new tab;
|
|
1066
|
+
* relative links (to other files in the share) become plain text
|
|
1067
|
+
* - Images with a relative source are replaced by their alt text, because the
|
|
1068
|
+
* share's relative paths do not resolve on the documentation site
|
|
1069
|
+
*/
|
|
1070
|
+
async function renderMarkdown(source, imageLabel) {
|
|
1071
|
+
const [{ default: MarkdownIt }, { default: DOMPurify }] = await Promise.all([import("markdown-it"), import("dompurify")]);
|
|
1072
|
+
const probe = DOMPurify.isSupported ? DOMPurify.sanitize("<img src=\"x\" onerror=\"1\"><script>1<\/script>") : "<script";
|
|
1073
|
+
if (/<script|onerror/i.test(probe)) throw new Error("HTML sanitiser unavailable");
|
|
1074
|
+
const html = new MarkdownIt({
|
|
1075
|
+
html: true,
|
|
1076
|
+
linkify: true
|
|
1077
|
+
}).render(source);
|
|
1078
|
+
const template = document.createElement("template");
|
|
1079
|
+
template.innerHTML = DOMPurify.sanitize(html);
|
|
1080
|
+
const fragment = template.content;
|
|
1081
|
+
for (const link of fragment.querySelectorAll("a")) {
|
|
1082
|
+
const href = link.getAttribute("href") ?? "";
|
|
1083
|
+
if (/^(https?:|mailto:)/i.test(href)) {
|
|
1084
|
+
link.setAttribute("target", "_blank");
|
|
1085
|
+
link.setAttribute("rel", "noopener noreferrer");
|
|
1086
|
+
} else link.replaceWith(...link.childNodes);
|
|
1087
|
+
}
|
|
1088
|
+
for (const image of fragment.querySelectorAll("img")) {
|
|
1089
|
+
const src = image.getAttribute("src") ?? "";
|
|
1090
|
+
if (/^(https?:|data:image\/)/i.test(src)) continue;
|
|
1091
|
+
const placeholder = document.createElement("span");
|
|
1092
|
+
placeholder.className = "fl-md-image";
|
|
1093
|
+
placeholder.textContent = imageLabel(image.getAttribute("alt") || src.split("/").pop() || "");
|
|
1094
|
+
image.replaceWith(placeholder);
|
|
1095
|
+
}
|
|
1096
|
+
return template.innerHTML;
|
|
1097
|
+
}
|
|
1010
1098
|
// src/client/PreviewDialog.vue
|
|
1011
1099
|
const _hoisted_1$1 = ["aria-label"];
|
|
1012
1100
|
const _hoisted_2$1 = {
|
|
@@ -1031,33 +1119,40 @@ const _hoisted_13$1 = [
|
|
|
1031
1119
|
"aria-label",
|
|
1032
1120
|
"title"
|
|
1033
1121
|
];
|
|
1034
|
-
const _hoisted_14$1 =
|
|
1035
|
-
const _hoisted_15$1 = ["aria-
|
|
1036
|
-
const _hoisted_16$1 =
|
|
1037
|
-
const _hoisted_17$1 = ["
|
|
1038
|
-
const _hoisted_18$1 =
|
|
1122
|
+
const _hoisted_14$1 = ["aria-label"];
|
|
1123
|
+
const _hoisted_15$1 = ["aria-pressed", "onClick"];
|
|
1124
|
+
const _hoisted_16$1 = { class: "fl-preview-tools fl-preview-actions" };
|
|
1125
|
+
const _hoisted_17$1 = ["aria-label", "title"];
|
|
1126
|
+
const _hoisted_18$1 = ["aria-label", "title"];
|
|
1127
|
+
const _hoisted_19$1 = ["data-mode"];
|
|
1128
|
+
const _hoisted_20$1 = {
|
|
1039
1129
|
key: 0,
|
|
1040
1130
|
class: "fl-preview-status",
|
|
1041
1131
|
role: "status"
|
|
1042
1132
|
};
|
|
1043
|
-
const
|
|
1044
|
-
const
|
|
1045
|
-
const
|
|
1046
|
-
const
|
|
1047
|
-
key: 4,
|
|
1048
|
-
class: "fl-preview-text"
|
|
1049
|
-
};
|
|
1050
|
-
const _hoisted_23$1 = {
|
|
1133
|
+
const _hoisted_21$1 = ["src", "alt"];
|
|
1134
|
+
const _hoisted_22$1 = ["src"];
|
|
1135
|
+
const _hoisted_23$1 = ["src"];
|
|
1136
|
+
const _hoisted_24$1 = {
|
|
1051
1137
|
key: 0,
|
|
1052
1138
|
class: "fl-preview-note"
|
|
1053
1139
|
};
|
|
1054
|
-
const
|
|
1055
|
-
const
|
|
1140
|
+
const _hoisted_25$1 = ["innerHTML"];
|
|
1141
|
+
const _hoisted_26$1 = {
|
|
1142
|
+
key: 1,
|
|
1143
|
+
class: "fl-preview-status",
|
|
1144
|
+
role: "status"
|
|
1145
|
+
};
|
|
1146
|
+
const _hoisted_27$1 = { class: "fl-preview-note" };
|
|
1147
|
+
const _hoisted_28$1 = ["srcdoc", "title"];
|
|
1148
|
+
const _hoisted_29$1 = { key: 3 };
|
|
1149
|
+
const _hoisted_30$1 = ["src", "title"];
|
|
1150
|
+
const _hoisted_31$1 = {
|
|
1056
1151
|
key: 6,
|
|
1057
1152
|
class: "fl-preview-info"
|
|
1058
1153
|
};
|
|
1059
|
-
const
|
|
1060
|
-
const
|
|
1154
|
+
const _hoisted_32$1 = ["data-kind"];
|
|
1155
|
+
const _hoisted_33$1 = { class: "fl-preview-message" };
|
|
1061
1156
|
const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
1062
1157
|
__name: "PreviewDialog",
|
|
1063
1158
|
props: {
|
|
@@ -1090,7 +1185,9 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1090
1185
|
/**
|
|
1091
1186
|
* File preview in a native <dialog>, without leaving the page.
|
|
1092
1187
|
*
|
|
1093
|
-
* image / video / audio: native elements; text: the first `maxTextBytes
|
|
1188
|
+
* image / video / audio: native elements; text: the first `maxTextBytes`,
|
|
1189
|
+
* with Markdown and HTML switchable between source (default) and rendered
|
|
1190
|
+
* (Markdown sanitised in VitePress' doc style, HTML in a script-less sandbox);
|
|
1094
1191
|
* through a Range request; pdf: the first matching OpenList `iframe_previews`
|
|
1095
1192
|
* viewer, else the browser's reader through a Blob. Anything else (Office
|
|
1096
1193
|
* included), failures, oversized PDFs and demo mode: file info + download.
|
|
@@ -1109,8 +1206,18 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1109
1206
|
const blobUrl = ref("");
|
|
1110
1207
|
const rotation = ref(0);
|
|
1111
1208
|
const actualSize = ref(false);
|
|
1209
|
+
/** Markdown / HTML: show the source or render it. Kept while the page is open. */
|
|
1210
|
+
const view = ref("source");
|
|
1211
|
+
const renderedHtml = ref("");
|
|
1112
1212
|
let controller;
|
|
1113
1213
|
let loadSeq = 0;
|
|
1214
|
+
/** Text files that can also be rendered. */
|
|
1215
|
+
const renderAs = computed(() => {
|
|
1216
|
+
const name = props.file?.name ?? "";
|
|
1217
|
+
if (/\.(md|markdown)$/i.test(name)) return "markdown";
|
|
1218
|
+
if (/\.html?$/i.test(name)) return "html";
|
|
1219
|
+
return null;
|
|
1220
|
+
});
|
|
1114
1221
|
const tooLarge = computed(() => kind.value === "pdf" && !props.viewer && (props.file?.size ?? 0) > props.maxPdfBytes);
|
|
1115
1222
|
const mode = computed(() => {
|
|
1116
1223
|
if (props.demo) return "demo";
|
|
@@ -1120,6 +1227,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1120
1227
|
return kind.value;
|
|
1121
1228
|
});
|
|
1122
1229
|
const busy = computed(() => !props.demo && kind.value !== "none" && !props.error && !bodyFailed.value && (props.resolving || loadingBody.value || !props.src && !tooLarge.value));
|
|
1230
|
+
const canRender = computed(() => !!renderAs.value && mode.value === "text" && !busy.value);
|
|
1123
1231
|
const infoMessage = computed(() => {
|
|
1124
1232
|
const file = props.file;
|
|
1125
1233
|
if (!file) return "";
|
|
@@ -1140,6 +1248,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1140
1248
|
loadingBody.value = false;
|
|
1141
1249
|
bodyFailed.value = false;
|
|
1142
1250
|
textContent.value = "";
|
|
1251
|
+
renderedHtml.value = "";
|
|
1143
1252
|
truncated.value = false;
|
|
1144
1253
|
rotation.value = 0;
|
|
1145
1254
|
actualSize.value = false;
|
|
@@ -1221,6 +1330,20 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1221
1330
|
props.error,
|
|
1222
1331
|
props.demo
|
|
1223
1332
|
], () => void loadBody(), { immediate: true });
|
|
1333
|
+
watch(() => [
|
|
1334
|
+
view.value,
|
|
1335
|
+
renderAs.value,
|
|
1336
|
+
textContent.value
|
|
1337
|
+
], async ([current, as, text]) => {
|
|
1338
|
+
if (current !== "rendered" || as !== "markdown" || !text) return;
|
|
1339
|
+
const seq = loadSeq;
|
|
1340
|
+
try {
|
|
1341
|
+
const html = await renderMarkdown(text, (alt) => t("imageOmitted", { alt }));
|
|
1342
|
+
if (seq === loadSeq) renderedHtml.value = html;
|
|
1343
|
+
} catch {
|
|
1344
|
+
if (seq === loadSeq) view.value = "source";
|
|
1345
|
+
}
|
|
1346
|
+
});
|
|
1224
1347
|
watch(() => props.open, (open) => {
|
|
1225
1348
|
const el = dialog.value;
|
|
1226
1349
|
if (!el) return;
|
|
@@ -1270,49 +1393,66 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1270
1393
|
"aria-hidden": "true"
|
|
1271
1394
|
}, toDisplayString(unref(extOf)(__props.file.name) || "FILE"), 9, _hoisted_4$1),
|
|
1272
1395
|
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)]),
|
|
1273
|
-
__props.total > 1 || mode.value === "image" && !busy.value ? (openBlock(), createElementBlock("div", _hoisted_8$1, [
|
|
1274
|
-
|
|
1275
|
-
|
|
1396
|
+
__props.total > 1 || mode.value === "image" && !busy.value || canRender.value ? (openBlock(), createElementBlock("div", _hoisted_8$1, [
|
|
1397
|
+
__props.total > 1 ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
1398
|
+
createElementVNode("button", {
|
|
1399
|
+
class: "fl-preview-tool",
|
|
1400
|
+
type: "button",
|
|
1401
|
+
"aria-label": t("previous"),
|
|
1402
|
+
title: t("previous"),
|
|
1403
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("prev"))
|
|
1404
|
+
}, [createVNode(_sfc_main$2, {
|
|
1405
|
+
name: "previous",
|
|
1406
|
+
size: 18
|
|
1407
|
+
})], 8, _hoisted_9$1),
|
|
1408
|
+
createElementVNode("span", _hoisted_10$1, toDisplayString(__props.position) + " / " + toDisplayString(__props.total), 1),
|
|
1409
|
+
createElementVNode("button", {
|
|
1410
|
+
class: "fl-preview-tool",
|
|
1411
|
+
type: "button",
|
|
1412
|
+
"aria-label": t("next"),
|
|
1413
|
+
title: t("next"),
|
|
1414
|
+
onClick: _cache[1] || (_cache[1] = ($event) => emit("next"))
|
|
1415
|
+
}, [createVNode(_sfc_main$2, {
|
|
1416
|
+
name: "next",
|
|
1417
|
+
size: 18
|
|
1418
|
+
})], 8, _hoisted_11$1)
|
|
1419
|
+
], 64)) : createCommentVNode("v-if", true),
|
|
1420
|
+
mode.value === "image" && !busy.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createElementVNode("button", {
|
|
1421
|
+
class: "fl-preview-tool fl-preview-rotate",
|
|
1276
1422
|
type: "button",
|
|
1277
|
-
"aria-label": t("
|
|
1278
|
-
title: t("
|
|
1279
|
-
onClick: _cache[
|
|
1423
|
+
"aria-label": t("rotate"),
|
|
1424
|
+
title: t("rotate"),
|
|
1425
|
+
onClick: _cache[2] || (_cache[2] = ($event) => rotation.value = (rotation.value + 90) % 360)
|
|
1280
1426
|
}, [createVNode(_sfc_main$2, {
|
|
1281
|
-
name: "
|
|
1282
|
-
size:
|
|
1283
|
-
})], 8,
|
|
1284
|
-
|
|
1285
|
-
createElementVNode("button", {
|
|
1286
|
-
class: "fl-preview-tool",
|
|
1427
|
+
name: "refresh",
|
|
1428
|
+
size: 17
|
|
1429
|
+
})], 8, _hoisted_12$1), createElementVNode("button", {
|
|
1430
|
+
class: "fl-preview-tool fl-preview-zoom",
|
|
1287
1431
|
type: "button",
|
|
1288
|
-
"aria-
|
|
1289
|
-
|
|
1290
|
-
|
|
1432
|
+
"aria-pressed": actualSize.value,
|
|
1433
|
+
"aria-label": t("actualSize"),
|
|
1434
|
+
title: t("actualSize"),
|
|
1435
|
+
onClick: _cache[3] || (_cache[3] = ($event) => actualSize.value = !actualSize.value)
|
|
1291
1436
|
}, [createVNode(_sfc_main$2, {
|
|
1292
|
-
name: "
|
|
1293
|
-
size:
|
|
1294
|
-
})], 8,
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
}, [createVNode(_sfc_main$2, {
|
|
1312
|
-
name: actualSize.value ? "zoomOut" : "zoomIn",
|
|
1313
|
-
size: 17
|
|
1314
|
-
}, null, 8, ["name"])], 8, _hoisted_13$1)], 64)) : createCommentVNode("v-if", true)])) : createCommentVNode("v-if", true),
|
|
1315
|
-
createElementVNode("div", _hoisted_14$1, [!__props.demo ? (openBlock(), createElementBlock("button", {
|
|
1437
|
+
name: actualSize.value ? "zoomOut" : "zoomIn",
|
|
1438
|
+
size: 17
|
|
1439
|
+
}, null, 8, ["name"])], 8, _hoisted_13$1)], 64)) : createCommentVNode("v-if", true),
|
|
1440
|
+
canRender.value ? (openBlock(), createElementBlock("span", {
|
|
1441
|
+
key: 2,
|
|
1442
|
+
class: "fl-preview-view",
|
|
1443
|
+
role: "group",
|
|
1444
|
+
"aria-label": t("viewMode")
|
|
1445
|
+
}, [(openBlock(), createElementBlock(Fragment, null, renderList(["source", "rendered"], (option) => {
|
|
1446
|
+
return createElementVNode("button", {
|
|
1447
|
+
key: option,
|
|
1448
|
+
class: "fl-preview-view-btn",
|
|
1449
|
+
type: "button",
|
|
1450
|
+
"aria-pressed": view.value === option,
|
|
1451
|
+
onClick: ($event) => view.value = option
|
|
1452
|
+
}, toDisplayString(t(option === "source" ? "viewSource" : "viewRendered")), 9, _hoisted_15$1);
|
|
1453
|
+
}), 64))], 8, _hoisted_14$1)) : createCommentVNode("v-if", true)
|
|
1454
|
+
])) : createCommentVNode("v-if", true),
|
|
1455
|
+
createElementVNode("div", _hoisted_16$1, [!__props.demo ? (openBlock(), createElementBlock("button", {
|
|
1316
1456
|
key: 0,
|
|
1317
1457
|
class: "fl-preview-tool fl-preview-download",
|
|
1318
1458
|
type: "button",
|
|
@@ -1322,7 +1462,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1322
1462
|
}, [createVNode(_sfc_main$2, {
|
|
1323
1463
|
name: "download",
|
|
1324
1464
|
size: 17
|
|
1325
|
-
})], 8,
|
|
1465
|
+
})], 8, _hoisted_17$1)) : createCommentVNode("v-if", true), createElementVNode("button", {
|
|
1326
1466
|
class: "fl-preview-tool fl-preview-close",
|
|
1327
1467
|
type: "button",
|
|
1328
1468
|
autofocus: "",
|
|
@@ -1332,18 +1472,18 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1332
1472
|
}, [createVNode(_sfc_main$2, {
|
|
1333
1473
|
name: "close",
|
|
1334
1474
|
size: 18
|
|
1335
|
-
})], 8,
|
|
1475
|
+
})], 8, _hoisted_18$1)])
|
|
1336
1476
|
]), createElementVNode("div", {
|
|
1337
1477
|
class: "fl-preview-body",
|
|
1338
1478
|
"data-mode": busy.value ? "loading" : mode.value
|
|
1339
|
-
}, [busy.value ? (openBlock(), createElementBlock("div",
|
|
1479
|
+
}, [busy.value ? (openBlock(), createElementBlock("div", _hoisted_20$1, toDisplayString(t("loading")), 1)) : mode.value === "image" ? (openBlock(), createElementBlock("img", {
|
|
1340
1480
|
key: 1,
|
|
1341
1481
|
class: normalizeClass(["fl-preview-image", { "fl-preview-actual": actualSize.value }]),
|
|
1342
1482
|
src: __props.src,
|
|
1343
1483
|
alt: __props.file.name,
|
|
1344
1484
|
style: normalizeStyle({ transform: rotation.value ? `rotate(${rotation.value}deg)` : void 0 }),
|
|
1345
1485
|
onError: markFailed
|
|
1346
|
-
}, null, 46,
|
|
1486
|
+
}, null, 46, _hoisted_21$1)) : mode.value === "video" ? (openBlock(), createElementBlock("video", {
|
|
1347
1487
|
key: 2,
|
|
1348
1488
|
class: "fl-preview-video",
|
|
1349
1489
|
src: __props.src,
|
|
@@ -1351,25 +1491,42 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1351
1491
|
playsinline: "",
|
|
1352
1492
|
preload: "metadata",
|
|
1353
1493
|
onError: markFailed
|
|
1354
|
-
}, null, 40,
|
|
1494
|
+
}, null, 40, _hoisted_22$1)) : mode.value === "audio" ? (openBlock(), createElementBlock("audio", {
|
|
1355
1495
|
key: 3,
|
|
1356
1496
|
class: "fl-preview-audio",
|
|
1357
1497
|
src: __props.src,
|
|
1358
1498
|
controls: "",
|
|
1359
1499
|
preload: "metadata",
|
|
1360
1500
|
onError: markFailed
|
|
1361
|
-
}, null, 40,
|
|
1501
|
+
}, null, 40, _hoisted_23$1)) : mode.value === "text" ? (openBlock(), createElementBlock("div", {
|
|
1502
|
+
key: 4,
|
|
1503
|
+
class: normalizeClass(["fl-preview-text", { "fl-preview-rendered": renderAs.value && view.value === "rendered" }])
|
|
1504
|
+
}, [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", {
|
|
1505
|
+
key: 0,
|
|
1506
|
+
class: "vp-doc fl-md",
|
|
1507
|
+
innerHTML: renderedHtml.value
|
|
1508
|
+
}, 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 }, [
|
|
1509
|
+
createElementVNode("p", _hoisted_27$1, toDisplayString(t("htmlSandboxNote")), 1),
|
|
1510
|
+
createCommentVNode(" empty sandbox: no scripts, no forms, no popups, an opaque origin "),
|
|
1511
|
+
createElementVNode("iframe", {
|
|
1512
|
+
class: "fl-preview-html",
|
|
1513
|
+
sandbox: "",
|
|
1514
|
+
referrerpolicy: "no-referrer",
|
|
1515
|
+
srcdoc: textContent.value,
|
|
1516
|
+
title: __props.file.name
|
|
1517
|
+
}, null, 8, _hoisted_28$1)
|
|
1518
|
+
], 64)) : (openBlock(), createElementBlock("pre", _hoisted_29$1, toDisplayString(textContent.value), 1))], 2)) : mode.value === "pdf" ? (openBlock(), createElementBlock("iframe", {
|
|
1362
1519
|
key: 5,
|
|
1363
1520
|
class: "fl-preview-frame",
|
|
1364
1521
|
src: __props.viewer || blobUrl.value,
|
|
1365
1522
|
title: __props.file.name
|
|
1366
|
-
}, null, 8,
|
|
1523
|
+
}, null, 8, _hoisted_30$1)) : (openBlock(), createElementBlock("div", _hoisted_31$1, [
|
|
1367
1524
|
createElementVNode("span", {
|
|
1368
1525
|
class: "fl-type fl-preview-badge",
|
|
1369
1526
|
"data-kind": unref(fileKind)(__props.file.name),
|
|
1370
1527
|
"aria-hidden": "true"
|
|
1371
|
-
}, toDisplayString(unref(extOf)(__props.file.name) || "FILE"), 9,
|
|
1372
|
-
createElementVNode("p",
|
|
1528
|
+
}, toDisplayString(unref(extOf)(__props.file.name) || "FILE"), 9, _hoisted_32$1),
|
|
1529
|
+
createElementVNode("p", _hoisted_33$1, toDisplayString(infoMessage.value), 1),
|
|
1373
1530
|
!__props.demo ? (openBlock(), createElementBlock("button", {
|
|
1374
1531
|
key: 0,
|
|
1375
1532
|
class: "fl-btn fl-btn-primary",
|
|
@@ -1379,7 +1536,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
|
1379
1536
|
name: "download",
|
|
1380
1537
|
size: 15
|
|
1381
1538
|
}), createTextVNode(" " + toDisplayString(t("download")), 1)])) : createCommentVNode("v-if", true)
|
|
1382
|
-
]))], 8,
|
|
1539
|
+
]))], 8, _hoisted_19$1)])) : createCommentVNode("v-if", true)], 40, _hoisted_1$1);
|
|
1383
1540
|
};
|
|
1384
1541
|
}
|
|
1385
1542
|
});
|
|
@@ -1406,14 +1563,22 @@ function toNodes(items, parent, password) {
|
|
|
1406
1563
|
zipPath: [...parent.zipPath, item.name]
|
|
1407
1564
|
}));
|
|
1408
1565
|
}
|
|
1409
|
-
function sortTree(nodes, key, ascending) {
|
|
1410
|
-
nodes.sort((a, b) => compareNodes(a, b, key, ascending));
|
|
1411
|
-
for (const node of nodes) if (node.children.length) sortTree(node.children, key, ascending);
|
|
1566
|
+
function sortTree(nodes, key, ascending, locale) {
|
|
1567
|
+
nodes.sort((a, b) => compareNodes(a, b, key, ascending, locale));
|
|
1568
|
+
for (const node of nodes) if (node.children.length) sortTree(node.children, key, ascending, locale);
|
|
1412
1569
|
}
|
|
1413
1570
|
/** Loaded files at or under a node. */
|
|
1414
1571
|
function descendantFiles(node) {
|
|
1415
1572
|
return node.is_dir ? node.children.flatMap(descendantFiles) : [node];
|
|
1416
1573
|
}
|
|
1574
|
+
/** Every loaded folder (expanded or not), e.g. what a deep search has read. */
|
|
1575
|
+
function loadedPathsOf(nodes, out = /* @__PURE__ */ new Set()) {
|
|
1576
|
+
for (const node of nodes) if (node.is_dir && node.loaded) {
|
|
1577
|
+
out.add(node.path);
|
|
1578
|
+
loadedPathsOf(node.children, out);
|
|
1579
|
+
}
|
|
1580
|
+
return out;
|
|
1581
|
+
}
|
|
1417
1582
|
function expandedPathsOf(nodes, out = /* @__PURE__ */ new Set()) {
|
|
1418
1583
|
for (const node of nodes) if (node.is_dir && node.expanded) {
|
|
1419
1584
|
out.add(node.path);
|
|
@@ -1616,104 +1781,105 @@ const _hoisted_13 = { class: "fl-clear-text" };
|
|
|
1616
1781
|
const _hoisted_14 = { class: "fl-tools" };
|
|
1617
1782
|
const _hoisted_15 = { class: "fl-search" };
|
|
1618
1783
|
const _hoisted_16 = ["placeholder", "aria-label"];
|
|
1619
|
-
const _hoisted_17 = [
|
|
1784
|
+
const _hoisted_17 = ["aria-label", "title"];
|
|
1785
|
+
const _hoisted_18 = [
|
|
1620
1786
|
"disabled",
|
|
1621
1787
|
"aria-label",
|
|
1622
1788
|
"title"
|
|
1623
1789
|
];
|
|
1624
|
-
const
|
|
1625
|
-
const
|
|
1790
|
+
const _hoisted_19 = { class: "fl-row fl-head" };
|
|
1791
|
+
const _hoisted_20 = {
|
|
1626
1792
|
key: 0,
|
|
1627
1793
|
class: "fl-progress",
|
|
1628
1794
|
"aria-hidden": "true"
|
|
1629
1795
|
};
|
|
1630
|
-
const
|
|
1631
|
-
const
|
|
1796
|
+
const _hoisted_21 = { class: "fl-cell fl-cell-check" };
|
|
1797
|
+
const _hoisted_22 = [
|
|
1632
1798
|
"aria-label",
|
|
1633
1799
|
"disabled",
|
|
1634
1800
|
"checked",
|
|
1635
1801
|
".indeterminate"
|
|
1636
1802
|
];
|
|
1637
|
-
const
|
|
1638
|
-
const
|
|
1803
|
+
const _hoisted_23 = ["aria-sort", "onClick"];
|
|
1804
|
+
const _hoisted_24 = {
|
|
1639
1805
|
class: "fl-sort",
|
|
1640
1806
|
"aria-hidden": "true"
|
|
1641
1807
|
};
|
|
1642
|
-
const
|
|
1643
|
-
const
|
|
1644
|
-
const
|
|
1808
|
+
const _hoisted_25 = { class: "fl-cell fl-cell-op" };
|
|
1809
|
+
const _hoisted_26 = { class: "fl-sort-select" };
|
|
1810
|
+
const _hoisted_27 = {
|
|
1645
1811
|
class: "fl-sort-current",
|
|
1646
1812
|
"aria-hidden": "true"
|
|
1647
1813
|
};
|
|
1648
|
-
const
|
|
1649
|
-
const
|
|
1650
|
-
const
|
|
1814
|
+
const _hoisted_28 = ["aria-label"];
|
|
1815
|
+
const _hoisted_29 = ["value"];
|
|
1816
|
+
const _hoisted_30 = {
|
|
1651
1817
|
key: 0,
|
|
1652
1818
|
class: "fl-status"
|
|
1653
1819
|
};
|
|
1654
|
-
const
|
|
1655
|
-
const
|
|
1656
|
-
const
|
|
1657
|
-
const
|
|
1820
|
+
const _hoisted_31 = ["for"];
|
|
1821
|
+
const _hoisted_32 = ["id"];
|
|
1822
|
+
const _hoisted_33 = { class: "fl-unlock" };
|
|
1823
|
+
const _hoisted_34 = [
|
|
1658
1824
|
"id",
|
|
1659
1825
|
"data-unlock",
|
|
1660
1826
|
"placeholder",
|
|
1661
1827
|
"aria-invalid",
|
|
1662
1828
|
"aria-describedby"
|
|
1663
1829
|
];
|
|
1664
|
-
const
|
|
1830
|
+
const _hoisted_35 = [
|
|
1665
1831
|
"aria-pressed",
|
|
1666
1832
|
"aria-label",
|
|
1667
1833
|
"title"
|
|
1668
1834
|
];
|
|
1669
|
-
const
|
|
1670
|
-
const
|
|
1835
|
+
const _hoisted_36 = ["disabled"];
|
|
1836
|
+
const _hoisted_37 = {
|
|
1671
1837
|
key: 2,
|
|
1672
1838
|
class: "fl-status fl-error"
|
|
1673
1839
|
};
|
|
1674
|
-
const
|
|
1840
|
+
const _hoisted_38 = {
|
|
1675
1841
|
key: 0,
|
|
1676
1842
|
class: "fl-hint"
|
|
1677
1843
|
};
|
|
1678
|
-
const
|
|
1844
|
+
const _hoisted_39 = {
|
|
1679
1845
|
key: 1,
|
|
1680
1846
|
class: "fl-hint"
|
|
1681
1847
|
};
|
|
1682
|
-
const
|
|
1848
|
+
const _hoisted_40 = {
|
|
1683
1849
|
key: 3,
|
|
1684
1850
|
class: "fl-status"
|
|
1685
1851
|
};
|
|
1686
|
-
const
|
|
1687
|
-
const
|
|
1688
|
-
const
|
|
1689
|
-
const
|
|
1690
|
-
const _hoisted_44 = ["onClick"];
|
|
1852
|
+
const _hoisted_41 = { class: "fl-cell fl-more-cell" };
|
|
1853
|
+
const _hoisted_42 = { class: "fl-more-count" };
|
|
1854
|
+
const _hoisted_43 = ["onClick"];
|
|
1855
|
+
const _hoisted_44 = { class: "fl-more-page" };
|
|
1691
1856
|
const _hoisted_45 = ["onClick"];
|
|
1692
|
-
const _hoisted_46 =
|
|
1693
|
-
const _hoisted_47 =
|
|
1857
|
+
const _hoisted_46 = ["onClick"];
|
|
1858
|
+
const _hoisted_47 = { class: "fl-cell fl-cell-check" };
|
|
1859
|
+
const _hoisted_48 = [
|
|
1694
1860
|
"disabled",
|
|
1695
1861
|
"aria-label",
|
|
1696
1862
|
"checked",
|
|
1697
1863
|
".indeterminate",
|
|
1698
1864
|
"onChange"
|
|
1699
1865
|
];
|
|
1700
|
-
const
|
|
1701
|
-
const
|
|
1866
|
+
const _hoisted_49 = ["title"];
|
|
1867
|
+
const _hoisted_50 = [
|
|
1702
1868
|
"aria-label",
|
|
1703
1869
|
"title",
|
|
1704
1870
|
"onClick"
|
|
1705
1871
|
];
|
|
1706
|
-
const
|
|
1872
|
+
const _hoisted_51 = [
|
|
1707
1873
|
"aria-label",
|
|
1708
1874
|
"aria-expanded",
|
|
1709
1875
|
"onClick"
|
|
1710
1876
|
];
|
|
1711
|
-
const
|
|
1877
|
+
const _hoisted_52 = {
|
|
1712
1878
|
key: 2,
|
|
1713
1879
|
class: "fl-arrow-placeholder",
|
|
1714
1880
|
"aria-hidden": "true"
|
|
1715
1881
|
};
|
|
1716
|
-
const
|
|
1882
|
+
const _hoisted_53 = {
|
|
1717
1883
|
key: 3,
|
|
1718
1884
|
class: "fl-folder-icon",
|
|
1719
1885
|
viewBox: "0 0 24 24",
|
|
@@ -1721,27 +1887,39 @@ const _hoisted_52 = {
|
|
|
1721
1887
|
height: "18",
|
|
1722
1888
|
"aria-hidden": "true"
|
|
1723
1889
|
};
|
|
1724
|
-
const
|
|
1725
|
-
const
|
|
1726
|
-
const
|
|
1727
|
-
const _hoisted_56 = { class: "fl-meta" };
|
|
1890
|
+
const _hoisted_54 = ["data-kind"];
|
|
1891
|
+
const _hoisted_55 = { class: "fl-label" };
|
|
1892
|
+
const _hoisted_56 = ["onClick"];
|
|
1728
1893
|
const _hoisted_57 = {
|
|
1894
|
+
key: 0,
|
|
1895
|
+
class: "fl-hit"
|
|
1896
|
+
};
|
|
1897
|
+
const _hoisted_58 = { class: "fl-meta" };
|
|
1898
|
+
const _hoisted_59 = {
|
|
1899
|
+
key: 2,
|
|
1900
|
+
class: "fl-locked-note"
|
|
1901
|
+
};
|
|
1902
|
+
const _hoisted_60 = {
|
|
1729
1903
|
key: 5,
|
|
1904
|
+
class: "fl-locked-note fl-locked-hint"
|
|
1905
|
+
};
|
|
1906
|
+
const _hoisted_61 = {
|
|
1907
|
+
key: 6,
|
|
1730
1908
|
class: "fl-count"
|
|
1731
1909
|
};
|
|
1732
|
-
const
|
|
1733
|
-
const
|
|
1910
|
+
const _hoisted_62 = ["id"];
|
|
1911
|
+
const _hoisted_63 = [
|
|
1734
1912
|
"aria-label",
|
|
1735
1913
|
"title",
|
|
1736
1914
|
"onClick"
|
|
1737
1915
|
];
|
|
1738
|
-
const
|
|
1739
|
-
key:
|
|
1916
|
+
const _hoisted_64 = {
|
|
1917
|
+
key: 9,
|
|
1740
1918
|
class: "fl-loading",
|
|
1741
1919
|
"aria-hidden": "true"
|
|
1742
1920
|
};
|
|
1743
|
-
const
|
|
1744
|
-
const
|
|
1921
|
+
const _hoisted_65 = ["onSubmit", "onKeydown"];
|
|
1922
|
+
const _hoisted_66 = [
|
|
1745
1923
|
"onUpdate:modelValue",
|
|
1746
1924
|
"data-unlock",
|
|
1747
1925
|
"placeholder",
|
|
@@ -1749,40 +1927,51 @@ const _hoisted_62 = [
|
|
|
1749
1927
|
"aria-invalid",
|
|
1750
1928
|
"aria-describedby"
|
|
1751
1929
|
];
|
|
1752
|
-
const
|
|
1930
|
+
const _hoisted_67 = [
|
|
1753
1931
|
"aria-pressed",
|
|
1754
1932
|
"aria-label",
|
|
1755
1933
|
"title",
|
|
1756
1934
|
"onClick"
|
|
1757
1935
|
];
|
|
1758
|
-
const
|
|
1759
|
-
const
|
|
1936
|
+
const _hoisted_68 = ["disabled"];
|
|
1937
|
+
const _hoisted_69 = [
|
|
1760
1938
|
"aria-label",
|
|
1761
1939
|
"title",
|
|
1762
1940
|
"onClick"
|
|
1763
1941
|
];
|
|
1764
|
-
const
|
|
1765
|
-
const
|
|
1766
|
-
const
|
|
1767
|
-
const
|
|
1768
|
-
const
|
|
1942
|
+
const _hoisted_70 = { class: "fl-cell fl-cell-size" };
|
|
1943
|
+
const _hoisted_71 = { class: "fl-cell fl-cell-date" };
|
|
1944
|
+
const _hoisted_72 = { class: "fl-cell fl-cell-op" };
|
|
1945
|
+
const _hoisted_73 = ["aria-label", "onClick"];
|
|
1946
|
+
const _hoisted_74 = [
|
|
1769
1947
|
"aria-label",
|
|
1770
1948
|
"aria-expanded",
|
|
1771
1949
|
"onClick"
|
|
1772
1950
|
];
|
|
1773
|
-
const
|
|
1951
|
+
const _hoisted_75 = [
|
|
1774
1952
|
"aria-label",
|
|
1775
1953
|
"title",
|
|
1776
1954
|
"onClick"
|
|
1777
1955
|
];
|
|
1778
|
-
const
|
|
1956
|
+
const _hoisted_76 = [
|
|
1779
1957
|
"aria-label",
|
|
1780
1958
|
"title",
|
|
1781
1959
|
"onClick"
|
|
1782
1960
|
];
|
|
1783
|
-
const
|
|
1784
|
-
const _hoisted_74 = {
|
|
1961
|
+
const _hoisted_77 = {
|
|
1785
1962
|
key: 4,
|
|
1963
|
+
class: "fl-deep",
|
|
1964
|
+
role: "status"
|
|
1965
|
+
};
|
|
1966
|
+
const _hoisted_78 = { class: "fl-deep-text" };
|
|
1967
|
+
const _hoisted_79 = {
|
|
1968
|
+
key: 0,
|
|
1969
|
+
class: "fl-deep-bar",
|
|
1970
|
+
"aria-hidden": "true"
|
|
1971
|
+
};
|
|
1972
|
+
const _hoisted_80 = { class: "fl-foot" };
|
|
1973
|
+
const _hoisted_81 = {
|
|
1974
|
+
key: 5,
|
|
1786
1975
|
class: "fl-toast",
|
|
1787
1976
|
role: "status"
|
|
1788
1977
|
};
|
|
@@ -1806,6 +1995,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1806
1995
|
default: true
|
|
1807
1996
|
},
|
|
1808
1997
|
pageSize: { default: 200 },
|
|
1998
|
+
deepSearchLimit: { default: 500 },
|
|
1999
|
+
deepSearch: { default: "manual" },
|
|
1809
2000
|
zipWarnSize: {},
|
|
1810
2001
|
zipMaxSize: {},
|
|
1811
2002
|
locale: { default: "zh-CN" },
|
|
@@ -1870,7 +2061,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1870
2061
|
return isNetworkError(error) ? `${message} ${t("corsHint")}` : message;
|
|
1871
2062
|
}
|
|
1872
2063
|
function sortNodes(nodes) {
|
|
1873
|
-
sortTree(nodes, sortKey.value, sortAsc.value);
|
|
2064
|
+
sortTree(nodes, sortKey.value, sortAsc.value, props.locale);
|
|
1874
2065
|
}
|
|
1875
2066
|
function focusUnlock(path) {
|
|
1876
2067
|
nextTick(() => {
|
|
@@ -1975,7 +2166,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1975
2166
|
* Load a folder's children. A password error locks the folder instead of
|
|
1976
2167
|
* toasting; `prompt` also shows its same-row input (and focuses it).
|
|
1977
2168
|
*/
|
|
1978
|
-
async function loadChildren(node, prompt = false) {
|
|
2169
|
+
async function loadChildren(node, prompt = false, quiet = false) {
|
|
1979
2170
|
if (node.loaded) return true;
|
|
1980
2171
|
if (node.lock) {
|
|
1981
2172
|
if (prompt) startPrompt(node, prompt === "focus");
|
|
@@ -1991,7 +2182,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1991
2182
|
node.expanded = false;
|
|
1992
2183
|
node.lock = newLock(false, passwords.dropStale(rememberedAt) ? "stale" : "");
|
|
1993
2184
|
if (prompt) startPrompt(node, prompt === "focus");
|
|
1994
|
-
} else showToast(failureMessage("folderFailed", error));
|
|
2185
|
+
} else if (!quiet) showToast(failureMessage("folderFailed", error));
|
|
1995
2186
|
return false;
|
|
1996
2187
|
} finally {
|
|
1997
2188
|
node.loading = false;
|
|
@@ -2038,6 +2229,21 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2038
2229
|
zipPath: [item.is_dir ? name : item.name || sourceName(path)]
|
|
2039
2230
|
};
|
|
2040
2231
|
}
|
|
2232
|
+
/** Run at most `size` tasks at once. */
|
|
2233
|
+
function concurrency(size) {
|
|
2234
|
+
let active = 0;
|
|
2235
|
+
const waiting = [];
|
|
2236
|
+
return async (task) => {
|
|
2237
|
+
if (active >= size) await new Promise((resolve) => waiting.push(resolve));
|
|
2238
|
+
active += 1;
|
|
2239
|
+
try {
|
|
2240
|
+
return await task();
|
|
2241
|
+
} finally {
|
|
2242
|
+
active -= 1;
|
|
2243
|
+
waiting.shift()?.();
|
|
2244
|
+
}
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2041
2247
|
class RootLockedError extends Error {
|
|
2042
2248
|
rememberedAt;
|
|
2043
2249
|
constructor(rememberedAt) {
|
|
@@ -2049,7 +2255,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2049
2255
|
* Build a fresh tree. `expandedPaths` re-opens those folders (refresh keeps
|
|
2050
2256
|
* what was open); without it the configured `expanded` defaults apply.
|
|
2051
2257
|
*/
|
|
2052
|
-
async function buildTree(expandedPaths) {
|
|
2258
|
+
async function buildTree(expandedPaths, loadedPaths) {
|
|
2053
2259
|
let nodes;
|
|
2054
2260
|
if (props.sources.length) nodes = await Promise.all(props.sources.map((source) => loadSourceRoot(source, expandedPaths ? expandedPaths.has(source.path) : source.expanded ?? props.expanded)));
|
|
2055
2261
|
else {
|
|
@@ -2060,13 +2266,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2060
2266
|
throw isPasswordError(error) ? new RootLockedError(rememberedAt) : error;
|
|
2061
2267
|
}
|
|
2062
2268
|
}
|
|
2269
|
+
const limit = concurrency(4);
|
|
2063
2270
|
const open = async (list) => {
|
|
2064
|
-
|
|
2065
|
-
const wanted = node.expanded || expandedPaths?.has(node.path);
|
|
2066
|
-
|
|
2067
|
-
node.
|
|
2068
|
-
|
|
2069
|
-
|
|
2271
|
+
await Promise.all(list.map(async (node) => {
|
|
2272
|
+
const wanted = !!(node.expanded || expandedPaths?.has(node.path));
|
|
2273
|
+
const keep = !!loadedPaths?.has(node.path);
|
|
2274
|
+
if (!node.is_dir || node.lock || !(wanted || keep)) return;
|
|
2275
|
+
const ok = await limit(() => loadChildren(node, wanted ? "show" : false, !wanted));
|
|
2276
|
+
node.expanded = wanted && ok;
|
|
2277
|
+
if (ok && (expandedPaths || loadedPaths)) await open(node.children);
|
|
2278
|
+
}));
|
|
2070
2279
|
};
|
|
2071
2280
|
await open(nodes);
|
|
2072
2281
|
return nodes;
|
|
@@ -2078,6 +2287,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2078
2287
|
}
|
|
2079
2288
|
/** First load, or a retry after an error: shows the loading state. */
|
|
2080
2289
|
async function loadRoot() {
|
|
2290
|
+
stopDeepSearch();
|
|
2081
2291
|
refreshSeq += 1;
|
|
2082
2292
|
refreshing.value = false;
|
|
2083
2293
|
loadingRoot.value = true;
|
|
@@ -2101,6 +2311,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2101
2311
|
*/
|
|
2102
2312
|
async function refresh() {
|
|
2103
2313
|
if (refreshing.value || loadingRoot.value) return;
|
|
2314
|
+
stopDeepSearch();
|
|
2104
2315
|
if (!roots.value.length || errorMsg.value || rootLock.value) {
|
|
2105
2316
|
await loadRoot();
|
|
2106
2317
|
return;
|
|
@@ -2108,7 +2319,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2108
2319
|
const seq = ++refreshSeq;
|
|
2109
2320
|
refreshing.value = true;
|
|
2110
2321
|
try {
|
|
2111
|
-
const fresh = await buildTree(expandedPathsOf(roots.value));
|
|
2322
|
+
const fresh = await buildTree(expandedPathsOf(roots.value), deepMode.value ? loadedPathsOf(roots.value) : void 0);
|
|
2112
2323
|
if (seq !== refreshSeq) return;
|
|
2113
2324
|
roots.value = fresh;
|
|
2114
2325
|
const existing = new Set(fresh.flatMap(descendantFiles).map((file) => file.path));
|
|
@@ -2181,7 +2392,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2181
2392
|
const rows = computed(() => {
|
|
2182
2393
|
const out = [];
|
|
2183
2394
|
const query = keyword.value.trim().toLowerCase();
|
|
2184
|
-
const matches = (node) => !query || node.name.toLowerCase().includes(query) || node.is_dir && node.children.some(matches);
|
|
2395
|
+
const matches = (node) => !query || node.name.toLowerCase().includes(query) || node.is_dir && (node.children.some(matches) || deepMode.value && !!node.lock);
|
|
2185
2396
|
const walk = (nodes, key, depth) => {
|
|
2186
2397
|
const list = nodes.filter(matches);
|
|
2187
2398
|
const limit = query ? list.length : Math.min(list.length, limitOf(key));
|
|
@@ -2200,6 +2411,172 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2200
2411
|
return out;
|
|
2201
2412
|
});
|
|
2202
2413
|
const visible = computed(() => rows.value.filter((row) => !isMore(row)));
|
|
2414
|
+
/** A name split around the filter keyword, for highlighting. */
|
|
2415
|
+
function nameParts(name) {
|
|
2416
|
+
const query = keyword.value.trim().toLowerCase();
|
|
2417
|
+
if (!query) return [{
|
|
2418
|
+
text: name,
|
|
2419
|
+
hit: false
|
|
2420
|
+
}];
|
|
2421
|
+
const parts = [];
|
|
2422
|
+
const lower = name.toLowerCase();
|
|
2423
|
+
let from = 0;
|
|
2424
|
+
for (let at = lower.indexOf(query); at !== -1; at = lower.indexOf(query, from)) {
|
|
2425
|
+
if (at > from) parts.push({
|
|
2426
|
+
text: name.slice(from, at),
|
|
2427
|
+
hit: false
|
|
2428
|
+
});
|
|
2429
|
+
parts.push({
|
|
2430
|
+
text: name.slice(at, at + query.length),
|
|
2431
|
+
hit: true
|
|
2432
|
+
});
|
|
2433
|
+
from = at + query.length;
|
|
2434
|
+
}
|
|
2435
|
+
if (from < name.length) parts.push({
|
|
2436
|
+
text: name.slice(from),
|
|
2437
|
+
hit: false
|
|
2438
|
+
});
|
|
2439
|
+
return parts;
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* The filter only sees loaded folders. Deep search loads the rest under the
|
|
2443
|
+
* mounted paths (four at a time, `deepSearchLimit` per run) so matches appear
|
|
2444
|
+
* in place, in their folders, like any other row. Locked folders are skipped.
|
|
2445
|
+
*/
|
|
2446
|
+
const deep = reactive({
|
|
2447
|
+
running: false,
|
|
2448
|
+
searched: 0,
|
|
2449
|
+
failed: 0,
|
|
2450
|
+
capped: false,
|
|
2451
|
+
done: false
|
|
2452
|
+
});
|
|
2453
|
+
/**
|
|
2454
|
+
* Deep search mode: entered by “Try deep search” (or at once with
|
|
2455
|
+
* `deep-search="auto"`), left by clearing the filter. While on, new keywords,
|
|
2456
|
+
* refreshes and newly unlocked folders are searched without asking again.
|
|
2457
|
+
*/
|
|
2458
|
+
const deepMode = ref(false);
|
|
2459
|
+
let deepSeq = 0;
|
|
2460
|
+
/** Folders that are known but not loaded yet (locked ones excluded). */
|
|
2461
|
+
function unloadedFolders() {
|
|
2462
|
+
const out = [];
|
|
2463
|
+
const walk = (nodes) => {
|
|
2464
|
+
for (const node of nodes) {
|
|
2465
|
+
if (!node.is_dir || node.lock) continue;
|
|
2466
|
+
if (!node.loaded) out.push(node);
|
|
2467
|
+
else walk(node.children);
|
|
2468
|
+
}
|
|
2469
|
+
};
|
|
2470
|
+
walk(roots.value);
|
|
2471
|
+
return out;
|
|
2472
|
+
}
|
|
2473
|
+
const hasUnloaded = computed(() => unloadedFolders().length > 0);
|
|
2474
|
+
const deepRow = computed(() => {
|
|
2475
|
+
if (!keyword.value.trim()) return null;
|
|
2476
|
+
if (deep.running) return "running";
|
|
2477
|
+
if (deep.capped) return "capped";
|
|
2478
|
+
if (!deepMode.value) return hasUnloaded.value ? "idle" : null;
|
|
2479
|
+
return deep.done ? "done" : null;
|
|
2480
|
+
});
|
|
2481
|
+
/** “Try deep search”, “Continue”, and the automatic runs of deep search mode. */
|
|
2482
|
+
async function deepSearch() {
|
|
2483
|
+
deepMode.value = true;
|
|
2484
|
+
const seq = ++deepSeq;
|
|
2485
|
+
const limit = Math.max(1, Math.floor(props.deepSearchLimit ?? 500));
|
|
2486
|
+
const runStart = deep.searched;
|
|
2487
|
+
Object.assign(deep, {
|
|
2488
|
+
running: true,
|
|
2489
|
+
capped: false,
|
|
2490
|
+
done: false
|
|
2491
|
+
});
|
|
2492
|
+
let wave = unloadedFolders();
|
|
2493
|
+
while (wave.length && seq === deepSeq) {
|
|
2494
|
+
const next = [];
|
|
2495
|
+
const queue = [...wave];
|
|
2496
|
+
const worker = async () => {
|
|
2497
|
+
for (let node = queue.shift(); node && seq === deepSeq; node = queue.shift()) {
|
|
2498
|
+
if (deep.searched - runStart >= limit) return;
|
|
2499
|
+
deep.searched += 1;
|
|
2500
|
+
if (!await loadChildren(node, false, true)) {
|
|
2501
|
+
if (!node.lock) deep.failed += 1;
|
|
2502
|
+
continue;
|
|
2503
|
+
}
|
|
2504
|
+
for (const child of node.children) if (child.is_dir && !child.loaded && !child.lock) next.push(child);
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
await Promise.all(Array.from({ length: Math.min(4, queue.length) }, worker));
|
|
2508
|
+
if (deep.searched - runStart >= limit) break;
|
|
2509
|
+
wave = next;
|
|
2510
|
+
}
|
|
2511
|
+
if (seq !== deepSeq) return;
|
|
2512
|
+
deep.running = false;
|
|
2513
|
+
deep.capped = hasUnloaded.value && deep.searched - runStart >= limit;
|
|
2514
|
+
deep.done = !deep.capped;
|
|
2515
|
+
}
|
|
2516
|
+
/** Pause a run (internal: refresh, reload). */
|
|
2517
|
+
function stopDeepSearch() {
|
|
2518
|
+
deepSeq += 1;
|
|
2519
|
+
deep.running = false;
|
|
2520
|
+
}
|
|
2521
|
+
/** The user's Stop: keep the mode, offer “Continue”, do not restart by itself. */
|
|
2522
|
+
function pauseDeepSearch() {
|
|
2523
|
+
const wasRunning = deep.running;
|
|
2524
|
+
stopDeepSearch();
|
|
2525
|
+
if (wasRunning && hasUnloaded.value) deep.capped = true;
|
|
2526
|
+
}
|
|
2527
|
+
function clearFilter() {
|
|
2528
|
+
keyword.value = "";
|
|
2529
|
+
}
|
|
2530
|
+
watch(keyword, (value) => {
|
|
2531
|
+
if (value.trim()) {
|
|
2532
|
+
if (props.deepSearch === "auto") deepMode.value = true;
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
stopDeepSearch();
|
|
2536
|
+
deepMode.value = false;
|
|
2537
|
+
Object.assign(deep, {
|
|
2538
|
+
searched: 0,
|
|
2539
|
+
failed: 0,
|
|
2540
|
+
capped: false,
|
|
2541
|
+
done: false
|
|
2542
|
+
});
|
|
2543
|
+
});
|
|
2544
|
+
watch(() => deepMode.value && hasUnloaded.value && !deep.running && !deep.capped && !refreshing.value, (ready) => {
|
|
2545
|
+
if (ready) deepSearch();
|
|
2546
|
+
});
|
|
2547
|
+
/**
|
|
2548
|
+
* Counted from the tree, so a refresh that re-reads the same folders shows the
|
|
2549
|
+
* same numbers: folders read (or found locked), and the locked ones.
|
|
2550
|
+
*/
|
|
2551
|
+
const folderCounts = computed(() => {
|
|
2552
|
+
const counts = {
|
|
2553
|
+
checked: 0,
|
|
2554
|
+
locked: 0
|
|
2555
|
+
};
|
|
2556
|
+
const walk = (nodes) => {
|
|
2557
|
+
for (const node of nodes) {
|
|
2558
|
+
if (!node.is_dir) continue;
|
|
2559
|
+
if (node.lock) {
|
|
2560
|
+
counts.checked += 1;
|
|
2561
|
+
counts.locked += 1;
|
|
2562
|
+
} else if (node.loaded) {
|
|
2563
|
+
counts.checked += 1;
|
|
2564
|
+
walk(node.children);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
};
|
|
2568
|
+
walk(roots.value);
|
|
2569
|
+
return counts;
|
|
2570
|
+
});
|
|
2571
|
+
const lockedFolders = computed(() => folderCounts.value.locked);
|
|
2572
|
+
function lockedInSearch(node) {
|
|
2573
|
+
return deepMode.value && !!node.lock && !!keyword.value.trim();
|
|
2574
|
+
}
|
|
2575
|
+
const deepSummary = computed(() => [
|
|
2576
|
+
t(deepRow.value === "capped" ? "deepCapped" : "deepDone", { count: folderCounts.value.checked }),
|
|
2577
|
+
lockedFolders.value ? t("deepSkipped", { count: lockedFolders.value }) : "",
|
|
2578
|
+
deep.failed ? t("deepFailed", { count: deep.failed }) : ""
|
|
2579
|
+
].filter(Boolean).join(" · "));
|
|
2203
2580
|
const stats = computed(() => folderStats(roots.value, selected.value));
|
|
2204
2581
|
const noStats = {
|
|
2205
2582
|
files: 0,
|
|
@@ -2525,15 +2902,30 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2525
2902
|
name: "close",
|
|
2526
2903
|
size: 18
|
|
2527
2904
|
}), createElementVNode("span", _hoisted_13, toDisplayString(t("clear")), 1)], 8, _hoisted_12)) : createCommentVNode("v-if", true),
|
|
2528
|
-
createElementVNode("div", _hoisted_14, [createElementVNode("label", _hoisted_15, [
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2905
|
+
createElementVNode("div", _hoisted_14, [createElementVNode("label", _hoisted_15, [
|
|
2906
|
+
createVNode(_sfc_main$2, {
|
|
2907
|
+
name: "search",
|
|
2908
|
+
size: 15
|
|
2909
|
+
}),
|
|
2910
|
+
withDirectives(createElementVNode("input", {
|
|
2911
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => keyword.value = $event),
|
|
2912
|
+
type: "text",
|
|
2913
|
+
placeholder: t("filter"),
|
|
2914
|
+
"aria-label": t("filter"),
|
|
2915
|
+
onKeydown: _cache[1] || (_cache[1] = withKeys(($event) => keyword.value && ($event.preventDefault(), clearFilter()), ["esc"]))
|
|
2916
|
+
}, null, 40, _hoisted_16), [[vModelText, keyword.value]]),
|
|
2917
|
+
keyword.value ? (openBlock(), createElementBlock("button", {
|
|
2918
|
+
key: 0,
|
|
2919
|
+
class: "fl-search-clear",
|
|
2920
|
+
type: "button",
|
|
2921
|
+
"aria-label": t("clearFilter"),
|
|
2922
|
+
title: t("clearFilter"),
|
|
2923
|
+
onClick: clearFilter
|
|
2924
|
+
}, [createVNode(_sfc_main$2, {
|
|
2925
|
+
name: "close",
|
|
2926
|
+
size: 12
|
|
2927
|
+
})], 8, _hoisted_17)) : createCommentVNode("v-if", true)
|
|
2928
|
+
]), createElementVNode("button", {
|
|
2537
2929
|
class: normalizeClass(["fl-icon-btn fl-refresh", { "fl-spinning": refreshing.value }]),
|
|
2538
2930
|
type: "button",
|
|
2539
2931
|
disabled: refreshing.value || loadingRoot.value,
|
|
@@ -2543,7 +2935,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2543
2935
|
}, [createVNode(_sfc_main$2, {
|
|
2544
2936
|
name: "refresh",
|
|
2545
2937
|
size: 15
|
|
2546
|
-
})], 10,
|
|
2938
|
+
})], 10, _hoisted_18)]),
|
|
2547
2939
|
batchBusy.value ? (openBlock(), createElementBlock("button", {
|
|
2548
2940
|
key: 4,
|
|
2549
2941
|
class: "fl-btn fl-zip-cancel",
|
|
@@ -2559,16 +2951,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2559
2951
|
size: 15
|
|
2560
2952
|
}), createTextVNode(" " + toDisplayString(t("batch")), 1)])) : createCommentVNode("v-if", true)
|
|
2561
2953
|
]),
|
|
2562
|
-
createElementVNode("div",
|
|
2563
|
-
refreshing.value ? (openBlock(), createElementBlock("span",
|
|
2564
|
-
createElementVNode("label",
|
|
2954
|
+
createElementVNode("div", _hoisted_19, [
|
|
2955
|
+
refreshing.value ? (openBlock(), createElementBlock("span", _hoisted_20)) : createCommentVNode("v-if", true),
|
|
2956
|
+
createElementVNode("label", _hoisted_21, [createElementVNode("input", {
|
|
2565
2957
|
type: "checkbox",
|
|
2566
2958
|
"aria-label": t("selectAll"),
|
|
2567
2959
|
disabled: batchBusy.value,
|
|
2568
2960
|
checked: allChecked.value,
|
|
2569
2961
|
".indeterminate": someChecked.value,
|
|
2570
2962
|
onChange: toggleCheckAll
|
|
2571
|
-
}, null, 40,
|
|
2963
|
+
}, null, 40, _hoisted_22)]),
|
|
2572
2964
|
(openBlock(), createElementBlock(Fragment, null, renderList([
|
|
2573
2965
|
"name",
|
|
2574
2966
|
"size",
|
|
@@ -2582,23 +2974,23 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2582
2974
|
type: "button",
|
|
2583
2975
|
"aria-sort": sortKey.value === column ? sortAsc.value ? "ascending" : "descending" : void 0,
|
|
2584
2976
|
onClick: ($event) => setSort(column)
|
|
2585
|
-
}, [createTextVNode(toDisplayString(t(column === "name" ? "columnName" : column === "size" ? "columnSize" : "columnDate")), 1), createElementVNode("span",
|
|
2977
|
+
}, [createTextVNode(toDisplayString(t(column === "name" ? "columnName" : column === "size" ? "columnSize" : "columnDate")), 1), createElementVNode("span", _hoisted_24, toDisplayString(sortKey.value === column ? sortAsc.value ? "▲" : "▼" : ""), 1)], 8, _hoisted_23)], 2);
|
|
2586
2978
|
}), 64)),
|
|
2587
|
-
createElementVNode("span",
|
|
2979
|
+
createElementVNode("span", _hoisted_25, [createElementVNode("label", _hoisted_26, [
|
|
2588
2980
|
createElementVNode("span", null, toDisplayString(t("sortBy")), 1),
|
|
2589
|
-
createElementVNode("span",
|
|
2981
|
+
createElementVNode("span", _hoisted_27, toDisplayString(sortOptions.value.find((option) => option.value === sortValue.value)?.label), 1),
|
|
2590
2982
|
withDirectives(createElementVNode("select", {
|
|
2591
|
-
"onUpdate:modelValue": _cache[
|
|
2983
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => sortValue.value = $event),
|
|
2592
2984
|
"aria-label": t("sortBy")
|
|
2593
2985
|
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(sortOptions.value, (option) => {
|
|
2594
2986
|
return openBlock(), createElementBlock("option", {
|
|
2595
2987
|
key: option.value,
|
|
2596
2988
|
value: option.value
|
|
2597
|
-
}, toDisplayString(option.label), 9,
|
|
2598
|
-
}), 128))], 8,
|
|
2989
|
+
}, toDisplayString(option.label), 9, _hoisted_29);
|
|
2990
|
+
}), 128))], 8, _hoisted_28), [[vModelSelect, sortValue.value]])
|
|
2599
2991
|
])])
|
|
2600
2992
|
]),
|
|
2601
|
-
loadingRoot.value ? (openBlock(), createElementBlock("div",
|
|
2993
|
+
loadingRoot.value ? (openBlock(), createElementBlock("div", _hoisted_30, toDisplayString(t("loading")), 1)) : rootLock.value ? (openBlock(), createElementBlock("form", {
|
|
2602
2994
|
key: 1,
|
|
2603
2995
|
class: normalizeClass(["fl-unlock-root", { "fl-unlock-error": rootLock.value.error }]),
|
|
2604
2996
|
onSubmit: withModifiers(submitRootUnlock, ["prevent"])
|
|
@@ -2610,24 +3002,24 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2610
3002
|
createElementVNode("label", {
|
|
2611
3003
|
class: "fl-unlock-label",
|
|
2612
3004
|
for: `${unref(uid)}-root`
|
|
2613
|
-
}, toDisplayString(t("passwordPrompt")), 9,
|
|
3005
|
+
}, toDisplayString(t("passwordPrompt")), 9, _hoisted_31),
|
|
2614
3006
|
rootLock.value.error ? (openBlock(), createElementBlock("span", {
|
|
2615
3007
|
key: 0,
|
|
2616
3008
|
id: `${unref(uid)}-root-error`,
|
|
2617
3009
|
class: "fl-unlock-message",
|
|
2618
3010
|
role: "alert"
|
|
2619
|
-
}, toDisplayString(unlockErrorText(rootLock.value.error)), 9,
|
|
2620
|
-
createElementVNode("span",
|
|
3011
|
+
}, toDisplayString(unlockErrorText(rootLock.value.error)), 9, _hoisted_32)) : createCommentVNode("v-if", true),
|
|
3012
|
+
createElementVNode("span", _hoisted_33, [
|
|
2621
3013
|
withDirectives(createElementVNode("input", {
|
|
2622
3014
|
id: `${unref(uid)}-root`,
|
|
2623
|
-
"onUpdate:modelValue": _cache[
|
|
3015
|
+
"onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => rootLock.value.input = $event),
|
|
2624
3016
|
type: "password",
|
|
2625
3017
|
autocomplete: "off",
|
|
2626
3018
|
"data-unlock": __props.path,
|
|
2627
3019
|
placeholder: t("passwordPlaceholder"),
|
|
2628
3020
|
"aria-invalid": !!rootLock.value.error,
|
|
2629
3021
|
"aria-describedby": rootLock.value.error ? `${unref(uid)}-root-error` : void 0
|
|
2630
|
-
}, null, 8,
|
|
3022
|
+
}, null, 8, _hoisted_34), [[vModelText, rootLock.value.input]]),
|
|
2631
3023
|
canRemember.value ? (openBlock(), createElementBlock("button", {
|
|
2632
3024
|
key: 0,
|
|
2633
3025
|
class: "fl-unlock-remember",
|
|
@@ -2635,49 +3027,49 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2635
3027
|
"aria-pressed": rootLock.value.remember,
|
|
2636
3028
|
"aria-label": t("rememberPassword"),
|
|
2637
3029
|
title: t("rememberPassword"),
|
|
2638
|
-
onClick: _cache[
|
|
2639
|
-
}, [createVNode(_sfc_main$2, { name: "key" })], 8,
|
|
3030
|
+
onClick: _cache[4] || (_cache[4] = ($event) => rootLock.value.remember = !rootLock.value.remember)
|
|
3031
|
+
}, [createVNode(_sfc_main$2, { name: "key" })], 8, _hoisted_35)) : createCommentVNode("v-if", true),
|
|
2640
3032
|
createElementVNode("button", {
|
|
2641
3033
|
class: "fl-unlock-submit",
|
|
2642
3034
|
type: "submit",
|
|
2643
3035
|
disabled: rootLock.value.busy || !rootLock.value.input
|
|
2644
|
-
}, toDisplayString(t("open")), 9,
|
|
3036
|
+
}, toDisplayString(t("open")), 9, _hoisted_36)
|
|
2645
3037
|
])
|
|
2646
|
-
], 34)) : errorMsg.value ? (openBlock(), createElementBlock("div",
|
|
3038
|
+
], 34)) : errorMsg.value ? (openBlock(), createElementBlock("div", _hoisted_37, [
|
|
2647
3039
|
createTextVNode(toDisplayString(t("loadFailed", { message: errorMsg.value })) + " ", 1),
|
|
2648
|
-
/guest/i.test(errorMsg.value) ? (openBlock(), createElementBlock("p",
|
|
2649
|
-
unref(isNetworkError)(errorMsg.value) ? (openBlock(), createElementBlock("p",
|
|
3040
|
+
/guest/i.test(errorMsg.value) ? (openBlock(), createElementBlock("p", _hoisted_38, toDisplayString(t("guestHint")), 1)) : createCommentVNode("v-if", true),
|
|
3041
|
+
unref(isNetworkError)(errorMsg.value) ? (openBlock(), createElementBlock("p", _hoisted_39, toDisplayString(t("corsHint")), 1)) : createCommentVNode("v-if", true),
|
|
2650
3042
|
createElementVNode("button", {
|
|
2651
3043
|
class: "fl-btn",
|
|
2652
3044
|
type: "button",
|
|
2653
3045
|
onClick: loadRoot
|
|
2654
3046
|
}, toDisplayString(t("retry")), 1)
|
|
2655
|
-
])) : visible.value.length === 0 ? (openBlock(), createElementBlock("div",
|
|
3047
|
+
])) : visible.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_40, toDisplayString(t("empty")), 1)) : createCommentVNode("v-if", true),
|
|
2656
3048
|
(openBlock(true), createElementBlock(Fragment, null, renderList(rows.value, (node, index) => {
|
|
2657
3049
|
return openBlock(), createElementBlock(Fragment, { key: isMore(node) ? `more:${node.key}` : node.path }, [isMore(node) ? (openBlock(), createElementBlock("div", {
|
|
2658
3050
|
key: 0,
|
|
2659
3051
|
class: "fl-row fl-more",
|
|
2660
3052
|
style: normalizeStyle({ "--fl-depth": node.depth })
|
|
2661
|
-
}, [_cache[
|
|
2662
|
-
_cache[
|
|
3053
|
+
}, [_cache[10] || (_cache[10] = createElementVNode("span", { class: "fl-cell fl-cell-check" }, null, -1)), createElementVNode("span", _hoisted_41, [
|
|
3054
|
+
_cache[9] || (_cache[9] = createElementVNode("span", {
|
|
2663
3055
|
class: "fl-indent",
|
|
2664
3056
|
"aria-hidden": "true"
|
|
2665
3057
|
}, null, -1)),
|
|
2666
|
-
createElementVNode("span",
|
|
3058
|
+
createElementVNode("span", _hoisted_42, toDisplayString(t("moreRemaining", { count: node.remaining })), 1),
|
|
2667
3059
|
node.remaining > (__props.pageSize || 0) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createElementVNode("button", {
|
|
2668
3060
|
class: "fl-more-btn",
|
|
2669
3061
|
type: "button",
|
|
2670
3062
|
onClick: ($event) => showMore(node, false)
|
|
2671
|
-
}, [createTextVNode(toDisplayString(t("showMore")), 1), createElementVNode("span",
|
|
3063
|
+
}, [createTextVNode(toDisplayString(t("showMore")), 1), createElementVNode("span", _hoisted_44, toDisplayString(` ${t("folderItems", { count: __props.pageSize })}`), 1)], 8, _hoisted_43), createElementVNode("button", {
|
|
2672
3064
|
class: "fl-more-btn fl-more-all",
|
|
2673
3065
|
type: "button",
|
|
2674
3066
|
onClick: ($event) => showMore(node, true)
|
|
2675
|
-
}, toDisplayString(t("showAll")), 9,
|
|
3067
|
+
}, toDisplayString(t("showAll")), 9, _hoisted_45)], 64)) : (openBlock(), createElementBlock("button", {
|
|
2676
3068
|
key: 1,
|
|
2677
3069
|
class: "fl-more-btn",
|
|
2678
3070
|
type: "button",
|
|
2679
3071
|
onClick: ($event) => showMore(node, true)
|
|
2680
|
-
}, toDisplayString(t("showRest", { count: node.remaining })), 9,
|
|
3072
|
+
}, toDisplayString(t("showRest", { count: node.remaining })), 9, _hoisted_46))
|
|
2681
3073
|
])], 4)) : (openBlock(), createElementBlock("div", {
|
|
2682
3074
|
key: 1,
|
|
2683
3075
|
class: normalizeClass(["fl-row", {
|
|
@@ -2690,19 +3082,19 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2690
3082
|
}]),
|
|
2691
3083
|
style: normalizeStyle({ "--fl-depth": node.depth })
|
|
2692
3084
|
}, [
|
|
2693
|
-
createElementVNode("label",
|
|
3085
|
+
createElementVNode("label", _hoisted_47, [createElementVNode("input", {
|
|
2694
3086
|
type: "checkbox",
|
|
2695
3087
|
disabled: !!node.lock || batchBusy.value,
|
|
2696
3088
|
"aria-label": t("selectItem", { name: node.name }),
|
|
2697
3089
|
checked: isChecked(node),
|
|
2698
3090
|
".indeterminate": isIndeterminate(node),
|
|
2699
3091
|
onChange: ($event) => toggleCheck(node, $event)
|
|
2700
|
-
}, null, 40,
|
|
3092
|
+
}, null, 40, _hoisted_48)]),
|
|
2701
3093
|
createElementVNode("span", {
|
|
2702
3094
|
class: "fl-cell fl-cell-name",
|
|
2703
3095
|
title: node.isRoot ? node.path : void 0
|
|
2704
3096
|
}, [
|
|
2705
|
-
_cache[
|
|
3097
|
+
_cache[12] || (_cache[12] = createElementVNode("span", {
|
|
2706
3098
|
class: "fl-indent",
|
|
2707
3099
|
"aria-hidden": "true"
|
|
2708
3100
|
}, null, -1)),
|
|
@@ -2716,7 +3108,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2716
3108
|
}, [createVNode(_sfc_main$2, {
|
|
2717
3109
|
name: "lock",
|
|
2718
3110
|
size: 14
|
|
2719
|
-
})], 8,
|
|
3111
|
+
})], 8, _hoisted_50)) : node.is_dir ? (openBlock(), createElementBlock("button", {
|
|
2720
3112
|
key: 1,
|
|
2721
3113
|
class: normalizeClass(["fl-arrow", { "fl-arrow-open": node.expanded }]),
|
|
2722
3114
|
type: "button",
|
|
@@ -2726,26 +3118,29 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2726
3118
|
}, [createVNode(_sfc_main$2, {
|
|
2727
3119
|
name: "next",
|
|
2728
3120
|
size: 14
|
|
2729
|
-
})], 10,
|
|
2730
|
-
node.is_dir ? (openBlock(), createElementBlock("svg",
|
|
3121
|
+
})], 10, _hoisted_51)) : (openBlock(), createElementBlock("span", _hoisted_52)),
|
|
3122
|
+
node.is_dir ? (openBlock(), createElementBlock("svg", _hoisted_53, [..._cache[11] || (_cache[11] = [createElementVNode("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }, null, -1)])])) : (openBlock(), createElementBlock("span", {
|
|
2731
3123
|
key: 4,
|
|
2732
3124
|
class: "fl-type",
|
|
2733
3125
|
"data-kind": unref(fileKind)(node.name),
|
|
2734
3126
|
"aria-hidden": "true"
|
|
2735
|
-
}, toDisplayString(unref(extOf)(node.name) || "FILE"), 9,
|
|
2736
|
-
createElementVNode("span",
|
|
3127
|
+
}, toDisplayString(unref(extOf)(node.name) || "FILE"), 9, _hoisted_54)),
|
|
3128
|
+
createElementVNode("span", _hoisted_55, [createElementVNode("span", {
|
|
2737
3129
|
class: normalizeClass(["fl-name", { "fl-name-dir": node.is_dir }]),
|
|
2738
3130
|
onClick: ($event) => node.lock || node.is_dir ? toggleExpand(node) : download(node)
|
|
2739
|
-
},
|
|
2740
|
-
|
|
3131
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(nameParts(node.name), (part, at) => {
|
|
3132
|
+
return openBlock(), createElementBlock(Fragment, { key: at }, [part.hit ? (openBlock(), createElementBlock("mark", _hoisted_57, toDisplayString(part.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
3133
|
+
}), 128))], 10, _hoisted_56), createElementVNode("span", _hoisted_58, [!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)) : lockedInSearch(node) ? (openBlock(), createElementBlock("span", _hoisted_59, toDisplayString(t("deepLocked")), 1)) : createCommentVNode("v-if", true)])]),
|
|
3134
|
+
lockedInSearch(node) && !node.lock?.prompting ? (openBlock(), createElementBlock("span", _hoisted_60, toDisplayString(t("deepLocked")), 1)) : createCommentVNode("v-if", true),
|
|
3135
|
+
node.is_dir && node.loaded ? (openBlock(), createElementBlock("span", _hoisted_61, toDisplayString(t("folderItems", { count: statsOf(node).files })), 1)) : createCommentVNode("v-if", true),
|
|
2741
3136
|
node.lock?.prompting && node.lock.error ? (openBlock(), createElementBlock("span", {
|
|
2742
|
-
key:
|
|
3137
|
+
key: 7,
|
|
2743
3138
|
id: `${unref(uid)}-error-${index}`,
|
|
2744
3139
|
class: "fl-unlock-message",
|
|
2745
3140
|
role: "alert"
|
|
2746
|
-
}, toDisplayString(unlockErrorText(node.lock.error)), 9,
|
|
3141
|
+
}, toDisplayString(unlockErrorText(node.lock.error)), 9, _hoisted_62)) : createCommentVNode("v-if", true),
|
|
2747
3142
|
!node.lock && unref(remembered).has(node.path) ? (openBlock(), createElementBlock("button", {
|
|
2748
|
-
key:
|
|
3143
|
+
key: 8,
|
|
2749
3144
|
class: "fl-remembered",
|
|
2750
3145
|
type: "button",
|
|
2751
3146
|
"aria-label": `${t("forgetPassword")} ${node.name}`,
|
|
@@ -2754,9 +3149,9 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2754
3149
|
}, [createVNode(_sfc_main$2, {
|
|
2755
3150
|
name: "key",
|
|
2756
3151
|
size: 11
|
|
2757
|
-
}), createTextVNode(" " + toDisplayString(t("remembered")) + " · " + toDisplayString(t("forgetPassword")), 1)], 8,
|
|
2758
|
-
node.loading ? (openBlock(), createElementBlock("span",
|
|
2759
|
-
], 8,
|
|
3152
|
+
}), createTextVNode(" " + toDisplayString(t("remembered")) + " · " + toDisplayString(t("forgetPassword")), 1)], 8, _hoisted_63)) : createCommentVNode("v-if", true),
|
|
3153
|
+
node.loading ? (openBlock(), createElementBlock("span", _hoisted_64, "…")) : createCommentVNode("v-if", true)
|
|
3154
|
+
], 8, _hoisted_49),
|
|
2760
3155
|
node.lock?.prompting ? (openBlock(), createElementBlock("form", {
|
|
2761
3156
|
key: 0,
|
|
2762
3157
|
class: "fl-unlock fl-unlock-row",
|
|
@@ -2777,7 +3172,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2777
3172
|
"aria-label": t("passwordFor", { name: node.name }),
|
|
2778
3173
|
"aria-invalid": !!node.lock.error,
|
|
2779
3174
|
"aria-describedby": node.lock.error ? `${unref(uid)}-error-${index}` : void 0
|
|
2780
|
-
}, null, 8,
|
|
3175
|
+
}, null, 8, _hoisted_66), [[vModelText, node.lock.input]]),
|
|
2781
3176
|
canRemember.value ? (openBlock(), createElementBlock("button", {
|
|
2782
3177
|
key: 0,
|
|
2783
3178
|
class: "fl-unlock-remember",
|
|
@@ -2786,12 +3181,12 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2786
3181
|
"aria-label": t("rememberPassword"),
|
|
2787
3182
|
title: t("rememberPassword"),
|
|
2788
3183
|
onClick: ($event) => node.lock.remember = !node.lock.remember
|
|
2789
|
-
}, [createVNode(_sfc_main$2, { name: "key" })], 8,
|
|
3184
|
+
}, [createVNode(_sfc_main$2, { name: "key" })], 8, _hoisted_67)) : createCommentVNode("v-if", true),
|
|
2790
3185
|
createElementVNode("button", {
|
|
2791
3186
|
class: "fl-unlock-submit",
|
|
2792
3187
|
type: "submit",
|
|
2793
3188
|
disabled: node.lock.busy || !node.lock.input
|
|
2794
|
-
}, toDisplayString(t("open")), 9,
|
|
3189
|
+
}, toDisplayString(t("open")), 9, _hoisted_68),
|
|
2795
3190
|
createElementVNode("button", {
|
|
2796
3191
|
class: "fl-unlock-cancel",
|
|
2797
3192
|
type: "button",
|
|
@@ -2801,11 +3196,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2801
3196
|
}, [createVNode(_sfc_main$2, {
|
|
2802
3197
|
name: "close",
|
|
2803
3198
|
size: 14
|
|
2804
|
-
})], 8,
|
|
2805
|
-
], 40,
|
|
2806
|
-
createElementVNode("span",
|
|
2807
|
-
createElementVNode("span",
|
|
2808
|
-
createElementVNode("span",
|
|
3199
|
+
})], 8, _hoisted_69)
|
|
3200
|
+
], 40, _hoisted_65)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
3201
|
+
createElementVNode("span", _hoisted_70, toDisplayString(node.is_dir ? "—" : unref(formatSize)(node.size)), 1),
|
|
3202
|
+
createElementVNode("span", _hoisted_71, toDisplayString(unref(formatDate)(node.modified)), 1),
|
|
3203
|
+
createElementVNode("span", _hoisted_72, [node.lock ? (openBlock(), createElementBlock("button", {
|
|
2809
3204
|
key: 0,
|
|
2810
3205
|
class: "fl-action fl-arrow-end fl-lock",
|
|
2811
3206
|
type: "button",
|
|
@@ -2814,7 +3209,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2814
3209
|
}, [createVNode(_sfc_main$2, {
|
|
2815
3210
|
name: "lock",
|
|
2816
3211
|
size: 18
|
|
2817
|
-
})], 8,
|
|
3212
|
+
})], 8, _hoisted_73)) : node.is_dir ? (openBlock(), createElementBlock("button", {
|
|
2818
3213
|
key: 1,
|
|
2819
3214
|
class: normalizeClass(["fl-action fl-arrow-end", { "fl-arrow-open": node.expanded }]),
|
|
2820
3215
|
type: "button",
|
|
@@ -2824,25 +3219,38 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2824
3219
|
}, [createVNode(_sfc_main$2, {
|
|
2825
3220
|
name: "next",
|
|
2826
3221
|
size: 18
|
|
2827
|
-
})], 10,
|
|
3222
|
+
})], 10, _hoisted_74)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [canPreview(node) ? (openBlock(), createElementBlock("button", {
|
|
2828
3223
|
key: 0,
|
|
2829
3224
|
class: "fl-action fl-preview-btn",
|
|
2830
3225
|
type: "button",
|
|
2831
3226
|
"aria-label": `${t("preview")} ${node.name}`,
|
|
2832
3227
|
title: t("preview"),
|
|
2833
3228
|
onClick: ($event) => preview(node)
|
|
2834
|
-
}, [createVNode(_sfc_main$2, { name: "eye" })], 8,
|
|
3229
|
+
}, [createVNode(_sfc_main$2, { name: "eye" })], 8, _hoisted_75)) : createCommentVNode("v-if", true), createElementVNode("button", {
|
|
2835
3230
|
class: "fl-action fl-download",
|
|
2836
3231
|
type: "button",
|
|
2837
3232
|
"aria-label": `${t("download")} ${node.name}`,
|
|
2838
3233
|
title: t("download"),
|
|
2839
3234
|
onClick: ($event) => download(node)
|
|
2840
|
-
}, [createVNode(_sfc_main$2, { name: "download" })], 8,
|
|
3235
|
+
}, [createVNode(_sfc_main$2, { name: "download" })], 8, _hoisted_76)], 64))])
|
|
2841
3236
|
], 64))
|
|
2842
3237
|
], 6))], 64);
|
|
2843
3238
|
}), 128)),
|
|
2844
|
-
|
|
2845
|
-
|
|
3239
|
+
deepRow.value ? (openBlock(), createElementBlock("div", _hoisted_77, [createElementVNode("p", _hoisted_78, [deepRow.value === "running" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(t("deepRunning", { count: folderCounts.value.checked })) + " · ", 1), createElementVNode("button", {
|
|
3240
|
+
class: "fl-deep-link",
|
|
3241
|
+
type: "button",
|
|
3242
|
+
onClick: pauseDeepSearch
|
|
3243
|
+
}, toDisplayString(t("deepStop")), 1)], 64)) : deepRow.value === "idle" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(t("deepHint")), 1), createElementVNode("button", {
|
|
3244
|
+
class: "fl-deep-link",
|
|
3245
|
+
type: "button",
|
|
3246
|
+
onClick: deepSearch
|
|
3247
|
+
}, toDisplayString(t("deepSearch")), 1)], 64)) : deepRow.value === "capped" ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createTextVNode(toDisplayString(deepSummary.value) + " · ", 1), createElementVNode("button", {
|
|
3248
|
+
class: "fl-deep-link",
|
|
3249
|
+
type: "button",
|
|
3250
|
+
onClick: deepSearch
|
|
3251
|
+
}, toDisplayString(t("deepContinue")), 1)], 64)) : (openBlock(), createElementBlock(Fragment, { key: 3 }, [createTextVNode(toDisplayString(deepSummary.value), 1)], 64))]), deepRow.value === "running" ? (openBlock(), createElementBlock("span", _hoisted_79)) : createCommentVNode("v-if", true)])) : createCommentVNode("v-if", true),
|
|
3252
|
+
createElementVNode("div", _hoisted_80, [createElementVNode("span", null, toDisplayString(t("loaded", { count: allFiles.value.length })) + " · " + toDisplayString(t("totalSize", { size: unref(formatSize)(unref(totalSize)(allFiles.value)) })), 1)]),
|
|
3253
|
+
toast.value ? (openBlock(), createElementBlock("div", _hoisted_81, toDisplayString(toast.value), 1)) : createCommentVNode("v-if", true),
|
|
2846
3254
|
createVNode(_sfc_main$3, {
|
|
2847
3255
|
open: !!zipDialog.value,
|
|
2848
3256
|
title: zipDialog.value?.title ?? "",
|
|
@@ -2851,7 +3259,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2851
3259
|
"confirm-label": zipDialog.value?.confirm,
|
|
2852
3260
|
"cancel-label": zipDialog.value?.cancel ?? "",
|
|
2853
3261
|
onConfirm: confirmZip,
|
|
2854
|
-
onClose: _cache[
|
|
3262
|
+
onClose: _cache[5] || (_cache[5] = ($event) => zipNotice.value = null)
|
|
2855
3263
|
}, null, 8, [
|
|
2856
3264
|
"open",
|
|
2857
3265
|
"title",
|
|
@@ -2872,9 +3280,9 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2872
3280
|
position: previewPosition.value,
|
|
2873
3281
|
total: previewPosition.value ? previewList.value.length : 0,
|
|
2874
3282
|
onClose: closePreview,
|
|
2875
|
-
onPrev: _cache[
|
|
2876
|
-
onNext: _cache[
|
|
2877
|
-
onDownload: _cache[
|
|
3283
|
+
onPrev: _cache[6] || (_cache[6] = ($event) => stepPreview(-1)),
|
|
3284
|
+
onNext: _cache[7] || (_cache[7] = ($event) => stepPreview(1)),
|
|
3285
|
+
onDownload: _cache[8] || (_cache[8] = ($event) => previewNode.value && download(previewNode.value))
|
|
2878
3286
|
}, null, 8, [
|
|
2879
3287
|
"open",
|
|
2880
3288
|
"file",
|