@vp-tw/dirwell 0.1.0-alpha.6 → 0.1.0-alpha.7
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/CHANGELOG.md +6 -0
- package/THIRD_PARTY_NOTICES.md +8 -0
- package/dist/bin.mjs +1 -1
- package/dist/crosswave-runtime.js +15 -17
- package/dist/index.d.mts +22 -1
- package/dist/index.mjs +179 -66
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @vp-tw/dirwell
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.7
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Make Crosswave categories configurable with ordered labels, decorative icons, overlapping extension/MIME filters, and exported default definitions. Infer filename types with the pinned Node-only MIME database; generated browser assets contain membership IDs without the database. Add a runnable custom-category example and configuration documentation.
|
|
8
|
+
|
|
3
9
|
## 0.1.0-alpha.6
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -12,3 +12,11 @@ in the published package, and in `src/vscode-icons/NOTICE.txt` in the source tre
|
|
|
12
12
|
Generated sites include this notice as `vscode-icons-NOTICE.txt`. The default
|
|
13
13
|
theme footer links to the [Ledger theme README](https://github.com/vp-tw/dirwell/blob/main/src/theme-default/README.md),
|
|
14
14
|
which links to the source notice.
|
|
15
|
+
|
|
16
|
+
## MIME classification
|
|
17
|
+
|
|
18
|
+
Crosswave uses [`mime` 4.1.0](https://github.com/broofa/mime), MIT-licensed,
|
|
19
|
+
Copyright (c) 2023 Robert Kieffer, for filename-based classification during Node
|
|
20
|
+
generation. It uses optimized MIME database mappings; no MIME database is emitted
|
|
21
|
+
to generated browser assets. The installed dependency includes its full MIT
|
|
22
|
+
license. MIME estimates do not validate file contents or change serving headers.
|
package/dist/bin.mjs
CHANGED
|
@@ -153,6 +153,11 @@
|
|
|
153
153
|
reject(new Error("Folder data is incomplete"));
|
|
154
154
|
return;
|
|
155
155
|
}
|
|
156
|
+
if (
|
|
157
|
+
doc.querySelector("[data-cw-root]").dataset.cwCategoryConfig !==
|
|
158
|
+
main.dataset.cwCategoryConfig
|
|
159
|
+
)
|
|
160
|
+
throw new Error("Folder category configuration changed; open normally to reload");
|
|
156
161
|
const url = new URL(route.url);
|
|
157
162
|
if (local && url.pathname.endsWith("/")) url.pathname += payload.outputName;
|
|
158
163
|
const base = new URL(payload.baseHref ?? ".", url);
|
|
@@ -478,10 +483,8 @@
|
|
|
478
483
|
const empty = document.querySelector("[data-cw-empty]");
|
|
479
484
|
const status = document.querySelector("[data-cw-status]");
|
|
480
485
|
const detail = document.querySelector(".cw-detail");
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
if (linkIcon) icons.set("link", linkIcon);
|
|
484
|
-
let category = "all",
|
|
486
|
+
const belongs = (row, id) => row.dataset.cwCategories.split(" ").includes(id);
|
|
487
|
+
let category = tabs[0].dataset.cwCategory,
|
|
485
488
|
selected = null,
|
|
486
489
|
composing = false,
|
|
487
490
|
searchTimer;
|
|
@@ -505,7 +508,7 @@
|
|
|
505
508
|
document.querySelector(`[data-cw-detail-${field}]`).textContent = row.dataset[data];
|
|
506
509
|
const host = document.querySelector("[data-cw-detail-icon]");
|
|
507
510
|
host.replaceChildren();
|
|
508
|
-
const icon =
|
|
511
|
+
const icon = row.querySelector(".cw-entry-icon svg");
|
|
509
512
|
if (icon) host.append(icon.cloneNode(true));
|
|
510
513
|
const link = row.querySelector("a");
|
|
511
514
|
const open = document.querySelector("[data-cw-open]");
|
|
@@ -546,7 +549,7 @@
|
|
|
546
549
|
let count = 0;
|
|
547
550
|
for (const row of rows) {
|
|
548
551
|
row.hidden =
|
|
549
|
-
(
|
|
552
|
+
!belongs(row, category) ||
|
|
550
553
|
!`${row.dataset.name} ${row.dataset.path}`.toLocaleLowerCase().includes(query);
|
|
551
554
|
if (!row.hidden) count++;
|
|
552
555
|
}
|
|
@@ -604,10 +607,7 @@
|
|
|
604
607
|
};
|
|
605
608
|
for (const tab of tabs) {
|
|
606
609
|
tab.addEventListener("click", () => setCategory(tab.dataset.cwCategory));
|
|
607
|
-
const count =
|
|
608
|
-
tab.dataset.cwCategory === "all"
|
|
609
|
-
? rows.length
|
|
610
|
-
: rows.filter((row) => row.dataset.category === tab.dataset.cwCategory).length;
|
|
610
|
+
const count = rows.filter((row) => belongs(row, tab.dataset.cwCategory)).length;
|
|
611
611
|
tab.querySelector("small").textContent = String(count);
|
|
612
612
|
}
|
|
613
613
|
main.addEventListener("pointerover", (event) => {
|
|
@@ -763,7 +763,7 @@
|
|
|
763
763
|
document.querySelector("#cw-wave").addEventListener("cw-wave-fallback", updateMotion);
|
|
764
764
|
reduced.addEventListener("change", updateMotion);
|
|
765
765
|
updateMotion();
|
|
766
|
-
setCategory(
|
|
766
|
+
setCategory(tabs[0].dataset.cwCategory);
|
|
767
767
|
const scroll = document.querySelector(".cw-list-scroll");
|
|
768
768
|
const fitList = () => {
|
|
769
769
|
const footer = document.querySelector(".cw-bottom");
|
|
@@ -814,17 +814,15 @@
|
|
|
814
814
|
.replaceChildren(...doc.querySelector(".cw-files").childNodes);
|
|
815
815
|
main.dataset.cwPath = doc.querySelector("[data-cw-root]").dataset.cwPath;
|
|
816
816
|
rows = [...main.querySelectorAll("[data-cw-entry]")];
|
|
817
|
-
const nextLinkIcon = rows.find((row) => row.dataset.icon === "link")?.querySelector("svg");
|
|
818
|
-
if (nextLinkIcon) icons.set("link", nextLinkIcon);
|
|
819
817
|
for (const tab of tabs)
|
|
820
818
|
tab.querySelector("small").textContent = String(
|
|
821
|
-
tab.dataset.cwCategory
|
|
822
|
-
? rows.length
|
|
823
|
-
: rows.filter((row) => row.dataset.category === tab.dataset.cwCategory).length,
|
|
819
|
+
rows.filter((row) => belongs(row, tab.dataset.cwCategory)).length,
|
|
824
820
|
);
|
|
825
821
|
search.value = view?.query ?? "";
|
|
826
822
|
setCategory(
|
|
827
|
-
tabs.some((tab) => tab.dataset.cwCategory === view?.category)
|
|
823
|
+
tabs.some((tab) => tab.dataset.cwCategory === view?.category)
|
|
824
|
+
? view.category
|
|
825
|
+
: tabs[0].dataset.cwCategory,
|
|
828
826
|
false,
|
|
829
827
|
false,
|
|
830
828
|
);
|
package/dist/index.d.mts
CHANGED
|
@@ -36,9 +36,30 @@ interface DefaultThemeOptions {
|
|
|
36
36
|
export declare function createDefaultTheme(options?: DefaultThemeOptions): ExplorerTheme;
|
|
37
37
|
export declare const defaultTheme: ExplorerTheme;
|
|
38
38
|
//#endregion
|
|
39
|
+
//#region src/theme-crosswave/icons.d.ts
|
|
40
|
+
type CrosswaveIcon = "all" | "folder" | "image" | "audio" | "video" | "document" | "other" | "link" | "back" | "search" | "pause" | "play" | "controller";
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/theme-crosswave/categories.d.ts
|
|
43
|
+
type CrosswaveCategoryIcon = CrosswaveIcon | {
|
|
44
|
+
readonly svg: string;
|
|
45
|
+
};
|
|
46
|
+
type CrosswaveCategoryMatch = "all" | "folders" | "other" | {
|
|
47
|
+
readonly extensions?: readonly string[];
|
|
48
|
+
readonly mimeTypes?: readonly string[];
|
|
49
|
+
};
|
|
50
|
+
interface CrosswaveCategory {
|
|
51
|
+
readonly id: string;
|
|
52
|
+
readonly label: string;
|
|
53
|
+
readonly icon?: CrosswaveCategoryIcon;
|
|
54
|
+
readonly match: CrosswaveCategoryMatch;
|
|
55
|
+
}
|
|
56
|
+
/** Default rail definitions. Supply a mapped/filtered copy to change labels, order, or visibility. */
|
|
57
|
+
export declare const defaultCrosswaveCategories: readonly CrosswaveCategory[];
|
|
58
|
+
//#endregion
|
|
39
59
|
//#region src/theme-crosswave.d.ts
|
|
40
60
|
type CrosswaveColor = "azure" | "violet" | "amber" | "rose" | "jade" | "graphite";
|
|
41
61
|
interface CrosswaveThemeOptions {
|
|
62
|
+
readonly categories?: readonly CrosswaveCategory[];
|
|
42
63
|
readonly color?: CrosswaveColor;
|
|
43
64
|
readonly backgroundMotion?: boolean;
|
|
44
65
|
readonly pageTransitions?: boolean;
|
|
@@ -55,5 +76,5 @@ interface PlainThemeOptions {
|
|
|
55
76
|
/** A no-icon, no-script theme that leaves navigation and controls to the browser. */
|
|
56
77
|
export declare function createPlainTheme(options?: PlainThemeOptions): ExplorerTheme;
|
|
57
78
|
//#endregion
|
|
58
|
-
export { type BreadcrumbItem, type BreadcrumbsProps, type CrosswaveColor, type CrosswaveThemeOptions, type DefaultThemeIconSet, type DefaultThemeIconVariant, type DefaultThemeRuntimeConfig, type DirectoryData, type DirwellConfig, type DirwellConfigContext, type DirwellConfigInput, type DirwellThemeComponentOverrides, type DirwellThemeComponents, type EmptyStateProps, type EntryKind, type EntryListItem, type EntryListProps, type EntryNavigation, type EntryRowProps, type ExplorerTheme, type FileMetadata, type FileSystemEntry, type FileTimes, type FooterProps, type GenerateOptions, type IconName, type IconProps, type NameSortMode, type OutputNameResolver, type PageShellProps, type PlainThemeOptions, type RenderedPage, type SortDirection, type SortField, type SortOptions, type SymlinkMetadata, type ThemeContext, type ToolbarProps, defineConfig, loadDirwellConfig, resolveGenerateOptions, resolveThemeComponents };
|
|
79
|
+
export { type BreadcrumbItem, type BreadcrumbsProps, type CrosswaveCategory, type CrosswaveCategoryIcon, type CrosswaveCategoryMatch, type CrosswaveColor, type CrosswaveThemeOptions, type DefaultThemeIconSet, type DefaultThemeIconVariant, type DefaultThemeRuntimeConfig, type DirectoryData, type DirwellConfig, type DirwellConfigContext, type DirwellConfigInput, type DirwellThemeComponentOverrides, type DirwellThemeComponents, type EmptyStateProps, type EntryKind, type EntryListItem, type EntryListProps, type EntryNavigation, type EntryRowProps, type ExplorerTheme, type FileMetadata, type FileSystemEntry, type FileTimes, type FooterProps, type GenerateOptions, type IconName, type IconProps, type NameSortMode, type OutputNameResolver, type PageShellProps, type PlainThemeOptions, type RenderedPage, type SortDirection, type SortField, type SortOptions, type SymlinkMetadata, type ThemeContext, type ToolbarProps, defineConfig, loadDirwellConfig, resolveGenerateOptions, resolveThemeComponents };
|
|
59
80
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { t as resolveThemeComponents } from "./theme-components-CXzvdQY-.mjs";
|
|
|
3
3
|
import { t as createExplorerDevServer } from "./dev-server-Q7NC1wiB.mjs";
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
|
+
import mime from "mime";
|
|
6
7
|
//#region src/theme-crosswave/icons.ts
|
|
7
8
|
const paths = {
|
|
8
9
|
all: "<rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>",
|
|
@@ -24,6 +25,171 @@ function crosswaveIcon(name) {
|
|
|
24
25
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.45" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${paths[name]}</svg>`;
|
|
25
26
|
}
|
|
26
27
|
//#endregion
|
|
28
|
+
//#region src/theme-crosswave/categories.ts
|
|
29
|
+
const documents = [
|
|
30
|
+
"text/*",
|
|
31
|
+
"application/pdf",
|
|
32
|
+
"application/json",
|
|
33
|
+
"application/*+json",
|
|
34
|
+
"application/xml",
|
|
35
|
+
"application/*+xml",
|
|
36
|
+
"application/msword",
|
|
37
|
+
"application/rtf",
|
|
38
|
+
"application/sql",
|
|
39
|
+
"application/epub+zip",
|
|
40
|
+
"application/vnd.ms-excel",
|
|
41
|
+
"application/vnd.ms-powerpoint",
|
|
42
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
43
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
44
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
45
|
+
];
|
|
46
|
+
/** Default rail definitions. Supply a mapped/filtered copy to change labels, order, or visibility. */
|
|
47
|
+
const defaultCrosswaveCategories = Object.freeze([
|
|
48
|
+
{
|
|
49
|
+
id: "all",
|
|
50
|
+
label: "All files",
|
|
51
|
+
icon: "all",
|
|
52
|
+
match: "all"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "folder",
|
|
56
|
+
label: "Folders",
|
|
57
|
+
icon: "folder",
|
|
58
|
+
match: "folders"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "image",
|
|
62
|
+
label: "Photos",
|
|
63
|
+
icon: "image",
|
|
64
|
+
match: { mimeTypes: ["image/*"] }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "audio",
|
|
68
|
+
label: "Music",
|
|
69
|
+
icon: "audio",
|
|
70
|
+
match: { mimeTypes: ["audio/*", "application/vnd.apple.mpegurl"] }
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "video",
|
|
74
|
+
label: "Videos",
|
|
75
|
+
icon: "video",
|
|
76
|
+
match: { mimeTypes: ["video/*"] }
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "document",
|
|
80
|
+
label: "Documents",
|
|
81
|
+
icon: "document",
|
|
82
|
+
match: { mimeTypes: documents }
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "other",
|
|
86
|
+
label: "Other",
|
|
87
|
+
icon: "other",
|
|
88
|
+
match: "other"
|
|
89
|
+
}
|
|
90
|
+
].map((category) => Object.freeze({
|
|
91
|
+
...category,
|
|
92
|
+
match: typeof category.match === "string" ? category.match : Object.freeze({
|
|
93
|
+
...category.match.extensions ? { extensions: Object.freeze([...category.match.extensions]) } : {},
|
|
94
|
+
...category.match.mimeTypes ? { mimeTypes: Object.freeze([...category.match.mimeTypes]) } : {}
|
|
95
|
+
})
|
|
96
|
+
})));
|
|
97
|
+
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
98
|
+
function strings(value, normalize) {
|
|
99
|
+
if (value === void 0) return void 0;
|
|
100
|
+
if (!Array.isArray(value)) throw new TypeError("Crosswave category rules must be arrays of strings");
|
|
101
|
+
return Object.freeze([...new Set(Array.from(value, (item) => {
|
|
102
|
+
if (typeof item !== "string") throw new TypeError("Crosswave category rules must be arrays of strings");
|
|
103
|
+
return normalize(item);
|
|
104
|
+
}))]);
|
|
105
|
+
}
|
|
106
|
+
function extension(value) {
|
|
107
|
+
const normalized = value.toLowerCase().replace(/^\./, "");
|
|
108
|
+
if (!/^[a-z0-9+_-]+(?:\.[a-z0-9+_-]+)*$/.test(normalized)) throw new TypeError(`Invalid Crosswave extension: ${value}`);
|
|
109
|
+
return normalized;
|
|
110
|
+
}
|
|
111
|
+
function mediaType(value) {
|
|
112
|
+
const normalized = value.toLowerCase();
|
|
113
|
+
if (!/^[a-z0-9!#$&^_.+-]+\/(?:[a-z0-9!#$&^_.+-]+|\*|\*\+[a-z0-9!#$&^_.-]+)$/.test(normalized)) throw new TypeError(`Invalid Crosswave MIME pattern: ${value}`);
|
|
114
|
+
return normalized;
|
|
115
|
+
}
|
|
116
|
+
function match(value) {
|
|
117
|
+
if (value === "all" || value === "folders" || value === "other") return value;
|
|
118
|
+
if (!record(value) || Object.keys(value).some((key) => !["extensions", "mimeTypes"].includes(key))) throw new TypeError("Crosswave match must be all, folders, other, or extension/MIME rules");
|
|
119
|
+
const extensions = strings(value.extensions, extension);
|
|
120
|
+
const mimeTypes = strings(value.mimeTypes, mediaType);
|
|
121
|
+
if (!extensions?.length && !mimeTypes?.length) throw new TypeError("Crosswave category needs at least one matching rule");
|
|
122
|
+
return Object.freeze({
|
|
123
|
+
...extensions ? { extensions } : {},
|
|
124
|
+
...mimeTypes ? { mimeTypes } : {}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function icon(value, rule) {
|
|
128
|
+
if (value === void 0) return crosswaveIcon(rule === "all" ? "all" : rule === "folders" ? "folder" : "other");
|
|
129
|
+
if (typeof value === "string" && isCrosswaveIcon(value)) return crosswaveIcon(value);
|
|
130
|
+
if (record(value) && typeof value.svg === "string" && /^\s*<svg\b/i.test(value.svg) && /<\/svg>\s*$/i.test(value.svg)) return value.svg.replace(/<svg\b/i, "<svg aria-hidden=\"true\"");
|
|
131
|
+
throw new TypeError("Crosswave icon must be a built-in icon name or trusted SVG markup");
|
|
132
|
+
}
|
|
133
|
+
function isCrosswaveIcon(value) {
|
|
134
|
+
return [
|
|
135
|
+
"all",
|
|
136
|
+
"folder",
|
|
137
|
+
"image",
|
|
138
|
+
"audio",
|
|
139
|
+
"video",
|
|
140
|
+
"document",
|
|
141
|
+
"other",
|
|
142
|
+
"link",
|
|
143
|
+
"back",
|
|
144
|
+
"search",
|
|
145
|
+
"pause",
|
|
146
|
+
"play",
|
|
147
|
+
"controller"
|
|
148
|
+
].includes(value);
|
|
149
|
+
}
|
|
150
|
+
function resolveCrosswaveCategories(value) {
|
|
151
|
+
const input = value === void 0 ? defaultCrosswaveCategories : value;
|
|
152
|
+
if (!Array.isArray(input) || !input.length) throw new TypeError("Crosswave categories must be a nonempty array");
|
|
153
|
+
const ids = /* @__PURE__ */ new Set();
|
|
154
|
+
return Object.freeze(Array.from(input).map((category) => {
|
|
155
|
+
if (!record(category) || typeof category.id !== "string" || !/^[a-z][a-z0-9_-]{0,63}$/.test(category.id)) throw new TypeError("Crosswave category id must be a lowercase identifier");
|
|
156
|
+
if (ids.has(category.id)) throw new TypeError(`Duplicate Crosswave category id: ${category.id}`);
|
|
157
|
+
ids.add(category.id);
|
|
158
|
+
if (typeof category.label !== "string" || !category.label.trim()) throw new TypeError("Crosswave category label must be nonempty");
|
|
159
|
+
const rule = match(category.match);
|
|
160
|
+
return Object.freeze({
|
|
161
|
+
id: category.id,
|
|
162
|
+
label: category.label,
|
|
163
|
+
svg: icon(category.icon, rule),
|
|
164
|
+
match: rule
|
|
165
|
+
});
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
function matchesMimeType(pattern, type) {
|
|
169
|
+
if (pattern === type) return true;
|
|
170
|
+
const [group, subtype] = pattern.split("/");
|
|
171
|
+
if (!subtype || !type.startsWith(`${group}/`)) return false;
|
|
172
|
+
return subtype === "*" || subtype.startsWith("*+") && type.endsWith(subtype.slice(1));
|
|
173
|
+
}
|
|
174
|
+
function entryMimeType(entry) {
|
|
175
|
+
if (entry.kind === "directory" || entry.symlink?.targetKind === "directory") return null;
|
|
176
|
+
return mime.getType(`./${entry.name}`) ?? (entry.symlink?.targetKind === "file" && !entry.symlink.isBroken && !entry.symlink.isOutsideRoot && !entry.symlink.isTargetExcluded && entry.symlink.targetRelativePath ? mime.getType(`./${entry.symlink.targetRelativePath}`) : null);
|
|
177
|
+
}
|
|
178
|
+
function entryCategory(entry, type) {
|
|
179
|
+
if (entry.kind === "directory" || entry.symlink?.targetKind === "directory") return "folder";
|
|
180
|
+
if (type?.startsWith("image/")) return "image";
|
|
181
|
+
if (type?.startsWith("audio/") || type === "application/vnd.apple.mpegurl") return "audio";
|
|
182
|
+
if (type?.startsWith("video/")) return "video";
|
|
183
|
+
if (type && documents.some((pattern) => matchesMimeType(pattern, type))) return "document";
|
|
184
|
+
return "other";
|
|
185
|
+
}
|
|
186
|
+
function entryCategories(entry, type, categories) {
|
|
187
|
+
const folder = entry.kind === "directory" || entry.symlink?.targetKind === "directory";
|
|
188
|
+
const filename = entry.name.toLowerCase();
|
|
189
|
+
const specific = categories.filter((category) => typeof category.match !== "string" && !folder && (category.match.extensions?.some((ext) => filename.endsWith(`.${ext}`)) || type && category.match.mimeTypes?.some((pattern) => matchesMimeType(pattern, type))));
|
|
190
|
+
return categories.filter((category) => category.match === "all" || folder && category.match === "folders" || !folder && category.match === "other" && !specific.length || specific.includes(category));
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
27
193
|
//#region src/theme-crosswave/styles.ts
|
|
28
194
|
const crosswaveStyles = String.raw`
|
|
29
195
|
:root{color-scheme:dark;--cw-top:#2b4f85;--cw-bottom:#091a3a;--cw-text:#f7fbff;--cw-muted:#edf4fc;--cw-focus:#fff;--cw-ease:cubic-bezier(.16,1,.3,1);font-family:"Segoe UI",Helvetica,Arial,sans-serif;font-synthesis:none}
|
|
@@ -55,64 +221,6 @@ function navigationData(context, relativePath, href) {
|
|
|
55
221
|
const dataHref = context.mode === "mpa" ? context.assetHref(asset) : `${href.endsWith("/") ? href : href.slice(0, href.lastIndexOf("/") + 1)}${asset}`;
|
|
56
222
|
return ` data-cw-page="${escapeHtml(dataHref)}" data-cw-path="${escapeHtml(relativePath)}"`;
|
|
57
223
|
}
|
|
58
|
-
const categories = [
|
|
59
|
-
["all", "All files"],
|
|
60
|
-
["folder", "Folders"],
|
|
61
|
-
["image", "Photos"],
|
|
62
|
-
["audio", "Music"],
|
|
63
|
-
["video", "Videos"],
|
|
64
|
-
["document", "Documents"],
|
|
65
|
-
["other", "Other"]
|
|
66
|
-
];
|
|
67
|
-
const extensions = Object.fromEntries([
|
|
68
|
-
...[
|
|
69
|
-
"png",
|
|
70
|
-
"jpg",
|
|
71
|
-
"jpeg",
|
|
72
|
-
"webp",
|
|
73
|
-
"gif",
|
|
74
|
-
"avif",
|
|
75
|
-
"svg",
|
|
76
|
-
"bmp",
|
|
77
|
-
"heic"
|
|
78
|
-
].map((x) => [x, "image"]),
|
|
79
|
-
...[
|
|
80
|
-
"mp3",
|
|
81
|
-
"wav",
|
|
82
|
-
"ogg",
|
|
83
|
-
"flac",
|
|
84
|
-
"m4a",
|
|
85
|
-
"aac",
|
|
86
|
-
"aiff",
|
|
87
|
-
"opus"
|
|
88
|
-
].map((x) => [x, "audio"]),
|
|
89
|
-
...[
|
|
90
|
-
"mp4",
|
|
91
|
-
"webm",
|
|
92
|
-
"mov",
|
|
93
|
-
"mkv",
|
|
94
|
-
"avi",
|
|
95
|
-
"m4v"
|
|
96
|
-
].map((x) => [x, "video"]),
|
|
97
|
-
...[
|
|
98
|
-
"txt",
|
|
99
|
-
"md",
|
|
100
|
-
"pdf",
|
|
101
|
-
"json",
|
|
102
|
-
"csv",
|
|
103
|
-
"html",
|
|
104
|
-
"xml",
|
|
105
|
-
"doc",
|
|
106
|
-
"docx",
|
|
107
|
-
"rtf",
|
|
108
|
-
"yaml",
|
|
109
|
-
"yml"
|
|
110
|
-
].map((x) => [x, "document"])
|
|
111
|
-
]);
|
|
112
|
-
function category(entry) {
|
|
113
|
-
if (entry.kind === "directory" || entry.symlink?.targetKind === "directory") return "folder";
|
|
114
|
-
return extensions[entry.name.split(".").at(-1)?.toLowerCase() ?? ""] ?? "other";
|
|
115
|
-
}
|
|
116
224
|
function size(bytes) {
|
|
117
225
|
const units = [
|
|
118
226
|
"B",
|
|
@@ -133,18 +241,21 @@ function status$1(entry) {
|
|
|
133
241
|
if (entry.kind === "directory") return "Folder";
|
|
134
242
|
return "File";
|
|
135
243
|
}
|
|
136
|
-
function row(entry, context, index) {
|
|
244
|
+
function row(entry, context, index, categories) {
|
|
137
245
|
const href = context.hrefFor(entry);
|
|
138
|
-
const
|
|
246
|
+
const mimeType = entryMimeType(entry);
|
|
247
|
+
const type = entryCategory(entry, mimeType);
|
|
248
|
+
const matches = entryCategories(entry, mimeType, categories);
|
|
139
249
|
const icon = entry.kind === "symlink" ? "link" : type;
|
|
250
|
+
const svg = crosswaveIcon(icon);
|
|
140
251
|
const label = `${entry.name}${type === "folder" ? "/" : ""}`;
|
|
141
252
|
const unavailable = entry.symlink?.isOutsideRoot || entry.symlink?.isTargetExcluded || entry.symlink?.isBroken;
|
|
142
253
|
const bytes = type === "folder" ? "Folder" : size(entry.metadata.size);
|
|
143
254
|
const instant = new Date(entry.metadata.times.modifiedAt);
|
|
144
255
|
const modified = Number.isNaN(instant.getTime()) ? "Unknown" : `${instant.toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
145
256
|
const target = entry.symlink === null ? "" : `<span class="cw-target">${escapeHtml(entry.symlink.target)}</span>`;
|
|
146
|
-
const data = `data-cw-entry data-category="${type}" data-icon="${icon}" data-name="${escapeHtml(entry.name)}" data-path="${escapeHtml(entry.relativePath)}" data-size="${escapeHtml(bytes)}" data-modified="${modified}" data-status="${escapeHtml(status$1(entry))}" data-exits="${context.exitsExplorerFor(entry)}"`;
|
|
147
|
-
const contents = `<span class="cw-entry-icon">${
|
|
257
|
+
const data = `data-cw-entry data-category="${type}" data-cw-categories="${matches.map((category) => category.id).join(" ")}" data-icon="${icon}" data-name="${escapeHtml(entry.name)}" data-path="${escapeHtml(entry.relativePath)}" data-size="${escapeHtml(bytes)}" data-modified="${modified}" data-status="${escapeHtml(status$1(entry))}" data-exits="${context.exitsExplorerFor(entry)}"`;
|
|
258
|
+
const contents = `<span class="cw-entry-icon">${svg}</span><span class="cw-entry-label"><span class="cw-name">${escapeHtml(label)}</span>${target}<span class="cw-row-meta">${escapeHtml(status$1(entry))}${type === "folder" || unavailable ? "" : ` · ${bytes}`}</span><span class="cw-row-date">Modified ${modified}</span></span>`;
|
|
148
259
|
return `<li class="cw-row${index === 0 ? " cw-selected" : ""}" ${data}>${href === null ? `<span class="cw-entry cw-unavailable">${contents}</span>` : `<a class="cw-entry" href="${escapeHtml(href)}"${context.exitsExplorerFor(entry) ? " target=\"_blank\" rel=\"noopener\"" : ` data-cw-navigation${navigationData(context, entry.symlink?.targetRelativePath ?? entry.relativePath, href)}`}>${contents}</a>`}</li>`;
|
|
149
260
|
}
|
|
150
261
|
/** PSP-inspired media navigation with original wave graphics and ordinary static links. */
|
|
@@ -164,6 +275,8 @@ function createCrosswaveTheme(options = {}) {
|
|
|
164
275
|
"gamepad"
|
|
165
276
|
]) if (options[key] !== void 0 && typeof options[key] !== "boolean") throw new TypeError(`${key} must be a boolean`);
|
|
166
277
|
const project = resolveThemeProject(options.project);
|
|
278
|
+
const categories = resolveCrosswaveCategories(options.categories);
|
|
279
|
+
const categoryConfig = createHash("sha256").update(JSON.stringify(categories)).digest("hex").slice(0, 24);
|
|
167
280
|
return {
|
|
168
281
|
name: "crosswave",
|
|
169
282
|
searchIndex: false,
|
|
@@ -173,14 +286,14 @@ function createCrosswaveTheme(options = {}) {
|
|
|
173
286
|
const parent = directory.parent === null ? null : context.hrefForDirectory(directory.parent.relativePath);
|
|
174
287
|
const segments = directory.current.relativePath.split("/").filter(Boolean);
|
|
175
288
|
const breadcrumbs = segments.map((segment, i) => i === segments.length - 1 ? `<span aria-current="page">${escapeHtml(segment)}</span>` : `<a data-cw-navigation${navigationData(context, segments.slice(0, i + 1).join("/"), context.hrefForDirectory(segments.slice(0, i + 1).join("/")))} href="${escapeHtml(context.hrefForDirectory(segments.slice(0, i + 1).join("/")))}">${escapeHtml(segment)}</a>`).join("<span class=\"cw-separator\" aria-hidden=\"true\">/</span>");
|
|
176
|
-
const tabs = categories.map((
|
|
289
|
+
const tabs = categories.map(({ id: type, label, svg }, i) => `<button role="tab" id="cw-tab-${type}" aria-selected="${i === 0}" aria-controls="cw-panel" tabindex="${i === 0 ? 0 : -1}" data-cw-category="${type}" class="cw-category${i === 0 ? " cw-active" : ""}">${svg}<span>${escapeHtml(label)}</span><small data-cw-category-count="${type}"></small></button>`).join("");
|
|
177
290
|
const html = `<!doctype html>
|
|
178
291
|
<html lang="en" data-cw-color="${color}" data-cw-motion="${options.backgroundMotion !== false}" data-cw-transitions="${options.pageTransitions !== false}" data-cw-gamepad="${options.gamepad !== false}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">${context.documentBaseHref === null ? "" : `<base href="${escapeHtml(context.documentBaseHref)}">`}<title>${escapeHtml(path)} · ${escapeHtml(project.name)} · Crosswave</title><link rel="stylesheet" href="${escapeHtml(context.assetHref("crosswave.css"))}"><script src="${escapeHtml(context.assetHref("crosswave.js"))}"><\/script></head>
|
|
179
292
|
<body><a class="cw-skip" href="${escapeHtml(context.hrefForDirectory(directory.current.relativePath))}#cw-panel">Skip to files</a><div class="cw-background" aria-hidden="true"><canvas id="cw-wave"></canvas><svg class="cw-fallback-wave" viewBox="0 0 1440 900" preserveAspectRatio="none"><path d="M-80 570C220 300 370 690 760 450S1260 290 1510 510"/><path d="M-80 540C220 640 400 330 820 470S1240 620 1510 360"/></svg></div>
|
|
180
293
|
<header class="cw-top"><a class="cw-brand" data-cw-navigation${navigationData(context, "", context.hrefForDirectory(""))} href="${escapeHtml(context.hrefForDirectory(""))}">${escapeHtml(project.name)}<span>Crosswave</span></a><nav class="cw-location" aria-label="Location"><a data-cw-navigation${navigationData(context, "", context.hrefForDirectory(""))} href="${escapeHtml(context.hrefForDirectory(""))}">Home</a>${breadcrumbs ? `<span class="cw-separator" aria-hidden="true">/</span>${breadcrumbs}` : ""}</nav><time class="cw-clock" aria-label="Current time" hidden></time></header>
|
|
181
|
-
<main class="cw-stage" data-cw-root="${escapeHtml(context.hrefForDirectory(""))}" data-cw-path="${escapeHtml(directory.current.relativePath)}" data-cw-page="${escapeHtml(context.assetHref(pageAsset(directory.current.relativePath)))}" data-cw-file="${escapeHtml(context.outputName)}"><h1 class="cw-sr">Files in ${escapeHtml(path)}</h1><nav class="cw-rail" aria-label="File categories" hidden><div class="cw-categories" role="tablist" aria-label="File categories">${tabs}</div></nav>
|
|
294
|
+
<main class="cw-stage" data-cw-root="${escapeHtml(context.hrefForDirectory(""))}" data-cw-path="${escapeHtml(directory.current.relativePath)}" data-cw-page="${escapeHtml(context.assetHref(pageAsset(directory.current.relativePath)))}" data-cw-file="${escapeHtml(context.outputName)}" data-cw-category-config="${categoryConfig}"><h1 class="cw-sr">Files in ${escapeHtml(path)}</h1><nav class="cw-rail" aria-label="File categories" hidden><div class="cw-categories" role="tablist" aria-label="File categories">${tabs}</div></nav>
|
|
182
295
|
<section class="cw-browser" id="cw-panel" aria-label="Files"><div class="cw-list-head"><div class="cw-directory">${parent === null ? "<span class=\"cw-parent-placeholder\"></span>" : `<a class="cw-parent" data-cw-parent data-cw-navigation${navigationData(context, directory.parent?.relativePath ?? "", parent)} href="${escapeHtml(parent)}" aria-label="Parent directory">${crosswaveIcon("back")}</a>`}<h2 data-cw-heading>All files</h2><span data-cw-count>${directory.entries.length} items</span></div><div class="cw-search" hidden>${crosswaveIcon("search")}<label class="cw-sr" for="cw-search">Search this folder</label><input id="cw-search" type="search" placeholder="Search this folder" autocomplete="off" spellcheck="false"><button type="button" data-cw-clear hidden>Clear</button></div></div>
|
|
183
|
-
<div class="cw-list-scroll"><ul class="cw-files">${directory.entries.map((entry, i) => row(entry, context, i)).join("")}</ul><p class="cw-empty" data-cw-empty${directory.entries.length ? " hidden" : ""}>${directory.entries.length ? "No files match this search." : "This folder is empty."}</p></div><p class="cw-sr" role="status" aria-live="polite" data-cw-status></p></section>
|
|
296
|
+
<div class="cw-list-scroll"><ul class="cw-files">${directory.entries.map((entry, i) => row(entry, context, i, categories)).join("")}</ul><p class="cw-empty" data-cw-empty${directory.entries.length ? " hidden" : ""}>${directory.entries.length ? "No files match this search." : "This folder is empty."}</p></div><p class="cw-sr" role="status" aria-live="polite" data-cw-status></p></section>
|
|
184
297
|
<aside class="cw-detail" aria-label="Selected file details" hidden><div class="cw-detail-symbol" data-cw-detail-icon></div><h2 data-cw-detail-name></h2><p data-cw-detail-status></p><dl><div><dt>Path</dt><dd data-cw-detail-path></dd></div><div><dt>Size</dt><dd data-cw-detail-size></dd></div><div><dt>Modified</dt><dd data-cw-detail-modified></dd></div></dl><a class="cw-open" data-cw-open>Open file</a></aside></main>
|
|
185
298
|
<footer class="cw-bottom"><span class="cw-key-help" hidden><span><kbd>Arrow keys</kbd> Navigate</span><span><kbd>Enter</kbd> Open</span><span><kbd>Backspace</kbd> Parent</span></span><span class="cw-controller" role="status" hidden>${crosswaveIcon("controller")}<span data-cw-controller-label></span></span><div class="cw-appearance" hidden><label class="cw-sr" for="cw-color">Background color</label><select id="cw-color" aria-label="Background color"><option value="azure">Azure</option><option value="violet">Violet</option><option value="amber">Amber</option><option value="rose">Rose</option><option value="jade">Jade</option><option value="graphite">Graphite</option></select><button data-cw-motion aria-pressed="false" type="button">${crosswaveIcon("pause")}<span>Pause waves</span></button></div></footer><noscript><p class="cw-noscript">All files are available below. Categories, search, animated waves, and controller navigation need JavaScript.</p></noscript></body></html>`;
|
|
186
299
|
const asset = pageAsset(directory.current.relativePath);
|
|
@@ -272,6 +385,6 @@ function createPlainTheme(options = {}) {
|
|
|
272
385
|
};
|
|
273
386
|
}
|
|
274
387
|
//#endregion
|
|
275
|
-
export { compareEntries, createCrosswaveTheme, createDefaultTheme, createExplorerDevServer, createPlainTheme, defaultOutputName, defaultTheme, defaultThemeComponents, defineConfig, escapeHtml, generateExplorer, loadDirwellConfig, readGeneratedPage, resolveGenerateOptions, resolveThemeComponents };
|
|
388
|
+
export { compareEntries, createCrosswaveTheme, createDefaultTheme, createExplorerDevServer, createPlainTheme, defaultCrosswaveCategories, defaultOutputName, defaultTheme, defaultThemeComponents, defineConfig, escapeHtml, generateExplorer, loadDirwellConfig, readGeneratedPage, resolveGenerateOptions, resolveThemeComponents };
|
|
276
389
|
|
|
277
390
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["status","escapeHtml"],"sources":["../src/theme-crosswave/icons.ts","../src/theme-crosswave/styles.ts","../src/theme-crosswave.ts","../src/theme-plain.ts"],"sourcesContent":["export type CrosswaveIcon =\n | \"all\"\n | \"folder\"\n | \"image\"\n | \"audio\"\n | \"video\"\n | \"document\"\n | \"other\"\n | \"link\"\n | \"back\"\n | \"search\"\n | \"pause\"\n | \"play\"\n | \"controller\";\n\nconst paths: Record<CrosswaveIcon, string> = {\n all: '<rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>',\n folder:\n '<path d=\"M3 7V5a1 1 0 0 1 1-1h5l2 3h9a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1Z\"/><path d=\"M3 9h18\"/>',\n image:\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"1.5\"/><circle cx=\"8\" cy=\"9\" r=\"1.5\"/><path d=\"m3 17 5-5 4 4 4-6 5 7\"/>',\n audio:\n '<path d=\"M9 18V5l11-2v13M9 8l11-2\"/><ellipse cx=\"6\" cy=\"18\" rx=\"3\" ry=\"2.5\"/><ellipse cx=\"17\" cy=\"16\" rx=\"3\" ry=\"2.5\"/>',\n video: '<rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"1.5\"/><path d=\"m10 8 6 4-6 4Z\"/>',\n document: '<path d=\"M5 3h9l5 5v13H5ZM14 3v5h5M8 12h8M8 16h6\"/>',\n other: '<path d=\"m12 3 9 5v9l-9 5-9-5V8ZM3 8l9 5 9-5M12 13v9\"/>',\n link: '<path d=\"m9 15 6-6M8 10l-3 3a4 4 0 0 0 6 6l3-3M10 8l3-3a4 4 0 0 1 6 6l-3 3\"/>',\n back: '<path d=\"m10 5-7 7 7 7M3 12h18\"/>',\n search: '<circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\"/><path d=\"m16 16 5 5\"/>',\n pause: '<path d=\"M8 5v14M16 5v14\"/>',\n play: '<path d=\"m8 4 12 8-12 8Z\"/>',\n controller:\n '<path d=\"M7 7h10c3 0 4 3 5 9 0 3-2 4-4 1l-2-2H8l-2 2c-2 3-4 2-4-1 1-6 2-9 5-9ZM6 11h4M8 9v4M16 10h.01M19 13h.01\"/>',\n};\n\n/** Original geometric icons, not copied console artwork. */\nexport function crosswaveIcon(name: CrosswaveIcon): string {\n return `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.45\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${paths[name]}</svg>`;\n}\n","export const crosswaveStyles = String.raw`\n:root{color-scheme:dark;--cw-top:#2b4f85;--cw-bottom:#091a3a;--cw-text:#f7fbff;--cw-muted:#edf4fc;--cw-focus:#fff;--cw-ease:cubic-bezier(.16,1,.3,1);font-family:\"Segoe UI\",Helvetica,Arial,sans-serif;font-synthesis:none}\n:root[data-cw-color=violet]{--cw-top:#5c407d;--cw-bottom:#241532;--cw-muted:#edf4fc}:root[data-cw-color=amber]{--cw-top:#704d1a;--cw-bottom:#30200b;--cw-muted:#edf4fc}:root[data-cw-color=rose]{--cw-top:#704055;--cw-bottom:#351b29;--cw-muted:#edf4fc}:root[data-cw-color=jade]{--cw-top:#215e52;--cw-bottom:#0b2a28;--cw-muted:#edf4fc}:root[data-cw-color=graphite]{--cw-top:#424d61;--cw-bottom:#181d28;--cw-muted:#edf4fc}\n*{box-sizing:border-box}[hidden]{display:none!important}html{scroll-padding-bottom:110px;min-height:100%;background:var(--cw-bottom)}body{margin:0;color:var(--cw-text);background:var(--cw-bottom);min-height:100svh;font-size:16px;line-height:1.5}::selection{background:#d0e4fc;color:#132848}a{color:inherit;text-decoration:none}button,input,select{font:inherit;color:inherit}button,select{cursor:pointer}button{border:0}a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--cw-focus);outline-offset:6px;border-radius:3px}svg{display:block;width:24px;height:24px;flex:none}.cw-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.cw-skip{position:fixed;left:24px;top:16px;padding:12px 18px;background:#fff;color:#142e53;z-index:20;transform:translateY(-150%)}.cw-skip:focus{transform:none}\n.cw-background{position:fixed;inset:0;z-index:0;pointer-events:none;background:radial-gradient(ellipse at 28% 4%,#c0d7ff12,transparent 68%),linear-gradient(165deg,var(--cw-top),var(--cw-bottom));view-transition-name:cw-background;overflow:hidden}.cw-background canvas{width:100%;height:100%;display:block}.cw-fallback-wave{position:absolute;inset:0;width:100%;height:100%;fill:none;stroke:#d5e8ff;stroke-width:18;opacity:.06;filter:blur(3px)}.cw-background[data-ready] .cw-fallback-wave{display:none}\n.cw-top,.cw-stage,.cw-bottom,.cw-noscript{position:relative;z-index:1}.cw-top{display:flex;align-items:center;gap:32px;padding:30px 48px 20px;min-height:92px;view-transition-name:cw-header}.cw-brand{font-size:20px;font-weight:500;letter-spacing:-.02em;white-space:nowrap}.cw-brand span{display:block;font-size:12px;letter-spacing:.08em;color:var(--cw-muted);font-weight:400}.cw-location{display:flex;gap:12px;align-items:center;min-width:0;flex-wrap:wrap;font-size:13px;color:var(--cw-muted)}.cw-location a:hover{text-decoration:underline;text-underline-offset:5px}.cw-separator{opacity:.6}.cw-clock{margin-left:auto;white-space:nowrap;font-variant-numeric:tabular-nums;font-size:14px;color:var(--cw-muted)}\n.cw-stage{max-width:1500px;margin:28px auto 110px;padding:0 72px;display:grid;grid-template-columns:minmax(0,1.55fr) minmax(220px,1fr);column-gap:80px}.cw-rail{grid-column:1/-1;margin:0 -12px 22px;overflow-x:auto;padding:12px 12px 24px;scrollbar-width:none}.cw-rail::-webkit-scrollbar{display:none}.cw-categories{padding-left:104px;padding-right:calc(100vw - 200px);display:flex;gap:32px;min-width:max-content;align-items:flex-start}.cw-category{position:relative;display:flex;flex-direction:column;align-items:center;gap:12px;min-width:94px;min-height:96px;background:none;padding:10px 4px;color:var(--cw-muted);transition:color .2s,transform .3s var(--cw-ease);flex:none}.cw-category svg{width:40px;height:40px;filter:drop-shadow(0 3px 2px #07182a55)}.cw-category span{font-size:14px;white-space:nowrap}.cw-category small{font-size:10px;position:absolute;right:8px;top:0;color:var(--cw-muted)}.cw-category:hover{color:#fff;transform:translateY(-3px)}.cw-category.cw-active{color:#fff;transform:scale(1.1)}.cw-category.cw-active:after{content:\"\";position:absolute;left:calc(50% - 12px);bottom:-8px;width:24px;height:2px;background:#e6f2ff;box-shadow:0 3px 10px #bddaff55}\n.cw-browser{padding-left:104px;min-width:0;view-transition-name:cw-browser}.cw-list-head{display:flex;align-items:center;justify-content:space-between;gap:24px;min-height:58px;margin:0 0 12px;padding:0 4px}.cw-directory{display:flex;align-items:center;gap:16px;min-width:0}.cw-directory h2{font-size:17px;font-weight:400;margin:0;white-space:nowrap}.cw-directory>[data-cw-count]{font-size:12px;color:var(--cw-muted);white-space:nowrap}.cw-parent,.cw-parent-placeholder{width:34px;height:44px;display:flex;align-items:center;justify-content:center;flex:none}.cw-parent:hover{background:#ffffff12;border-radius:5px}.cw-search{display:flex;align-items:center;gap:8px;border-bottom:1px solid #d5e7ff55;min-width:140px;width:220px;padding:6px 0}.cw-search svg{width:17px;height:17px;color:var(--cw-muted)}.cw-search input{background:none;border:0;min-width:0;width:100%;outline-offset:3px;font-size:13px;caret-color:#fff}.cw-search input::placeholder{color:var(--cw-muted);opacity:1}.cw-search button{background:none;font-size:11px;padding:6px 0}.cw-search input::-webkit-search-cancel-button{display:none}\n.cw-list-scroll{max-height:calc(100svh - 365px);min-height:220px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:#d6e8fa66 transparent;padding:14px 12px 24px 8px;scroll-padding-block:20px;overscroll-behavior:contain}.cw-list-scroll::-webkit-scrollbar{width:5px}.cw-list-scroll::-webkit-scrollbar-thumb{background:#d6e8fa66;border-radius:4px}.cw-files{list-style:none;padding:0;margin:0}.cw-row{margin:0;min-height:76px;padding:0}.cw-entry{display:flex;align-items:center;gap:25px;padding:14px 18px;min-height:76px;transition:background .2s,opacity .2s,transform .3s var(--cw-ease);border-radius:7px}.cw-entry-icon{display:flex;width:42px;min-width:42px;align-items:center;justify-content:center;transition:transform .3s var(--cw-ease);filter:drop-shadow(0 3px 2px #08192c66)}.cw-entry-icon svg{width:34px;height:34px}.cw-entry-label{min-width:0;display:block}.cw-name{display:block;font-size:18px;font-weight:400;line-height:1.4;overflow-wrap:anywhere;text-shadow:0 2px 3px #07192b66}.cw-row-meta{display:block;font-size:11px;color:var(--cw-muted);margin-top:3px}.cw-row-date{display:none;font-size:11px;color:var(--cw-muted);margin-top:4px}.cw-target{display:block;font-size:11px;color:var(--cw-muted);overflow-wrap:anywhere;margin-top:4px}.cw-row.cw-selected .cw-entry{background:linear-gradient(90deg,#f4f9ff08,#f4f9ff04);transform:translateX(5px)}.cw-row.cw-selected .cw-entry-icon{transform:scale(1.22)}.cw-row.cw-selected .cw-name{color:#fff}.cw-row:hover .cw-entry{background:#eef7ff08}.cw-unavailable{color:var(--cw-muted)}.cw-notice{position:fixed;bottom:90px;left:48px;right:24px;z-index:3;margin:0;background:var(--cw-bottom);border-radius:4px;font-size:12px;color:var(--cw-muted);padding:12px 24px;max-width:52ch}.cw-empty{padding:36px 28px;color:var(--cw-muted);font-size:15px}.cw-detail{min-width:0;align-self:center;padding:20px 0 28px;view-transition-name:cw-detail}.cw-detail-symbol{margin-bottom:28px;filter:drop-shadow(0 12px 12px #04152e44)}.cw-detail-symbol svg{width:110px;height:110px;stroke-width:.8;opacity:.9}.cw-detail h2{font-size:25px;font-weight:300;margin:0 0 7px;overflow-wrap:anywhere;line-height:1.3;letter-spacing:-.015em}.cw-detail>p{font-size:12px;color:var(--cw-muted);margin:0 0 25px}.cw-detail dl{margin:0;font-size:12px}.cw-detail dl>div{display:grid;grid-template-columns:70px minmax(0,1fr);gap:10px;margin-bottom:10px}.cw-detail dt{color:var(--cw-muted)}.cw-detail dd{margin:0;overflow-wrap:anywhere}.cw-open{display:inline-flex;margin-top:24px;min-height:44px;align-items:center;font-size:14px;border-bottom:1px solid #e8f1ff88;gap:12px}.cw-open:hover{border-bottom-color:#fff}.cw-open:after{content:\"\";display:block;width:7px;height:7px;border-top:1px solid currentColor;border-right:1px solid currentColor;transform:rotate(45deg)}\n.cw-bottom{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:22px 48px;position:fixed;bottom:0;left:0;right:0;background:linear-gradient(0deg,var(--cw-bottom),transparent);min-height:82px}.cw-key-help{display:flex;gap:20px;flex-wrap:wrap;font-size:11px;color:var(--cw-muted)}kbd{font:inherit;color:#fff;margin-right:5px}.cw-appearance{display:flex;align-items:center;gap:16px;flex:none}.cw-appearance select{background:#0b193b70;border:1px solid #d7e9ff33;padding:7px 24px 7px 10px;border-radius:4px;max-width:116px;font-size:12px;min-height:36px}.cw-appearance button{display:flex;align-items:center;gap:8px;background:transparent;font-size:12px;min-height:40px;padding:6px 0}.cw-appearance button svg{width:16px;height:16px}.cw-controller{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--cw-muted)}.cw-controller svg{width:22px;height:22px}.cw-noscript{padding:0 48px 24px;color:var(--cw-muted);font-size:13px}\nbody:not([data-cw-enhanced]) .cw-browser{padding-left:0}body:not([data-cw-enhanced]) .cw-stage{display:block;margin-top:20px}body:not([data-cw-enhanced]) .cw-list-scroll{max-height:none;min-height:0}body:not([data-cw-enhanced]) .cw-parent-placeholder{display:none}body:not([data-cw-enhanced]) .cw-bottom{display:none}\n.cw-navigation-notice{position:fixed;z-index:5;left:48px;right:24px;bottom:90px;background:var(--cw-bottom);color:var(--cw-text);padding:12px 16px;border-radius:4px;font-size:13px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}.cw-navigation-notice[hidden]{display:none}.cw-navigation-notice button,.cw-navigation-notice a{background:none;min-height:44px;padding:6px 12px;border:1px solid var(--cw-muted);border-radius:4px}.cw-transition-layer *{animation:none!important;transition:none!important}.cw-transition-layer [data-cw-status]{display:none}\n@view-transition{navigation:auto}::view-transition-old(root),::view-transition-new(root){animation:none}::view-transition-old(cw-background),::view-transition-new(cw-background){animation-duration:420ms}::view-transition-old(cw-browser),::view-transition-old(cw-detail){animation:cw-out 180ms ease-in both}::view-transition-new(cw-browser),::view-transition-new(cw-detail){animation:cw-in 420ms var(--cw-ease) both}::view-transition-old(cw-header),::view-transition-new(cw-header){animation-duration:240ms}@keyframes cw-out{to{opacity:0;transform:translateX(-22px);filter:blur(3px)}}@keyframes cw-in{from{opacity:0;transform:translateX(32px);filter:blur(5px)}}:root[data-cw-transitions=false]::view-transition-group(*){animation-duration:0s}:root[data-cw-transitions=false]::view-transition-old(*),:root[data-cw-transitions=false]::view-transition-new(*){animation:none}.cw-category-changing .cw-files{animation:cw-category-in 260ms var(--cw-ease)}@keyframes cw-category-in{from{opacity:.35;transform:translateX(12px)}}\n@media(min-width:1500px){.cw-stage{margin-top:55px}.cw-categories{gap:48px}.cw-category{min-width:112px}.cw-list-scroll{max-height:calc(100svh - 405px)}}\n@media(max-width:1000px){.cw-top{padding:24px 30px;gap:22px}.cw-stage{padding:0 36px;column-gap:35px}.cw-categories{padding-left:44px;gap:20px}.cw-browser{padding-left:44px}.cw-category{min-width:82px}.cw-list-head{flex-wrap:wrap;gap:8px}.cw-search{margin-left:52px;width:calc(100% - 52px)}.cw-list-scroll{max-height:calc(100svh - 390px)}.cw-bottom{padding:20px 30px}.cw-key-help{gap:12px}.cw-detail h2{font-size:21px}.cw-clock{font-size:12px}}\n@media(max-width:680px){.cw-top{padding:22px 20px 12px;flex-wrap:wrap;gap:16px;min-height:104px}.cw-brand{font-size:18px}.cw-brand span{font-size:10px}.cw-clock{order:1;font-size:11px}.cw-location{order:2;width:100%;font-size:12px;gap:8px}.cw-stage{padding:0 16px;margin:12px auto 100px;display:block}.cw-rail{margin:0 -8px 8px;padding:12px 8px 20px}.cw-categories{padding-left:0;gap:14px}.cw-browser{padding-left:0}.cw-category{min-width:76px;min-height:83px;padding:10px 2px;gap:10px}.cw-category svg{width:33px;height:33px}.cw-category span{font-size:12px}.cw-category small{right:5px}.cw-list-head{padding:0 12px;margin-bottom:8px}.cw-directory{gap:12px}.cw-directory h2{font-size:16px}.cw-search{margin-left:46px;width:calc(100% - 46px);min-height:44px}.cw-search input,.cw-search button{min-height:44px}.cw-list-scroll{max-height:none;min-height:150px;padding:8px 2px 18px}.cw-entry{gap:20px;padding:14px 12px;min-height:78px}.cw-entry-icon{width:36px;min-width:36px}.cw-entry-icon svg{width:29px;height:29px}.cw-name{font-size:17px}.cw-detail{display:none!important}.cw-row-date{display:block}.cw-bottom{padding:16px 20px;min-height:76px;gap:12px}.cw-key-help{display:none!important}.cw-appearance{width:100%;justify-content:space-between}.cw-appearance select,.cw-appearance button{min-height:44px}.cw-controller{position:absolute;bottom:72px;right:20px;background:var(--cw-bottom);padding:6px 9px;border-radius:4px;max-width:calc(100% - 40px)}.cw-notice{left:20px;right:20px;bottom:90px}.cw-noscript{padding:0 24px 24px}}\n@media(max-height:520px){.cw-stage{grid-template-columns:minmax(0,1fr)}.cw-detail{display:none!important}.cw-row-date{display:block}.cw-top{flex-wrap:nowrap}.cw-location{order:0;width:auto}.cw-clock{display:none!important}.cw-top{min-height:58px;padding-top:10px;padding-bottom:8px}.cw-stage{margin-top:0;margin-bottom:70px}.cw-rail{margin-bottom:0;padding-bottom:12px}.cw-category{min-height:68px;gap:4px;padding-top:4px}.cw-category svg{width:28px;height:28px}.cw-list-scroll{max-height:calc(100svh - 235px);min-height:80px}.cw-list-head{min-height:36px;margin-bottom:0}.cw-detail-symbol{margin-bottom:10px}.cw-detail-symbol svg{width:60px;height:60px}.cw-detail>p{margin-bottom:12px}.cw-bottom{min-height:60px;padding-block:10px}.cw-list-head{flex-wrap:nowrap}.cw-search{margin-left:0;width:170px}.cw-detail dl>div{margin-bottom:4px}}\n@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation:none!important;transition:none!important}::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}.cw-row.cw-selected .cw-entry,.cw-category.cw-active,.cw-category:hover{transform:none}}\n`;\n","import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport type { ExplorerTheme, FileSystemEntry, ThemeContext } from \"./model.ts\";\nimport { escapeHtml } from \"./theme-default/components.ts\";\nimport { resolveThemeProject, type ThemeProjectOptions } from \"./theme-project.ts\";\nimport { crosswaveIcon, type CrosswaveIcon } from \"./theme-crosswave/icons.ts\";\nimport { crosswaveStyles } from \"./theme-crosswave/styles.ts\";\n\nconst runtime = await readFile(new URL(\"./crosswave-runtime.js\", import.meta.url), \"utf8\");\nfunction pageAsset(relativePath: string): string {\n return `crosswave-page-${createHash(\"sha256\").update(relativePath).digest(\"hex\").slice(0, 24)}.js`;\n}\nfunction navigationData(context: ThemeContext, relativePath: string, href: string): string {\n const asset = pageAsset(relativePath);\n const dataHref =\n context.mode === \"mpa\"\n ? context.assetHref(asset)\n : `${href.endsWith(\"/\") ? href : href.slice(0, href.lastIndexOf(\"/\") + 1)}${asset}`;\n return ` data-cw-page=\"${escapeHtml(dataHref)}\" data-cw-path=\"${escapeHtml(relativePath)}\"`;\n}\nexport type CrosswaveColor = \"azure\" | \"violet\" | \"amber\" | \"rose\" | \"jade\" | \"graphite\";\nexport interface CrosswaveThemeOptions {\n readonly color?: CrosswaveColor;\n readonly backgroundMotion?: boolean;\n readonly pageTransitions?: boolean;\n readonly gamepad?: boolean;\n readonly project?: ThemeProjectOptions;\n}\n\nconst categories = [\n [\"all\", \"All files\"],\n [\"folder\", \"Folders\"],\n [\"image\", \"Photos\"],\n [\"audio\", \"Music\"],\n [\"video\", \"Videos\"],\n [\"document\", \"Documents\"],\n [\"other\", \"Other\"],\n] as const;\nconst extensions: Record<string, CrosswaveIcon> = Object.fromEntries([\n ...[\"png\", \"jpg\", \"jpeg\", \"webp\", \"gif\", \"avif\", \"svg\", \"bmp\", \"heic\"].map((x) => [x, \"image\"]),\n ...[\"mp3\", \"wav\", \"ogg\", \"flac\", \"m4a\", \"aac\", \"aiff\", \"opus\"].map((x) => [x, \"audio\"]),\n ...[\"mp4\", \"webm\", \"mov\", \"mkv\", \"avi\", \"m4v\"].map((x) => [x, \"video\"]),\n ...[\"txt\", \"md\", \"pdf\", \"json\", \"csv\", \"html\", \"xml\", \"doc\", \"docx\", \"rtf\", \"yaml\", \"yml\"].map(\n (x) => [x, \"document\"],\n ),\n]) as Record<string, CrosswaveIcon>;\n\nfunction category(entry: FileSystemEntry): CrosswaveIcon {\n if (entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\") return \"folder\";\n return extensions[entry.name.split(\".\").at(-1)?.toLowerCase() ?? \"\"] ?? \"other\";\n}\nfunction size(bytes: number): string {\n const units = [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\"];\n const index = bytes > 0 ? Math.min(4, Math.floor(Math.log(bytes) / Math.log(1024))) : 0;\n return `${(bytes / 1024 ** index).toLocaleString(\"en-US\", { maximumFractionDigits: index === 0 ? 0 : 1 })} ${units[index]}`;\n}\nfunction status(entry: FileSystemEntry): string {\n if (entry.symlink?.isBroken) return \"Broken link · declared target only\";\n if (entry.symlink?.isOutsideRoot) return \"Outside root · declared target only\";\n if (entry.symlink?.isTargetExcluded) return \"Excluded target · declared target only\";\n if (entry.symlink?.isCycle) return \"Directory link · cycle\";\n if (entry.kind === \"symlink\") return \"Symbolic link\";\n if (entry.kind === \"directory\") return \"Folder\";\n return \"File\";\n}\nfunction row(entry: FileSystemEntry, context: ThemeContext, index: number): string {\n const href = context.hrefFor(entry);\n const type = category(entry);\n const icon = entry.kind === \"symlink\" ? \"link\" : type;\n const label = `${entry.name}${type === \"folder\" ? \"/\" : \"\"}`;\n const unavailable =\n entry.symlink?.isOutsideRoot || entry.symlink?.isTargetExcluded || entry.symlink?.isBroken;\n const bytes = type === \"folder\" ? \"Folder\" : size(entry.metadata.size);\n const instant = new Date(entry.metadata.times.modifiedAt);\n const modified = Number.isNaN(instant.getTime())\n ? \"Unknown\"\n : `${instant.toISOString().slice(0, 16).replace(\"T\", \" \")} UTC`;\n const target =\n entry.symlink === null\n ? \"\"\n : `<span class=\"cw-target\">${escapeHtml(entry.symlink.target)}</span>`;\n const data = `data-cw-entry data-category=\"${type}\" data-icon=\"${icon}\" data-name=\"${escapeHtml(entry.name)}\" data-path=\"${escapeHtml(entry.relativePath)}\" data-size=\"${escapeHtml(bytes)}\" data-modified=\"${modified}\" data-status=\"${escapeHtml(status(entry))}\" data-exits=\"${context.exitsExplorerFor(entry)}\"`;\n const contents = `<span class=\"cw-entry-icon\">${crosswaveIcon(icon)}</span><span class=\"cw-entry-label\"><span class=\"cw-name\">${escapeHtml(label)}</span>${target}<span class=\"cw-row-meta\">${escapeHtml(status(entry))}${type === \"folder\" || unavailable ? \"\" : ` · ${bytes}`}</span><span class=\"cw-row-date\">Modified ${modified}</span></span>`;\n return `<li class=\"cw-row${index === 0 ? \" cw-selected\" : \"\"}\" ${data}>${href === null ? `<span class=\"cw-entry cw-unavailable\">${contents}</span>` : `<a class=\"cw-entry\" href=\"${escapeHtml(href)}\"${context.exitsExplorerFor(entry) ? ' target=\"_blank\" rel=\"noopener\"' : ` data-cw-navigation${navigationData(context, entry.symlink?.targetRelativePath ?? entry.relativePath, href)}`}>${contents}</a>`}</li>`;\n}\n\n/** PSP-inspired media navigation with original wave graphics and ordinary static links. */\nexport function createCrosswaveTheme(options: CrosswaveThemeOptions = {}): ExplorerTheme {\n const color = options.color ?? \"azure\";\n if (![\"azure\", \"violet\", \"amber\", \"rose\", \"jade\", \"graphite\"].includes(color))\n throw new TypeError(\"Unknown Crosswave color\");\n for (const key of [\"backgroundMotion\", \"pageTransitions\", \"gamepad\"] as const) {\n if (options[key] !== undefined && typeof options[key] !== \"boolean\")\n throw new TypeError(`${key} must be a boolean`);\n }\n const project = resolveThemeProject(options.project);\n return {\n name: \"crosswave\",\n searchIndex: false,\n render(context) {\n const { directory } = context;\n const path = directory.current.relativePath || \"/\";\n const parent =\n directory.parent === null ? null : context.hrefForDirectory(directory.parent.relativePath);\n const segments = directory.current.relativePath.split(\"/\").filter(Boolean);\n const breadcrumbs = segments\n .map((segment, i) =>\n i === segments.length - 1\n ? `<span aria-current=\"page\">${escapeHtml(segment)}</span>`\n : `<a data-cw-navigation${navigationData(context, segments.slice(0, i + 1).join(\"/\"), context.hrefForDirectory(segments.slice(0, i + 1).join(\"/\")))} href=\"${escapeHtml(context.hrefForDirectory(segments.slice(0, i + 1).join(\"/\")))}\">${escapeHtml(segment)}</a>`,\n )\n .join('<span class=\"cw-separator\" aria-hidden=\"true\">/</span>');\n const tabs = categories\n .map(\n ([type, label], i) =>\n `<button role=\"tab\" id=\"cw-tab-${type}\" aria-selected=\"${i === 0}\" aria-controls=\"cw-panel\" tabindex=\"${i === 0 ? 0 : -1}\" data-cw-category=\"${type}\" class=\"cw-category${i === 0 ? \" cw-active\" : \"\"}\">${crosswaveIcon(type)}<span>${label}</span><small data-cw-category-count=\"${type}\"></small></button>`,\n )\n .join(\"\");\n const html = `<!doctype html>\n<html lang=\"en\" data-cw-color=\"${color}\" data-cw-motion=\"${options.backgroundMotion !== false}\" data-cw-transitions=\"${options.pageTransitions !== false}\" data-cw-gamepad=\"${options.gamepad !== false}\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">${context.documentBaseHref === null ? \"\" : `<base href=\"${escapeHtml(context.documentBaseHref)}\">`}<title>${escapeHtml(path)} · ${escapeHtml(project.name)} · Crosswave</title><link rel=\"stylesheet\" href=\"${escapeHtml(context.assetHref(\"crosswave.css\"))}\"><script src=\"${escapeHtml(context.assetHref(\"crosswave.js\"))}\"></script></head>\n<body><a class=\"cw-skip\" href=\"${escapeHtml(context.hrefForDirectory(directory.current.relativePath))}#cw-panel\">Skip to files</a><div class=\"cw-background\" aria-hidden=\"true\"><canvas id=\"cw-wave\"></canvas><svg class=\"cw-fallback-wave\" viewBox=\"0 0 1440 900\" preserveAspectRatio=\"none\"><path d=\"M-80 570C220 300 370 690 760 450S1260 290 1510 510\"/><path d=\"M-80 540C220 640 400 330 820 470S1240 620 1510 360\"/></svg></div>\n<header class=\"cw-top\"><a class=\"cw-brand\" data-cw-navigation${navigationData(context, \"\", context.hrefForDirectory(\"\"))} href=\"${escapeHtml(context.hrefForDirectory(\"\"))}\">${escapeHtml(project.name)}<span>Crosswave</span></a><nav class=\"cw-location\" aria-label=\"Location\"><a data-cw-navigation${navigationData(context, \"\", context.hrefForDirectory(\"\"))} href=\"${escapeHtml(context.hrefForDirectory(\"\"))}\">Home</a>${breadcrumbs ? `<span class=\"cw-separator\" aria-hidden=\"true\">/</span>${breadcrumbs}` : \"\"}</nav><time class=\"cw-clock\" aria-label=\"Current time\" hidden></time></header>\n<main class=\"cw-stage\" data-cw-root=\"${escapeHtml(context.hrefForDirectory(\"\"))}\" data-cw-path=\"${escapeHtml(directory.current.relativePath)}\" data-cw-page=\"${escapeHtml(context.assetHref(pageAsset(directory.current.relativePath)))}\" data-cw-file=\"${escapeHtml(context.outputName)}\"><h1 class=\"cw-sr\">Files in ${escapeHtml(path)}</h1><nav class=\"cw-rail\" aria-label=\"File categories\" hidden><div class=\"cw-categories\" role=\"tablist\" aria-label=\"File categories\">${tabs}</div></nav>\n<section class=\"cw-browser\" id=\"cw-panel\" aria-label=\"Files\"><div class=\"cw-list-head\"><div class=\"cw-directory\">${parent === null ? '<span class=\"cw-parent-placeholder\"></span>' : `<a class=\"cw-parent\" data-cw-parent data-cw-navigation${navigationData(context, directory.parent?.relativePath ?? \"\", parent)} href=\"${escapeHtml(parent)}\" aria-label=\"Parent directory\">${crosswaveIcon(\"back\")}</a>`}<h2 data-cw-heading>All files</h2><span data-cw-count>${directory.entries.length} items</span></div><div class=\"cw-search\" hidden>${crosswaveIcon(\"search\")}<label class=\"cw-sr\" for=\"cw-search\">Search this folder</label><input id=\"cw-search\" type=\"search\" placeholder=\"Search this folder\" autocomplete=\"off\" spellcheck=\"false\"><button type=\"button\" data-cw-clear hidden>Clear</button></div></div>\n<div class=\"cw-list-scroll\"><ul class=\"cw-files\">${directory.entries.map((entry, i) => row(entry, context, i)).join(\"\")}</ul><p class=\"cw-empty\" data-cw-empty${directory.entries.length ? \" hidden\" : \"\"}>${directory.entries.length ? \"No files match this search.\" : \"This folder is empty.\"}</p></div><p class=\"cw-sr\" role=\"status\" aria-live=\"polite\" data-cw-status></p></section>\n<aside class=\"cw-detail\" aria-label=\"Selected file details\" hidden><div class=\"cw-detail-symbol\" data-cw-detail-icon></div><h2 data-cw-detail-name></h2><p data-cw-detail-status></p><dl><div><dt>Path</dt><dd data-cw-detail-path></dd></div><div><dt>Size</dt><dd data-cw-detail-size></dd></div><div><dt>Modified</dt><dd data-cw-detail-modified></dd></div></dl><a class=\"cw-open\" data-cw-open>Open file</a></aside></main>\n<footer class=\"cw-bottom\"><span class=\"cw-key-help\" hidden><span><kbd>Arrow keys</kbd> Navigate</span><span><kbd>Enter</kbd> Open</span><span><kbd>Backspace</kbd> Parent</span></span><span class=\"cw-controller\" role=\"status\" hidden>${crosswaveIcon(\"controller\")}<span data-cw-controller-label></span></span><div class=\"cw-appearance\" hidden><label class=\"cw-sr\" for=\"cw-color\">Background color</label><select id=\"cw-color\" aria-label=\"Background color\"><option value=\"azure\">Azure</option><option value=\"violet\">Violet</option><option value=\"amber\">Amber</option><option value=\"rose\">Rose</option><option value=\"jade\">Jade</option><option value=\"graphite\">Graphite</option></select><button data-cw-motion aria-pressed=\"false\" type=\"button\">${crosswaveIcon(\"pause\")}<span>Pause waves</span></button></div></footer><noscript><p class=\"cw-noscript\">All files are available below. Categories, search, animated waves, and controller navigation need JavaScript.</p></noscript></body></html>`;\n const asset = pageAsset(directory.current.relativePath);\n const payload = JSON.stringify({\n version: 1,\n id: asset,\n path: directory.current.relativePath,\n outputName: context.outputName,\n baseHref: context.documentBaseHref,\n html,\n });\n return {\n html,\n assets: {\n \"crosswave.css\": crosswaveStyles,\n \"crosswave.js\": runtime,\n [asset]: `window.dispatchEvent(new CustomEvent(\"dirwell:crosswave-page\",{detail:${payload}}));`,\n },\n };\n },\n };\n}\n","import type { ExplorerTheme, FileSystemEntry, ThemeContext } from \"./model.ts\";\nimport { resolveThemeProject, type ThemeProjectOptions } from \"./theme-project.ts\";\nimport { utcTimestamp } from \"./timestamp.ts\";\n\nexport interface PlainThemeOptions {\n readonly project?: ThemeProjectOptions;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n}\n\nfunction externalHref(value: string | undefined): string | null {\n if (value === undefined) return null;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" || url.protocol === \"http:\" ? url.href : null;\n } catch {\n return null;\n }\n}\n\nfunction externalLink(label: string, href: string | null): string {\n return href === null\n ? escapeHtml(label)\n : `<a href=\"${escapeHtml(href)}\" target=\"_blank\" rel=\"noopener\">${escapeHtml(label)}</a>`;\n}\n\nfunction status(entry: FileSystemEntry): string {\n if (entry.symlink?.isBroken) return \"broken link\";\n if (entry.symlink?.isOutsideRoot) return \"unavailable link\";\n if (entry.symlink?.isCycle) return \"cycle link\";\n if (entry.kind === \"symlink\") return \"link\";\n if (entry.kind === \"directory\") return \"folder\";\n return \"file\";\n}\n\nfunction renderBreadcrumbs(context: ThemeContext): string {\n const segments = context.directory.current.relativePath.split(\"/\").filter(Boolean);\n if (segments.length === 0) return \"\";\n const ancestors = segments.slice(0, -1).map((segment, index) => {\n const relativePath = segments.slice(0, index + 1).join(\"/\");\n return `<a href=\"${escapeHtml(context.hrefForDirectory(relativePath))}\">${escapeHtml(segment)}</a>`;\n });\n return `<nav aria-label=\"Breadcrumb\"><a href=\"${escapeHtml(context.hrefForDirectory(\"\"))}\">Home</a> / ${ancestors.length === 0 ? \"\" : `${ancestors.join(\" / \")} / `}<span aria-current=\"page\">${escapeHtml(segments.at(-1) ?? \"\")}</span></nav>`;\n}\n\nfunction renderEntry(entry: FileSystemEntry, context: ThemeContext): string {\n const directoryLike = entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n const name = `${escapeHtml(entry.name)}${directoryLike ? \"/\" : \"\"}`;\n const href = context.hrefFor(entry);\n const exitsExplorer = context.exitsExplorerFor(entry);\n const link =\n href === null\n ? `<span>${name}</span>`\n : `<a href=\"${escapeHtml(href)}\"${exitsExplorer ? ' target=\"_blank\" rel=\"noopener\"' : \"\"}>${name}</a>`;\n const details = [status(entry)];\n if (!directoryLike) details.push(`${entry.metadata.size} B`);\n if (entry.symlink !== null) details.push(`Target: ${entry.symlink.target}`);\n if (exitsExplorer) details.push(\"opens in a new tab\");\n const modified = utcTimestamp(entry.metadata.times.modifiedAt);\n return `<li>${link}<small>${escapeHtml(details.join(\" · \"))} · ${modified === null ? \"Unknown\" : `<time datetime=\"${modified.datetime}\">${modified.label}</time>`}</small></li>`;\n}\n\n/** A no-icon, no-script theme that leaves navigation and controls to the browser. */\nexport function createPlainTheme(options: PlainThemeOptions = {}): ExplorerTheme {\n const project = resolveThemeProject(options.project);\n const repositoryHref = externalHref(project.repositoryUrl);\n const licenseHref = externalHref(\n options.project?.licenseUrl ??\n (options.project?.license === undefined ? \"https://opensource.org/license/mit\" : undefined),\n );\n return {\n name: \"plain\",\n searchIndex: false,\n render(context) {\n const visiblePath =\n context.directory.current.relativePath === \"\"\n ? \"/\"\n : `/${context.directory.current.relativePath}/`;\n const parentHref =\n context.directory.parent === null\n ? null\n : context.hrefForDirectory(context.directory.parent.relativePath);\n const parentRow =\n parentHref === null ? \"\" : `<li><a href=\"${escapeHtml(parentHref)}\">../</a></li>`;\n const rows = context.directory.entries.map((entry) => renderEntry(entry, context)).join(\"\");\n const count = context.directory.entries.length;\n const footer = `<footer><hr><p>Repository: ${externalLink(project.name, repositoryHref)} by ${escapeHtml(project.author)} · ${externalLink(project.license, licenseHref)}</p></footer>`;\n const html = `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta name=\"color-scheme\" content=\"light\">${context.documentBaseHref === null ? \"\" : `<base href=\"${escapeHtml(context.documentBaseHref)}\">`}<title>Index of ${escapeHtml(visiblePath)}</title><style>body{max-width:72ch;margin:2rem auto;padding:0 1rem}h1,li,nav{overflow-wrap:anywhere}li{margin:.6rem 0}small{display:block}</style></head>\n<body><main>${renderBreadcrumbs(context)}<h1>Index of ${escapeHtml(visiblePath)}</h1><p>${count} ${count === 1 ? \"entry\" : \"entries\"}</p>${count === 0 ? \"<p>This directory is empty.</p>\" : \"\"}<ul>${parentRow}${rows}</ul></main>${footer}</body></html>`;\n return { html };\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["status","escapeHtml"],"sources":["../src/theme-crosswave/icons.ts","../src/theme-crosswave/categories.ts","../src/theme-crosswave/styles.ts","../src/theme-crosswave.ts","../src/theme-plain.ts"],"sourcesContent":["export type CrosswaveIcon =\n | \"all\"\n | \"folder\"\n | \"image\"\n | \"audio\"\n | \"video\"\n | \"document\"\n | \"other\"\n | \"link\"\n | \"back\"\n | \"search\"\n | \"pause\"\n | \"play\"\n | \"controller\";\n\nconst paths: Record<CrosswaveIcon, string> = {\n all: '<rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>',\n folder:\n '<path d=\"M3 7V5a1 1 0 0 1 1-1h5l2 3h9a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1Z\"/><path d=\"M3 9h18\"/>',\n image:\n '<rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"1.5\"/><circle cx=\"8\" cy=\"9\" r=\"1.5\"/><path d=\"m3 17 5-5 4 4 4-6 5 7\"/>',\n audio:\n '<path d=\"M9 18V5l11-2v13M9 8l11-2\"/><ellipse cx=\"6\" cy=\"18\" rx=\"3\" ry=\"2.5\"/><ellipse cx=\"17\" cy=\"16\" rx=\"3\" ry=\"2.5\"/>',\n video: '<rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"1.5\"/><path d=\"m10 8 6 4-6 4Z\"/>',\n document: '<path d=\"M5 3h9l5 5v13H5ZM14 3v5h5M8 12h8M8 16h6\"/>',\n other: '<path d=\"m12 3 9 5v9l-9 5-9-5V8ZM3 8l9 5 9-5M12 13v9\"/>',\n link: '<path d=\"m9 15 6-6M8 10l-3 3a4 4 0 0 0 6 6l3-3M10 8l3-3a4 4 0 0 1 6 6l-3 3\"/>',\n back: '<path d=\"m10 5-7 7 7 7M3 12h18\"/>',\n search: '<circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\"/><path d=\"m16 16 5 5\"/>',\n pause: '<path d=\"M8 5v14M16 5v14\"/>',\n play: '<path d=\"m8 4 12 8-12 8Z\"/>',\n controller:\n '<path d=\"M7 7h10c3 0 4 3 5 9 0 3-2 4-4 1l-2-2H8l-2 2c-2 3-4 2-4-1 1-6 2-9 5-9ZM6 11h4M8 9v4M16 10h.01M19 13h.01\"/>',\n};\n\n/** Original geometric icons, not copied console artwork. */\nexport function crosswaveIcon(name: CrosswaveIcon): string {\n return `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.45\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${paths[name]}</svg>`;\n}\n","import mime from \"mime\";\nimport type { FileSystemEntry } from \"../model.ts\";\nimport { crosswaveIcon, type CrosswaveIcon } from \"./icons.ts\";\n\nexport type CrosswaveCategoryIcon = CrosswaveIcon | { readonly svg: string };\nexport type CrosswaveCategoryMatch =\n | \"all\"\n | \"folders\"\n | \"other\"\n | { readonly extensions?: readonly string[]; readonly mimeTypes?: readonly string[] };\nexport interface CrosswaveCategory {\n readonly id: string;\n readonly label: string;\n readonly icon?: CrosswaveCategoryIcon;\n readonly match: CrosswaveCategoryMatch;\n}\nexport interface ResolvedCrosswaveCategory {\n readonly id: string;\n readonly label: string;\n readonly svg: string;\n readonly match: CrosswaveCategoryMatch;\n}\nconst documents = [\n \"text/*\",\n \"application/pdf\",\n \"application/json\",\n \"application/*+json\",\n \"application/xml\",\n \"application/*+xml\",\n \"application/msword\",\n \"application/rtf\",\n \"application/sql\",\n \"application/epub+zip\",\n \"application/vnd.ms-excel\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n] as const;\nconst definitions: readonly CrosswaveCategory[] = [\n { id: \"all\", label: \"All files\", icon: \"all\", match: \"all\" },\n { id: \"folder\", label: \"Folders\", icon: \"folder\", match: \"folders\" },\n { id: \"image\", label: \"Photos\", icon: \"image\", match: { mimeTypes: [\"image/*\"] } },\n {\n id: \"audio\",\n label: \"Music\",\n icon: \"audio\",\n match: { mimeTypes: [\"audio/*\", \"application/vnd.apple.mpegurl\"] },\n },\n { id: \"video\", label: \"Videos\", icon: \"video\", match: { mimeTypes: [\"video/*\"] } },\n { id: \"document\", label: \"Documents\", icon: \"document\", match: { mimeTypes: documents } },\n { id: \"other\", label: \"Other\", icon: \"other\", match: \"other\" },\n];\n/** Default rail definitions. Supply a mapped/filtered copy to change labels, order, or visibility. */\nexport const defaultCrosswaveCategories: readonly CrosswaveCategory[] = Object.freeze(\n definitions.map((category) =>\n Object.freeze({\n ...category,\n match:\n typeof category.match === \"string\"\n ? category.match\n : Object.freeze({\n ...(category.match.extensions\n ? { extensions: Object.freeze([...category.match.extensions]) }\n : {}),\n ...(category.match.mimeTypes\n ? { mimeTypes: Object.freeze([...category.match.mimeTypes]) }\n : {}),\n }),\n }),\n ),\n);\nconst record = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\nfunction strings(\n value: unknown,\n normalize: (value: string) => string,\n): readonly string[] | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value))\n throw new TypeError(\"Crosswave category rules must be arrays of strings\");\n return Object.freeze([\n ...new Set(\n Array.from(value, (item: unknown) => {\n if (typeof item !== \"string\")\n throw new TypeError(\"Crosswave category rules must be arrays of strings\");\n return normalize(item);\n }),\n ),\n ]);\n}\nfunction extension(value: string): string {\n const normalized = value.toLowerCase().replace(/^\\./, \"\");\n if (!/^[a-z0-9+_-]+(?:\\.[a-z0-9+_-]+)*$/.test(normalized))\n throw new TypeError(`Invalid Crosswave extension: ${value}`);\n return normalized;\n}\nfunction mediaType(value: string): string {\n const normalized = value.toLowerCase();\n if (!/^[a-z0-9!#$&^_.+-]+\\/(?:[a-z0-9!#$&^_.+-]+|\\*|\\*\\+[a-z0-9!#$&^_.-]+)$/.test(normalized))\n throw new TypeError(`Invalid Crosswave MIME pattern: ${value}`);\n return normalized;\n}\nfunction match(value: unknown): CrosswaveCategoryMatch {\n if (value === \"all\" || value === \"folders\" || value === \"other\") return value;\n if (\n !record(value) ||\n Object.keys(value).some((key) => ![\"extensions\", \"mimeTypes\"].includes(key))\n )\n throw new TypeError(\"Crosswave match must be all, folders, other, or extension/MIME rules\");\n const extensions = strings(value.extensions, extension);\n const mimeTypes = strings(value.mimeTypes, mediaType);\n if (!extensions?.length && !mimeTypes?.length)\n throw new TypeError(\"Crosswave category needs at least one matching rule\");\n return Object.freeze({\n ...(extensions ? { extensions } : {}),\n ...(mimeTypes ? { mimeTypes } : {}),\n });\n}\nfunction icon(value: unknown, rule: CrosswaveCategoryMatch): string {\n if (value === undefined)\n return crosswaveIcon(rule === \"all\" ? \"all\" : rule === \"folders\" ? \"folder\" : \"other\");\n if (typeof value === \"string\" && isCrosswaveIcon(value)) return crosswaveIcon(value);\n if (\n record(value) &&\n typeof value.svg === \"string\" &&\n /^\\s*<svg\\b/i.test(value.svg) &&\n /<\\/svg>\\s*$/i.test(value.svg)\n )\n return value.svg.replace(/<svg\\b/i, '<svg aria-hidden=\"true\"');\n throw new TypeError(\"Crosswave icon must be a built-in icon name or trusted SVG markup\");\n}\nfunction isCrosswaveIcon(value: string): value is CrosswaveIcon {\n return [\n \"all\",\n \"folder\",\n \"image\",\n \"audio\",\n \"video\",\n \"document\",\n \"other\",\n \"link\",\n \"back\",\n \"search\",\n \"pause\",\n \"play\",\n \"controller\",\n ].includes(value);\n}\nexport function resolveCrosswaveCategories(value: unknown): readonly ResolvedCrosswaveCategory[] {\n const input = value === undefined ? defaultCrosswaveCategories : value;\n if (!Array.isArray(input) || !input.length)\n throw new TypeError(\"Crosswave categories must be a nonempty array\");\n const ids = new Set<string>();\n return Object.freeze(\n Array.from(input).map((category: unknown) => {\n if (\n !record(category) ||\n typeof category.id !== \"string\" ||\n !/^[a-z][a-z0-9_-]{0,63}$/.test(category.id)\n )\n throw new TypeError(\"Crosswave category id must be a lowercase identifier\");\n if (ids.has(category.id))\n throw new TypeError(`Duplicate Crosswave category id: ${category.id}`);\n ids.add(category.id);\n if (typeof category.label !== \"string\" || !category.label.trim())\n throw new TypeError(\"Crosswave category label must be nonempty\");\n const rule = match(category.match);\n return Object.freeze({\n id: category.id,\n label: category.label,\n svg: icon(category.icon, rule),\n match: rule,\n });\n }),\n );\n}\nexport function matchesMimeType(pattern: string, type: string): boolean {\n if (pattern === type) return true;\n const [group, subtype] = pattern.split(\"/\");\n if (!subtype || !type.startsWith(`${group}/`)) return false;\n return subtype === \"*\" || (subtype.startsWith(\"*+\") && type.endsWith(subtype.slice(1)));\n}\nexport function entryMimeType(entry: FileSystemEntry): string | null {\n if (entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\") return null;\n return (\n mime.getType(`./${entry.name}`) ??\n (entry.symlink?.targetKind === \"file\" &&\n !entry.symlink.isBroken &&\n !entry.symlink.isOutsideRoot &&\n !entry.symlink.isTargetExcluded &&\n entry.symlink.targetRelativePath\n ? mime.getType(`./${entry.symlink.targetRelativePath}`)\n : null)\n );\n}\nexport function entryCategory(entry: FileSystemEntry, type: string | null): CrosswaveIcon {\n if (entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\") return \"folder\";\n if (type?.startsWith(\"image/\")) return \"image\";\n if (type?.startsWith(\"audio/\") || type === \"application/vnd.apple.mpegurl\") return \"audio\";\n if (type?.startsWith(\"video/\")) return \"video\";\n if (type && documents.some((pattern) => matchesMimeType(pattern, type))) return \"document\";\n return \"other\";\n}\nexport function entryCategories(\n entry: FileSystemEntry,\n type: string | null,\n categories: readonly ResolvedCrosswaveCategory[],\n): readonly ResolvedCrosswaveCategory[] {\n const folder = entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n const filename = entry.name.toLowerCase();\n const specific = categories.filter(\n (category) =>\n typeof category.match !== \"string\" &&\n !folder &&\n (category.match.extensions?.some((ext) => filename.endsWith(`.${ext}`)) ||\n (type && category.match.mimeTypes?.some((pattern) => matchesMimeType(pattern, type)))),\n );\n return categories.filter(\n (category) =>\n category.match === \"all\" ||\n (folder && category.match === \"folders\") ||\n (!folder && category.match === \"other\" && !specific.length) ||\n specific.includes(category),\n );\n}\n","export const crosswaveStyles = String.raw`\n:root{color-scheme:dark;--cw-top:#2b4f85;--cw-bottom:#091a3a;--cw-text:#f7fbff;--cw-muted:#edf4fc;--cw-focus:#fff;--cw-ease:cubic-bezier(.16,1,.3,1);font-family:\"Segoe UI\",Helvetica,Arial,sans-serif;font-synthesis:none}\n:root[data-cw-color=violet]{--cw-top:#5c407d;--cw-bottom:#241532;--cw-muted:#edf4fc}:root[data-cw-color=amber]{--cw-top:#704d1a;--cw-bottom:#30200b;--cw-muted:#edf4fc}:root[data-cw-color=rose]{--cw-top:#704055;--cw-bottom:#351b29;--cw-muted:#edf4fc}:root[data-cw-color=jade]{--cw-top:#215e52;--cw-bottom:#0b2a28;--cw-muted:#edf4fc}:root[data-cw-color=graphite]{--cw-top:#424d61;--cw-bottom:#181d28;--cw-muted:#edf4fc}\n*{box-sizing:border-box}[hidden]{display:none!important}html{scroll-padding-bottom:110px;min-height:100%;background:var(--cw-bottom)}body{margin:0;color:var(--cw-text);background:var(--cw-bottom);min-height:100svh;font-size:16px;line-height:1.5}::selection{background:#d0e4fc;color:#132848}a{color:inherit;text-decoration:none}button,input,select{font:inherit;color:inherit}button,select{cursor:pointer}button{border:0}a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--cw-focus);outline-offset:6px;border-radius:3px}svg{display:block;width:24px;height:24px;flex:none}.cw-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.cw-skip{position:fixed;left:24px;top:16px;padding:12px 18px;background:#fff;color:#142e53;z-index:20;transform:translateY(-150%)}.cw-skip:focus{transform:none}\n.cw-background{position:fixed;inset:0;z-index:0;pointer-events:none;background:radial-gradient(ellipse at 28% 4%,#c0d7ff12,transparent 68%),linear-gradient(165deg,var(--cw-top),var(--cw-bottom));view-transition-name:cw-background;overflow:hidden}.cw-background canvas{width:100%;height:100%;display:block}.cw-fallback-wave{position:absolute;inset:0;width:100%;height:100%;fill:none;stroke:#d5e8ff;stroke-width:18;opacity:.06;filter:blur(3px)}.cw-background[data-ready] .cw-fallback-wave{display:none}\n.cw-top,.cw-stage,.cw-bottom,.cw-noscript{position:relative;z-index:1}.cw-top{display:flex;align-items:center;gap:32px;padding:30px 48px 20px;min-height:92px;view-transition-name:cw-header}.cw-brand{font-size:20px;font-weight:500;letter-spacing:-.02em;white-space:nowrap}.cw-brand span{display:block;font-size:12px;letter-spacing:.08em;color:var(--cw-muted);font-weight:400}.cw-location{display:flex;gap:12px;align-items:center;min-width:0;flex-wrap:wrap;font-size:13px;color:var(--cw-muted)}.cw-location a:hover{text-decoration:underline;text-underline-offset:5px}.cw-separator{opacity:.6}.cw-clock{margin-left:auto;white-space:nowrap;font-variant-numeric:tabular-nums;font-size:14px;color:var(--cw-muted)}\n.cw-stage{max-width:1500px;margin:28px auto 110px;padding:0 72px;display:grid;grid-template-columns:minmax(0,1.55fr) minmax(220px,1fr);column-gap:80px}.cw-rail{grid-column:1/-1;margin:0 -12px 22px;overflow-x:auto;padding:12px 12px 24px;scrollbar-width:none}.cw-rail::-webkit-scrollbar{display:none}.cw-categories{padding-left:104px;padding-right:calc(100vw - 200px);display:flex;gap:32px;min-width:max-content;align-items:flex-start}.cw-category{position:relative;display:flex;flex-direction:column;align-items:center;gap:12px;min-width:94px;min-height:96px;background:none;padding:10px 4px;color:var(--cw-muted);transition:color .2s,transform .3s var(--cw-ease);flex:none}.cw-category svg{width:40px;height:40px;filter:drop-shadow(0 3px 2px #07182a55)}.cw-category span{font-size:14px;white-space:nowrap}.cw-category small{font-size:10px;position:absolute;right:8px;top:0;color:var(--cw-muted)}.cw-category:hover{color:#fff;transform:translateY(-3px)}.cw-category.cw-active{color:#fff;transform:scale(1.1)}.cw-category.cw-active:after{content:\"\";position:absolute;left:calc(50% - 12px);bottom:-8px;width:24px;height:2px;background:#e6f2ff;box-shadow:0 3px 10px #bddaff55}\n.cw-browser{padding-left:104px;min-width:0;view-transition-name:cw-browser}.cw-list-head{display:flex;align-items:center;justify-content:space-between;gap:24px;min-height:58px;margin:0 0 12px;padding:0 4px}.cw-directory{display:flex;align-items:center;gap:16px;min-width:0}.cw-directory h2{font-size:17px;font-weight:400;margin:0;white-space:nowrap}.cw-directory>[data-cw-count]{font-size:12px;color:var(--cw-muted);white-space:nowrap}.cw-parent,.cw-parent-placeholder{width:34px;height:44px;display:flex;align-items:center;justify-content:center;flex:none}.cw-parent:hover{background:#ffffff12;border-radius:5px}.cw-search{display:flex;align-items:center;gap:8px;border-bottom:1px solid #d5e7ff55;min-width:140px;width:220px;padding:6px 0}.cw-search svg{width:17px;height:17px;color:var(--cw-muted)}.cw-search input{background:none;border:0;min-width:0;width:100%;outline-offset:3px;font-size:13px;caret-color:#fff}.cw-search input::placeholder{color:var(--cw-muted);opacity:1}.cw-search button{background:none;font-size:11px;padding:6px 0}.cw-search input::-webkit-search-cancel-button{display:none}\n.cw-list-scroll{max-height:calc(100svh - 365px);min-height:220px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:#d6e8fa66 transparent;padding:14px 12px 24px 8px;scroll-padding-block:20px;overscroll-behavior:contain}.cw-list-scroll::-webkit-scrollbar{width:5px}.cw-list-scroll::-webkit-scrollbar-thumb{background:#d6e8fa66;border-radius:4px}.cw-files{list-style:none;padding:0;margin:0}.cw-row{margin:0;min-height:76px;padding:0}.cw-entry{display:flex;align-items:center;gap:25px;padding:14px 18px;min-height:76px;transition:background .2s,opacity .2s,transform .3s var(--cw-ease);border-radius:7px}.cw-entry-icon{display:flex;width:42px;min-width:42px;align-items:center;justify-content:center;transition:transform .3s var(--cw-ease);filter:drop-shadow(0 3px 2px #08192c66)}.cw-entry-icon svg{width:34px;height:34px}.cw-entry-label{min-width:0;display:block}.cw-name{display:block;font-size:18px;font-weight:400;line-height:1.4;overflow-wrap:anywhere;text-shadow:0 2px 3px #07192b66}.cw-row-meta{display:block;font-size:11px;color:var(--cw-muted);margin-top:3px}.cw-row-date{display:none;font-size:11px;color:var(--cw-muted);margin-top:4px}.cw-target{display:block;font-size:11px;color:var(--cw-muted);overflow-wrap:anywhere;margin-top:4px}.cw-row.cw-selected .cw-entry{background:linear-gradient(90deg,#f4f9ff08,#f4f9ff04);transform:translateX(5px)}.cw-row.cw-selected .cw-entry-icon{transform:scale(1.22)}.cw-row.cw-selected .cw-name{color:#fff}.cw-row:hover .cw-entry{background:#eef7ff08}.cw-unavailable{color:var(--cw-muted)}.cw-notice{position:fixed;bottom:90px;left:48px;right:24px;z-index:3;margin:0;background:var(--cw-bottom);border-radius:4px;font-size:12px;color:var(--cw-muted);padding:12px 24px;max-width:52ch}.cw-empty{padding:36px 28px;color:var(--cw-muted);font-size:15px}.cw-detail{min-width:0;align-self:center;padding:20px 0 28px;view-transition-name:cw-detail}.cw-detail-symbol{margin-bottom:28px;filter:drop-shadow(0 12px 12px #04152e44)}.cw-detail-symbol svg{width:110px;height:110px;stroke-width:.8;opacity:.9}.cw-detail h2{font-size:25px;font-weight:300;margin:0 0 7px;overflow-wrap:anywhere;line-height:1.3;letter-spacing:-.015em}.cw-detail>p{font-size:12px;color:var(--cw-muted);margin:0 0 25px}.cw-detail dl{margin:0;font-size:12px}.cw-detail dl>div{display:grid;grid-template-columns:70px minmax(0,1fr);gap:10px;margin-bottom:10px}.cw-detail dt{color:var(--cw-muted)}.cw-detail dd{margin:0;overflow-wrap:anywhere}.cw-open{display:inline-flex;margin-top:24px;min-height:44px;align-items:center;font-size:14px;border-bottom:1px solid #e8f1ff88;gap:12px}.cw-open:hover{border-bottom-color:#fff}.cw-open:after{content:\"\";display:block;width:7px;height:7px;border-top:1px solid currentColor;border-right:1px solid currentColor;transform:rotate(45deg)}\n.cw-bottom{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:22px 48px;position:fixed;bottom:0;left:0;right:0;background:linear-gradient(0deg,var(--cw-bottom),transparent);min-height:82px}.cw-key-help{display:flex;gap:20px;flex-wrap:wrap;font-size:11px;color:var(--cw-muted)}kbd{font:inherit;color:#fff;margin-right:5px}.cw-appearance{display:flex;align-items:center;gap:16px;flex:none}.cw-appearance select{background:#0b193b70;border:1px solid #d7e9ff33;padding:7px 24px 7px 10px;border-radius:4px;max-width:116px;font-size:12px;min-height:36px}.cw-appearance button{display:flex;align-items:center;gap:8px;background:transparent;font-size:12px;min-height:40px;padding:6px 0}.cw-appearance button svg{width:16px;height:16px}.cw-controller{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--cw-muted)}.cw-controller svg{width:22px;height:22px}.cw-noscript{padding:0 48px 24px;color:var(--cw-muted);font-size:13px}\nbody:not([data-cw-enhanced]) .cw-browser{padding-left:0}body:not([data-cw-enhanced]) .cw-stage{display:block;margin-top:20px}body:not([data-cw-enhanced]) .cw-list-scroll{max-height:none;min-height:0}body:not([data-cw-enhanced]) .cw-parent-placeholder{display:none}body:not([data-cw-enhanced]) .cw-bottom{display:none}\n.cw-navigation-notice{position:fixed;z-index:5;left:48px;right:24px;bottom:90px;background:var(--cw-bottom);color:var(--cw-text);padding:12px 16px;border-radius:4px;font-size:13px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}.cw-navigation-notice[hidden]{display:none}.cw-navigation-notice button,.cw-navigation-notice a{background:none;min-height:44px;padding:6px 12px;border:1px solid var(--cw-muted);border-radius:4px}.cw-transition-layer *{animation:none!important;transition:none!important}.cw-transition-layer [data-cw-status]{display:none}\n@view-transition{navigation:auto}::view-transition-old(root),::view-transition-new(root){animation:none}::view-transition-old(cw-background),::view-transition-new(cw-background){animation-duration:420ms}::view-transition-old(cw-browser),::view-transition-old(cw-detail){animation:cw-out 180ms ease-in both}::view-transition-new(cw-browser),::view-transition-new(cw-detail){animation:cw-in 420ms var(--cw-ease) both}::view-transition-old(cw-header),::view-transition-new(cw-header){animation-duration:240ms}@keyframes cw-out{to{opacity:0;transform:translateX(-22px);filter:blur(3px)}}@keyframes cw-in{from{opacity:0;transform:translateX(32px);filter:blur(5px)}}:root[data-cw-transitions=false]::view-transition-group(*){animation-duration:0s}:root[data-cw-transitions=false]::view-transition-old(*),:root[data-cw-transitions=false]::view-transition-new(*){animation:none}.cw-category-changing .cw-files{animation:cw-category-in 260ms var(--cw-ease)}@keyframes cw-category-in{from{opacity:.35;transform:translateX(12px)}}\n@media(min-width:1500px){.cw-stage{margin-top:55px}.cw-categories{gap:48px}.cw-category{min-width:112px}.cw-list-scroll{max-height:calc(100svh - 405px)}}\n@media(max-width:1000px){.cw-top{padding:24px 30px;gap:22px}.cw-stage{padding:0 36px;column-gap:35px}.cw-categories{padding-left:44px;gap:20px}.cw-browser{padding-left:44px}.cw-category{min-width:82px}.cw-list-head{flex-wrap:wrap;gap:8px}.cw-search{margin-left:52px;width:calc(100% - 52px)}.cw-list-scroll{max-height:calc(100svh - 390px)}.cw-bottom{padding:20px 30px}.cw-key-help{gap:12px}.cw-detail h2{font-size:21px}.cw-clock{font-size:12px}}\n@media(max-width:680px){.cw-top{padding:22px 20px 12px;flex-wrap:wrap;gap:16px;min-height:104px}.cw-brand{font-size:18px}.cw-brand span{font-size:10px}.cw-clock{order:1;font-size:11px}.cw-location{order:2;width:100%;font-size:12px;gap:8px}.cw-stage{padding:0 16px;margin:12px auto 100px;display:block}.cw-rail{margin:0 -8px 8px;padding:12px 8px 20px}.cw-categories{padding-left:0;gap:14px}.cw-browser{padding-left:0}.cw-category{min-width:76px;min-height:83px;padding:10px 2px;gap:10px}.cw-category svg{width:33px;height:33px}.cw-category span{font-size:12px}.cw-category small{right:5px}.cw-list-head{padding:0 12px;margin-bottom:8px}.cw-directory{gap:12px}.cw-directory h2{font-size:16px}.cw-search{margin-left:46px;width:calc(100% - 46px);min-height:44px}.cw-search input,.cw-search button{min-height:44px}.cw-list-scroll{max-height:none;min-height:150px;padding:8px 2px 18px}.cw-entry{gap:20px;padding:14px 12px;min-height:78px}.cw-entry-icon{width:36px;min-width:36px}.cw-entry-icon svg{width:29px;height:29px}.cw-name{font-size:17px}.cw-detail{display:none!important}.cw-row-date{display:block}.cw-bottom{padding:16px 20px;min-height:76px;gap:12px}.cw-key-help{display:none!important}.cw-appearance{width:100%;justify-content:space-between}.cw-appearance select,.cw-appearance button{min-height:44px}.cw-controller{position:absolute;bottom:72px;right:20px;background:var(--cw-bottom);padding:6px 9px;border-radius:4px;max-width:calc(100% - 40px)}.cw-notice{left:20px;right:20px;bottom:90px}.cw-noscript{padding:0 24px 24px}}\n@media(max-height:520px){.cw-stage{grid-template-columns:minmax(0,1fr)}.cw-detail{display:none!important}.cw-row-date{display:block}.cw-top{flex-wrap:nowrap}.cw-location{order:0;width:auto}.cw-clock{display:none!important}.cw-top{min-height:58px;padding-top:10px;padding-bottom:8px}.cw-stage{margin-top:0;margin-bottom:70px}.cw-rail{margin-bottom:0;padding-bottom:12px}.cw-category{min-height:68px;gap:4px;padding-top:4px}.cw-category svg{width:28px;height:28px}.cw-list-scroll{max-height:calc(100svh - 235px);min-height:80px}.cw-list-head{min-height:36px;margin-bottom:0}.cw-detail-symbol{margin-bottom:10px}.cw-detail-symbol svg{width:60px;height:60px}.cw-detail>p{margin-bottom:12px}.cw-bottom{min-height:60px;padding-block:10px}.cw-list-head{flex-wrap:nowrap}.cw-search{margin-left:0;width:170px}.cw-detail dl>div{margin-bottom:4px}}\n@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation:none!important;transition:none!important}::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}.cw-row.cw-selected .cw-entry,.cw-category.cw-active,.cw-category:hover{transform:none}}\n`;\n","import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport type { ExplorerTheme, FileSystemEntry, ThemeContext } from \"./model.ts\";\nimport { escapeHtml } from \"./theme-default/components.ts\";\nimport { resolveThemeProject, type ThemeProjectOptions } from \"./theme-project.ts\";\nimport { crosswaveIcon } from \"./theme-crosswave/icons.ts\";\nimport {\n entryCategory,\n entryCategories,\n entryMimeType,\n resolveCrosswaveCategories,\n type CrosswaveCategory,\n type ResolvedCrosswaveCategory,\n} from \"./theme-crosswave/categories.ts\";\nexport { defaultCrosswaveCategories } from \"./theme-crosswave/categories.ts\";\nexport type {\n CrosswaveCategory,\n CrosswaveCategoryMatch,\n CrosswaveCategoryIcon,\n} from \"./theme-crosswave/categories.ts\";\nimport { crosswaveStyles } from \"./theme-crosswave/styles.ts\";\n\nconst runtime = await readFile(new URL(\"./crosswave-runtime.js\", import.meta.url), \"utf8\");\nfunction pageAsset(relativePath: string): string {\n return `crosswave-page-${createHash(\"sha256\").update(relativePath).digest(\"hex\").slice(0, 24)}.js`;\n}\nfunction navigationData(context: ThemeContext, relativePath: string, href: string): string {\n const asset = pageAsset(relativePath);\n const dataHref =\n context.mode === \"mpa\"\n ? context.assetHref(asset)\n : `${href.endsWith(\"/\") ? href : href.slice(0, href.lastIndexOf(\"/\") + 1)}${asset}`;\n return ` data-cw-page=\"${escapeHtml(dataHref)}\" data-cw-path=\"${escapeHtml(relativePath)}\"`;\n}\nexport type CrosswaveColor = \"azure\" | \"violet\" | \"amber\" | \"rose\" | \"jade\" | \"graphite\";\nexport interface CrosswaveThemeOptions {\n readonly categories?: readonly CrosswaveCategory[];\n readonly color?: CrosswaveColor;\n readonly backgroundMotion?: boolean;\n readonly pageTransitions?: boolean;\n readonly gamepad?: boolean;\n readonly project?: ThemeProjectOptions;\n}\n\nfunction size(bytes: number): string {\n const units = [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\"];\n const index = bytes > 0 ? Math.min(4, Math.floor(Math.log(bytes) / Math.log(1024))) : 0;\n return `${(bytes / 1024 ** index).toLocaleString(\"en-US\", { maximumFractionDigits: index === 0 ? 0 : 1 })} ${units[index]}`;\n}\nfunction status(entry: FileSystemEntry): string {\n if (entry.symlink?.isBroken) return \"Broken link · declared target only\";\n if (entry.symlink?.isOutsideRoot) return \"Outside root · declared target only\";\n if (entry.symlink?.isTargetExcluded) return \"Excluded target · declared target only\";\n if (entry.symlink?.isCycle) return \"Directory link · cycle\";\n if (entry.kind === \"symlink\") return \"Symbolic link\";\n if (entry.kind === \"directory\") return \"Folder\";\n return \"File\";\n}\nfunction row(\n entry: FileSystemEntry,\n context: ThemeContext,\n index: number,\n categories: readonly ResolvedCrosswaveCategory[],\n): string {\n const href = context.hrefFor(entry);\n const mimeType = entryMimeType(entry);\n const type = entryCategory(entry, mimeType);\n const matches = entryCategories(entry, mimeType, categories);\n const icon = entry.kind === \"symlink\" ? \"link\" : type;\n const svg = crosswaveIcon(icon);\n const label = `${entry.name}${type === \"folder\" ? \"/\" : \"\"}`;\n const unavailable =\n entry.symlink?.isOutsideRoot || entry.symlink?.isTargetExcluded || entry.symlink?.isBroken;\n const bytes = type === \"folder\" ? \"Folder\" : size(entry.metadata.size);\n const instant = new Date(entry.metadata.times.modifiedAt);\n const modified = Number.isNaN(instant.getTime())\n ? \"Unknown\"\n : `${instant.toISOString().slice(0, 16).replace(\"T\", \" \")} UTC`;\n const target =\n entry.symlink === null\n ? \"\"\n : `<span class=\"cw-target\">${escapeHtml(entry.symlink.target)}</span>`;\n const data = `data-cw-entry data-category=\"${type}\" data-cw-categories=\"${matches.map((category) => category.id).join(\" \")}\" data-icon=\"${icon}\" data-name=\"${escapeHtml(entry.name)}\" data-path=\"${escapeHtml(entry.relativePath)}\" data-size=\"${escapeHtml(bytes)}\" data-modified=\"${modified}\" data-status=\"${escapeHtml(status(entry))}\" data-exits=\"${context.exitsExplorerFor(entry)}\"`;\n const contents = `<span class=\"cw-entry-icon\">${svg}</span><span class=\"cw-entry-label\"><span class=\"cw-name\">${escapeHtml(label)}</span>${target}<span class=\"cw-row-meta\">${escapeHtml(status(entry))}${type === \"folder\" || unavailable ? \"\" : ` · ${bytes}`}</span><span class=\"cw-row-date\">Modified ${modified}</span></span>`;\n return `<li class=\"cw-row${index === 0 ? \" cw-selected\" : \"\"}\" ${data}>${href === null ? `<span class=\"cw-entry cw-unavailable\">${contents}</span>` : `<a class=\"cw-entry\" href=\"${escapeHtml(href)}\"${context.exitsExplorerFor(entry) ? ' target=\"_blank\" rel=\"noopener\"' : ` data-cw-navigation${navigationData(context, entry.symlink?.targetRelativePath ?? entry.relativePath, href)}`}>${contents}</a>`}</li>`;\n}\n\n/** PSP-inspired media navigation with original wave graphics and ordinary static links. */\nexport function createCrosswaveTheme(options: CrosswaveThemeOptions = {}): ExplorerTheme {\n const color = options.color ?? \"azure\";\n if (![\"azure\", \"violet\", \"amber\", \"rose\", \"jade\", \"graphite\"].includes(color))\n throw new TypeError(\"Unknown Crosswave color\");\n for (const key of [\"backgroundMotion\", \"pageTransitions\", \"gamepad\"] as const) {\n if (options[key] !== undefined && typeof options[key] !== \"boolean\")\n throw new TypeError(`${key} must be a boolean`);\n }\n const project = resolveThemeProject(options.project);\n const categories = resolveCrosswaveCategories(options.categories);\n const categoryConfig = createHash(\"sha256\")\n .update(JSON.stringify(categories))\n .digest(\"hex\")\n .slice(0, 24);\n return {\n name: \"crosswave\",\n searchIndex: false,\n render(context) {\n const { directory } = context;\n const path = directory.current.relativePath || \"/\";\n const parent =\n directory.parent === null ? null : context.hrefForDirectory(directory.parent.relativePath);\n const segments = directory.current.relativePath.split(\"/\").filter(Boolean);\n const breadcrumbs = segments\n .map((segment, i) =>\n i === segments.length - 1\n ? `<span aria-current=\"page\">${escapeHtml(segment)}</span>`\n : `<a data-cw-navigation${navigationData(context, segments.slice(0, i + 1).join(\"/\"), context.hrefForDirectory(segments.slice(0, i + 1).join(\"/\")))} href=\"${escapeHtml(context.hrefForDirectory(segments.slice(0, i + 1).join(\"/\")))}\">${escapeHtml(segment)}</a>`,\n )\n .join('<span class=\"cw-separator\" aria-hidden=\"true\">/</span>');\n const tabs = categories\n .map(\n ({ id: type, label, svg }, i) =>\n `<button role=\"tab\" id=\"cw-tab-${type}\" aria-selected=\"${i === 0}\" aria-controls=\"cw-panel\" tabindex=\"${i === 0 ? 0 : -1}\" data-cw-category=\"${type}\" class=\"cw-category${i === 0 ? \" cw-active\" : \"\"}\">${svg}<span>${escapeHtml(label)}</span><small data-cw-category-count=\"${type}\"></small></button>`,\n )\n .join(\"\");\n const html = `<!doctype html>\n<html lang=\"en\" data-cw-color=\"${color}\" data-cw-motion=\"${options.backgroundMotion !== false}\" data-cw-transitions=\"${options.pageTransitions !== false}\" data-cw-gamepad=\"${options.gamepad !== false}\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">${context.documentBaseHref === null ? \"\" : `<base href=\"${escapeHtml(context.documentBaseHref)}\">`}<title>${escapeHtml(path)} · ${escapeHtml(project.name)} · Crosswave</title><link rel=\"stylesheet\" href=\"${escapeHtml(context.assetHref(\"crosswave.css\"))}\"><script src=\"${escapeHtml(context.assetHref(\"crosswave.js\"))}\"></script></head>\n<body><a class=\"cw-skip\" href=\"${escapeHtml(context.hrefForDirectory(directory.current.relativePath))}#cw-panel\">Skip to files</a><div class=\"cw-background\" aria-hidden=\"true\"><canvas id=\"cw-wave\"></canvas><svg class=\"cw-fallback-wave\" viewBox=\"0 0 1440 900\" preserveAspectRatio=\"none\"><path d=\"M-80 570C220 300 370 690 760 450S1260 290 1510 510\"/><path d=\"M-80 540C220 640 400 330 820 470S1240 620 1510 360\"/></svg></div>\n<header class=\"cw-top\"><a class=\"cw-brand\" data-cw-navigation${navigationData(context, \"\", context.hrefForDirectory(\"\"))} href=\"${escapeHtml(context.hrefForDirectory(\"\"))}\">${escapeHtml(project.name)}<span>Crosswave</span></a><nav class=\"cw-location\" aria-label=\"Location\"><a data-cw-navigation${navigationData(context, \"\", context.hrefForDirectory(\"\"))} href=\"${escapeHtml(context.hrefForDirectory(\"\"))}\">Home</a>${breadcrumbs ? `<span class=\"cw-separator\" aria-hidden=\"true\">/</span>${breadcrumbs}` : \"\"}</nav><time class=\"cw-clock\" aria-label=\"Current time\" hidden></time></header>\n<main class=\"cw-stage\" data-cw-root=\"${escapeHtml(context.hrefForDirectory(\"\"))}\" data-cw-path=\"${escapeHtml(directory.current.relativePath)}\" data-cw-page=\"${escapeHtml(context.assetHref(pageAsset(directory.current.relativePath)))}\" data-cw-file=\"${escapeHtml(context.outputName)}\" data-cw-category-config=\"${categoryConfig}\"><h1 class=\"cw-sr\">Files in ${escapeHtml(path)}</h1><nav class=\"cw-rail\" aria-label=\"File categories\" hidden><div class=\"cw-categories\" role=\"tablist\" aria-label=\"File categories\">${tabs}</div></nav>\n<section class=\"cw-browser\" id=\"cw-panel\" aria-label=\"Files\"><div class=\"cw-list-head\"><div class=\"cw-directory\">${parent === null ? '<span class=\"cw-parent-placeholder\"></span>' : `<a class=\"cw-parent\" data-cw-parent data-cw-navigation${navigationData(context, directory.parent?.relativePath ?? \"\", parent)} href=\"${escapeHtml(parent)}\" aria-label=\"Parent directory\">${crosswaveIcon(\"back\")}</a>`}<h2 data-cw-heading>All files</h2><span data-cw-count>${directory.entries.length} items</span></div><div class=\"cw-search\" hidden>${crosswaveIcon(\"search\")}<label class=\"cw-sr\" for=\"cw-search\">Search this folder</label><input id=\"cw-search\" type=\"search\" placeholder=\"Search this folder\" autocomplete=\"off\" spellcheck=\"false\"><button type=\"button\" data-cw-clear hidden>Clear</button></div></div>\n<div class=\"cw-list-scroll\"><ul class=\"cw-files\">${directory.entries.map((entry, i) => row(entry, context, i, categories)).join(\"\")}</ul><p class=\"cw-empty\" data-cw-empty${directory.entries.length ? \" hidden\" : \"\"}>${directory.entries.length ? \"No files match this search.\" : \"This folder is empty.\"}</p></div><p class=\"cw-sr\" role=\"status\" aria-live=\"polite\" data-cw-status></p></section>\n<aside class=\"cw-detail\" aria-label=\"Selected file details\" hidden><div class=\"cw-detail-symbol\" data-cw-detail-icon></div><h2 data-cw-detail-name></h2><p data-cw-detail-status></p><dl><div><dt>Path</dt><dd data-cw-detail-path></dd></div><div><dt>Size</dt><dd data-cw-detail-size></dd></div><div><dt>Modified</dt><dd data-cw-detail-modified></dd></div></dl><a class=\"cw-open\" data-cw-open>Open file</a></aside></main>\n<footer class=\"cw-bottom\"><span class=\"cw-key-help\" hidden><span><kbd>Arrow keys</kbd> Navigate</span><span><kbd>Enter</kbd> Open</span><span><kbd>Backspace</kbd> Parent</span></span><span class=\"cw-controller\" role=\"status\" hidden>${crosswaveIcon(\"controller\")}<span data-cw-controller-label></span></span><div class=\"cw-appearance\" hidden><label class=\"cw-sr\" for=\"cw-color\">Background color</label><select id=\"cw-color\" aria-label=\"Background color\"><option value=\"azure\">Azure</option><option value=\"violet\">Violet</option><option value=\"amber\">Amber</option><option value=\"rose\">Rose</option><option value=\"jade\">Jade</option><option value=\"graphite\">Graphite</option></select><button data-cw-motion aria-pressed=\"false\" type=\"button\">${crosswaveIcon(\"pause\")}<span>Pause waves</span></button></div></footer><noscript><p class=\"cw-noscript\">All files are available below. Categories, search, animated waves, and controller navigation need JavaScript.</p></noscript></body></html>`;\n const asset = pageAsset(directory.current.relativePath);\n const payload = JSON.stringify({\n version: 1,\n id: asset,\n path: directory.current.relativePath,\n outputName: context.outputName,\n baseHref: context.documentBaseHref,\n html,\n });\n return {\n html,\n assets: {\n \"crosswave.css\": crosswaveStyles,\n \"crosswave.js\": runtime,\n [asset]: `window.dispatchEvent(new CustomEvent(\"dirwell:crosswave-page\",{detail:${payload}}));`,\n },\n };\n },\n };\n}\n","import type { ExplorerTheme, FileSystemEntry, ThemeContext } from \"./model.ts\";\nimport { resolveThemeProject, type ThemeProjectOptions } from \"./theme-project.ts\";\nimport { utcTimestamp } from \"./timestamp.ts\";\n\nexport interface PlainThemeOptions {\n readonly project?: ThemeProjectOptions;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n}\n\nfunction externalHref(value: string | undefined): string | null {\n if (value === undefined) return null;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" || url.protocol === \"http:\" ? url.href : null;\n } catch {\n return null;\n }\n}\n\nfunction externalLink(label: string, href: string | null): string {\n return href === null\n ? escapeHtml(label)\n : `<a href=\"${escapeHtml(href)}\" target=\"_blank\" rel=\"noopener\">${escapeHtml(label)}</a>`;\n}\n\nfunction status(entry: FileSystemEntry): string {\n if (entry.symlink?.isBroken) return \"broken link\";\n if (entry.symlink?.isOutsideRoot) return \"unavailable link\";\n if (entry.symlink?.isCycle) return \"cycle link\";\n if (entry.kind === \"symlink\") return \"link\";\n if (entry.kind === \"directory\") return \"folder\";\n return \"file\";\n}\n\nfunction renderBreadcrumbs(context: ThemeContext): string {\n const segments = context.directory.current.relativePath.split(\"/\").filter(Boolean);\n if (segments.length === 0) return \"\";\n const ancestors = segments.slice(0, -1).map((segment, index) => {\n const relativePath = segments.slice(0, index + 1).join(\"/\");\n return `<a href=\"${escapeHtml(context.hrefForDirectory(relativePath))}\">${escapeHtml(segment)}</a>`;\n });\n return `<nav aria-label=\"Breadcrumb\"><a href=\"${escapeHtml(context.hrefForDirectory(\"\"))}\">Home</a> / ${ancestors.length === 0 ? \"\" : `${ancestors.join(\" / \")} / `}<span aria-current=\"page\">${escapeHtml(segments.at(-1) ?? \"\")}</span></nav>`;\n}\n\nfunction renderEntry(entry: FileSystemEntry, context: ThemeContext): string {\n const directoryLike = entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n const name = `${escapeHtml(entry.name)}${directoryLike ? \"/\" : \"\"}`;\n const href = context.hrefFor(entry);\n const exitsExplorer = context.exitsExplorerFor(entry);\n const link =\n href === null\n ? `<span>${name}</span>`\n : `<a href=\"${escapeHtml(href)}\"${exitsExplorer ? ' target=\"_blank\" rel=\"noopener\"' : \"\"}>${name}</a>`;\n const details = [status(entry)];\n if (!directoryLike) details.push(`${entry.metadata.size} B`);\n if (entry.symlink !== null) details.push(`Target: ${entry.symlink.target}`);\n if (exitsExplorer) details.push(\"opens in a new tab\");\n const modified = utcTimestamp(entry.metadata.times.modifiedAt);\n return `<li>${link}<small>${escapeHtml(details.join(\" · \"))} · ${modified === null ? \"Unknown\" : `<time datetime=\"${modified.datetime}\">${modified.label}</time>`}</small></li>`;\n}\n\n/** A no-icon, no-script theme that leaves navigation and controls to the browser. */\nexport function createPlainTheme(options: PlainThemeOptions = {}): ExplorerTheme {\n const project = resolveThemeProject(options.project);\n const repositoryHref = externalHref(project.repositoryUrl);\n const licenseHref = externalHref(\n options.project?.licenseUrl ??\n (options.project?.license === undefined ? \"https://opensource.org/license/mit\" : undefined),\n );\n return {\n name: \"plain\",\n searchIndex: false,\n render(context) {\n const visiblePath =\n context.directory.current.relativePath === \"\"\n ? \"/\"\n : `/${context.directory.current.relativePath}/`;\n const parentHref =\n context.directory.parent === null\n ? null\n : context.hrefForDirectory(context.directory.parent.relativePath);\n const parentRow =\n parentHref === null ? \"\" : `<li><a href=\"${escapeHtml(parentHref)}\">../</a></li>`;\n const rows = context.directory.entries.map((entry) => renderEntry(entry, context)).join(\"\");\n const count = context.directory.entries.length;\n const footer = `<footer><hr><p>Repository: ${externalLink(project.name, repositoryHref)} by ${escapeHtml(project.author)} · ${externalLink(project.license, licenseHref)}</p></footer>`;\n const html = `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta name=\"color-scheme\" content=\"light\">${context.documentBaseHref === null ? \"\" : `<base href=\"${escapeHtml(context.documentBaseHref)}\">`}<title>Index of ${escapeHtml(visiblePath)}</title><style>body{max-width:72ch;margin:2rem auto;padding:0 1rem}h1,li,nav{overflow-wrap:anywhere}li{margin:.6rem 0}small{display:block}</style></head>\n<body><main>${renderBreadcrumbs(context)}<h1>Index of ${escapeHtml(visiblePath)}</h1><p>${count} ${count === 1 ? \"entry\" : \"entries\"}</p>${count === 0 ? \"<p>This directory is empty.</p>\" : \"\"}<ul>${parentRow}${rows}</ul></main>${footer}</body></html>`;\n return { html };\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vp-tw/dirwell",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
4
|
"description": "A static file explorer with safe symlinks, themes, and live reload.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directory-index",
|
|
@@ -106,6 +106,7 @@
|
|
|
106
106
|
"citty": "0.2.2",
|
|
107
107
|
"giget": "3.3.1",
|
|
108
108
|
"jiti": "2.7.0",
|
|
109
|
+
"mime": "4.1.0",
|
|
109
110
|
"picomatch": "4.0.7",
|
|
110
111
|
"unplugin": "3.4.0"
|
|
111
112
|
},
|