@dirwell/theme-ledger 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/dist/client.js +25 -25
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +16 -33
- package/dist/index.mjs.map +1 -1
- package/dist/{model-B0qQsjRT.d.mts → model-CeW-leLg.d.mts} +5 -3
- package/dist/model.d.mts +1 -1
- package/dist/source/Ledger.svelte +76 -19
- package/dist/source/shared/live-search.mjs +28 -0
- package/dist/source/shared/navigation.mjs +1 -0
- package/package.json +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { onMount, tick, untrack } from "svelte";
|
|
3
|
+
import { queryLiveArchive } from "./shared/live-search.mjs";
|
|
3
4
|
import {
|
|
4
5
|
readView,
|
|
5
6
|
createViewHistory,
|
|
@@ -168,17 +169,6 @@
|
|
|
168
169
|
return () => cancelAnimationFrame(frame);
|
|
169
170
|
});
|
|
170
171
|
const isDirectory = directoryLike;
|
|
171
|
-
function safeHref(record) {
|
|
172
|
-
if (
|
|
173
|
-
typeof location !== "undefined" &&
|
|
174
|
-
location.protocol === "file:" &&
|
|
175
|
-
isDirectory(record) &&
|
|
176
|
-
!record.exitsExplorer &&
|
|
177
|
-
record.href?.endsWith("/")
|
|
178
|
-
)
|
|
179
|
-
return record.href + "index.html";
|
|
180
|
-
return record.href;
|
|
181
|
-
}
|
|
182
172
|
async function hydrateData() {
|
|
183
173
|
if (complete || !model.manifestHref || location.protocol === "file:")
|
|
184
174
|
return;
|
|
@@ -205,6 +195,7 @@
|
|
|
205
195
|
}
|
|
206
196
|
}
|
|
207
197
|
async function navigate(event, record) {
|
|
198
|
+
selected = record.id;
|
|
208
199
|
saveView();
|
|
209
200
|
const dataHref = record.presentation?.folderDataHref;
|
|
210
201
|
if (
|
|
@@ -346,7 +337,7 @@
|
|
|
346
337
|
if (globalOpen) {
|
|
347
338
|
if (event.key === "Escape") {
|
|
348
339
|
event.preventDefault();
|
|
349
|
-
|
|
340
|
+
closeArchive();
|
|
350
341
|
}
|
|
351
342
|
return;
|
|
352
343
|
}
|
|
@@ -401,6 +392,44 @@
|
|
|
401
392
|
localStorage.setItem("dirwell-color-scheme", value);
|
|
402
393
|
} catch {}
|
|
403
394
|
}
|
|
395
|
+
let archiveDebounce,
|
|
396
|
+
archiveTrigger,
|
|
397
|
+
archivePending = false;
|
|
398
|
+
async function closeArchive() {
|
|
399
|
+
globalOpen = false;
|
|
400
|
+
await tick();
|
|
401
|
+
archiveTrigger?.focus();
|
|
402
|
+
}
|
|
403
|
+
function scheduleArchiveSearch() {
|
|
404
|
+
archiveAbort?.abort();
|
|
405
|
+
archiveRequest++;
|
|
406
|
+
archivePending = false;
|
|
407
|
+
clearTimeout(archiveDebounce);
|
|
408
|
+
archiveDebounce = setTimeout(() => {
|
|
409
|
+
archiveDebounce = undefined;
|
|
410
|
+
searchArchive();
|
|
411
|
+
}, 300);
|
|
412
|
+
}
|
|
413
|
+
$effect(() => {
|
|
414
|
+
if (!globalOpen || !model.searchQueryHref) return;
|
|
415
|
+
const timer = setInterval(() => {
|
|
416
|
+
if (
|
|
417
|
+
document.visibilityState === "visible" &&
|
|
418
|
+
globalQuery.trim() &&
|
|
419
|
+
!archivePending &&
|
|
420
|
+
archiveDebounce === undefined
|
|
421
|
+
)
|
|
422
|
+
searchArchive();
|
|
423
|
+
}, model.searchRefreshIntervalMs ?? 2000);
|
|
424
|
+
return () => {
|
|
425
|
+
clearInterval(timer);
|
|
426
|
+
clearTimeout(archiveDebounce);
|
|
427
|
+
archiveDebounce = undefined;
|
|
428
|
+
archivePending = false;
|
|
429
|
+
archiveAbort?.abort();
|
|
430
|
+
archiveRequest++;
|
|
431
|
+
};
|
|
432
|
+
});
|
|
404
433
|
async function searchArchive() {
|
|
405
434
|
const token = ++archiveRequest;
|
|
406
435
|
archiveAbort?.abort();
|
|
@@ -413,7 +442,24 @@
|
|
|
413
442
|
return;
|
|
414
443
|
}
|
|
415
444
|
globalStatus = ui.labels.searchingArchive;
|
|
445
|
+
archivePending = true;
|
|
416
446
|
try {
|
|
447
|
+
if (model.searchQueryHref) {
|
|
448
|
+
const result = await queryLiveArchive(
|
|
449
|
+
model.searchQueryHref,
|
|
450
|
+
query,
|
|
451
|
+
local.signal,
|
|
452
|
+
);
|
|
453
|
+
if (token !== archiveRequest || local.signal.aborted) return;
|
|
454
|
+
globalRows = result.records;
|
|
455
|
+
globalStatus = result.truncated
|
|
456
|
+
? label(ui, "truncatedResults", {
|
|
457
|
+
shown: result.records.length,
|
|
458
|
+
count: result.records.length + "+",
|
|
459
|
+
})
|
|
460
|
+
: label(ui, "archiveResults", { count: result.records.length });
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
417
463
|
const url = new URL(model.searchIndexHref, document.baseURI);
|
|
418
464
|
let all =
|
|
419
465
|
archiveCacheHref === url.href && Date.now() - archiveCacheAt < 60000
|
|
@@ -488,6 +534,8 @@
|
|
|
488
534
|
globalStatus = ui.labels.archiveError;
|
|
489
535
|
archiveFailed = true;
|
|
490
536
|
}
|
|
537
|
+
} finally {
|
|
538
|
+
if (token === archiveRequest) archivePending = false;
|
|
491
539
|
}
|
|
492
540
|
}
|
|
493
541
|
onMount(() => {
|
|
@@ -681,9 +729,10 @@
|
|
|
681
729
|
><option value="dark">{ui.labels.dark}</option></select
|
|
682
730
|
></label
|
|
683
731
|
>
|
|
684
|
-
{#if model.searchIndexHref}
|
|
732
|
+
{#if model.searchIndexHref || model.searchQueryHref}
|
|
685
733
|
<button
|
|
686
|
-
|
|
734
|
+
bind:this={archiveTrigger}
|
|
735
|
+
disabled={!ready}
|
|
687
736
|
onclick={() => (globalOpen = true)}>{ui.labels.searchArchive}</button
|
|
688
737
|
>
|
|
689
738
|
{/if}
|
|
@@ -756,7 +805,7 @@
|
|
|
756
805
|
{#if column === "name"}<span class="identity"
|
|
757
806
|
>{#if entry.href}<a
|
|
758
807
|
class="name"
|
|
759
|
-
href={
|
|
808
|
+
href={entry.href}
|
|
760
809
|
target={entry.exitsExplorer ? "_blank" : undefined}
|
|
761
810
|
rel={entry.exitsExplorer ? "noopener" : undefined}
|
|
762
811
|
onclick={(event) => navigate(event, entry)}
|
|
@@ -808,7 +857,7 @@
|
|
|
808
857
|
})}{/if}
|
|
809
858
|
{#each ui.actions as action}
|
|
810
859
|
{#if action === "download" && entry.href && !isDirectory(entry) && !entry.link?.unavailable}<a
|
|
811
|
-
href={
|
|
860
|
+
href={entry.href}
|
|
812
861
|
download>{ui.labels.download}</a
|
|
813
862
|
>
|
|
814
863
|
{:else if action !== "download"}<button
|
|
@@ -882,7 +931,15 @@
|
|
|
882
931
|
</dialog>{/if}
|
|
883
932
|
{#if globalOpen}<dialog
|
|
884
933
|
bind:this={globalDialog}
|
|
885
|
-
|
|
934
|
+
aria-label={ui.labels.searchArchive}
|
|
935
|
+
onclose={closeArchive}
|
|
936
|
+
onkeydown={(event) => {
|
|
937
|
+
if (event.key === "Escape") {
|
|
938
|
+
event.preventDefault();
|
|
939
|
+
event.stopPropagation();
|
|
940
|
+
closeArchive();
|
|
941
|
+
}
|
|
942
|
+
}}
|
|
886
943
|
>
|
|
887
944
|
<h2>{ui.labels.searchArchive}</h2>
|
|
888
945
|
<input
|
|
@@ -890,7 +947,7 @@
|
|
|
890
947
|
bind:this={globalInput}
|
|
891
948
|
type="search"
|
|
892
949
|
bind:value={globalQuery}
|
|
893
|
-
oninput={
|
|
950
|
+
oninput={scheduleArchiveSearch}
|
|
894
951
|
/>
|
|
895
952
|
<p role="status">{globalStatus}</p>
|
|
896
953
|
{#if archiveFailed}<button onclick={searchArchive}>{ui.labels.retry}</button
|
|
@@ -905,5 +962,5 @@
|
|
|
905
962
|
>{:else}{record.path}{/if}
|
|
906
963
|
</li>{/each}
|
|
907
964
|
</ul>
|
|
908
|
-
<button onclick={
|
|
965
|
+
<button onclick={closeArchive}>{ui.labels.close}</button>
|
|
909
966
|
</dialog>{/if}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// ../shared-theme/src/live-search.ts
|
|
2
|
+
async function queryLiveArchive(href, query, signal) {
|
|
3
|
+
const url = new URL(href, document.baseURI);
|
|
4
|
+
if (url.origin !== location.origin) throw new Error("Unsafe search endpoint");
|
|
5
|
+
url.searchParams.set("q", query);
|
|
6
|
+
const response = await fetch(url, { signal, cache: "no-store" });
|
|
7
|
+
if (!response.ok) throw new Error("Archive search unavailable");
|
|
8
|
+
const data = await response.json();
|
|
9
|
+
if (typeof data !== "object" || data === null || !("version" in data) || data.version !== 1 || !("results" in data) || !Array.isArray(data.results) || data.results.length > 1e4 || !("truncated" in data) || typeof data.truncated !== "boolean")
|
|
10
|
+
throw new Error("Invalid archive results");
|
|
11
|
+
const records = data.results.map((entry) => {
|
|
12
|
+
if (typeof entry !== "object" || entry === null || !("name" in entry) || typeof entry.name !== "string" || !("path" in entry) || typeof entry.path !== "string" || !("kind" in entry) || !["file", "directory"].includes(String(entry.kind)) || !("href" in entry) || entry.href !== null && typeof entry.href !== "string")
|
|
13
|
+
throw new Error("Invalid archive record");
|
|
14
|
+
const target = entry.href === null ? null : new URL(entry.href, url);
|
|
15
|
+
if (target && !["http:", "https:"].includes(target.protocol))
|
|
16
|
+
throw new Error("Unsafe archive link");
|
|
17
|
+
return {
|
|
18
|
+
name: entry.name,
|
|
19
|
+
path: entry.path,
|
|
20
|
+
href: target?.href ?? null,
|
|
21
|
+
exitsExplorer: entry.kind !== "directory"
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
return { records, truncated: data.truncated };
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
queryLiveArchive
|
|
28
|
+
};
|
|
@@ -402,6 +402,7 @@ async function fetchThemePage(dataHref, pageHref, theme, signal, fresh = false,
|
|
|
402
402
|
resolveCustomization(data.ui);
|
|
403
403
|
const result = {
|
|
404
404
|
...data,
|
|
405
|
+
searchQueryHref: link(data.searchQueryHref ?? null),
|
|
405
406
|
updatesHref: link(data.updatesHref ?? null),
|
|
406
407
|
entries,
|
|
407
408
|
dataHref: source.href,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dirwell/theme-ledger",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Svelte-owned searchable directory explorer.",
|
|
5
5
|
"homepage": "https://vp-tw.github.io/dirwell/themes/",
|
|
6
6
|
"bugs": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"tag": "latest"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@dirwell/core": "0.0.
|
|
41
|
+
"@dirwell/core": "0.0.3",
|
|
42
42
|
"svelte": "5.57.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|