vitepress-plugin-filelist 0.5.1 → 0.6.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.
@@ -92,6 +92,29 @@ interface FileListLabels {
92
92
  unlockFolder: string;
93
93
  /** @deprecated Network failures now append `corsHint`. */
94
94
  networkHint: string;
95
+ /** Batch progress detail. `{done}` `{total}` sizes, `{files}` `{count}` file counts */
96
+ batchProgress: string;
97
+ /** Batch finished. `{name}` `{count}` `{size}` */
98
+ batchDone: string;
99
+ batchCanceled: string;
100
+ /** In-memory ZIP over `zipWarnSize`. `{size}` */
101
+ zipWarnTitle: string;
102
+ /** `{limit}` */
103
+ zipWarnText: string;
104
+ /** Desktop browsers that cannot stream: point to one that can. */
105
+ zipStreamTip: string;
106
+ zipContinue: string;
107
+ /** In-memory ZIP over `zipMaxSize`. `{size}` */
108
+ zipBlockTitle: string;
109
+ /** `{limit}` */
110
+ zipBlockText: string;
111
+ gotIt: string;
112
+ /** Last row of a long folder. `{count}` */
113
+ moreRemaining: string;
114
+ showMore: string;
115
+ showAll: string;
116
+ /** Fewer than a page left. `{count}` */
117
+ showRest: string;
95
118
  }
96
119
  interface FileListSource {
97
120
  /** OpenList path, directory or single file. */
@@ -123,6 +146,21 @@ interface FileListProps {
123
146
  * cleared when the tab closes). Defaults to `true`; `false` hides the option.
124
147
  */
125
148
  rememberPassword?: boolean;
149
+ /**
150
+ * Rows shown per folder before “Show more”. Defaults to `200`; `0` shows
151
+ * everything. Data is still loaded in full; only rendering is limited.
152
+ */
153
+ pageSize?: number;
154
+ /**
155
+ * Browsers that must build the ZIP in memory (Firefox, Safari) ask first above
156
+ * this many MB. Defaults to 500 (200 on touch devices); `0` never asks.
157
+ */
158
+ zipWarnSize?: number;
159
+ /**
160
+ * …and refuse above this many MB. Defaults to 2048 (1024 on touch devices);
161
+ * `0` means no limit. Chrome and Edge stream to disk and skip both checks.
162
+ */
163
+ zipMaxSize?: number;
126
164
  /** Built-in locale. Defaults to `zh-CN`. */
127
165
  locale?: FileListLocale;
128
166
  /** Per-key label overrides on top of the locale preset. */
@@ -148,6 +186,7 @@ declare const _default: import("vue").DefineComponent<FileListProps, {}, {}, {},
148
186
  password: string;
149
187
  title: string;
150
188
  rememberPassword: boolean;
189
+ pageSize: number;
151
190
  locale: FileListLocale;
152
191
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
153
192
  declare global {
@@ -1,4 +1,4 @@
1
- import { computed, defineComponent, mergeProps, nextTick, onBeforeUnmount, onMounted, ref, unref, useId, useSSRContext, watch } from "vue";
1
+ import { computed, defineComponent, mergeProps, nextTick, onBeforeUnmount, onMounted, reactive, ref, unref, useId, useSSRContext, watch } from "vue";
2
2
  import { ssrIncludeBooleanAttr, ssrInterpolate, ssrLooseContain, ssrLooseEqual, ssrRenderAttr, ssrRenderAttrs, ssrRenderClass, ssrRenderComponent, ssrRenderList, ssrRenderStyle } from "vue/server-renderer";
3
3
  // src/client/utils.ts
4
4
  function joinPath(parent, name) {
@@ -520,6 +520,60 @@ function createDemoApi(delay = 220) {
520
520
  clearCache: () => {}
521
521
  };
522
522
  }
523
+ // src/client/ConfirmDialog.vue
524
+ const _sfc_main$3 = /*@__PURE__*/ defineComponent({
525
+ __name: "ConfirmDialog",
526
+ __ssrInlineRender: true,
527
+ props: {
528
+ open: { type: Boolean },
529
+ title: {},
530
+ text: {},
531
+ tip: {},
532
+ confirmLabel: {},
533
+ cancelLabel: {}
534
+ },
535
+ emits: ["confirm", "close"],
536
+ setup(__props, { emit: __emit }) {
537
+ /** Small native <dialog> for a yes / no question or a notice (bottom sheet on phones). */
538
+ const props = __props;
539
+ const dialog = ref();
540
+ watch(() => props.open, (open) => {
541
+ const el = dialog.value;
542
+ if (!el) return;
543
+ if (open && !el.open) {
544
+ if (typeof el.showModal === "function") el.showModal();
545
+ else el.setAttribute("open", "");
546
+ } else if (!open && el.open) {
547
+ if (typeof el.close === "function") el.close();
548
+ else el.removeAttribute("open");
549
+ }
550
+ }, { flush: "post" });
551
+ return (_ctx, _push, _parent, _attrs) => {
552
+ _push(`<dialog${ssrRenderAttrs(mergeProps({
553
+ ref_key: "dialog",
554
+ ref: dialog,
555
+ class: "fl-confirm",
556
+ "aria-label": __props.title
557
+ }, _attrs))}>`);
558
+ if (__props.open) {
559
+ _push(`<div class="fl-confirm-panel"><p class="fl-confirm-title">${ssrInterpolate(__props.title)}</p><p class="fl-confirm-text">${ssrInterpolate(__props.text)}</p>`);
560
+ if (__props.tip) _push(`<p class="fl-confirm-text">${ssrInterpolate(__props.tip)}</p>`);
561
+ else _push(`<!---->`);
562
+ _push(`<div class="fl-confirm-actions">`);
563
+ if (__props.confirmLabel) _push(`<button class="fl-btn" type="button">${ssrInterpolate(__props.cancelLabel)}</button>`);
564
+ else _push(`<!---->`);
565
+ _push(`<button class="fl-btn fl-btn-primary" type="button" autofocus>${ssrInterpolate(__props.confirmLabel || __props.cancelLabel)}</button></div></div>`);
566
+ } else _push(`<!---->`);
567
+ _push(`</dialog>`);
568
+ };
569
+ }
570
+ });
571
+ const _sfc_setup$3 = _sfc_main$3.setup;
572
+ _sfc_main$3.setup = (props, ctx) => {
573
+ const ssrContext = useSSRContext();
574
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/client/ConfirmDialog.vue");
575
+ return _sfc_setup$3 ? _sfc_setup$3(props, ctx) : void 0;
576
+ };
523
577
  // src/client/download.ts
524
578
  /** Open a file URL; OpenList serves `/p/` and `/d/` as attachments, so this downloads. */
525
579
  function openUrl(url) {
@@ -531,6 +585,80 @@ function openUrl(url) {
531
585
  anchor.click();
532
586
  anchor.remove();
533
587
  }
588
+ function saveBlob(blob, name) {
589
+ const href = URL.createObjectURL(blob);
590
+ const anchor = document.createElement("a");
591
+ anchor.href = href;
592
+ anchor.download = name;
593
+ document.body.appendChild(anchor);
594
+ anchor.click();
595
+ anchor.remove();
596
+ setTimeout(() => URL.revokeObjectURL(href), 3e4);
597
+ }
598
+ /** File-system-safe ZIP name from a title. */
599
+ function zipFileName(title) {
600
+ return `${title.replace(/[\\/:*?"<>|]/g, "_")}.zip`;
601
+ }
602
+ /**
603
+ * The ZIP as a stream: files are fetched one after another and passed through
604
+ * as they arrive, stored without compression (most shared files are already
605
+ * compressed). Clashing entry names get ` (2)`, ` (3)`...
606
+ */
607
+ async function zipStream(files, options) {
608
+ const { makeZip } = await import("client-zip");
609
+ const progress = {
610
+ bytes: 0,
611
+ totalBytes: files.reduce((sum, file) => sum + (Number.isFinite(file.size) ? file.size : 0), 0),
612
+ files: 0,
613
+ totalFiles: files.length
614
+ };
615
+ const report = () => options.onProgress({ ...progress });
616
+ report();
617
+ async function* entries() {
618
+ const used = /* @__PURE__ */ new Set();
619
+ for (const file of files) {
620
+ const response = await fetch(await options.urlOf(file), { signal: options.signal });
621
+ if (!response.ok || !response.body) throw new Error(`${file.name}:HTTP ${response.status}`);
622
+ const counted = response.body.pipeThrough(new TransformStream({
623
+ transform(chunk, controller) {
624
+ progress.bytes += chunk.byteLength;
625
+ report();
626
+ controller.enqueue(chunk);
627
+ },
628
+ flush() {
629
+ progress.files += 1;
630
+ report();
631
+ }
632
+ }));
633
+ const modified = new Date(file.modified);
634
+ yield {
635
+ name: uniqueEntryName(zipEntryName(file.zipPath), used),
636
+ lastModified: Number.isNaN(modified.getTime()) ? void 0 : modified,
637
+ input: counted
638
+ };
639
+ }
640
+ }
641
+ return makeZip(entries());
642
+ }
643
+ /** Build the ZIP in memory (browsers without file streaming). */
644
+ async function zipToBlob(files, options) {
645
+ const reader = (await zipStream(files, options)).getReader();
646
+ const chunks = [];
647
+ const abort = () => void reader.cancel(options.signal.reason).catch(() => {});
648
+ options.signal.addEventListener("abort", abort, { once: true });
649
+ if (options.signal.aborted) abort();
650
+ try {
651
+ for (;;) {
652
+ const { done, value } = await reader.read();
653
+ if (done) break;
654
+ chunks.push(value);
655
+ }
656
+ } finally {
657
+ options.signal.removeEventListener("abort", abort);
658
+ }
659
+ options.signal.throwIfAborted();
660
+ return new Blob(chunks, { type: "application/zip" });
661
+ }
534
662
  // src/client/icons.ts
535
663
  /** Stroke icons on a 24×24 grid, drawn by `Icon.vue`. */
536
664
  const ICONS = {
@@ -692,7 +820,21 @@ const fileListLocales = {
692
820
  remembered: "已记住",
693
821
  forgetPassword: "忘记",
694
822
  unlockFolder: "输入 {name} 的密码",
695
- networkHint: "可能被跨域拦截:请检查 OpenList 的 cors.allow_origins,或 OpenList-Worker 的 ALLOW_URLS。"
823
+ networkHint: "可能被跨域拦截:请检查 OpenList 的 cors.allow_origins,或 OpenList-Worker 的 ALLOW_URLS。",
824
+ batchProgress: "{done} / {total} · {files} / {count} 个文件",
825
+ batchDone: "已打包 {name}({count} 个文件,{size})",
826
+ batchCanceled: "已取消下载",
827
+ zipWarnTitle: "文件较大({size})",
828
+ zipWarnText: "当前浏览器要先把文件全部放进内存再打包,超过 {limit} 可能失败或让页面崩溃。建议分批下载。",
829
+ zipStreamTip: "在 Chrome 或 Edge 中可以边下载边保存,不受大小限制。",
830
+ zipContinue: "仍然下载",
831
+ zipBlockTitle: "无法打包 {size}",
832
+ zipBlockText: "当前浏览器一次最多打包 {limit},请分批勾选下载。",
833
+ gotIt: "知道了",
834
+ moreRemaining: "还有 {count} 项",
835
+ showMore: "显示更多",
836
+ showAll: "全部显示",
837
+ showRest: "显示剩余 {count} 项"
696
838
  },
697
839
  "en-US": {
698
840
  allFiles: "All files",
@@ -755,7 +897,21 @@ const fileListLocales = {
755
897
  remembered: "Remembered",
756
898
  forgetPassword: "Forget",
757
899
  unlockFolder: "Enter the password for {name}",
758
- networkHint: "Possibly blocked by CORS: check cors.allow_origins in OpenList, or ALLOW_URLS in OpenList-Worker."
900
+ networkHint: "Possibly blocked by CORS: check cors.allow_origins in OpenList, or ALLOW_URLS in OpenList-Worker.",
901
+ batchProgress: "{done} / {total} · {files} / {count} files",
902
+ batchDone: "Saved {name} ({count} files, {size})",
903
+ batchCanceled: "Download cancelled",
904
+ zipWarnTitle: "Large download ({size})",
905
+ zipWarnText: "This browser has to hold every file in memory to build the ZIP. Above {limit} it may fail or crash the page. Consider downloading in smaller batches.",
906
+ zipStreamTip: "Chrome and Edge save while downloading, with no size limit.",
907
+ zipContinue: "Download anyway",
908
+ zipBlockTitle: "Cannot zip {size}",
909
+ zipBlockText: "This browser can zip at most {limit} at once. Select fewer files.",
910
+ gotIt: "OK",
911
+ moreRemaining: "{count} more",
912
+ showMore: "Show more",
913
+ showAll: "Show all",
914
+ showRest: "Show the remaining {count}"
759
915
  }
760
916
  };
761
917
  function formatLabel(template, values = {}) {
@@ -1242,6 +1398,9 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1242
1398
  type: Boolean,
1243
1399
  default: true
1244
1400
  },
1401
+ pageSize: { default: 200 },
1402
+ zipWarnSize: {},
1403
+ zipMaxSize: {},
1245
1404
  locale: { default: "zh-CN" },
1246
1405
  labels: {}
1247
1406
  },
@@ -1284,7 +1443,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1284
1443
  const siteTitle = ref("");
1285
1444
  const iframePreviews = ref("");
1286
1445
  const batchBusy = ref(false);
1287
- const batchProgress = ref(0);
1446
+ const zipProgress = ref(null);
1447
+ let zipAbort = null;
1288
1448
  let toastTimer;
1289
1449
  /** The folder listed in single-path mode, as the parent of the top rows. */
1290
1450
  const listedFolder = computed(() => ({
@@ -1444,6 +1604,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1444
1604
  errorMsg.value = "";
1445
1605
  try {
1446
1606
  roots.value = await buildTree();
1607
+ shown.clear();
1447
1608
  rootLock.value = null;
1448
1609
  selected.value = /* @__PURE__ */ new Set();
1449
1610
  api.value.clearCache();
@@ -1482,20 +1643,37 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1482
1643
  applySort(key, direction !== "desc");
1483
1644
  }
1484
1645
  });
1485
- const visible = computed(() => {
1646
+ /** A “show more” row closing a folder whose children are cut at `pageSize`. */
1647
+ const shown = reactive(/* @__PURE__ */ new Map());
1648
+ function limitOf(key) {
1649
+ const size = Math.max(0, Math.floor(props.pageSize ?? 200));
1650
+ return size ? shown.get(key) ?? size : Number.POSITIVE_INFINITY;
1651
+ }
1652
+ function isMore(row) {
1653
+ return "more" in row;
1654
+ }
1655
+ const rows = computed(() => {
1486
1656
  const out = [];
1487
1657
  const query = keyword.value.trim().toLowerCase();
1488
1658
  const matches = (node) => !query || node.name.toLowerCase().includes(query) || node.is_dir && node.children.some(matches);
1489
- const walk = (nodes) => {
1490
- for (const node of nodes) {
1491
- if (!matches(node)) continue;
1659
+ const walk = (nodes, key, depth) => {
1660
+ const list = nodes.filter(matches);
1661
+ const limit = query ? list.length : Math.min(list.length, limitOf(key));
1662
+ for (const node of list.slice(0, limit)) {
1492
1663
  out.push(node);
1493
- if (node.is_dir && (node.expanded || query) && node.children.length) walk(node.children);
1664
+ if (node.is_dir && (node.expanded || query) && node.children.length) walk(node.children, node.path, node.depth + 1);
1494
1665
  }
1666
+ if (limit < list.length) out.push({
1667
+ more: true,
1668
+ key,
1669
+ depth,
1670
+ remaining: list.length - limit
1671
+ });
1495
1672
  };
1496
- walk(roots.value);
1673
+ walk(roots.value, props.sources.length ? "" : props.path, 0);
1497
1674
  return out;
1498
1675
  });
1676
+ const visible = computed(() => rows.value.filter((row) => !isMore(row)));
1499
1677
  const stats = computed(() => folderStats(roots.value, selected.value));
1500
1678
  const noStats = {
1501
1679
  files: 0,
@@ -1534,6 +1712,52 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1534
1712
  showToast(t("downloadFailed", { message: errorText(error) }));
1535
1713
  }
1536
1714
  }
1715
+ const zipNotice = ref(null);
1716
+ /** Only desktop browsers get “use Chrome or Edge”; on iPhone every browser is Safari. */
1717
+ const zipTip = computed(() => typeof window !== "undefined" && !window.matchMedia?.("(pointer: coarse)").matches ? t("zipStreamTip") : "");
1718
+ function zipName() {
1719
+ return zipFileName(props.title || t("defaultZip"));
1720
+ }
1721
+ async function zipInMemory(files) {
1722
+ const name = zipName();
1723
+ await runZip(files, async (options) => saveBlob(await zipToBlob(files, options), name), name);
1724
+ }
1725
+ function confirmZip() {
1726
+ const notice = zipNotice.value;
1727
+ zipNotice.value = null;
1728
+ if (notice?.kind === "warn") zipInMemory(notice.files);
1729
+ }
1730
+ async function runZip(files, run, name) {
1731
+ const controller = new AbortController();
1732
+ zipAbort = controller;
1733
+ batchBusy.value = true;
1734
+ zipProgress.value = null;
1735
+ try {
1736
+ await run({
1737
+ urlOf: fileUrlOf,
1738
+ signal: controller.signal,
1739
+ onProgress: (progress) => zipProgress.value = progress
1740
+ });
1741
+ showToast(t("batchDone", {
1742
+ name,
1743
+ count: files.length,
1744
+ size: formatSize(totalSize(files))
1745
+ }));
1746
+ selected.value = /* @__PURE__ */ new Set();
1747
+ } catch (error) {
1748
+ showToast(controller.signal.aborted ? t("batchCanceled") : t("batchFailed", { message: errorText(error) }));
1749
+ } finally {
1750
+ zipAbort = null;
1751
+ batchBusy.value = false;
1752
+ zipProgress.value = null;
1753
+ }
1754
+ }
1755
+ const zipPercent = computed(() => {
1756
+ const progress = zipProgress.value;
1757
+ if (!progress) return 0;
1758
+ const ratio = progress.totalBytes ? progress.bytes / progress.totalBytes : progress.totalFiles ? progress.files / progress.totalFiles : 0;
1759
+ return Math.min(100, Math.floor(ratio * 100));
1760
+ });
1537
1761
  /** Only types the dialog can actually render get a preview button. */
1538
1762
  function canPreview(node) {
1539
1763
  return !node.is_dir && previewKind(node.name) !== "none";
@@ -1605,6 +1829,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1605
1829
  window.addEventListener("resize", measureStickyTop, { passive: true });
1606
1830
  });
1607
1831
  onBeforeUnmount(() => {
1832
+ zipAbort?.abort();
1608
1833
  window.removeEventListener("resize", measureStickyTop);
1609
1834
  clearTimeout(toastTimer);
1610
1835
  });
@@ -1619,14 +1844,24 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1619
1844
  style: { "--fl-sticky-top-auto": `${stickyTop.value}px` },
1620
1845
  "aria-busy": refreshing.value || loadingRoot.value
1621
1846
  }, _attrs))}><div class="fl-bar">`);
1622
- if (selected.value.size) _push(`<div class="fl-selection" role="status"><span class="fl-selection-count">${ssrInterpolate(t("selected", { count: selected.value.size }))}</span><span class="fl-selection-size">${ssrInterpolate(t("totalSize", { size: unref(formatSize)(unref(totalSize)(selectedFiles.value)) }))}</span></div>`);
1847
+ if (batchBusy.value) {
1848
+ _push(`<div class="fl-selection" role="status"><span class="fl-selection-count">${ssrInterpolate(t("batching", { percent: zipPercent.value }))}</span>`);
1849
+ if (zipProgress.value) _push(`<span class="fl-selection-size">${ssrInterpolate(t("batchProgress", {
1850
+ done: unref(formatSize)(zipProgress.value.bytes),
1851
+ total: unref(formatSize)(zipProgress.value.totalBytes),
1852
+ files: zipProgress.value.files,
1853
+ count: zipProgress.value.totalFiles
1854
+ }))}</span>`);
1855
+ else _push(`<!---->`);
1856
+ _push(`<span class="fl-zip-bar" style="${ssrRenderStyle({ width: `${zipPercent.value}%` })}" aria-hidden="true"></span></div>`);
1857
+ } else if (selected.value.size) _push(`<div class="fl-selection" role="status"><span class="fl-selection-count">${ssrInterpolate(t("selected", { count: selected.value.size }))}</span><span class="fl-selection-size">${ssrInterpolate(t("totalSize", { size: unref(formatSize)(unref(totalSize)(selectedFiles.value)) }))}</span></div>`);
1623
1858
  else {
1624
1859
  _push(`<div class="fl-title"><span class="fl-title-text">${ssrInterpolate(displayTitle.value)}</span>`);
1625
1860
  if (demo.value) _push(`<span class="fl-demo-badge">${ssrInterpolate(t("demoBadge"))}</span>`);
1626
1861
  else _push(`<!---->`);
1627
1862
  _push(`</div>`);
1628
1863
  }
1629
- if (selected.value.size) {
1864
+ if (selected.value.size && !batchBusy.value) {
1630
1865
  _push(`<button class="fl-clear" type="button"${ssrRenderAttr("aria-label", t("clearSelection"))}${ssrRenderAttr("title", t("clearSelection"))}>`);
1631
1866
  _push(ssrRenderComponent(_sfc_main$2, {
1632
1867
  class: "fl-clear-icon",
@@ -1646,18 +1881,19 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1646
1881
  size: 15
1647
1882
  }, null, _parent));
1648
1883
  _push(`</button></div>`);
1649
- if (selected.value.size) {
1650
- _push(`<button class="fl-btn fl-btn-primary" type="button"${ssrIncludeBooleanAttr(batchBusy.value) ? " disabled" : ""}>`);
1884
+ if (batchBusy.value) _push(`<button class="fl-btn fl-zip-cancel" type="button">${ssrInterpolate(t("cancel"))}</button>`);
1885
+ else if (selected.value.size) {
1886
+ _push(`<button class="fl-btn fl-btn-primary" type="button">`);
1651
1887
  _push(ssrRenderComponent(_sfc_main$2, {
1652
1888
  name: "download",
1653
1889
  size: 15
1654
1890
  }, null, _parent));
1655
- _push(` ${ssrInterpolate(batchBusy.value ? t("batching", { percent: batchProgress.value }) : t("batch"))}</button>`);
1891
+ _push(` ${ssrInterpolate(t("batch"))}</button>`);
1656
1892
  } else _push(`<!---->`);
1657
1893
  _push(`</div><div class="fl-row fl-head">`);
1658
1894
  if (refreshing.value) _push(`<span class="fl-progress" aria-hidden="true"></span>`);
1659
1895
  else _push(`<!---->`);
1660
- _push(`<label class="fl-cell fl-cell-check"><input type="checkbox"${ssrRenderAttr("aria-label", t("selectAll"))}${ssrIncludeBooleanAttr(allChecked.value) ? " checked" : ""}${ssrRenderAttr("indeterminate", someChecked.value)}></label><!--[-->`);
1896
+ _push(`<label class="fl-cell fl-cell-check"><input type="checkbox"${ssrRenderAttr("aria-label", t("selectAll"))}${ssrIncludeBooleanAttr(batchBusy.value) ? " disabled" : ""}${ssrIncludeBooleanAttr(allChecked.value) ? " checked" : ""}${ssrRenderAttr("indeterminate", someChecked.value)}></label><!--[-->`);
1661
1897
  ssrRenderList([
1662
1898
  "name",
1663
1899
  "size",
@@ -1697,105 +1933,124 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1697
1933
  } else if (visible.value.length === 0) _push(`<div class="fl-status">${ssrInterpolate(t("empty"))}</div>`);
1698
1934
  else _push(`<!---->`);
1699
1935
  _push(`<!--[-->`);
1700
- ssrRenderList(visible.value, (node, index) => {
1701
- _push(`<div class="${ssrRenderClass([{
1702
- "fl-row-dir": node.is_dir,
1703
- "fl-row-root": node.isRoot,
1704
- "fl-row-selected": !node.is_dir && selected.value.has(node.path),
1705
- "fl-row-locked": !!node.lock,
1706
- "fl-row-prompting": node.lock?.prompting,
1707
- "fl-row-error": node.lock?.prompting && !!node.lock.error
1708
- }, "fl-row"])}" style="${ssrRenderStyle({ "--fl-depth": node.depth })}"><label class="fl-cell fl-cell-check"><input type="checkbox"${ssrIncludeBooleanAttr(!!node.lock) ? " disabled" : ""}${ssrRenderAttr("aria-label", t("selectItem", { name: node.name }))}${ssrIncludeBooleanAttr(isChecked(node)) ? " checked" : ""}${ssrRenderAttr("indeterminate", isIndeterminate(node))}></label><span class="fl-cell fl-cell-name"${ssrRenderAttr("title", node.isRoot ? node.path : void 0)}><span class="fl-indent" aria-hidden="true"></span>`);
1709
- if (node.lock) {
1710
- _push(`<button class="fl-arrow fl-lock" type="button"${ssrRenderAttr("aria-label", t("unlockFolder", { name: node.name }))}${ssrRenderAttr("title", t("unlockFolder", { name: node.name }))}>`);
1711
- _push(ssrRenderComponent(_sfc_main$2, {
1712
- name: "lock",
1713
- size: 14
1714
- }, null, _parent));
1715
- _push(`</button>`);
1716
- } else if (node.is_dir) {
1717
- _push(`<button type="button" class="${ssrRenderClass([{ "fl-arrow-open": node.expanded }, "fl-arrow"])}"${ssrRenderAttr("aria-label", t(node.expanded ? "collapse" : "expand", { name: node.name }))}${ssrRenderAttr("aria-expanded", node.expanded)}>`);
1718
- _push(ssrRenderComponent(_sfc_main$2, {
1719
- name: "next",
1720
- size: 14
1721
- }, null, _parent));
1722
- _push(`</button>`);
1723
- } else _push(`<span class="fl-arrow-placeholder" aria-hidden="true"></span>`);
1724
- if (node.is_dir) _push(`<svg class="fl-folder-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><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"></path></svg>`);
1725
- else _push(`<span class="fl-type"${ssrRenderAttr("data-kind", unref(fileKind)(node.name))} aria-hidden="true">${ssrInterpolate(unref(extOf)(node.name) || "FILE")}</span>`);
1726
- _push(`<span class="fl-label"><span class="${ssrRenderClass([{ "fl-name-dir": node.is_dir }, "fl-name"])}">${ssrInterpolate(node.name)}</span><span class="fl-meta">`);
1727
- if (!node.is_dir) _push(`<!--[-->${ssrInterpolate(unref(formatSize)(node.size))} · ${ssrInterpolate(unref(formatDate)(node.modified))}<!--]-->`);
1728
- else if (node.loaded) _push(`<!--[-->${ssrInterpolate(t("folderItems", { count: statsOf(node).files }))} · ${ssrInterpolate(unref(formatSize)(statsOf(node).bytes))}<!--]-->`);
1729
- else _push(`<!---->`);
1730
- _push(`</span></span>`);
1731
- if (node.is_dir && node.loaded) _push(`<span class="fl-count">${ssrInterpolate(t("folderItems", { count: statsOf(node).files }))}</span>`);
1732
- else _push(`<!---->`);
1733
- if (node.lock?.prompting && node.lock.error) _push(`<span${ssrRenderAttr("id", `${unref(uid)}-error-${index}`)} class="fl-unlock-message" role="alert">${ssrInterpolate(unlockErrorText(node.lock.error))}</span>`);
1734
- else _push(`<!---->`);
1735
- if (!node.lock && unref(remembered).has(node.path)) {
1736
- _push(`<button class="fl-remembered" type="button"${ssrRenderAttr("aria-label", `${t("forgetPassword")} ${node.name}`)}${ssrRenderAttr("title", t("forgetPassword"))}>`);
1737
- _push(ssrRenderComponent(_sfc_main$2, {
1738
- name: "key",
1739
- size: 11
1740
- }, null, _parent));
1741
- _push(` ${ssrInterpolate(t("remembered"))} · ${ssrInterpolate(t("forgetPassword"))}</button>`);
1742
- } else _push(`<!---->`);
1743
- if (node.loading) _push(`<span class="fl-loading" aria-hidden="true">…</span>`);
1744
- else _push(`<!---->`);
1745
- _push(`</span>`);
1746
- if (node.lock?.prompting) {
1747
- _push(`<form class="fl-unlock fl-unlock-row">`);
1748
- _push(ssrRenderComponent(_sfc_main$2, {
1749
- class: "fl-unlock-lock",
1750
- name: "lock",
1751
- size: 18
1752
- }, null, _parent));
1753
- _push(`<input${ssrRenderAttr("value", node.lock.input)} type="password" autocomplete="off"${ssrRenderAttr("data-unlock", node.path)}${ssrRenderAttr("placeholder", node.lock.error ? unlockErrorText(node.lock.error) : t("passwordPlaceholder"))}${ssrRenderAttr("aria-label", t("passwordFor", { name: node.name }))}${ssrRenderAttr("aria-invalid", !!node.lock.error)}${ssrRenderAttr("aria-describedby", node.lock.error ? `${unref(uid)}-error-${index}` : void 0)}>`);
1754
- if (canRemember.value) {
1755
- _push(`<button class="fl-unlock-remember" type="button"${ssrRenderAttr("aria-pressed", node.lock.remember)}${ssrRenderAttr("aria-label", t("rememberPassword"))}${ssrRenderAttr("title", t("rememberPassword"))}>`);
1756
- _push(ssrRenderComponent(_sfc_main$2, { name: "key" }, null, _parent));
1757
- _push(`</button>`);
1758
- } else _push(`<!---->`);
1759
- _push(`<button class="fl-unlock-submit" type="submit"${ssrIncludeBooleanAttr(node.lock.busy || !node.lock.input) ? " disabled" : ""}>${ssrInterpolate(t("open"))}</button><button class="fl-unlock-cancel" type="button"${ssrRenderAttr("aria-label", t("cancel"))}${ssrRenderAttr("title", t("cancel"))}>`);
1760
- _push(ssrRenderComponent(_sfc_main$2, {
1761
- name: "close",
1762
- size: 14
1763
- }, null, _parent));
1764
- _push(`</button></form>`);
1936
+ ssrRenderList(rows.value, (node, index) => {
1937
+ _push(`<!--[-->`);
1938
+ if (isMore(node)) {
1939
+ _push(`<div class="fl-row fl-more" style="${ssrRenderStyle({ "--fl-depth": node.depth })}"><span class="fl-cell fl-cell-check"></span><span class="fl-cell fl-more-cell"><span class="fl-indent" aria-hidden="true"></span><span class="fl-more-count">${ssrInterpolate(t("moreRemaining", { count: node.remaining }))}</span>`);
1940
+ if (node.remaining > (__props.pageSize || 0)) _push(`<!--[--><button class="fl-more-btn" type="button">${ssrInterpolate(t("showMore"))}<span class="fl-more-page">${ssrInterpolate(` ${t("folderItems", { count: __props.pageSize })}`)}</span></button><button class="fl-more-btn fl-more-all" type="button">${ssrInterpolate(t("showAll"))}</button><!--]-->`);
1941
+ else _push(`<button class="fl-more-btn" type="button">${ssrInterpolate(t("showRest", { count: node.remaining }))}</button>`);
1942
+ _push(`</span></div>`);
1765
1943
  } else {
1766
- _push(`<!--[--><span class="fl-cell fl-cell-size">${ssrInterpolate(node.is_dir ? "—" : unref(formatSize)(node.size))}</span><span class="fl-cell fl-cell-date">${ssrInterpolate(unref(formatDate)(node.modified))}</span><span class="fl-cell fl-cell-op">`);
1944
+ _push(`<div class="${ssrRenderClass([{
1945
+ "fl-row-dir": node.is_dir,
1946
+ "fl-row-root": node.isRoot,
1947
+ "fl-row-selected": !node.is_dir && selected.value.has(node.path),
1948
+ "fl-row-locked": !!node.lock,
1949
+ "fl-row-prompting": node.lock?.prompting,
1950
+ "fl-row-error": node.lock?.prompting && !!node.lock.error
1951
+ }, "fl-row"])}" style="${ssrRenderStyle({ "--fl-depth": node.depth })}"><label class="fl-cell fl-cell-check"><input type="checkbox"${ssrIncludeBooleanAttr(!!node.lock || batchBusy.value) ? " disabled" : ""}${ssrRenderAttr("aria-label", t("selectItem", { name: node.name }))}${ssrIncludeBooleanAttr(isChecked(node)) ? " checked" : ""}${ssrRenderAttr("indeterminate", isIndeterminate(node))}></label><span class="fl-cell fl-cell-name"${ssrRenderAttr("title", node.isRoot ? node.path : void 0)}><span class="fl-indent" aria-hidden="true"></span>`);
1767
1952
  if (node.lock) {
1768
- _push(`<button class="fl-action fl-arrow-end fl-lock" type="button"${ssrRenderAttr("aria-label", t("unlockFolder", { name: node.name }))}>`);
1953
+ _push(`<button class="fl-arrow fl-lock" type="button"${ssrRenderAttr("aria-label", t("unlockFolder", { name: node.name }))}${ssrRenderAttr("title", t("unlockFolder", { name: node.name }))}>`);
1769
1954
  _push(ssrRenderComponent(_sfc_main$2, {
1770
1955
  name: "lock",
1771
- size: 18
1956
+ size: 14
1772
1957
  }, null, _parent));
1773
1958
  _push(`</button>`);
1774
1959
  } else if (node.is_dir) {
1775
- _push(`<button type="button" class="${ssrRenderClass([{ "fl-arrow-open": node.expanded }, "fl-action fl-arrow-end"])}"${ssrRenderAttr("aria-label", t(node.expanded ? "collapse" : "expand", { name: node.name }))}${ssrRenderAttr("aria-expanded", node.expanded)}>`);
1960
+ _push(`<button type="button" class="${ssrRenderClass([{ "fl-arrow-open": node.expanded }, "fl-arrow"])}"${ssrRenderAttr("aria-label", t(node.expanded ? "collapse" : "expand", { name: node.name }))}${ssrRenderAttr("aria-expanded", node.expanded)}>`);
1776
1961
  _push(ssrRenderComponent(_sfc_main$2, {
1777
1962
  name: "next",
1778
- size: 18
1963
+ size: 14
1779
1964
  }, null, _parent));
1780
1965
  _push(`</button>`);
1781
- } else {
1782
- _push(`<!--[-->`);
1783
- if (canPreview(node)) {
1784
- _push(`<button class="fl-action fl-preview-btn" type="button"${ssrRenderAttr("aria-label", `${t("preview")} ${node.name}`)}${ssrRenderAttr("title", t("preview"))}>`);
1785
- _push(ssrRenderComponent(_sfc_main$2, { name: "eye" }, null, _parent));
1966
+ } else _push(`<span class="fl-arrow-placeholder" aria-hidden="true"></span>`);
1967
+ if (node.is_dir) _push(`<svg class="fl-folder-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><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"></path></svg>`);
1968
+ else _push(`<span class="fl-type"${ssrRenderAttr("data-kind", unref(fileKind)(node.name))} aria-hidden="true">${ssrInterpolate(unref(extOf)(node.name) || "FILE")}</span>`);
1969
+ _push(`<span class="fl-label"><span class="${ssrRenderClass([{ "fl-name-dir": node.is_dir }, "fl-name"])}">${ssrInterpolate(node.name)}</span><span class="fl-meta">`);
1970
+ if (!node.is_dir) _push(`<!--[-->${ssrInterpolate(unref(formatSize)(node.size))} · ${ssrInterpolate(unref(formatDate)(node.modified))}<!--]-->`);
1971
+ else if (node.loaded) _push(`<!--[-->${ssrInterpolate(t("folderItems", { count: statsOf(node).files }))} · ${ssrInterpolate(unref(formatSize)(statsOf(node).bytes))}<!--]-->`);
1972
+ else _push(`<!---->`);
1973
+ _push(`</span></span>`);
1974
+ if (node.is_dir && node.loaded) _push(`<span class="fl-count">${ssrInterpolate(t("folderItems", { count: statsOf(node).files }))}</span>`);
1975
+ else _push(`<!---->`);
1976
+ if (node.lock?.prompting && node.lock.error) _push(`<span${ssrRenderAttr("id", `${unref(uid)}-error-${index}`)} class="fl-unlock-message" role="alert">${ssrInterpolate(unlockErrorText(node.lock.error))}</span>`);
1977
+ else _push(`<!---->`);
1978
+ if (!node.lock && unref(remembered).has(node.path)) {
1979
+ _push(`<button class="fl-remembered" type="button"${ssrRenderAttr("aria-label", `${t("forgetPassword")} ${node.name}`)}${ssrRenderAttr("title", t("forgetPassword"))}>`);
1980
+ _push(ssrRenderComponent(_sfc_main$2, {
1981
+ name: "key",
1982
+ size: 11
1983
+ }, null, _parent));
1984
+ _push(` ${ssrInterpolate(t("remembered"))} · ${ssrInterpolate(t("forgetPassword"))}</button>`);
1985
+ } else _push(`<!---->`);
1986
+ if (node.loading) _push(`<span class="fl-loading" aria-hidden="true">…</span>`);
1987
+ else _push(`<!---->`);
1988
+ _push(`</span>`);
1989
+ if (node.lock?.prompting) {
1990
+ _push(`<form class="fl-unlock fl-unlock-row">`);
1991
+ _push(ssrRenderComponent(_sfc_main$2, {
1992
+ class: "fl-unlock-lock",
1993
+ name: "lock",
1994
+ size: 18
1995
+ }, null, _parent));
1996
+ _push(`<input${ssrRenderAttr("value", node.lock.input)} type="password" autocomplete="off"${ssrRenderAttr("data-unlock", node.path)}${ssrRenderAttr("placeholder", node.lock.error ? unlockErrorText(node.lock.error) : t("passwordPlaceholder"))}${ssrRenderAttr("aria-label", t("passwordFor", { name: node.name }))}${ssrRenderAttr("aria-invalid", !!node.lock.error)}${ssrRenderAttr("aria-describedby", node.lock.error ? `${unref(uid)}-error-${index}` : void 0)}>`);
1997
+ if (canRemember.value) {
1998
+ _push(`<button class="fl-unlock-remember" type="button"${ssrRenderAttr("aria-pressed", node.lock.remember)}${ssrRenderAttr("aria-label", t("rememberPassword"))}${ssrRenderAttr("title", t("rememberPassword"))}>`);
1999
+ _push(ssrRenderComponent(_sfc_main$2, { name: "key" }, null, _parent));
1786
2000
  _push(`</button>`);
1787
2001
  } else _push(`<!---->`);
1788
- _push(`<button class="fl-action fl-download" type="button"${ssrRenderAttr("aria-label", `${t("download")} ${node.name}`)}${ssrRenderAttr("title", t("download"))}>`);
1789
- _push(ssrRenderComponent(_sfc_main$2, { name: "download" }, null, _parent));
1790
- _push(`</button><!--]-->`);
2002
+ _push(`<button class="fl-unlock-submit" type="submit"${ssrIncludeBooleanAttr(node.lock.busy || !node.lock.input) ? " disabled" : ""}>${ssrInterpolate(t("open"))}</button><button class="fl-unlock-cancel" type="button"${ssrRenderAttr("aria-label", t("cancel"))}${ssrRenderAttr("title", t("cancel"))}>`);
2003
+ _push(ssrRenderComponent(_sfc_main$2, {
2004
+ name: "close",
2005
+ size: 14
2006
+ }, null, _parent));
2007
+ _push(`</button></form>`);
2008
+ } else {
2009
+ _push(`<!--[--><span class="fl-cell fl-cell-size">${ssrInterpolate(node.is_dir ? "—" : unref(formatSize)(node.size))}</span><span class="fl-cell fl-cell-date">${ssrInterpolate(unref(formatDate)(node.modified))}</span><span class="fl-cell fl-cell-op">`);
2010
+ if (node.lock) {
2011
+ _push(`<button class="fl-action fl-arrow-end fl-lock" type="button"${ssrRenderAttr("aria-label", t("unlockFolder", { name: node.name }))}>`);
2012
+ _push(ssrRenderComponent(_sfc_main$2, {
2013
+ name: "lock",
2014
+ size: 18
2015
+ }, null, _parent));
2016
+ _push(`</button>`);
2017
+ } else if (node.is_dir) {
2018
+ _push(`<button type="button" class="${ssrRenderClass([{ "fl-arrow-open": node.expanded }, "fl-action fl-arrow-end"])}"${ssrRenderAttr("aria-label", t(node.expanded ? "collapse" : "expand", { name: node.name }))}${ssrRenderAttr("aria-expanded", node.expanded)}>`);
2019
+ _push(ssrRenderComponent(_sfc_main$2, {
2020
+ name: "next",
2021
+ size: 18
2022
+ }, null, _parent));
2023
+ _push(`</button>`);
2024
+ } else {
2025
+ _push(`<!--[-->`);
2026
+ if (canPreview(node)) {
2027
+ _push(`<button class="fl-action fl-preview-btn" type="button"${ssrRenderAttr("aria-label", `${t("preview")} ${node.name}`)}${ssrRenderAttr("title", t("preview"))}>`);
2028
+ _push(ssrRenderComponent(_sfc_main$2, { name: "eye" }, null, _parent));
2029
+ _push(`</button>`);
2030
+ } else _push(`<!---->`);
2031
+ _push(`<button class="fl-action fl-download" type="button"${ssrRenderAttr("aria-label", `${t("download")} ${node.name}`)}${ssrRenderAttr("title", t("download"))}>`);
2032
+ _push(ssrRenderComponent(_sfc_main$2, { name: "download" }, null, _parent));
2033
+ _push(`</button><!--]-->`);
2034
+ }
2035
+ _push(`</span><!--]-->`);
1791
2036
  }
1792
- _push(`</span><!--]-->`);
2037
+ _push(`</div>`);
1793
2038
  }
1794
- _push(`</div>`);
2039
+ _push(`<!--]-->`);
1795
2040
  });
1796
2041
  _push(`<!--]--><div class="fl-foot"><span>${ssrInterpolate(t("loaded", { count: allFiles.value.length }))} · ${ssrInterpolate(t("totalSize", { size: unref(formatSize)(unref(totalSize)(allFiles.value)) }))}</span></div>`);
1797
2042
  if (toast.value) _push(`<div class="fl-toast" role="status">${ssrInterpolate(toast.value)}</div>`);
1798
2043
  else _push(`<!---->`);
2044
+ _push(ssrRenderComponent(_sfc_main$3, {
2045
+ open: !!zipNotice.value,
2046
+ title: zipNotice.value ? t(zipNotice.value.kind === "warn" ? "zipWarnTitle" : "zipBlockTitle", { size: unref(formatSize)(zipNotice.value.size) }) : "",
2047
+ text: zipNotice.value ? t(zipNotice.value.kind === "warn" ? "zipWarnText" : "zipBlockText", { limit: unref(formatSize)(zipNotice.value.limit) }) : "",
2048
+ tip: zipTip.value,
2049
+ "confirm-label": zipNotice.value?.kind === "warn" ? t("zipContinue") : void 0,
2050
+ "cancel-label": zipNotice.value?.kind === "warn" ? t("cancel") : t("gotIt"),
2051
+ onConfirm: confirmZip,
2052
+ onClose: ($event) => zipNotice.value = null
2053
+ }, null, _parent));
1799
2054
  _push(ssrRenderComponent(_sfc_main$1, {
1800
2055
  open: !!previewNode.value,
1801
2056
  file: previewNode.value,