@vp-tw/dirwell 0.1.0-alpha.5 → 0.1.0-alpha.6
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/dist/bin.mjs +1 -1
- package/dist/crosswave-runtime.js +514 -14
- package/dist/index.mjs +30 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @vp-tw/dirwell
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.6
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Keep Crosswave's background and controls alive while directory contents slide in opposite directions for entry and return. Generated navigation assets support SSG/MPA, custom URLs, static hosting, build adapters, and relative local HTML. Restore browsing state through history and handle cancelled, superseded, unavailable, incompatible, restricted, and timed-out loads with usable recovery controls.
|
|
8
|
+
|
|
3
9
|
## 0.1.0-alpha.5
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/bin.mjs
CHANGED
|
@@ -12,6 +12,437 @@
|
|
|
12
12
|
if (reduced.matches || root.dataset.cwTransitions === "false") transition.skipTransition();
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
|
+
// Included in the theme's private runtime closure, never installed as a global API.
|
|
16
|
+
function createCrosswaveNavigator({ main, reduced, getState, commit }) {
|
|
17
|
+
const rootUrl = new URL(main.dataset.cwRoot, document.baseURI);
|
|
18
|
+
const initialUrl = new URL(location.href);
|
|
19
|
+
initialUrl.hash = "";
|
|
20
|
+
const local = initialUrl.protocol === "file:";
|
|
21
|
+
const owner = rootUrl.href;
|
|
22
|
+
const scopePath = rootUrl.pathname.endsWith("/")
|
|
23
|
+
? rootUrl.pathname
|
|
24
|
+
: new URL(".", rootUrl).pathname;
|
|
25
|
+
let current = {
|
|
26
|
+
url: initialUrl.href,
|
|
27
|
+
asset: new URL(main.dataset.cwPage, document.baseURI).href,
|
|
28
|
+
path: main.dataset.cwPath,
|
|
29
|
+
};
|
|
30
|
+
const recent = new Map();
|
|
31
|
+
const historyViews = new Map();
|
|
32
|
+
const savedEntry = history.state?.dirwellCrosswave;
|
|
33
|
+
let index =
|
|
34
|
+
savedEntry?.owner === owner && Number.isInteger(savedEntry.index) ? savedEntry.index : 0,
|
|
35
|
+
request = 0,
|
|
36
|
+
pending,
|
|
37
|
+
animations = [];
|
|
38
|
+
const notice = document.createElement("div");
|
|
39
|
+
notice.className = "cw-navigation-notice";
|
|
40
|
+
notice.setAttribute("role", "status");
|
|
41
|
+
notice.hidden = true;
|
|
42
|
+
document.body.append(notice);
|
|
43
|
+
const allowed = (url) =>
|
|
44
|
+
url.protocol === rootUrl.protocol &&
|
|
45
|
+
url.host === rootUrl.host &&
|
|
46
|
+
url.pathname.startsWith(scopePath);
|
|
47
|
+
const absoluteLinks = (scope, base) => {
|
|
48
|
+
for (const link of scope.querySelectorAll("a[href]")) {
|
|
49
|
+
const url = new URL(link.getAttribute("href"), base);
|
|
50
|
+
if (local && url.pathname.endsWith("/")) url.pathname += "index.html";
|
|
51
|
+
link.href = url.href;
|
|
52
|
+
}
|
|
53
|
+
for (const link of scope.querySelectorAll("[data-cw-page]"))
|
|
54
|
+
link.dataset.cwPage = new URL(link.dataset.cwPage, base).href;
|
|
55
|
+
};
|
|
56
|
+
absoluteLinks(document, document.baseURI);
|
|
57
|
+
const ownsCurrentEntry = () => {
|
|
58
|
+
const entry = history.state?.dirwellCrosswave;
|
|
59
|
+
return entry?.owner === owner && entry.index === index && entry.route.path === current.path;
|
|
60
|
+
};
|
|
61
|
+
const state = (route = current, position = index, view = getState()) => ({
|
|
62
|
+
...history.state,
|
|
63
|
+
dirwellCrosswave: { owner, route, index: position, view },
|
|
64
|
+
});
|
|
65
|
+
try {
|
|
66
|
+
history.replaceState(state(), "");
|
|
67
|
+
} catch {
|
|
68
|
+
return;
|
|
69
|
+
} // Restricted hosts retain ordinary links and native transitions.
|
|
70
|
+
|
|
71
|
+
const stopAnimations = () => {
|
|
72
|
+
for (const { animation, layer } of animations) {
|
|
73
|
+
animation.cancel();
|
|
74
|
+
layer?.remove();
|
|
75
|
+
}
|
|
76
|
+
animations = [];
|
|
77
|
+
};
|
|
78
|
+
const show = (text, actions = []) => {
|
|
79
|
+
notice.replaceChildren(document.createTextNode(text));
|
|
80
|
+
for (const { label, run, href } of actions) {
|
|
81
|
+
const action = document.createElement(href ? "a" : "button");
|
|
82
|
+
action.textContent = label;
|
|
83
|
+
if (href) action.href = href;
|
|
84
|
+
else {
|
|
85
|
+
action.type = "button";
|
|
86
|
+
action.addEventListener("click", run);
|
|
87
|
+
}
|
|
88
|
+
notice.append(action);
|
|
89
|
+
}
|
|
90
|
+
notice.hidden = false;
|
|
91
|
+
};
|
|
92
|
+
const load = (route, signal) =>
|
|
93
|
+
new Promise((resolve, reject) => {
|
|
94
|
+
const asset = new URL(route.asset);
|
|
95
|
+
if (
|
|
96
|
+
!allowed(asset) ||
|
|
97
|
+
!/^crosswave-page-[a-f0-9]{24}\.js$/.test(asset.pathname.split("/").at(-1))
|
|
98
|
+
) {
|
|
99
|
+
reject(new Error("Unsupported folder data"));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const script = document.createElement("script");
|
|
103
|
+
script.async = true;
|
|
104
|
+
// Revalidate on every visit: a watcher may have rebuilt this directory.
|
|
105
|
+
asset.searchParams.set("cw-visit", String(Date.now()));
|
|
106
|
+
script.src = asset.href;
|
|
107
|
+
let payload;
|
|
108
|
+
const received = (event) => {
|
|
109
|
+
if (document.currentScript !== script) return;
|
|
110
|
+
const data = event.detail;
|
|
111
|
+
if (
|
|
112
|
+
data?.version === 1 &&
|
|
113
|
+
data.id === asset.pathname.split("/").at(-1) &&
|
|
114
|
+
data.path === route.path &&
|
|
115
|
+
typeof data.html === "string" &&
|
|
116
|
+
typeof data.outputName === "string" &&
|
|
117
|
+
(data.baseHref === null || typeof data.baseHref === "string")
|
|
118
|
+
)
|
|
119
|
+
payload = data;
|
|
120
|
+
};
|
|
121
|
+
const cleanup = () => {
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
removeEventListener("dirwell:crosswave-page", received);
|
|
124
|
+
signal.removeEventListener("abort", aborted);
|
|
125
|
+
script.remove();
|
|
126
|
+
script.onload = script.onerror = null;
|
|
127
|
+
};
|
|
128
|
+
const fail = (error) => {
|
|
129
|
+
cleanup();
|
|
130
|
+
reject(error);
|
|
131
|
+
};
|
|
132
|
+
const aborted = () => fail(new DOMException("Navigation superseded", "AbortError"));
|
|
133
|
+
const timer = setTimeout(() => fail(new Error("Folder loading timed out")), 8000);
|
|
134
|
+
script.onerror = () => fail(new Error("Folder data is unavailable"));
|
|
135
|
+
script.onload = () => {
|
|
136
|
+
cleanup();
|
|
137
|
+
try {
|
|
138
|
+
if (!payload) {
|
|
139
|
+
reject(new Error("Folder data is incompatible"));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const doc = new DOMParser().parseFromString(payload.html, "text/html");
|
|
143
|
+
const required = [
|
|
144
|
+
"[data-cw-root]",
|
|
145
|
+
".cw-files",
|
|
146
|
+
".cw-directory",
|
|
147
|
+
".cw-location",
|
|
148
|
+
".cw-stage > h1",
|
|
149
|
+
".cw-directory > :first-child",
|
|
150
|
+
".cw-empty",
|
|
151
|
+
];
|
|
152
|
+
if (required.some((selector) => !doc.querySelector(selector))) {
|
|
153
|
+
reject(new Error("Folder data is incomplete"));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const url = new URL(route.url);
|
|
157
|
+
if (local && url.pathname.endsWith("/")) url.pathname += payload.outputName;
|
|
158
|
+
const base = new URL(payload.baseHref ?? ".", url);
|
|
159
|
+
absoluteLinks(doc, base);
|
|
160
|
+
resolve({ doc, route: { ...route, url: url.href } });
|
|
161
|
+
} catch (error) {
|
|
162
|
+
reject(error);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
addEventListener("dirwell:crosswave-page", received);
|
|
166
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
167
|
+
try {
|
|
168
|
+
document.head.append(script);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
fail(error);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
const address = (route) => {
|
|
174
|
+
if (!local) return route.url;
|
|
175
|
+
const url = new URL(initialUrl);
|
|
176
|
+
const relative = (value) => new URL(value).pathname.slice(scopePath.length);
|
|
177
|
+
url.hash = `cw=${encodeURIComponent(relative(route.url))}&cw-data=${encodeURIComponent(relative(route.asset))}&cw-path=${encodeURIComponent(route.path)}`;
|
|
178
|
+
return url.href;
|
|
179
|
+
};
|
|
180
|
+
const snapshot = (element) => {
|
|
181
|
+
const rect = element.getBoundingClientRect();
|
|
182
|
+
const viewportBottom = Math.min(
|
|
183
|
+
innerHeight,
|
|
184
|
+
document.querySelector(".cw-bottom").getBoundingClientRect().top,
|
|
185
|
+
);
|
|
186
|
+
const clone = element.cloneNode(false);
|
|
187
|
+
if (element.matches(".cw-browser")) {
|
|
188
|
+
clone.append(element.querySelector(".cw-list-head").cloneNode(true));
|
|
189
|
+
const source = element.querySelector(".cw-list-scroll");
|
|
190
|
+
const scroll = source.cloneNode(false),
|
|
191
|
+
list = element.querySelector(".cw-files").cloneNode(false);
|
|
192
|
+
const filesRect = element.querySelector(".cw-files").getBoundingClientRect();
|
|
193
|
+
const sourceRect = source.getBoundingClientRect();
|
|
194
|
+
const visibleTop = Math.max(0, sourceRect.top);
|
|
195
|
+
const visibleBottom = Math.min(viewportBottom, sourceRect.bottom);
|
|
196
|
+
list.style.cssText = `position:relative;height:${source.scrollHeight}px`;
|
|
197
|
+
// Clone visible rows only; large directories must not duplicate their entire DOM for motion.
|
|
198
|
+
for (const row of element.querySelectorAll(".cw-row:not([hidden])")) {
|
|
199
|
+
const rowRect = row.getBoundingClientRect();
|
|
200
|
+
if (rowRect.bottom < visibleTop) continue;
|
|
201
|
+
if (rowRect.top > visibleBottom) break;
|
|
202
|
+
const copy = row.cloneNode(true);
|
|
203
|
+
copy.style.cssText = `position:absolute;left:0;right:0;top:${rowRect.top - filesRect.top}px;width:100%`;
|
|
204
|
+
list.append(copy);
|
|
205
|
+
}
|
|
206
|
+
scroll.append(list, element.querySelector(".cw-empty").cloneNode(true));
|
|
207
|
+
clone.append(scroll);
|
|
208
|
+
clone._scroll = { element: scroll, top: source.scrollTop };
|
|
209
|
+
} else clone.replaceChildren(...[...element.childNodes].map((node) => node.cloneNode(true)));
|
|
210
|
+
for (const node of [clone, ...clone.querySelectorAll("*")]) {
|
|
211
|
+
for (const name of node.getAttributeNames())
|
|
212
|
+
if (name.startsWith("data-cw-")) node.removeAttribute(name);
|
|
213
|
+
}
|
|
214
|
+
clone.removeAttribute("id");
|
|
215
|
+
clone.querySelectorAll("[id]").forEach((node) => node.removeAttribute("id"));
|
|
216
|
+
clone.setAttribute("aria-hidden", "true");
|
|
217
|
+
clone.inert = true;
|
|
218
|
+
clone.classList.add("cw-transition-layer");
|
|
219
|
+
clone.style.cssText = `position:fixed;left:${rect.left}px;top:${rect.top}px;width:${rect.width}px;height:${rect.height}px;margin:0;z-index:3;pointer-events:none;view-transition-name:none`;
|
|
220
|
+
clone.style.overflow = "hidden";
|
|
221
|
+
clone.style.clipPath = `inset(${Math.max(0, -rect.top)}px 0 ${Math.max(0, rect.bottom - viewportBottom)}px 0)`;
|
|
222
|
+
document.body.append(clone);
|
|
223
|
+
if (clone._scroll) clone._scroll.element.scrollTop = clone._scroll.top;
|
|
224
|
+
return clone;
|
|
225
|
+
};
|
|
226
|
+
const exchange = (loaded, view, direction, writeHistory, position) => {
|
|
227
|
+
const moving =
|
|
228
|
+
!reduced.matches &&
|
|
229
|
+
document.documentElement.dataset.cwTransitions !== "false" &&
|
|
230
|
+
typeof main.animate === "function";
|
|
231
|
+
stopAnimations();
|
|
232
|
+
const elements = [
|
|
233
|
+
document.querySelector(".cw-browser"),
|
|
234
|
+
document.querySelector(".cw-detail"),
|
|
235
|
+
];
|
|
236
|
+
const layers = moving
|
|
237
|
+
? elements.map((element) =>
|
|
238
|
+
element.hidden || !element.getBoundingClientRect().height ? null : snapshot(element),
|
|
239
|
+
)
|
|
240
|
+
: [];
|
|
241
|
+
try {
|
|
242
|
+
if (writeHistory) {
|
|
243
|
+
// Save the view at departure, including edits made while a request was pending.
|
|
244
|
+
if (ownsCurrentEntry()) history.replaceState(state(), "");
|
|
245
|
+
history.pushState(state(loaded.route, position, view ?? {}), "", address(loaded.route));
|
|
246
|
+
for (const key of historyViews.keys())
|
|
247
|
+
if (key > position || key < position - history.length + 1) historyViews.delete(key);
|
|
248
|
+
}
|
|
249
|
+
commit(loaded.doc, view);
|
|
250
|
+
current = loaded.route;
|
|
251
|
+
index = position;
|
|
252
|
+
historyViews.set(index, { path: current.path, view: getState() });
|
|
253
|
+
try {
|
|
254
|
+
history.replaceState(state(), "");
|
|
255
|
+
} catch {}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
layers.forEach((layer) => layer?.remove());
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
if (!moving) return;
|
|
261
|
+
try {
|
|
262
|
+
elements.forEach((element, i) => {
|
|
263
|
+
const layer = layers[i];
|
|
264
|
+
if (layer) {
|
|
265
|
+
const animation = layer.animate(
|
|
266
|
+
[
|
|
267
|
+
{ opacity: 1, transform: "translateX(0)" },
|
|
268
|
+
{ opacity: 0, transform: `translateX(${-direction * 24}px)` },
|
|
269
|
+
],
|
|
270
|
+
{ duration: 140, easing: "ease-out" },
|
|
271
|
+
);
|
|
272
|
+
animations.push({ animation, layer });
|
|
273
|
+
animation.finished.then(
|
|
274
|
+
() => layer.remove(),
|
|
275
|
+
() => layer.remove(),
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (!element.hidden) {
|
|
279
|
+
const animation = element.animate(
|
|
280
|
+
[
|
|
281
|
+
{ opacity: 0.25, transform: `translateX(${direction * 28}px)` },
|
|
282
|
+
{ opacity: 1, transform: "translateX(0)" },
|
|
283
|
+
],
|
|
284
|
+
{ duration: 240, easing: "cubic-bezier(.16,1,.3,1)" },
|
|
285
|
+
);
|
|
286
|
+
animations.push({ animation });
|
|
287
|
+
animation.finished.catch(() => {});
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
} catch {
|
|
291
|
+
stopAnimations();
|
|
292
|
+
layers.forEach((layer) => layer?.remove());
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const navigate = async (route, { view, pop = false, position = index + 1 } = {}) => {
|
|
296
|
+
historyViews.set(index, { path: current.path, view: getState() });
|
|
297
|
+
if (pop && historyViews.get(position)?.path === route.path)
|
|
298
|
+
view = historyViews.get(position).view;
|
|
299
|
+
const ticket = ++request;
|
|
300
|
+
pending?.abort();
|
|
301
|
+
const controller = new AbortController();
|
|
302
|
+
pending = controller;
|
|
303
|
+
if (!pop) {
|
|
304
|
+
const entry = history.state?.dirwellCrosswave;
|
|
305
|
+
position =
|
|
306
|
+
(entry?.owner === owner && Number.isInteger(entry.index) ? entry.index : index) + 1;
|
|
307
|
+
recent.delete(current.path);
|
|
308
|
+
recent.set(current.path, getState());
|
|
309
|
+
if (recent.size > 32) recent.delete(recent.keys().next().value);
|
|
310
|
+
if (view === undefined) view = recent.get(route.path);
|
|
311
|
+
try {
|
|
312
|
+
if (ownsCurrentEntry()) history.replaceState(state(), "");
|
|
313
|
+
} catch {
|
|
314
|
+
location.assign(route.url);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const direction = pop
|
|
319
|
+
? Math.sign(position - index) || 1
|
|
320
|
+
: current.path && (current.path.startsWith(`${route.path}/`) || route.path === "")
|
|
321
|
+
? -1
|
|
322
|
+
: 1;
|
|
323
|
+
main.setAttribute("aria-busy", "true");
|
|
324
|
+
const delay = setTimeout(() => {
|
|
325
|
+
if (ticket === request)
|
|
326
|
+
show("Loading folder…", [{ label: "Cancel", run: () => controller.abort() }]);
|
|
327
|
+
}, 120);
|
|
328
|
+
try {
|
|
329
|
+
const loaded = await load(route, controller.signal);
|
|
330
|
+
if (ticket !== request || controller.signal.aborted) return;
|
|
331
|
+
exchange(loaded, view, direction, !pop, position);
|
|
332
|
+
notice.hidden = true;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (ticket !== request) return;
|
|
335
|
+
if (pop || !ownsCurrentEntry()) {
|
|
336
|
+
// The browser has already changed its address: reload that exact destination on failure.
|
|
337
|
+
if (local) location.replace(route.url);
|
|
338
|
+
else location.reload();
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (error.name === "AbortError") {
|
|
342
|
+
notice.hidden = true;
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
show("Could not open this folder. Your current files are still available.", [
|
|
346
|
+
{ label: "Retry", run: () => navigate(route, { view }) },
|
|
347
|
+
{ label: "Open normally", href: route.url },
|
|
348
|
+
]);
|
|
349
|
+
} finally {
|
|
350
|
+
clearTimeout(delay);
|
|
351
|
+
if (ticket === request) {
|
|
352
|
+
main.removeAttribute("aria-busy");
|
|
353
|
+
pending = null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
document.addEventListener("click", (event) => {
|
|
358
|
+
if (event.target.closest?.(".cw-skip")) {
|
|
359
|
+
event.preventDefault();
|
|
360
|
+
const panel = document.querySelector("#cw-panel");
|
|
361
|
+
panel.tabIndex = -1;
|
|
362
|
+
panel.focus({ preventScroll: true });
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const link = event.target.closest?.("a[data-cw-navigation][data-cw-page]");
|
|
366
|
+
if (
|
|
367
|
+
!link ||
|
|
368
|
+
event.defaultPrevented ||
|
|
369
|
+
event.button !== 0 ||
|
|
370
|
+
event.metaKey ||
|
|
371
|
+
event.ctrlKey ||
|
|
372
|
+
event.shiftKey ||
|
|
373
|
+
event.altKey ||
|
|
374
|
+
link.target ||
|
|
375
|
+
link.hasAttribute("download")
|
|
376
|
+
)
|
|
377
|
+
return;
|
|
378
|
+
const url = new URL(link.href),
|
|
379
|
+
asset = new URL(link.dataset.cwPage);
|
|
380
|
+
if (!allowed(url) || !allowed(asset) || url.hash) return;
|
|
381
|
+
event.preventDefault();
|
|
382
|
+
if (link.dataset.cwPath === current.path) {
|
|
383
|
+
pending?.abort();
|
|
384
|
+
notice.hidden = true;
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
navigate({ url: url.href, asset: asset.href, path: link.dataset.cwPath });
|
|
388
|
+
});
|
|
389
|
+
document.addEventListener("keydown", (event) => {
|
|
390
|
+
if (
|
|
391
|
+
event.key === "Escape" &&
|
|
392
|
+
!event.isComposing &&
|
|
393
|
+
!event.altKey &&
|
|
394
|
+
!event.ctrlKey &&
|
|
395
|
+
!event.metaKey &&
|
|
396
|
+
!event.target.matches?.("input,textarea,select,[contenteditable]")
|
|
397
|
+
) {
|
|
398
|
+
pending?.abort();
|
|
399
|
+
notice.hidden = true;
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
addEventListener("popstate", (event) => {
|
|
403
|
+
const entry = event.state?.dirwellCrosswave;
|
|
404
|
+
if (entry?.owner !== owner || !allowed(new URL(entry.route.url))) {
|
|
405
|
+
location.reload();
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
navigate(entry.route, { view: entry.view, pop: true, position: entry.index });
|
|
409
|
+
});
|
|
410
|
+
addEventListener("pagehide", () => {
|
|
411
|
+
++request;
|
|
412
|
+
pending?.abort();
|
|
413
|
+
main.removeAttribute("aria-busy");
|
|
414
|
+
notice.hidden = true;
|
|
415
|
+
stopAnimations();
|
|
416
|
+
});
|
|
417
|
+
reduced.addEventListener("change", () => {
|
|
418
|
+
if (reduced.matches) stopAnimations();
|
|
419
|
+
});
|
|
420
|
+
if (local && initialUrl.href !== location.href) {
|
|
421
|
+
const hash = new URLSearchParams(location.hash.slice(1));
|
|
422
|
+
if (hash.has("cw") && hash.has("cw-data") && hash.has("cw-path")) {
|
|
423
|
+
const url = new URL(hash.get("cw"), rootUrl),
|
|
424
|
+
asset = new URL(hash.get("cw-data"), rootUrl);
|
|
425
|
+
if (allowed(url) && allowed(asset))
|
|
426
|
+
navigate(
|
|
427
|
+
{ url: url.href, asset: asset.href, path: hash.get("cw-path") },
|
|
428
|
+
{
|
|
429
|
+
pop: true,
|
|
430
|
+
position: index,
|
|
431
|
+
view: savedEntry?.owner === owner ? savedEntry.view : undefined,
|
|
432
|
+
},
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
cancel() {
|
|
438
|
+
if (!pending) return false;
|
|
439
|
+
pending.abort();
|
|
440
|
+
notice.hidden = true;
|
|
441
|
+
return true;
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
15
446
|
document.addEventListener("DOMContentLoaded", () => {
|
|
16
447
|
const main = document.querySelector("[data-cw-root]");
|
|
17
448
|
if (!main) return;
|
|
@@ -40,7 +471,7 @@
|
|
|
40
471
|
const savedColor = read("color");
|
|
41
472
|
if (Object.hasOwn(colors, savedColor)) root.dataset.cwColor = savedColor;
|
|
42
473
|
let paused = root.dataset.cwMotion === "false" || read("paused") === "true";
|
|
43
|
-
|
|
474
|
+
let rows = [...document.querySelectorAll("[data-cw-entry]")];
|
|
44
475
|
const tabs = [...document.querySelectorAll("[data-cw-category]")];
|
|
45
476
|
const search = document.querySelector("#cw-search");
|
|
46
477
|
const clear = document.querySelector("[data-cw-clear]");
|
|
@@ -55,7 +486,7 @@
|
|
|
55
486
|
composing = false,
|
|
56
487
|
searchTimer;
|
|
57
488
|
const visible = () => rows.filter((row) => !row.hidden);
|
|
58
|
-
const select = (row, focus = false) => {
|
|
489
|
+
const select = (row, focus = false, reveal = true) => {
|
|
59
490
|
if (selected) selected.classList.remove("cw-selected");
|
|
60
491
|
selected = row;
|
|
61
492
|
if (!row) {
|
|
@@ -90,16 +521,24 @@
|
|
|
90
521
|
open.target = "_blank";
|
|
91
522
|
open.rel = "noopener";
|
|
92
523
|
open.removeAttribute("data-cw-navigation");
|
|
524
|
+
delete open.dataset.cwPage;
|
|
525
|
+
delete open.dataset.cwPath;
|
|
93
526
|
} else {
|
|
94
527
|
open.removeAttribute("target");
|
|
95
528
|
open.removeAttribute("rel");
|
|
96
529
|
open.setAttribute("data-cw-navigation", "");
|
|
530
|
+
open.dataset.cwPage = link.dataset.cwPage;
|
|
531
|
+
open.dataset.cwPath = link.dataset.cwPath;
|
|
97
532
|
}
|
|
98
533
|
}
|
|
99
534
|
detail.hidden = false;
|
|
100
535
|
if (focus) {
|
|
101
536
|
link?.focus({ preventScroll: true });
|
|
102
|
-
|
|
537
|
+
if (reveal)
|
|
538
|
+
row.scrollIntoView({
|
|
539
|
+
block: "nearest",
|
|
540
|
+
behavior: reduced.matches ? "instant" : "smooth",
|
|
541
|
+
});
|
|
103
542
|
}
|
|
104
543
|
};
|
|
105
544
|
const applyFilter = () => {
|
|
@@ -123,7 +562,7 @@
|
|
|
123
562
|
clear.hidden = !search.value;
|
|
124
563
|
if (!selected || selected.hidden) select(visible()[0] ?? null);
|
|
125
564
|
};
|
|
126
|
-
const setCategory = (next, focus = false) => {
|
|
565
|
+
const setCategory = (next, focus = false, animate = true) => {
|
|
127
566
|
category = next;
|
|
128
567
|
for (const tab of tabs) {
|
|
129
568
|
const active = tab.dataset.cwCategory === next;
|
|
@@ -146,9 +585,11 @@
|
|
|
146
585
|
selected = null;
|
|
147
586
|
rows.forEach((row) => row.classList.remove("cw-selected"));
|
|
148
587
|
applyFilter();
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
588
|
+
if (animate) {
|
|
589
|
+
main.classList.remove("cw-category-changing");
|
|
590
|
+
void main.offsetWidth;
|
|
591
|
+
main.classList.add("cw-category-changing");
|
|
592
|
+
}
|
|
152
593
|
};
|
|
153
594
|
const moveCategory = (direction) => {
|
|
154
595
|
const index = tabs.findIndex((tab) => tab.dataset.cwCategory === category);
|
|
@@ -169,12 +610,14 @@
|
|
|
169
610
|
: rows.filter((row) => row.dataset.category === tab.dataset.cwCategory).length;
|
|
170
611
|
tab.querySelector("small").textContent = String(count);
|
|
171
612
|
}
|
|
172
|
-
|
|
173
|
-
row.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
613
|
+
main.addEventListener("pointerover", (event) => {
|
|
614
|
+
const row = event.target.closest?.("[data-cw-entry]");
|
|
615
|
+
if (row && event.pointerType !== "touch") select(row);
|
|
616
|
+
});
|
|
617
|
+
main.addEventListener("focusin", (event) => {
|
|
618
|
+
const row = event.target.closest?.("[data-cw-entry]");
|
|
619
|
+
if (row) select(row);
|
|
620
|
+
});
|
|
178
621
|
search.addEventListener("compositionstart", () => {
|
|
179
622
|
composing = true;
|
|
180
623
|
clearTimeout(searchTimer);
|
|
@@ -215,7 +658,10 @@
|
|
|
215
658
|
"Your browser blocked the new tab. Press Enter or click the file to open it.";
|
|
216
659
|
}
|
|
217
660
|
};
|
|
218
|
-
|
|
661
|
+
let navigation;
|
|
662
|
+
const back = () => {
|
|
663
|
+
if (!navigation?.cancel()) document.querySelector("[data-cw-parent]")?.click();
|
|
664
|
+
};
|
|
219
665
|
const editing = () =>
|
|
220
666
|
document.activeElement?.matches("input,textarea,select,[contenteditable=true]");
|
|
221
667
|
document.addEventListener("keydown", (event) => {
|
|
@@ -342,6 +788,60 @@
|
|
|
342
788
|
addEventListener("resize", fitList);
|
|
343
789
|
fitList();
|
|
344
790
|
|
|
791
|
+
navigation = createCrosswaveNavigator({
|
|
792
|
+
main,
|
|
793
|
+
reduced,
|
|
794
|
+
getState: () => ({
|
|
795
|
+
category,
|
|
796
|
+
query: search.value,
|
|
797
|
+
selected: selected?.dataset.path,
|
|
798
|
+
scroll: scroll.scrollTop,
|
|
799
|
+
}),
|
|
800
|
+
commit: (doc, view) => {
|
|
801
|
+
clearTimeout(searchTimer);
|
|
802
|
+
composing = false;
|
|
803
|
+
document.title = doc.title;
|
|
804
|
+
document
|
|
805
|
+
.querySelector(".cw-location")
|
|
806
|
+
.replaceChildren(...doc.querySelector(".cw-location").childNodes);
|
|
807
|
+
document.querySelector(".cw-stage > h1").textContent =
|
|
808
|
+
doc.querySelector(".cw-stage > h1").textContent;
|
|
809
|
+
document
|
|
810
|
+
.querySelector(".cw-directory")
|
|
811
|
+
.firstElementChild.replaceWith(doc.querySelector(".cw-directory").firstElementChild);
|
|
812
|
+
document
|
|
813
|
+
.querySelector(".cw-files")
|
|
814
|
+
.replaceChildren(...doc.querySelector(".cw-files").childNodes);
|
|
815
|
+
main.dataset.cwPath = doc.querySelector("[data-cw-root]").dataset.cwPath;
|
|
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
|
+
for (const tab of tabs)
|
|
820
|
+
tab.querySelector("small").textContent = String(
|
|
821
|
+
tab.dataset.cwCategory === "all"
|
|
822
|
+
? rows.length
|
|
823
|
+
: rows.filter((row) => row.dataset.category === tab.dataset.cwCategory).length,
|
|
824
|
+
);
|
|
825
|
+
search.value = view?.query ?? "";
|
|
826
|
+
setCategory(
|
|
827
|
+
tabs.some((tab) => tab.dataset.cwCategory === view?.category) ? view.category : "all",
|
|
828
|
+
false,
|
|
829
|
+
false,
|
|
830
|
+
);
|
|
831
|
+
main.classList.remove("cw-category-changing");
|
|
832
|
+
const row = rows.find((row) => !row.hidden && row.dataset.path === view?.selected);
|
|
833
|
+
select(row ?? visible()[0] ?? null, true, false);
|
|
834
|
+
if (!selected?.querySelector("a")) {
|
|
835
|
+
document.querySelector("#cw-panel").tabIndex = -1;
|
|
836
|
+
document.querySelector("#cw-panel").focus({ preventScroll: true });
|
|
837
|
+
}
|
|
838
|
+
scroll.scrollTop = view?.scroll ?? 0;
|
|
839
|
+
status.className = "cw-sr";
|
|
840
|
+
document.querySelector(".cw-skip").href = `${location.href.split("#")[0]}#cw-panel`;
|
|
841
|
+
// Persistent ResizeObservers update geometry after the browser lays out the new content.
|
|
842
|
+
},
|
|
843
|
+
});
|
|
844
|
+
|
|
345
845
|
// VueUse's useGamepad demonstrates event-driven connection and rAF polling.
|
|
346
846
|
// Read fresh standard-mapping snapshots; controls here are original and framework-free.
|
|
347
847
|
let padFrame = 0,
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { a as readGeneratedPage, c as resolveThemeProject, d as utcTimestamp, f
|
|
|
2
2
|
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
|
+
import { createHash } from "node:crypto";
|
|
5
6
|
//#region src/theme-crosswave/icons.ts
|
|
6
7
|
const paths = {
|
|
7
8
|
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\"/>",
|
|
@@ -35,6 +36,7 @@ const crosswaveStyles = String.raw`
|
|
|
35
36
|
.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)}
|
|
36
37
|
.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}
|
|
37
38
|
body: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}
|
|
39
|
+
.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}
|
|
38
40
|
@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)}}
|
|
39
41
|
@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)}}
|
|
40
42
|
@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}}
|
|
@@ -45,6 +47,14 @@ body:not([data-cw-enhanced]) .cw-browser{padding-left:0}body:not([data-cw-enhanc
|
|
|
45
47
|
//#endregion
|
|
46
48
|
//#region src/theme-crosswave.ts
|
|
47
49
|
const runtime = await readFile(new URL("./crosswave-runtime.js", import.meta.url), "utf8");
|
|
50
|
+
function pageAsset(relativePath) {
|
|
51
|
+
return `crosswave-page-${createHash("sha256").update(relativePath).digest("hex").slice(0, 24)}.js`;
|
|
52
|
+
}
|
|
53
|
+
function navigationData(context, relativePath, href) {
|
|
54
|
+
const asset = pageAsset(relativePath);
|
|
55
|
+
const dataHref = context.mode === "mpa" ? context.assetHref(asset) : `${href.endsWith("/") ? href : href.slice(0, href.lastIndexOf("/") + 1)}${asset}`;
|
|
56
|
+
return ` data-cw-page="${escapeHtml(dataHref)}" data-cw-path="${escapeHtml(relativePath)}"`;
|
|
57
|
+
}
|
|
48
58
|
const categories = [
|
|
49
59
|
["all", "All files"],
|
|
50
60
|
["folder", "Folders"],
|
|
@@ -135,7 +145,7 @@ function row(entry, context, index) {
|
|
|
135
145
|
const target = entry.symlink === null ? "" : `<span class="cw-target">${escapeHtml(entry.symlink.target)}</span>`;
|
|
136
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)}"`;
|
|
137
147
|
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$1(entry))}${type === "folder" || unavailable ? "" : ` · ${bytes}`}</span><span class="cw-row-date">Modified ${modified}</span></span>`;
|
|
138
|
-
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\"" :
|
|
148
|
+
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>`;
|
|
139
149
|
}
|
|
140
150
|
/** PSP-inspired media navigation with original wave graphics and ordinary static links. */
|
|
141
151
|
function createCrosswaveTheme(options = {}) {
|
|
@@ -162,21 +172,32 @@ function createCrosswaveTheme(options = {}) {
|
|
|
162
172
|
const path = directory.current.relativePath || "/";
|
|
163
173
|
const parent = directory.parent === null ? null : context.hrefForDirectory(directory.parent.relativePath);
|
|
164
174
|
const segments = directory.current.relativePath.split("/").filter(Boolean);
|
|
165
|
-
const breadcrumbs = segments.map((segment, i) => i === segments.length - 1 ? `<span aria-current="page">${escapeHtml(segment)}</span>` : `<a data-cw-navigation href="${escapeHtml(context.hrefForDirectory(segments.slice(0, i + 1).join("/")))}">${escapeHtml(segment)}</a>`).join("<span class=\"cw-separator\" aria-hidden=\"true\">/</span>");
|
|
175
|
+
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>");
|
|
166
176
|
const tabs = categories.map(([type, label], 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" : ""}">${crosswaveIcon(type)}<span>${label}</span><small data-cw-category-count="${type}"></small></button>`).join("");
|
|
167
|
-
|
|
168
|
-
html: `<!doctype html>
|
|
177
|
+
const html = `<!doctype html>
|
|
169
178
|
<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>
|
|
170
179
|
<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>
|
|
171
|
-
<header class="cw-top"><a class="cw-brand" data-cw-navigation href="${escapeHtml(context.hrefForDirectory(""))}">${escapeHtml(project.name)}<span>Crosswave</span></a><nav class="cw-location" aria-label="Location"><a data-cw-navigation 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>
|
|
172
|
-
<main class="cw-stage" data-cw-root="${escapeHtml(context.hrefForDirectory(""))}"><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>
|
|
173
|
-
<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 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>
|
|
180
|
+
<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>
|
|
182
|
+
<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>
|
|
174
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>
|
|
175
184
|
<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>
|
|
176
|
-
<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
|
|
185
|
+
<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
|
+
const asset = pageAsset(directory.current.relativePath);
|
|
187
|
+
const payload = JSON.stringify({
|
|
188
|
+
version: 1,
|
|
189
|
+
id: asset,
|
|
190
|
+
path: directory.current.relativePath,
|
|
191
|
+
outputName: context.outputName,
|
|
192
|
+
baseHref: context.documentBaseHref,
|
|
193
|
+
html
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
html,
|
|
177
197
|
assets: {
|
|
178
198
|
"crosswave.css": crosswaveStyles,
|
|
179
|
-
"crosswave.js": runtime
|
|
199
|
+
"crosswave.js": runtime,
|
|
200
|
+
[asset]: `window.dispatchEvent(new CustomEvent("dirwell:crosswave-page",{detail:${payload}}));`
|
|
180
201
|
}
|
|
181
202
|
};
|
|
182
203
|
}
|
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@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 { 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\");\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\"}>${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 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 href=\"${escapeHtml(context.hrefForDirectory(\"\"))}\">${escapeHtml(project.name)}<span>Crosswave</span></a><nav class=\"cw-location\" aria-label=\"Location\"><a data-cw-navigation 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(\"\"))}\"><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 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 return { html, assets: { \"crosswave.css\": crosswaveStyles, \"crosswave.js\": runtime } };\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/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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"}
|