vitepress-plugin-filelist 0.2.2 → 0.3.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.
@@ -1,5 +1,5 @@
1
1
  import "../style.css";
2
- import { Fragment, computed, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, normalizeClass, normalizeStyle, onMounted, openBlock, ref, renderList, toDisplayString, unref, vModelText, withDirectives, withKeys } from "vue";
2
+ import { Fragment, computed, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, toDisplayString, unref, vModelSelect, vModelText, withDirectives, withKeys } from "vue";
3
3
  // src/client/locales.ts
4
4
  const fileListLocales = {
5
5
  "zh-CN": {
@@ -7,7 +7,7 @@ const fileListLocales = {
7
7
  demoBadge: "演示数据",
8
8
  filter: "筛选文件名",
9
9
  refresh: "刷新",
10
- batch: "批量下载",
10
+ batch: "下载 ZIP",
11
11
  batchCount: "批量下载({count})",
12
12
  batching: "打包中 {percent}%",
13
13
  passwordPrompt: "该目录受密码保护:",
@@ -34,7 +34,16 @@ const fileListLocales = {
34
34
  downloadFailed: "下载失败:{message}",
35
35
  previewFailed: "预览失败:{message}",
36
36
  batchFailed: "打包失败:{message}",
37
- defaultZip: "资源"
37
+ defaultZip: "资源",
38
+ clear: "清除",
39
+ clearSelection: "取消选择",
40
+ totalSize: "共 {size}",
41
+ folderItems: "{count} 项",
42
+ sortBy: "排序",
43
+ selectAll: "全选",
44
+ selectItem: "选择 {name}",
45
+ expand: "展开 {name}",
46
+ collapse: "收起 {name}"
38
47
  },
39
48
  "en-US": {
40
49
  allFiles: "All files",
@@ -68,7 +77,16 @@ const fileListLocales = {
68
77
  downloadFailed: "Download failed: {message}",
69
78
  previewFailed: "Preview failed: {message}",
70
79
  batchFailed: "Failed to create ZIP: {message}",
71
- defaultZip: "resources"
80
+ defaultZip: "resources",
81
+ clear: "Clear",
82
+ clearSelection: "Clear selection",
83
+ totalSize: "{size} total",
84
+ folderItems: "{count} items",
85
+ sortBy: "Sort",
86
+ selectAll: "Select all",
87
+ selectItem: "Select {name}",
88
+ expand: "Expand {name}",
89
+ collapse: "Collapse {name}"
72
90
  }
73
91
  };
74
92
  function formatLabel(template, values = {}) {
@@ -87,6 +105,41 @@ function joinPath(parent, name) {
87
105
  function encodePath(path) {
88
106
  return path.split("/").map(encodeURIComponent).join("/");
89
107
  }
108
+ /**
109
+ * Make one path segment safe as a ZIP entry segment: no separators,
110
+ * no `.` / `..` traversal, no empty names.
111
+ */
112
+ function zipSegment(name) {
113
+ const cleaned = name.replace(/[\\/]/g, "_").trim();
114
+ if (!cleaned || cleaned === "." || cleaned === "..") return "_";
115
+ return cleaned;
116
+ }
117
+ /** Join path segments into a ZIP entry name, sanitising each segment. */
118
+ function zipEntryName(segments) {
119
+ return segments.map(zipSegment).join("/");
120
+ }
121
+ /**
122
+ * Return `name`, or `name (2)`, `name (3)`... when it is already in `used`.
123
+ * The suffix goes before the extension of the last segment. Adds the result to `used`.
124
+ */
125
+ function uniqueEntryName(name, used) {
126
+ let candidate = name;
127
+ if (used.has(candidate)) {
128
+ const slash = name.lastIndexOf("/");
129
+ const dir = name.slice(0, slash + 1);
130
+ const base = name.slice(slash + 1);
131
+ const dot = base.lastIndexOf(".");
132
+ const stem = dot > 0 ? base.slice(0, dot) : base;
133
+ const ext = dot > 0 ? base.slice(dot) : "";
134
+ let index = 2;
135
+ do {
136
+ candidate = `${dir}${stem} (${index})${ext}`;
137
+ index += 1;
138
+ } while (used.has(candidate));
139
+ }
140
+ used.add(candidate);
141
+ return candidate;
142
+ }
90
143
  function formatSize(size) {
91
144
  if (!size || !Number.isFinite(size)) return "-";
92
145
  const units = [
@@ -116,6 +169,97 @@ function extOf(name) {
116
169
  const match = name.match(/\.([a-z0-9]+)$/i);
117
170
  return match ? match[1].toUpperCase() : "";
118
171
  }
172
+ const FILE_KINDS = {};
173
+ for (const [kind, exts] of Object.entries({
174
+ doc: [
175
+ "doc",
176
+ "docx",
177
+ "odt",
178
+ "rtf",
179
+ "txt",
180
+ "md",
181
+ "pages",
182
+ "wps"
183
+ ],
184
+ sheet: [
185
+ "xls",
186
+ "xlsx",
187
+ "ods",
188
+ "csv",
189
+ "tsv",
190
+ "numbers",
191
+ "et"
192
+ ],
193
+ slide: [
194
+ "ppt",
195
+ "pptx",
196
+ "odp",
197
+ "key",
198
+ "dps"
199
+ ],
200
+ pdf: ["pdf"],
201
+ image: [
202
+ "png",
203
+ "jpg",
204
+ "jpeg",
205
+ "gif",
206
+ "webp",
207
+ "svg",
208
+ "bmp",
209
+ "ico",
210
+ "avif",
211
+ "heic"
212
+ ],
213
+ media: [
214
+ "mp3",
215
+ "wav",
216
+ "flac",
217
+ "aac",
218
+ "ogg",
219
+ "m4a",
220
+ "mp4",
221
+ "webm",
222
+ "mov",
223
+ "mkv",
224
+ "avi"
225
+ ],
226
+ archive: [
227
+ "zip",
228
+ "rar",
229
+ "7z",
230
+ "tar",
231
+ "gz",
232
+ "tgz",
233
+ "bz2",
234
+ "xz",
235
+ "iso"
236
+ ],
237
+ code: [
238
+ "js",
239
+ "ts",
240
+ "json",
241
+ "html",
242
+ "css",
243
+ "py",
244
+ "java",
245
+ "c",
246
+ "cpp",
247
+ "go",
248
+ "rs",
249
+ "sh",
250
+ "yml",
251
+ "yaml",
252
+ "xml"
253
+ ]
254
+ })) for (const ext of exts) FILE_KINDS[ext] = kind;
255
+ /** Rough file family, used to colour the type badge. */
256
+ function fileKind(name) {
257
+ return FILE_KINDS[extOf(name).toLowerCase()] ?? "other";
258
+ }
259
+ /** Sum of the `size` of the given entries, ignoring missing or invalid sizes. */
260
+ function totalSize(items) {
261
+ return items.reduce((sum, item) => sum + (Number.isFinite(item.size) && (item.size ?? 0) > 0 ? item.size : 0), 0);
262
+ }
119
263
  function compareNodes(a, b, key, ascending = true) {
120
264
  if (a.is_dir !== b.is_dir) return a.is_dir ? -1 : 1;
121
265
  let result = 0;
@@ -125,83 +269,152 @@ function compareNodes(a, b, key, ascending = true) {
125
269
  return ascending ? result : -result;
126
270
  }
127
271
  // src/client/FileList.vue
128
- const _hoisted_1 = { class: "fl-root" };
129
- const _hoisted_2 = { class: "fl-bar" };
130
- const _hoisted_3 = { class: "fl-title" };
131
- const _hoisted_4 = { class: "fl-title-text" };
272
+ const _hoisted_1 = { class: "fl-bar" };
273
+ const _hoisted_2 = {
274
+ key: 0,
275
+ class: "fl-selection",
276
+ role: "status"
277
+ };
278
+ const _hoisted_3 = { class: "fl-selection-count" };
279
+ const _hoisted_4 = { class: "fl-selection-size" };
132
280
  const _hoisted_5 = {
281
+ key: 1,
282
+ class: "fl-title"
283
+ };
284
+ const _hoisted_6 = { class: "fl-title-text" };
285
+ const _hoisted_7 = {
133
286
  key: 0,
134
287
  class: "fl-demo-badge"
135
288
  };
136
- const _hoisted_6 = { class: "fl-tools" };
137
- const _hoisted_7 = ["placeholder"];
138
- const _hoisted_8 = ["disabled"];
139
- const _hoisted_9 = {
289
+ const _hoisted_8 = ["aria-label", "title"];
290
+ const _hoisted_9 = { class: "fl-clear-text" };
291
+ const _hoisted_10 = { class: "fl-tools" };
292
+ const _hoisted_11 = { class: "fl-search" };
293
+ const _hoisted_12 = ["placeholder", "aria-label"];
294
+ const _hoisted_13 = ["aria-label", "title"];
295
+ const _hoisted_14 = ["disabled"];
296
+ const _hoisted_15 = {
140
297
  key: 0,
141
298
  class: "fl-password"
142
299
  };
143
- const _hoisted_10 = ["placeholder"];
144
- const _hoisted_11 = { class: "fl-row fl-head" };
145
- const _hoisted_12 = { class: "fl-cell fl-cell-check" };
146
- const _hoisted_13 = ["checked", ".indeterminate"];
147
- const _hoisted_14 = { class: "fl-sort" };
148
- const _hoisted_15 = { class: "fl-sort" };
149
- const _hoisted_16 = { class: "fl-sort" };
150
- const _hoisted_17 = {
300
+ const _hoisted_16 = ["placeholder", "aria-label"];
301
+ const _hoisted_17 = { class: "fl-row fl-head" };
302
+ const _hoisted_18 = { class: "fl-cell fl-cell-check" };
303
+ const _hoisted_19 = [
304
+ "aria-label",
305
+ "checked",
306
+ ".indeterminate"
307
+ ];
308
+ const _hoisted_20 = { class: "fl-cell fl-cell-name" };
309
+ const _hoisted_21 = ["aria-sort"];
310
+ const _hoisted_22 = {
311
+ class: "fl-sort",
312
+ "aria-hidden": "true"
313
+ };
314
+ const _hoisted_23 = { class: "fl-cell fl-cell-size" };
315
+ const _hoisted_24 = {
316
+ class: "fl-sort",
317
+ "aria-hidden": "true"
318
+ };
319
+ const _hoisted_25 = { class: "fl-cell fl-cell-date" };
320
+ const _hoisted_26 = {
321
+ class: "fl-sort",
322
+ "aria-hidden": "true"
323
+ };
324
+ const _hoisted_27 = { class: "fl-cell fl-cell-op" };
325
+ const _hoisted_28 = { class: "fl-sort-select" };
326
+ const _hoisted_29 = {
327
+ class: "fl-sort-current",
328
+ "aria-hidden": "true"
329
+ };
330
+ const _hoisted_30 = ["aria-label"];
331
+ const _hoisted_31 = ["value"];
332
+ const _hoisted_32 = {
151
333
  key: 1,
152
334
  class: "fl-status"
153
335
  };
154
- const _hoisted_18 = {
336
+ const _hoisted_33 = {
155
337
  key: 2,
156
338
  class: "fl-status fl-error"
157
339
  };
158
- const _hoisted_19 = {
340
+ const _hoisted_34 = {
159
341
  key: 0,
160
342
  class: "fl-hint"
161
343
  };
162
- const _hoisted_20 = {
344
+ const _hoisted_35 = {
163
345
  key: 1,
164
346
  class: "fl-hint"
165
347
  };
166
- const _hoisted_21 = {
348
+ const _hoisted_36 = {
167
349
  key: 3,
168
350
  class: "fl-status"
169
351
  };
170
- const _hoisted_22 = { class: "fl-cell fl-cell-check" };
171
- const _hoisted_23 = [
352
+ const _hoisted_37 = { class: "fl-cell fl-cell-check" };
353
+ const _hoisted_38 = [
354
+ "aria-label",
172
355
  "checked",
173
356
  ".indeterminate",
174
357
  "onChange"
175
358
  ];
176
- const _hoisted_24 = ["title"];
177
- const _hoisted_25 = ["onClick"];
178
- const _hoisted_26 = {
359
+ const _hoisted_39 = ["title"];
360
+ const _hoisted_40 = [
361
+ "aria-label",
362
+ "aria-expanded",
363
+ "onClick"
364
+ ];
365
+ const _hoisted_41 = {
179
366
  key: 1,
180
- class: "fl-arrow-placeholder"
367
+ class: "fl-arrow-placeholder",
368
+ "aria-hidden": "true"
181
369
  };
182
- const _hoisted_27 = ["onClick"];
183
- const _hoisted_28 = {
184
- key: 0,
185
- class: "fl-folder-icon"
370
+ const _hoisted_42 = {
371
+ key: 2,
372
+ class: "fl-folder-icon",
373
+ viewBox: "0 0 24 24",
374
+ width: "18",
375
+ height: "18",
376
+ "aria-hidden": "true"
186
377
  };
187
- const _hoisted_29 = {
188
- key: 1,
189
- class: "fl-ext-badge"
378
+ const _hoisted_43 = ["data-kind"];
379
+ const _hoisted_44 = { class: "fl-label" };
380
+ const _hoisted_45 = ["onClick"];
381
+ const _hoisted_46 = { class: "fl-meta" };
382
+ const _hoisted_47 = {
383
+ key: 4,
384
+ class: "fl-count"
190
385
  };
191
- const _hoisted_30 = {
192
- key: 2,
193
- class: "fl-loading"
386
+ const _hoisted_48 = {
387
+ key: 5,
388
+ class: "fl-loading",
389
+ "aria-hidden": "true"
194
390
  };
195
- const _hoisted_31 = { class: "fl-cell fl-cell-size" };
196
- const _hoisted_32 = { class: "fl-cell fl-cell-date" };
197
- const _hoisted_33 = { class: "fl-cell fl-cell-op" };
198
- const _hoisted_34 = ["title", "onClick"];
199
- const _hoisted_35 = ["title", "onClick"];
200
- const _hoisted_36 = { class: "fl-foot" };
201
- const _hoisted_37 = { key: 0 };
202
- const _hoisted_38 = {
391
+ const _hoisted_49 = { class: "fl-cell fl-cell-size" };
392
+ const _hoisted_50 = { class: "fl-cell fl-cell-date" };
393
+ const _hoisted_51 = { class: "fl-cell fl-cell-op" };
394
+ const _hoisted_52 = [
395
+ "aria-label",
396
+ "aria-expanded",
397
+ "onClick"
398
+ ];
399
+ const _hoisted_53 = [
400
+ "aria-label",
401
+ "title",
402
+ "onClick"
403
+ ];
404
+ const _hoisted_54 = [
405
+ "aria-label",
406
+ "title",
407
+ "onClick"
408
+ ];
409
+ const _hoisted_55 = { class: "fl-foot" };
410
+ const _hoisted_56 = {
411
+ key: 0,
412
+ class: "fl-foot-hint"
413
+ };
414
+ const _hoisted_57 = {
203
415
  key: 4,
204
- class: "fl-toast"
416
+ class: "fl-toast",
417
+ role: "status"
205
418
  };
206
419
  const _sfc_main = /*@__PURE__*/ defineComponent({
207
420
  __name: "FileList",
@@ -258,6 +471,9 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
258
471
  function errorText(error) {
259
472
  return error instanceof Error ? error.message : String(error);
260
473
  }
474
+ function isPasswordError(error) {
475
+ return /password|密码/i.test(errorText(error));
476
+ }
261
477
  const DEMO_TREE = {
262
478
  "/": [
263
479
  {
@@ -472,7 +688,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
472
688
  nodes.sort((a, b) => compareNodes(a, b, sortKey.value, sortAsc.value));
473
689
  for (const node of nodes) if (node.children.length) sortTree(node.children);
474
690
  }
475
- function toNodes(items, parent, depth, password = "") {
691
+ function toNodes(items, parent, depth, password = "", zipParent = []) {
476
692
  return items.map((item) => ({
477
693
  ...item,
478
694
  path: joinPath(parent, item.name),
@@ -481,7 +697,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
481
697
  loaded: false,
482
698
  loading: false,
483
699
  children: [],
484
- password
700
+ password,
701
+ zipPath: [...zipParent, item.name]
485
702
  }));
486
703
  }
487
704
  function setSort(key) {
@@ -490,6 +707,9 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
490
707
  sortKey.value = key;
491
708
  sortAsc.value = true;
492
709
  }
710
+ resortAll();
711
+ }
712
+ function resortAll() {
493
713
  if (props.sources.length) {
494
714
  for (const root of roots.value) if (root.children.length) sortTree(root.children);
495
715
  } else sortTree(roots.value);
@@ -502,11 +722,12 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
502
722
  if (node.loaded) return true;
503
723
  node.loading = true;
504
724
  try {
505
- node.children = toNodes(await listDir(node.path, node.password), node.path, node.depth + 1, node.password);
725
+ node.children = toNodes(await listDir(node.path, node.password || activePassword.value), node.path, node.depth + 1, node.password, node.zipPath);
506
726
  sortTree(node.children);
507
727
  node.loaded = true;
508
728
  return true;
509
729
  } catch (error) {
730
+ if (isPasswordError(error)) needsPassword.value = true;
510
731
  showToast(t("folderFailed", { message: errorText(error) }));
511
732
  return false;
512
733
  } finally {
@@ -531,16 +752,17 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
531
752
  } else try {
532
753
  const data = await request("/api/fs/get", {
533
754
  path,
534
- password
755
+ password: password || activePassword.value
535
756
  });
536
757
  item = {
537
758
  ...item,
538
759
  ...data
539
760
  };
540
761
  } catch {}
762
+ const name = source.title || item.name || sourceName(path);
541
763
  return {
542
764
  ...item,
543
- name: source.title || item.name || sourceName(path),
765
+ name,
544
766
  path,
545
767
  depth: 0,
546
768
  expanded: source.expanded ?? props.expanded,
@@ -548,7 +770,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
548
770
  loading: false,
549
771
  children: [],
550
772
  password,
551
- isRoot: true
773
+ isRoot: true,
774
+ zipPath: [item.is_dir ? name : item.name || sourceName(path)]
552
775
  };
553
776
  }
554
777
  async function loadRoot() {
@@ -571,7 +794,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
571
794
  downloadCache.clear();
572
795
  } catch (error) {
573
796
  const message = errorText(error);
574
- if (/password|密码/i.test(message)) needsPassword.value = true;
797
+ if (isPasswordError(error)) needsPassword.value = true;
575
798
  else errorMsg.value = message;
576
799
  } finally {
577
800
  loadingRoot.value = false;
@@ -613,10 +836,27 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
613
836
  const hit = files.filter((file) => selected.value.has(file.path)).length;
614
837
  return hit > 0 && hit < files.length;
615
838
  }
616
- async function toggleCheck(node) {
617
- if (node.is_dir && !node.loaded) await toggleExpand(node);
839
+ /** Load a directory and all of its sub-directories. */
840
+ async function loadTree(node) {
841
+ if (!node.is_dir) return true;
842
+ if (!await loadChildren(node)) return false;
843
+ for (const child of node.children) if (child.is_dir && !await loadTree(child)) return false;
844
+ return true;
845
+ }
846
+ async function toggleCheck(node, event) {
847
+ const input = event?.target;
848
+ if (node.is_dir && !isChecked(node)) {
849
+ if (await loadTree(node)) node.expanded = true;
850
+ else {
851
+ if (input) input.checked = isChecked(node);
852
+ return;
853
+ }
854
+ }
618
855
  const files = descendantFiles(node);
619
- if (!files.length) return;
856
+ if (!files.length) {
857
+ if (input) input.checked = false;
858
+ return;
859
+ }
620
860
  const next = new Set(selected.value);
621
861
  if (files.every((file) => next.has(file.path))) files.forEach((file) => next.delete(file.path));
622
862
  else files.forEach((file) => next.add(file.path));
@@ -625,6 +865,39 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
625
865
  const allFiles = computed(() => roots.value.flatMap(descendantFiles));
626
866
  const allChecked = computed(() => allFiles.value.length > 0 && allFiles.value.every((file) => selected.value.has(file.path)));
627
867
  const someChecked = computed(() => !allChecked.value && allFiles.value.some((file) => selected.value.has(file.path)));
868
+ const selectedFiles = computed(() => allFiles.value.filter((file) => selected.value.has(file.path)));
869
+ const selectedBytes = computed(() => totalSize(selectedFiles.value));
870
+ const loadedBytes = computed(() => totalSize(allFiles.value));
871
+ function clearSelection() {
872
+ selected.value = /* @__PURE__ */ new Set();
873
+ }
874
+ function folderFiles(node) {
875
+ return node.is_dir && node.loaded ? descendantFiles(node) : [];
876
+ }
877
+ /** Compact sort control for narrow containers, where header cells are hidden. */
878
+ const sortOptions = computed(() => [
879
+ "name",
880
+ "size",
881
+ "modified"
882
+ ].flatMap((key) => {
883
+ const label = t(key === "name" ? "columnName" : key === "size" ? "columnSize" : "columnDate");
884
+ return [{
885
+ value: `${key}:asc`,
886
+ label: `${label} ↑`
887
+ }, {
888
+ value: `${key}:desc`,
889
+ label: `${label} ↓`
890
+ }];
891
+ }));
892
+ const sortValue = computed({
893
+ get: () => `${sortKey.value}:${sortAsc.value ? "asc" : "desc"}`,
894
+ set: (value) => {
895
+ const [key, direction] = value.split(":");
896
+ sortKey.value = key;
897
+ sortAsc.value = direction !== "desc";
898
+ resortAll();
899
+ }
900
+ });
628
901
  function toggleCheckAll() {
629
902
  selected.value = allChecked.value ? /* @__PURE__ */ new Set() : new Set(allFiles.value.map((file) => file.path));
630
903
  }
@@ -696,6 +969,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
696
969
  batchProgress.value = 0;
697
970
  try {
698
971
  const entries = {};
972
+ const usedNames = /* @__PURE__ */ new Set();
699
973
  const queue = [...files];
700
974
  let done = 0;
701
975
  const worker = async () => {
@@ -704,7 +978,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
704
978
  if (!file) break;
705
979
  const response = await fetch(await resolveUrl(file));
706
980
  if (!response.ok) throw new Error(`${file.name}:HTTP ${response.status}`);
707
- entries[file.name] = new Uint8Array(await response.arrayBuffer());
981
+ entries[uniqueEntryName(zipEntryName(file.zipPath), usedNames)] = new Uint8Array(await response.arrayBuffer());
708
982
  done += 1;
709
983
  batchProgress.value = Math.round(done / files.length * 90);
710
984
  }
@@ -729,124 +1003,258 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
729
1003
  loadRoot();
730
1004
  }
731
1005
  const displayTitle = computed(() => props.title || siteTitle.value || (props.path === "/" ? t("allFiles") : props.path));
1006
+ const stickyTop = ref(0);
1007
+ function measureStickyTop() {
1008
+ if (typeof document === "undefined") return;
1009
+ let top = 0;
1010
+ for (const el of document.querySelectorAll(".VPNav, .VPLocalNav")) {
1011
+ const style = getComputedStyle(el);
1012
+ if (style.display === "none" || style.position !== "fixed" && style.position !== "sticky") continue;
1013
+ top = Math.max(top, (Number.parseFloat(style.top) || 0) + el.offsetHeight);
1014
+ }
1015
+ stickyTop.value = top;
1016
+ }
732
1017
  onMounted(() => {
733
1018
  loadRoot();
734
1019
  loadSiteTitle();
1020
+ measureStickyTop();
1021
+ window.addEventListener("resize", measureStickyTop, { passive: true });
1022
+ });
1023
+ onBeforeUnmount(() => {
1024
+ window.removeEventListener("resize", measureStickyTop);
1025
+ clearTimeout(toastTimer);
735
1026
  });
736
1027
  return (_ctx, _cache) => {
737
- return openBlock(), createElementBlock("div", _hoisted_1, [
738
- createElementVNode("div", _hoisted_2, [createElementVNode("div", _hoisted_3, [
739
- _cache[5] || (_cache[5] = createElementVNode("span", { class: "fl-title-icon" }, "🗂️", -1)),
740
- createElementVNode("span", _hoisted_4, toDisplayString(displayTitle.value), 1),
741
- demo.value ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(t("demoBadge")), 1)) : createCommentVNode("v-if", true)
742
- ]), createElementVNode("div", _hoisted_6, [
743
- withDirectives(createElementVNode("input", {
1028
+ return openBlock(), createElementBlock("div", {
1029
+ class: normalizeClass(["fl-root", { "fl-selecting": selected.value.size > 0 }]),
1030
+ style: normalizeStyle({ "--fl-sticky-top-auto": `${stickyTop.value}px` })
1031
+ }, [
1032
+ createElementVNode("div", _hoisted_1, [
1033
+ selected.value.size ? (openBlock(), createElementBlock("div", _hoisted_2, [createElementVNode("span", _hoisted_3, toDisplayString(t("selected", { count: selected.value.size })), 1), createElementVNode("span", _hoisted_4, toDisplayString(t("totalSize", { size: unref(formatSize)(selectedBytes.value) })), 1)])) : (openBlock(), createElementBlock("div", _hoisted_5, [createElementVNode("span", _hoisted_6, toDisplayString(displayTitle.value), 1), demo.value ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(t("demoBadge")), 1)) : createCommentVNode("v-if", true)])),
1034
+ selected.value.size ? (openBlock(), createElementBlock("button", {
1035
+ key: 2,
1036
+ class: "fl-clear",
1037
+ type: "button",
1038
+ "aria-label": t("clearSelection"),
1039
+ title: t("clearSelection"),
1040
+ onClick: clearSelection
1041
+ }, [_cache[6] || (_cache[6] = createElementVNode("svg", {
1042
+ class: "fl-clear-icon",
1043
+ viewBox: "0 0 24 24",
1044
+ width: "18",
1045
+ height: "18",
1046
+ fill: "none",
1047
+ stroke: "currentColor",
1048
+ "stroke-width": "2.2",
1049
+ "stroke-linecap": "round",
1050
+ "aria-hidden": "true"
1051
+ }, [createElementVNode("path", { d: "M6 6l12 12" }), createElementVNode("path", { d: "M18 6 6 18" })], -1)), createElementVNode("span", _hoisted_9, toDisplayString(t("clear")), 1)], 8, _hoisted_8)) : createCommentVNode("v-if", true),
1052
+ createElementVNode("div", _hoisted_10, [createElementVNode("label", _hoisted_11, [_cache[7] || (_cache[7] = createElementVNode("svg", {
1053
+ viewBox: "0 0 24 24",
1054
+ width: "15",
1055
+ height: "15",
1056
+ fill: "none",
1057
+ stroke: "currentColor",
1058
+ "stroke-width": "2",
1059
+ "stroke-linecap": "round",
1060
+ "aria-hidden": "true"
1061
+ }, [createElementVNode("circle", {
1062
+ cx: "11",
1063
+ cy: "11",
1064
+ r: "7"
1065
+ }), createElementVNode("path", { d: "m20 20-3.5-3.5" })], -1)), withDirectives(createElementVNode("input", {
744
1066
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => keyword.value = $event),
745
- class: "fl-search",
746
1067
  type: "text",
747
- placeholder: t("filter")
748
- }, null, 8, _hoisted_7), [[vModelText, keyword.value]]),
749
- createElementVNode("button", {
750
- class: "fl-btn",
1068
+ placeholder: t("filter"),
1069
+ "aria-label": t("filter")
1070
+ }, null, 8, _hoisted_12), [[vModelText, keyword.value]])]), createElementVNode("button", {
1071
+ class: "fl-icon-btn",
751
1072
  type: "button",
1073
+ "aria-label": t("refresh"),
1074
+ title: t("refresh"),
752
1075
  onClick: loadRoot
753
- }, toDisplayString(t("refresh")), 1),
754
- createElementVNode("button", {
1076
+ }, [..._cache[8] || (_cache[8] = [createElementVNode("svg", {
1077
+ viewBox: "0 0 24 24",
1078
+ width: "15",
1079
+ height: "15",
1080
+ fill: "none",
1081
+ stroke: "currentColor",
1082
+ "stroke-width": "2",
1083
+ "stroke-linecap": "round",
1084
+ "stroke-linejoin": "round",
1085
+ "aria-hidden": "true"
1086
+ }, [createElementVNode("path", { d: "M21 12a9 9 0 1 1-3-6.7" }), createElementVNode("path", { d: "M21 4v5h-5" })], -1)])], 8, _hoisted_13)]),
1087
+ selected.value.size ? (openBlock(), createElementBlock("button", {
1088
+ key: 3,
755
1089
  class: "fl-btn fl-btn-primary",
756
1090
  type: "button",
757
- disabled: selected.value.size === 0 || batchBusy.value,
1091
+ disabled: batchBusy.value,
758
1092
  onClick: batchDownload
759
- }, toDisplayString(batchBusy.value ? t("batching", { percent: batchProgress.value }) : selected.value.size ? t("batchCount", { count: selected.value.size }) : t("batch")), 9, _hoisted_8)
760
- ])]),
761
- needsPassword.value ? (openBlock(), createElementBlock("div", _hoisted_9, [
1093
+ }, [_cache[9] || (_cache[9] = createElementVNode("svg", {
1094
+ viewBox: "0 0 24 24",
1095
+ width: "15",
1096
+ height: "15",
1097
+ fill: "none",
1098
+ stroke: "currentColor",
1099
+ "stroke-width": "2.2",
1100
+ "stroke-linecap": "round",
1101
+ "stroke-linejoin": "round",
1102
+ "aria-hidden": "true"
1103
+ }, [
1104
+ createElementVNode("path", { d: "M12 3v12" }),
1105
+ createElementVNode("path", { d: "m7 10 5 5 5-5" }),
1106
+ createElementVNode("path", { d: "M5 21h14" })
1107
+ ], -1)), createTextVNode(" " + toDisplayString(batchBusy.value ? t("batching", { percent: batchProgress.value }) : t("batch")), 1)], 8, _hoisted_14)) : createCommentVNode("v-if", true)
1108
+ ]),
1109
+ needsPassword.value ? (openBlock(), createElementBlock("div", _hoisted_15, [
762
1110
  createElementVNode("span", null, toDisplayString(t("passwordPrompt")), 1),
763
1111
  withDirectives(createElementVNode("input", {
764
1112
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => passwordInput.value = $event),
765
1113
  type: "password",
766
1114
  placeholder: t("passwordPlaceholder"),
1115
+ "aria-label": t("passwordPlaceholder"),
767
1116
  onKeyup: withKeys(submitPassword, ["enter"])
768
- }, null, 40, _hoisted_10), [[vModelText, passwordInput.value]]),
1117
+ }, null, 40, _hoisted_16), [[vModelText, passwordInput.value]]),
769
1118
  createElementVNode("button", {
770
1119
  class: "fl-btn fl-btn-primary",
771
1120
  type: "button",
772
1121
  onClick: submitPassword
773
1122
  }, toDisplayString(t("open")), 1)
774
1123
  ])) : createCommentVNode("v-if", true),
775
- createElementVNode("div", _hoisted_11, [
776
- createElementVNode("span", _hoisted_12, [createElementVNode("input", {
1124
+ createElementVNode("div", _hoisted_17, [
1125
+ createElementVNode("label", _hoisted_18, [createElementVNode("input", {
777
1126
  type: "checkbox",
1127
+ "aria-label": t("selectAll"),
778
1128
  checked: allChecked.value,
779
1129
  ".indeterminate": someChecked.value,
780
1130
  onChange: toggleCheckAll
781
- }, null, 40, _hoisted_13)]),
782
- createElementVNode("span", {
783
- class: "fl-cell fl-cell-name",
1131
+ }, null, 40, _hoisted_19)]),
1132
+ createElementVNode("span", _hoisted_20, [createElementVNode("button", {
1133
+ class: "fl-sort-btn",
1134
+ type: "button",
1135
+ "aria-sort": sortKey.value === "name" ? sortAsc.value ? "ascending" : "descending" : void 0,
784
1136
  onClick: _cache[2] || (_cache[2] = ($event) => setSort("name"))
785
- }, [createTextVNode(toDisplayString(t("columnName")), 1), createElementVNode("span", _hoisted_14, toDisplayString(sortKey.value === "name" ? sortAsc.value ? "▲" : "▼" : ""), 1)]),
786
- createElementVNode("span", {
787
- class: "fl-cell fl-cell-size",
1137
+ }, [createTextVNode(toDisplayString(t("columnName")), 1), createElementVNode("span", _hoisted_22, toDisplayString(sortKey.value === "name" ? sortAsc.value ? "▲" : "▼" : ""), 1)], 8, _hoisted_21)]),
1138
+ createElementVNode("span", _hoisted_23, [createElementVNode("button", {
1139
+ class: "fl-sort-btn",
1140
+ type: "button",
788
1141
  onClick: _cache[3] || (_cache[3] = ($event) => setSort("size"))
789
- }, [createTextVNode(toDisplayString(t("columnSize")), 1), createElementVNode("span", _hoisted_15, toDisplayString(sortKey.value === "size" ? sortAsc.value ? "▲" : "▼" : ""), 1)]),
790
- createElementVNode("span", {
791
- class: "fl-cell fl-cell-date",
1142
+ }, [createTextVNode(toDisplayString(t("columnSize")), 1), createElementVNode("span", _hoisted_24, toDisplayString(sortKey.value === "size" ? sortAsc.value ? "▲" : "▼" : ""), 1)])]),
1143
+ createElementVNode("span", _hoisted_25, [createElementVNode("button", {
1144
+ class: "fl-sort-btn",
1145
+ type: "button",
792
1146
  onClick: _cache[4] || (_cache[4] = ($event) => setSort("modified"))
793
- }, [createTextVNode(toDisplayString(t("columnDate")), 1), createElementVNode("span", _hoisted_16, toDisplayString(sortKey.value === "modified" ? sortAsc.value ? "▲" : "▼" : ""), 1)]),
794
- _cache[6] || (_cache[6] = createElementVNode("span", { class: "fl-cell fl-cell-op" }, null, -1))
1147
+ }, [createTextVNode(toDisplayString(t("columnDate")), 1), createElementVNode("span", _hoisted_26, toDisplayString(sortKey.value === "modified" ? sortAsc.value ? "▲" : "▼" : ""), 1)])]),
1148
+ createElementVNode("span", _hoisted_27, [createElementVNode("label", _hoisted_28, [
1149
+ createElementVNode("span", null, toDisplayString(t("sortBy")), 1),
1150
+ createElementVNode("span", _hoisted_29, toDisplayString(sortOptions.value.find((option) => option.value === sortValue.value)?.label), 1),
1151
+ withDirectives(createElementVNode("select", {
1152
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => sortValue.value = $event),
1153
+ "aria-label": t("sortBy")
1154
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(sortOptions.value, (option) => {
1155
+ return openBlock(), createElementBlock("option", {
1156
+ key: option.value,
1157
+ value: option.value
1158
+ }, toDisplayString(option.label), 9, _hoisted_31);
1159
+ }), 128))], 8, _hoisted_30), [[vModelSelect, sortValue.value]])
1160
+ ])])
795
1161
  ]),
796
- loadingRoot.value ? (openBlock(), createElementBlock("div", _hoisted_17, toDisplayString(t("loading")), 1)) : errorMsg.value ? (openBlock(), createElementBlock("div", _hoisted_18, [
1162
+ loadingRoot.value ? (openBlock(), createElementBlock("div", _hoisted_32, toDisplayString(t("loading")), 1)) : errorMsg.value ? (openBlock(), createElementBlock("div", _hoisted_33, [
797
1163
  createTextVNode(toDisplayString(t("loadFailed", { message: errorMsg.value })) + " ", 1),
798
- /guest/i.test(errorMsg.value) ? (openBlock(), createElementBlock("p", _hoisted_19, toDisplayString(t("guestHint")), 1)) : createCommentVNode("v-if", true),
799
- /failed to fetch|networkerror/i.test(errorMsg.value) ? (openBlock(), createElementBlock("p", _hoisted_20, toDisplayString(t("corsHint")), 1)) : createCommentVNode("v-if", true),
1164
+ /guest/i.test(errorMsg.value) ? (openBlock(), createElementBlock("p", _hoisted_34, toDisplayString(t("guestHint")), 1)) : createCommentVNode("v-if", true),
1165
+ /failed to fetch|networkerror/i.test(errorMsg.value) ? (openBlock(), createElementBlock("p", _hoisted_35, toDisplayString(t("corsHint")), 1)) : createCommentVNode("v-if", true),
800
1166
  createElementVNode("button", {
801
1167
  class: "fl-btn",
802
1168
  type: "button",
803
1169
  onClick: loadRoot
804
1170
  }, toDisplayString(t("retry")), 1)
805
- ])) : visible.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_21, toDisplayString(t("empty")), 1)) : createCommentVNode("v-if", true),
1171
+ ])) : visible.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_36, toDisplayString(t("empty")), 1)) : createCommentVNode("v-if", true),
806
1172
  (openBlock(true), createElementBlock(Fragment, null, renderList(visible.value, (node) => {
807
1173
  return openBlock(), createElementBlock("div", {
808
1174
  key: node.path,
809
1175
  class: normalizeClass(["fl-row", {
810
1176
  "fl-row-dir": node.is_dir,
811
1177
  "fl-row-root": node.isRoot,
812
- "fl-row-selected": selected.value.has(node.path)
813
- }])
1178
+ "fl-row-selected": !node.is_dir && selected.value.has(node.path)
1179
+ }]),
1180
+ style: normalizeStyle({ "--fl-depth": node.depth })
814
1181
  }, [
815
- createElementVNode("span", _hoisted_22, [createElementVNode("input", {
1182
+ createElementVNode("label", _hoisted_37, [createElementVNode("input", {
816
1183
  type: "checkbox",
1184
+ "aria-label": t("selectItem", { name: node.name }),
817
1185
  checked: isChecked(node),
818
1186
  ".indeterminate": isIndeterminate(node),
819
- onChange: ($event) => toggleCheck(node)
820
- }, null, 40, _hoisted_23)]),
1187
+ onChange: ($event) => toggleCheck(node, $event)
1188
+ }, null, 40, _hoisted_38)]),
821
1189
  createElementVNode("span", {
822
1190
  class: "fl-cell fl-cell-name",
823
- title: node.isRoot ? node.path : void 0,
824
- style: normalizeStyle({ paddingLeft: `${node.depth * 22}px` })
825
- }, [node.is_dir ? (openBlock(), createElementBlock("button", {
1191
+ title: node.isRoot ? node.path : void 0
1192
+ }, [
1193
+ _cache[12] || (_cache[12] = createElementVNode("span", {
1194
+ class: "fl-indent",
1195
+ "aria-hidden": "true"
1196
+ }, null, -1)),
1197
+ node.is_dir ? (openBlock(), createElementBlock("button", {
1198
+ key: 0,
1199
+ class: normalizeClass(["fl-arrow", { "fl-arrow-open": node.expanded }]),
1200
+ type: "button",
1201
+ "aria-label": t(node.expanded ? "collapse" : "expand", { name: node.name }),
1202
+ "aria-expanded": node.expanded,
1203
+ onClick: ($event) => toggleExpand(node)
1204
+ }, [..._cache[10] || (_cache[10] = [createElementVNode("svg", {
1205
+ viewBox: "0 0 24 24",
1206
+ width: "14",
1207
+ height: "14",
1208
+ fill: "none",
1209
+ stroke: "currentColor",
1210
+ "stroke-width": "2.4",
1211
+ "stroke-linecap": "round",
1212
+ "stroke-linejoin": "round",
1213
+ "aria-hidden": "true"
1214
+ }, [createElementVNode("path", { d: "m9 6 6 6-6 6" })], -1)])], 10, _hoisted_40)) : (openBlock(), createElementBlock("span", _hoisted_41)),
1215
+ node.is_dir ? (openBlock(), createElementBlock("svg", _hoisted_42, [..._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", {
1216
+ key: 3,
1217
+ class: "fl-type",
1218
+ "data-kind": unref(fileKind)(node.name),
1219
+ "aria-hidden": "true"
1220
+ }, toDisplayString(unref(extOf)(node.name) || "FILE"), 9, _hoisted_43)),
1221
+ createElementVNode("span", _hoisted_44, [createElementVNode("span", {
1222
+ class: normalizeClass(["fl-name", { "fl-name-dir": node.is_dir }]),
1223
+ onClick: ($event) => node.is_dir ? toggleExpand(node) : download(node)
1224
+ }, toDisplayString(node.name), 11, _hoisted_45), createElementVNode("span", _hoisted_46, [node.is_dir ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [node.loaded ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(t("folderItems", { count: folderFiles(node).length })) + " · " + toDisplayString(unref(formatSize)(unref(totalSize)(folderFiles(node)))), 1)], 64)) : createCommentVNode("v-if", true)], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(unref(formatSize)(node.size)) + " · " + toDisplayString(unref(formatDate)(node.modified)), 1)], 64))])]),
1225
+ node.is_dir && node.loaded ? (openBlock(), createElementBlock("span", _hoisted_47, toDisplayString(t("folderItems", { count: folderFiles(node).length })), 1)) : createCommentVNode("v-if", true),
1226
+ node.loading ? (openBlock(), createElementBlock("span", _hoisted_48, "…")) : createCommentVNode("v-if", true)
1227
+ ], 8, _hoisted_39),
1228
+ createElementVNode("span", _hoisted_49, toDisplayString(node.is_dir ? "—" : unref(formatSize)(node.size)), 1),
1229
+ createElementVNode("span", _hoisted_50, toDisplayString(unref(formatDate)(node.modified)), 1),
1230
+ createElementVNode("span", _hoisted_51, [node.is_dir ? (openBlock(), createElementBlock("button", {
826
1231
  key: 0,
827
- class: normalizeClass(["fl-arrow", { "fl-arrow-open": node.expanded }]),
1232
+ class: normalizeClass(["fl-action fl-arrow-end", { "fl-arrow-open": node.expanded }]),
828
1233
  type: "button",
1234
+ "aria-label": t(node.expanded ? "collapse" : "expand", { name: node.name }),
1235
+ "aria-expanded": node.expanded,
829
1236
  onClick: ($event) => toggleExpand(node)
830
- }, "▸", 10, _hoisted_25)) : (openBlock(), createElementBlock("span", _hoisted_26)), createElementVNode("span", {
831
- class: normalizeClass(["fl-name", { "fl-name-link": node.is_dir }]),
832
- onClick: ($event) => node.is_dir ? toggleExpand(node) : download(node)
833
- }, [
834
- node.is_dir ? (openBlock(), createElementBlock("span", _hoisted_28, "📁")) : (openBlock(), createElementBlock("span", _hoisted_29, toDisplayString(unref(extOf)(node.name) || "FILE"), 1)),
835
- createTextVNode(" " + toDisplayString(node.name) + " ", 1),
836
- node.loading ? (openBlock(), createElementBlock("span", _hoisted_30, "…")) : createCommentVNode("v-if", true)
837
- ], 10, _hoisted_27)], 12, _hoisted_24),
838
- createElementVNode("span", _hoisted_31, toDisplayString(node.is_dir ? "-" : unref(formatSize)(node.size)), 1),
839
- createElementVNode("span", _hoisted_32, toDisplayString(unref(formatDate)(node.modified)), 1),
840
- createElementVNode("span", _hoisted_33, [!node.is_dir ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [canPreview(node) ? (openBlock(), createElementBlock("button", {
1237
+ }, [..._cache[13] || (_cache[13] = [createElementVNode("svg", {
1238
+ viewBox: "0 0 24 24",
1239
+ width: "18",
1240
+ height: "18",
1241
+ fill: "none",
1242
+ stroke: "currentColor",
1243
+ "stroke-width": "2.2",
1244
+ "stroke-linecap": "round",
1245
+ "stroke-linejoin": "round",
1246
+ "aria-hidden": "true"
1247
+ }, [createElementVNode("path", { d: "m9 6 6 6-6 6" })], -1)])], 10, _hoisted_52)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [canPreview(node) ? (openBlock(), createElementBlock("button", {
841
1248
  key: 0,
842
- class: "fl-download",
1249
+ class: "fl-action fl-download",
843
1250
  type: "button",
1251
+ "aria-label": `${t("preview")} ${node.name}`,
844
1252
  title: t("preview"),
845
1253
  onClick: ($event) => preview(node)
846
- }, [..._cache[7] || (_cache[7] = [createElementVNode("svg", {
1254
+ }, [..._cache[14] || (_cache[14] = [createElementVNode("svg", {
847
1255
  viewBox: "0 0 24 24",
848
- width: "15",
849
- height: "15",
1256
+ width: "16",
1257
+ height: "16",
850
1258
  fill: "none",
851
1259
  stroke: "currentColor",
852
1260
  "stroke-width": "2",
@@ -857,15 +1265,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
857
1265
  cx: "12",
858
1266
  cy: "12",
859
1267
  r: "3"
860
- })], -1)])], 8, _hoisted_34)) : createCommentVNode("v-if", true), createElementVNode("button", {
861
- class: "fl-download",
1268
+ })], -1)])], 8, _hoisted_53)) : createCommentVNode("v-if", true), createElementVNode("button", {
1269
+ class: "fl-action fl-download",
862
1270
  type: "button",
1271
+ "aria-label": `${t("download")} ${node.name}`,
863
1272
  title: t("download"),
864
1273
  onClick: ($event) => download(node)
865
- }, [..._cache[8] || (_cache[8] = [createElementVNode("svg", {
1274
+ }, [..._cache[15] || (_cache[15] = [createElementVNode("svg", {
866
1275
  viewBox: "0 0 24 24",
867
- width: "15",
868
- height: "15",
1276
+ width: "16",
1277
+ height: "16",
869
1278
  fill: "none",
870
1279
  stroke: "currentColor",
871
1280
  "stroke-width": "2",
@@ -876,12 +1285,12 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
876
1285
  createElementVNode("path", { d: "M12 3v12" }),
877
1286
  createElementVNode("path", { d: "m7 10 5 5 5-5" }),
878
1287
  createElementVNode("path", { d: "M5 21h14" })
879
- ], -1)])], 8, _hoisted_35)], 64)) : createCommentVNode("v-if", true)])
880
- ], 2);
1288
+ ], -1)])], 8, _hoisted_54)], 64))])
1289
+ ], 6);
881
1290
  }), 128)),
882
- createElementVNode("div", _hoisted_36, [createElementVNode("span", null, toDisplayString(t("loaded", { count: allFiles.value.length })) + " · " + toDisplayString(t("selected", { count: selected.value.size })), 1), unloadedFolders.value ? (openBlock(), createElementBlock("span", _hoisted_37, " · " + toDisplayString(t("unloaded", { count: unloadedFolders.value })), 1)) : createCommentVNode("v-if", true)]),
883
- toast.value ? (openBlock(), createElementBlock("div", _hoisted_38, toDisplayString(toast.value), 1)) : createCommentVNode("v-if", true)
884
- ]);
1291
+ createElementVNode("div", _hoisted_55, [createElementVNode("span", null, toDisplayString(t("loaded", { count: allFiles.value.length })) + " · " + toDisplayString(t("totalSize", { size: unref(formatSize)(loadedBytes.value) })), 1), unloadedFolders.value ? (openBlock(), createElementBlock("span", _hoisted_56, toDisplayString(t("unloaded", { count: unloadedFolders.value })), 1)) : createCommentVNode("v-if", true)]),
1292
+ toast.value ? (openBlock(), createElementBlock("div", _hoisted_57, toDisplayString(toast.value), 1)) : createCommentVNode("v-if", true)
1293
+ ], 6);
885
1294
  };
886
1295
  }
887
1296
  });
@@ -906,4 +1315,4 @@ function enhanceAppWithFileList({ app }) {
906
1315
  if (!app._context.components.FileList) app.component("FileList", _sfc_main);
907
1316
  if (!app._context.components.VPFileList) app.component("VPFileList", _sfc_main);
908
1317
  }
909
- export { _sfc_main as FileList, _sfc_main as VPFileList, compareNodes, encodePath, enhanceAppWithFileList, extOf, fileListLocales, formatDate, formatLabel, formatSize, joinPath, resolveLabels };
1318
+ export { _sfc_main as FileList, _sfc_main as VPFileList, compareNodes, encodePath, enhanceAppWithFileList, extOf, fileKind, fileListLocales, formatDate, formatLabel, formatSize, joinPath, resolveLabels, totalSize, uniqueEntryName, zipEntryName, zipSegment };