vitepress-plugin-filelist 0.7.0 → 0.7.2

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.
@@ -102,6 +102,10 @@ interface FileListLabels {
102
102
  deepContinue: string;
103
103
  deepSkipped: string;
104
104
  deepFailed: string;
105
+ /** Beside a locked folder shown in deep search results. */
106
+ deepLocked: string;
107
+ /** The × in the filter box. */
108
+ clearFilter: string;
105
109
  /** Markdown / HTML preview: the source / rendered switch. */
106
110
  viewMode: string;
107
111
  viewSource: string;
@@ -179,6 +183,11 @@ interface FileListProps {
179
183
  * Defaults to `500`.
180
184
  */
181
185
  deepSearchLimit?: number;
186
+ /**
187
+ * `manual` (default): “Try deep search” starts deep search mode.
188
+ * `auto`: typing a keyword starts it at once (for small mounts).
189
+ */
190
+ deepSearch?: 'manual' | 'auto';
182
191
  /**
183
192
  * Browsers that must build the ZIP in memory (Firefox, Safari) ask first above
184
193
  * this many MB. Defaults to 500 (200 on touch devices); `0` never asks.
@@ -216,6 +225,7 @@ declare const _default: import("vue").DefineComponent<FileListProps, {}, {}, {},
216
225
  rememberPassword: boolean;
217
226
  pageSize: number;
218
227
  deepSearchLimit: number;
228
+ deepSearch: "manual" | "auto";
219
229
  locale: FileListLocale;
220
230
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
221
231
  declare global {
@@ -575,8 +575,8 @@ const _sfc_main$3 = /*@__PURE__*/ defineComponent({
575
575
  "aria-label": __props.title
576
576
  }, _attrs))}>`);
577
577
  if (__props.open) {
578
- _push(`<div class="fl-confirm-panel"><p class="fl-confirm-title">${ssrInterpolate(__props.title)}</p><p class="fl-confirm-text">${ssrInterpolate(__props.text)}</p>`);
579
- if (__props.tip) _push(`<p class="fl-confirm-text">${ssrInterpolate(__props.tip)}</p>`);
578
+ _push(`<div class="fl-confirm-panel"><div class="fl-confirm-title">${ssrInterpolate(__props.title)}</div><div class="fl-confirm-text">${ssrInterpolate(__props.text)}</div>`);
579
+ if (__props.tip) _push(`<div class="fl-confirm-text">${ssrInterpolate(__props.tip)}</div>`);
580
580
  else _push(`<!---->`);
581
581
  _push(`<div class="fl-confirm-actions">`);
582
582
  if (__props.confirmLabel) _push(`<button class="fl-btn" type="button">${ssrInterpolate(__props.cancelLabel)}</button>`);
@@ -869,8 +869,8 @@ const fileListLocales = {
869
869
  forgetPassword: "忘记",
870
870
  unlockFolder: "输入 {name} 的密码",
871
871
  networkHint: "可能被跨域拦截:请检查 OpenList 的 cors.allow_origins,或 OpenList-Worker 的 ALLOW_URLS。",
872
- deepHint: "只筛选了已加载的内容",
873
- deepSearch: "深度搜索",
872
+ deepHint: "只筛选了已加载的内容,",
873
+ deepSearch: "尝试深度搜索",
874
874
  deepRunning: "正在深度搜索 · 已查找 {count} 个文件夹",
875
875
  deepStop: "停止",
876
876
  deepDone: "已搜索全部 {count} 个文件夹",
@@ -878,6 +878,8 @@ const fileListLocales = {
878
878
  deepContinue: "继续",
879
879
  deepSkipped: "跳过 {count} 个加密文件夹",
880
880
  deepFailed: "{count} 个文件夹读取失败",
881
+ deepLocked: "未搜索(需要密码)",
882
+ clearFilter: "清除筛选",
881
883
  viewMode: "显示方式",
882
884
  viewSource: "源码",
883
885
  viewRendered: "渲染",
@@ -963,8 +965,8 @@ const fileListLocales = {
963
965
  forgetPassword: "Forget",
964
966
  unlockFolder: "Enter the password for {name}",
965
967
  networkHint: "Possibly blocked by CORS: check cors.allow_origins in OpenList, or ALLOW_URLS in OpenList-Worker.",
966
- deepHint: "Only loaded folders are filtered",
967
- deepSearch: "Deep search",
968
+ deepHint: "Only loaded folders are filtered. ",
969
+ deepSearch: "Try deep search",
968
970
  deepRunning: "Searching · {count} folders checked",
969
971
  deepStop: "Stop",
970
972
  deepDone: "Searched all {count} folders",
@@ -972,6 +974,8 @@ const fileListLocales = {
972
974
  deepContinue: "Continue",
973
975
  deepSkipped: "{count} locked folders skipped",
974
976
  deepFailed: "{count} folders could not be read",
977
+ deepLocked: "Not searched (password needed)",
978
+ clearFilter: "Clear filter",
975
979
  viewMode: "View",
976
980
  viewSource: "Source",
977
981
  viewRendered: "Rendered",
@@ -1315,19 +1319,19 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
1315
1319
  else if (mode.value === "audio") _push(`<audio class="fl-preview-audio"${ssrRenderAttr("src", __props.src)} controls preload="metadata"></audio>`);
1316
1320
  else if (mode.value === "text") {
1317
1321
  _push(`<div class="${ssrRenderClass([{ "fl-preview-rendered": renderAs.value && view.value === "rendered" }, "fl-preview-text"])}">`);
1318
- if (truncated.value) _push(`<p class="fl-preview-note">${ssrInterpolate(t("textTruncated", { size: unref(formatSize)(__props.maxTextBytes) }))}</p>`);
1322
+ if (truncated.value) _push(`<div class="fl-preview-note">${ssrInterpolate(t("textTruncated", { size: unref(formatSize)(__props.maxTextBytes) }))}</div>`);
1319
1323
  else _push(`<!---->`);
1320
1324
  if (renderAs.value === "markdown" && view.value === "rendered") {
1321
1325
  _push(`<!--[--><!-- sanitised by renderMarkdown (DOMPurify) -->`);
1322
1326
  if (renderedHtml.value) _push(`<div class="vp-doc fl-md">${renderedHtml.value ?? ""}</div>`);
1323
1327
  else _push(`<div class="fl-preview-status" role="status">${ssrInterpolate(t("loading"))}</div>`);
1324
1328
  _push(`<!--]-->`);
1325
- } else if (renderAs.value === "html" && view.value === "rendered") _push(`<!--[--><p class="fl-preview-note">${ssrInterpolate(t("htmlSandboxNote"))}</p><!-- empty sandbox: no scripts, no forms, no popups, an opaque origin --><iframe class="fl-preview-html" sandbox="" referrerpolicy="no-referrer"${ssrRenderAttr("srcdoc", textContent.value)}${ssrRenderAttr("title", __props.file.name)}></iframe><!--]-->`);
1329
+ } else if (renderAs.value === "html" && view.value === "rendered") _push(`<!--[--><div class="fl-preview-note">${ssrInterpolate(t("htmlSandboxNote"))}</div><!-- empty sandbox: no scripts, no forms, no popups, an opaque origin --><iframe class="fl-preview-html" sandbox="" referrerpolicy="no-referrer"${ssrRenderAttr("srcdoc", textContent.value)}${ssrRenderAttr("title", __props.file.name)}></iframe><!--]-->`);
1326
1330
  else _push(`<pre>${ssrInterpolate(textContent.value)}</pre>`);
1327
1331
  _push(`</div>`);
1328
1332
  } else if (mode.value === "pdf") _push(`<iframe class="fl-preview-frame"${ssrRenderAttr("src", __props.viewer || blobUrl.value)}${ssrRenderAttr("title", __props.file.name)}></iframe>`);
1329
1333
  else {
1330
- _push(`<div class="fl-preview-info"><span class="fl-type fl-preview-badge"${ssrRenderAttr("data-kind", unref(fileKind)(__props.file.name))} aria-hidden="true">${ssrInterpolate(unref(extOf)(__props.file.name) || "FILE")}</span><p class="fl-preview-message">${ssrInterpolate(infoMessage.value)}</p>`);
1334
+ _push(`<div class="fl-preview-info"><span class="fl-type fl-preview-badge"${ssrRenderAttr("data-kind", unref(fileKind)(__props.file.name))} aria-hidden="true">${ssrInterpolate(unref(extOf)(__props.file.name) || "FILE")}</span><div class="fl-preview-message">${ssrInterpolate(infoMessage.value)}</div>`);
1331
1335
  if (!__props.demo) {
1332
1336
  _push(`<button class="fl-btn fl-btn-primary" type="button">`);
1333
1337
  _push(ssrRenderComponent(_sfc_main$2, {
@@ -1563,6 +1567,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1563
1567
  },
1564
1568
  pageSize: { default: 200 },
1565
1569
  deepSearchLimit: { default: 500 },
1570
+ deepSearch: { default: "manual" },
1566
1571
  zipWarnSize: {},
1567
1572
  zipMaxSize: {},
1568
1573
  locale: { default: "zh-CN" },
@@ -1722,6 +1727,21 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1722
1727
  zipPath: [item.is_dir ? name : item.name || sourceName(path)]
1723
1728
  };
1724
1729
  }
1730
+ /** Run at most `size` tasks at once. */
1731
+ function concurrency(size) {
1732
+ let active = 0;
1733
+ const waiting = [];
1734
+ return async (task) => {
1735
+ if (active >= size) await new Promise((resolve) => waiting.push(resolve));
1736
+ active += 1;
1737
+ try {
1738
+ return await task();
1739
+ } finally {
1740
+ active -= 1;
1741
+ waiting.shift()?.();
1742
+ }
1743
+ };
1744
+ }
1725
1745
  class RootLockedError extends Error {
1726
1746
  rememberedAt;
1727
1747
  constructor(rememberedAt) {
@@ -1733,7 +1753,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1733
1753
  * Build a fresh tree. `expandedPaths` re-opens those folders (refresh keeps
1734
1754
  * what was open); without it the configured `expanded` defaults apply.
1735
1755
  */
1736
- async function buildTree(expandedPaths) {
1756
+ async function buildTree(expandedPaths, loadedPaths) {
1737
1757
  let nodes;
1738
1758
  if (props.sources.length) nodes = await Promise.all(props.sources.map((source) => loadSourceRoot(source, expandedPaths ? expandedPaths.has(source.path) : source.expanded ?? props.expanded)));
1739
1759
  else {
@@ -1744,13 +1764,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1744
1764
  throw isPasswordError(error) ? new RootLockedError(rememberedAt) : error;
1745
1765
  }
1746
1766
  }
1767
+ const limit = concurrency(4);
1747
1768
  const open = async (list) => {
1748
- for (const node of list) {
1749
- const wanted = node.expanded || expandedPaths?.has(node.path);
1750
- if (!node.is_dir || node.lock || !wanted) continue;
1751
- node.expanded = await loadChildren(node, "show");
1752
- if (node.expanded && expandedPaths) await open(node.children);
1753
- }
1769
+ await Promise.all(list.map(async (node) => {
1770
+ const wanted = !!(node.expanded || expandedPaths?.has(node.path));
1771
+ const keep = !!loadedPaths?.has(node.path);
1772
+ if (!node.is_dir || node.lock || !(wanted || keep)) return;
1773
+ const ok = await limit(() => loadChildren(node, wanted ? "show" : false, !wanted));
1774
+ node.expanded = wanted && ok;
1775
+ if (ok && (expandedPaths || loadedPaths)) await open(node.children);
1776
+ }));
1754
1777
  };
1755
1778
  await open(nodes);
1756
1779
  return nodes;
@@ -1820,7 +1843,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1820
1843
  const rows = computed(() => {
1821
1844
  const out = [];
1822
1845
  const query = keyword.value.trim().toLowerCase();
1823
- const matches = (node) => !query || node.name.toLowerCase().includes(query) || node.is_dir && node.children.some(matches);
1846
+ const matches = (node) => !query || node.name.toLowerCase().includes(query) || node.is_dir && (node.children.some(matches) || deepMode.value && !!node.lock);
1824
1847
  const walk = (nodes, key, depth) => {
1825
1848
  const list = nodes.filter(matches);
1826
1849
  const limit = query ? list.length : Math.min(list.length, limitOf(key));
@@ -1874,11 +1897,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1874
1897
  const deep = reactive({
1875
1898
  running: false,
1876
1899
  searched: 0,
1877
- skipped: 0,
1878
1900
  failed: 0,
1879
1901
  capped: false,
1880
1902
  done: false
1881
1903
  });
1904
+ /**
1905
+ * Deep search mode: entered by “Try deep search” (or at once with
1906
+ * `deep-search="auto"`), left by clearing the filter. While on, new keywords,
1907
+ * refreshes and newly unlocked folders are searched without asking again.
1908
+ */
1909
+ const deepMode = ref(false);
1882
1910
  let deepSeq = 0;
1883
1911
  /** Folders that are known but not loaded yet (locked ones excluded). */
1884
1912
  function unloadedFolders() {
@@ -1898,27 +1926,97 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
1898
1926
  if (!keyword.value.trim()) return null;
1899
1927
  if (deep.running) return "running";
1900
1928
  if (deep.capped) return "capped";
1901
- if (hasUnloaded.value) return "idle";
1929
+ if (!deepMode.value) return hasUnloaded.value ? "idle" : null;
1902
1930
  return deep.done ? "done" : null;
1903
1931
  });
1932
+ /** “Try deep search”, “Continue”, and the automatic runs of deep search mode. */
1933
+ async function deepSearch() {
1934
+ deepMode.value = true;
1935
+ const seq = ++deepSeq;
1936
+ const limit = Math.max(1, Math.floor(props.deepSearchLimit ?? 500));
1937
+ const runStart = deep.searched;
1938
+ Object.assign(deep, {
1939
+ running: true,
1940
+ capped: false,
1941
+ done: false
1942
+ });
1943
+ let wave = unloadedFolders();
1944
+ while (wave.length && seq === deepSeq) {
1945
+ const next = [];
1946
+ const queue = [...wave];
1947
+ const worker = async () => {
1948
+ for (let node = queue.shift(); node && seq === deepSeq; node = queue.shift()) {
1949
+ if (deep.searched - runStart >= limit) return;
1950
+ deep.searched += 1;
1951
+ if (!await loadChildren(node, false, true)) {
1952
+ if (!node.lock) deep.failed += 1;
1953
+ continue;
1954
+ }
1955
+ for (const child of node.children) if (child.is_dir && !child.loaded && !child.lock) next.push(child);
1956
+ }
1957
+ };
1958
+ await Promise.all(Array.from({ length: Math.min(4, queue.length) }, worker));
1959
+ if (deep.searched - runStart >= limit) break;
1960
+ wave = next;
1961
+ }
1962
+ if (seq !== deepSeq) return;
1963
+ deep.running = false;
1964
+ deep.capped = hasUnloaded.value && deep.searched - runStart >= limit;
1965
+ deep.done = !deep.capped;
1966
+ }
1967
+ /** Pause a run (internal: refresh, reload). */
1904
1968
  function stopDeepSearch() {
1905
1969
  deepSeq += 1;
1906
1970
  deep.running = false;
1907
1971
  }
1908
1972
  watch(keyword, (value) => {
1909
- if (value.trim()) return;
1973
+ if (value.trim()) {
1974
+ if (props.deepSearch === "auto") deepMode.value = true;
1975
+ return;
1976
+ }
1910
1977
  stopDeepSearch();
1978
+ deepMode.value = false;
1911
1979
  Object.assign(deep, {
1912
1980
  searched: 0,
1913
- skipped: 0,
1914
1981
  failed: 0,
1915
1982
  capped: false,
1916
1983
  done: false
1917
1984
  });
1918
1985
  });
1986
+ watch(() => deepMode.value && hasUnloaded.value && !deep.running && !deep.capped && !refreshing.value, (ready) => {
1987
+ if (ready) deepSearch();
1988
+ });
1989
+ /**
1990
+ * Counted from the tree, so a refresh that re-reads the same folders shows the
1991
+ * same numbers: folders read (or found locked), and the locked ones.
1992
+ */
1993
+ const folderCounts = computed(() => {
1994
+ const counts = {
1995
+ checked: 0,
1996
+ locked: 0
1997
+ };
1998
+ const walk = (nodes) => {
1999
+ for (const node of nodes) {
2000
+ if (!node.is_dir) continue;
2001
+ if (node.lock) {
2002
+ counts.checked += 1;
2003
+ counts.locked += 1;
2004
+ } else if (node.loaded) {
2005
+ counts.checked += 1;
2006
+ walk(node.children);
2007
+ }
2008
+ }
2009
+ };
2010
+ walk(roots.value);
2011
+ return counts;
2012
+ });
2013
+ const lockedFolders = computed(() => folderCounts.value.locked);
2014
+ function lockedInSearch(node) {
2015
+ return deepMode.value && !!node.lock && !!keyword.value.trim();
2016
+ }
1919
2017
  const deepSummary = computed(() => [
1920
- deepRow.value === "capped" ? t("deepCapped", { count: deep.searched }) : t("deepDone", { count: deep.searched }),
1921
- deep.skipped ? t("deepSkipped", { count: deep.skipped }) : "",
2018
+ t(deepRow.value === "capped" ? "deepCapped" : "deepDone", { count: folderCounts.value.checked }),
2019
+ lockedFolders.value ? t("deepSkipped", { count: lockedFolders.value }) : "",
1922
2020
  deep.failed ? t("deepFailed", { count: deep.failed }) : ""
1923
2021
  ].filter(Boolean).join(" · "));
1924
2022
  const stats = computed(() => folderStats(roots.value, selected.value));
@@ -2150,7 +2248,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
2150
2248
  name: "search",
2151
2249
  size: 15
2152
2250
  }, null, _parent));
2153
- _push(`<input${ssrRenderAttr("value", keyword.value)} type="text"${ssrRenderAttr("placeholder", t("filter"))}${ssrRenderAttr("aria-label", t("filter"))}></label><button class="${ssrRenderClass([{ "fl-spinning": refreshing.value }, "fl-icon-btn fl-refresh"])}" type="button"${ssrIncludeBooleanAttr(refreshing.value || loadingRoot.value) ? " disabled" : ""}${ssrRenderAttr("aria-label", refreshing.value ? t("refreshing") : t("refresh"))}${ssrRenderAttr("title", refreshing.value ? t("refreshing") : t("refresh"))}>`);
2251
+ _push(`<input${ssrRenderAttr("value", keyword.value)} type="text"${ssrRenderAttr("placeholder", t("filter"))}${ssrRenderAttr("aria-label", t("filter"))}>`);
2252
+ if (keyword.value) {
2253
+ _push(`<button class="fl-search-clear" type="button"${ssrRenderAttr("aria-label", t("clearFilter"))}${ssrRenderAttr("title", t("clearFilter"))}>`);
2254
+ _push(ssrRenderComponent(_sfc_main$2, {
2255
+ name: "close",
2256
+ size: 12
2257
+ }, null, _parent));
2258
+ _push(`</button>`);
2259
+ } else _push(`<!---->`);
2260
+ _push(`</label><button class="${ssrRenderClass([{ "fl-spinning": refreshing.value }, "fl-icon-btn fl-refresh"])}" type="button"${ssrIncludeBooleanAttr(refreshing.value || loadingRoot.value) ? " disabled" : ""}${ssrRenderAttr("aria-label", refreshing.value ? t("refreshing") : t("refresh"))}${ssrRenderAttr("title", refreshing.value ? t("refreshing") : t("refresh"))}>`);
2154
2261
  _push(ssrRenderComponent(_sfc_main$2, {
2155
2262
  name: "refresh",
2156
2263
  size: 15
@@ -2200,9 +2307,9 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
2200
2307
  _push(`<button class="fl-unlock-submit" type="submit"${ssrIncludeBooleanAttr(rootLock.value.busy || !rootLock.value.input) ? " disabled" : ""}>${ssrInterpolate(t("open"))}</button></span></form>`);
2201
2308
  } else if (errorMsg.value) {
2202
2309
  _push(`<div class="fl-status fl-error">${ssrInterpolate(t("loadFailed", { message: errorMsg.value }))} `);
2203
- if (/guest/i.test(errorMsg.value)) _push(`<p class="fl-hint">${ssrInterpolate(t("guestHint"))}</p>`);
2310
+ if (/guest/i.test(errorMsg.value)) _push(`<div class="fl-hint">${ssrInterpolate(t("guestHint"))}</div>`);
2204
2311
  else _push(`<!---->`);
2205
- if (unref(isNetworkError)(errorMsg.value)) _push(`<p class="fl-hint">${ssrInterpolate(t("corsHint"))}</p>`);
2312
+ if (unref(isNetworkError)(errorMsg.value)) _push(`<div class="fl-hint">${ssrInterpolate(t("corsHint"))}</div>`);
2206
2313
  else _push(`<!---->`);
2207
2314
  _push(`<button class="fl-btn" type="button">${ssrInterpolate(t("retry"))}</button></div>`);
2208
2315
  } else if (visible.value.length === 0) _push(`<div class="fl-status">${ssrInterpolate(t("empty"))}</div>`);
@@ -2251,8 +2358,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
2251
2358
  _push(`<!--]--></span><span class="fl-meta">`);
2252
2359
  if (!node.is_dir) _push(`<!--[-->${ssrInterpolate(unref(formatSize)(node.size))} · ${ssrInterpolate(unref(formatDate)(node.modified))}<!--]-->`);
2253
2360
  else if (node.loaded) _push(`<!--[-->${ssrInterpolate(t("folderItems", { count: statsOf(node).files }))} · ${ssrInterpolate(unref(formatSize)(statsOf(node).bytes))}<!--]-->`);
2361
+ else if (lockedInSearch(node)) _push(`<span class="fl-locked-note">${ssrInterpolate(t("deepLocked"))}</span>`);
2254
2362
  else _push(`<!---->`);
2255
2363
  _push(`</span></span>`);
2364
+ if (lockedInSearch(node) && !node.lock?.prompting) _push(`<span class="fl-locked-note fl-locked-hint">${ssrInterpolate(t("deepLocked"))}</span>`);
2365
+ else _push(`<!---->`);
2256
2366
  if (node.is_dir && node.loaded) _push(`<span class="fl-count">${ssrInterpolate(t("folderItems", { count: statsOf(node).files }))}</span>`);
2257
2367
  else _push(`<!---->`);
2258
2368
  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>`);
@@ -2322,16 +2432,15 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
2322
2432
  });
2323
2433
  _push(`<!--]-->`);
2324
2434
  if (deepRow.value) {
2325
- _push(`<div class="fl-row fl-deep" role="status"><span class="fl-cell fl-cell-check"></span><span class="fl-cell fl-more-cell"><span class="fl-indent" aria-hidden="true"></span>`);
2326
- if (deepRow.value === "running") _push(`<!--[--><span class="fl-more-count">${ssrInterpolate(t("deepRunning", { count: deep.searched }))}</span><button class="fl-more-btn fl-more-all" type="button">${ssrInterpolate(t("deepStop"))}</button><span class="fl-deep-bar" aria-hidden="true"></span><!--]-->`);
2327
- else if (deepRow.value === "idle") _push(`<!--[--><span class="fl-more-count">${ssrInterpolate(deep.searched ? deepSummary.value : t("deepHint"))}</span><button class="fl-more-btn" type="button">${ssrInterpolate(t("deepSearch"))}</button><!--]-->`);
2328
- else {
2329
- _push(`<!--[--><span class="fl-more-count">${ssrInterpolate(deepSummary.value)}</span>`);
2330
- if (deepRow.value === "capped") _push(`<button class="fl-more-btn" type="button">${ssrInterpolate(t("deepContinue"))}</button>`);
2331
- else _push(`<!---->`);
2332
- _push(`<!--]-->`);
2333
- }
2334
- _push(`</span></div>`);
2435
+ _push(`<div class="fl-deep" role="status"><div class="fl-deep-text">`);
2436
+ if (deepRow.value === "running") _push(`<!--[-->${ssrInterpolate(t("deepRunning", { count: folderCounts.value.checked }))} · <button class="fl-deep-link" type="button">${ssrInterpolate(t("deepStop"))}</button><!--]-->`);
2437
+ else if (deepRow.value === "idle") _push(`<!--[-->${ssrInterpolate(t("deepHint"))}<button class="fl-deep-link" type="button">${ssrInterpolate(t("deepSearch"))}</button><!--]-->`);
2438
+ else if (deepRow.value === "capped") _push(`<!--[-->${ssrInterpolate(deepSummary.value)} · <button class="fl-deep-link" type="button">${ssrInterpolate(t("deepContinue"))}</button><!--]-->`);
2439
+ else _push(`<!--[-->${ssrInterpolate(deepSummary.value)}<!--]-->`);
2440
+ _push(`</div>`);
2441
+ if (deepRow.value === "running") _push(`<span class="fl-deep-bar" aria-hidden="true"></span>`);
2442
+ else _push(`<!---->`);
2443
+ _push(`</div>`);
2335
2444
  } else _push(`<!---->`);
2336
2445
  _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>`);
2337
2446
  if (toast.value) _push(`<div class="fl-toast" role="status">${ssrInterpolate(toast.value)}</div>`);
@@ -771,6 +771,7 @@
771
771
  .fl-hint {
772
772
  margin: 6px 0 0;
773
773
  font-size: 12px;
774
+ line-height: 1.5;
774
775
  color: var(--vp-c-text-2);
775
776
  }
776
777
 
@@ -824,8 +825,7 @@
824
825
 
825
826
  /* ---------------- long folders ---------------- */
826
827
 
827
- .fl-more,
828
- .fl-deep {
828
+ .fl-more {
829
829
  background: var(--vp-c-bg-soft);
830
830
  }
831
831
 
@@ -878,8 +878,69 @@
878
878
  border-radius: 3px;
879
879
  }
880
880
 
881
+ .fl-search-clear {
882
+ display: inline-flex;
883
+ flex: none;
884
+ align-items: center;
885
+ justify-content: center;
886
+ width: 20px;
887
+ height: 20px;
888
+ padding: 0;
889
+ color: var(--vp-c-text-2);
890
+ cursor: pointer;
891
+ background: var(--vp-c-default-soft);
892
+ border: 0;
893
+ border-radius: 50%;
894
+ }
895
+
896
+ .fl-search-clear:hover {
897
+ color: var(--vp-c-text-1);
898
+ }
899
+
900
+ /* the status line closing a filtered list: centred, wraps, never overflows */
881
901
  .fl-deep {
882
902
  position: relative;
903
+ display: flex;
904
+ align-items: center;
905
+ justify-content: center;
906
+ box-sizing: border-box;
907
+ min-height: 40px;
908
+ padding: 8px 16px;
909
+ text-align: center;
910
+ background: var(--vp-c-bg-soft);
911
+ border-bottom: 1px solid var(--vp-c-divider);
912
+ }
913
+
914
+ .fl-deep-text {
915
+ font-size: 13px;
916
+ line-height: 1.6;
917
+ color: var(--vp-c-text-2);
918
+ }
919
+
920
+ .fl-deep-link {
921
+ padding: 0;
922
+ font: inherit;
923
+ font-weight: 600;
924
+ color: var(--vp-c-brand-1);
925
+ text-decoration: underline;
926
+ text-underline-offset: 3px;
927
+ cursor: pointer;
928
+ background: none;
929
+ border: 0;
930
+ }
931
+
932
+ .fl-deep-link:hover {
933
+ color: var(--vp-c-brand-2);
934
+ }
935
+
936
+ .fl-locked-note {
937
+ font-size: 12px;
938
+ color: var(--fl-lock-fg);
939
+ white-space: nowrap;
940
+ }
941
+
942
+ .fl-locked-hint {
943
+ margin-left: 8px;
883
944
  }
884
945
 
885
946
  .fl-deep-bar {
@@ -925,6 +986,7 @@
925
986
  .fl-confirm-title {
926
987
  margin: 0 0 10px;
927
988
  font-size: 16px;
989
+ line-height: 1.5;
928
990
  font-weight: 600;
929
991
  }
930
992
 
@@ -963,7 +1025,31 @@
963
1025
  flex: 1 1 auto;
964
1026
  width: auto;
965
1027
  height: 40px;
966
- padding: 0 12px;
1028
+ padding: 0 4px 0 12px;
1029
+ }
1030
+
1031
+ .fl-search-clear {
1032
+ width: 36px;
1033
+ height: 36px;
1034
+ background: transparent;
1035
+ }
1036
+
1037
+ .fl-deep {
1038
+ min-height: 48px;
1039
+ }
1040
+
1041
+ .fl-deep-text {
1042
+ font-size: 14px;
1043
+ }
1044
+
1045
+ /* a taller tap target without changing the line */
1046
+ .fl-deep-link {
1047
+ padding: 11px 2px;
1048
+ margin: -11px 0;
1049
+ }
1050
+
1051
+ .fl-locked-hint {
1052
+ display: none;
967
1053
  }
968
1054
 
969
1055
  /* 16px keeps iOS Safari from zooming in on focus */
@@ -1476,6 +1562,7 @@
1476
1562
  margin: 0;
1477
1563
  padding: 8px 20px;
1478
1564
  font-size: 12px;
1565
+ line-height: 1.5;
1479
1566
  color: var(--vp-c-text-2);
1480
1567
  background: var(--vp-c-bg-soft);
1481
1568
  border-bottom: 1px solid var(--vp-c-divider);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitepress-plugin-filelist",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "packageManager": "pnpm@11.25.0",
5
5
  "type": "module",
6
6
  "description": "OpenList file browser for VitePress: tree view, batch ZIP download, i18n, password and token support.",