@vp-tw/dirwell 0.1.0-alpha.5 → 0.1.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/THIRD_PARTY_NOTICES.md +8 -0
- package/dist/bin.mjs +1 -1
- package/dist/crosswave-runtime.js +523 -25
- package/dist/index.d.mts +22 -1
- package/dist/index.mjs +207 -73
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.d.mts
CHANGED
|
@@ -36,9 +36,30 @@ interface DefaultThemeOptions {
|
|
|
36
36
|
export declare function createDefaultTheme(options?: DefaultThemeOptions): ExplorerTheme;
|
|
37
37
|
export declare const defaultTheme: ExplorerTheme;
|
|
38
38
|
//#endregion
|
|
39
|
+
//#region src/theme-crosswave/icons.d.ts
|
|
40
|
+
type CrosswaveIcon = "all" | "folder" | "image" | "audio" | "video" | "document" | "other" | "link" | "back" | "search" | "pause" | "play" | "controller";
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/theme-crosswave/categories.d.ts
|
|
43
|
+
type CrosswaveCategoryIcon = CrosswaveIcon | {
|
|
44
|
+
readonly svg: string;
|
|
45
|
+
};
|
|
46
|
+
type CrosswaveCategoryMatch = "all" | "folders" | "other" | {
|
|
47
|
+
readonly extensions?: readonly string[];
|
|
48
|
+
readonly mimeTypes?: readonly string[];
|
|
49
|
+
};
|
|
50
|
+
interface CrosswaveCategory {
|
|
51
|
+
readonly id: string;
|
|
52
|
+
readonly label: string;
|
|
53
|
+
readonly icon?: CrosswaveCategoryIcon;
|
|
54
|
+
readonly match: CrosswaveCategoryMatch;
|
|
55
|
+
}
|
|
56
|
+
/** Default rail definitions. Supply a mapped/filtered copy to change labels, order, or visibility. */
|
|
57
|
+
export declare const defaultCrosswaveCategories: readonly CrosswaveCategory[];
|
|
58
|
+
//#endregion
|
|
39
59
|
//#region src/theme-crosswave.d.ts
|
|
40
60
|
type CrosswaveColor = "azure" | "violet" | "amber" | "rose" | "jade" | "graphite";
|
|
41
61
|
interface CrosswaveThemeOptions {
|
|
62
|
+
readonly categories?: readonly CrosswaveCategory[];
|
|
42
63
|
readonly color?: CrosswaveColor;
|
|
43
64
|
readonly backgroundMotion?: boolean;
|
|
44
65
|
readonly pageTransitions?: boolean;
|
|
@@ -55,5 +76,5 @@ interface PlainThemeOptions {
|
|
|
55
76
|
/** A no-icon, no-script theme that leaves navigation and controls to the browser. */
|
|
56
77
|
export declare function createPlainTheme(options?: PlainThemeOptions): ExplorerTheme;
|
|
57
78
|
//#endregion
|
|
58
|
-
export { type BreadcrumbItem, type BreadcrumbsProps, type CrosswaveColor, type CrosswaveThemeOptions, type DefaultThemeIconSet, type DefaultThemeIconVariant, type DefaultThemeRuntimeConfig, type DirectoryData, type DirwellConfig, type DirwellConfigContext, type DirwellConfigInput, type DirwellThemeComponentOverrides, type DirwellThemeComponents, type EmptyStateProps, type EntryKind, type EntryListItem, type EntryListProps, type EntryNavigation, type EntryRowProps, type ExplorerTheme, type FileMetadata, type FileSystemEntry, type FileTimes, type FooterProps, type GenerateOptions, type IconName, type IconProps, type NameSortMode, type OutputNameResolver, type PageShellProps, type PlainThemeOptions, type RenderedPage, type SortDirection, type SortField, type SortOptions, type SymlinkMetadata, type ThemeContext, type ToolbarProps, defineConfig, loadDirwellConfig, resolveGenerateOptions, resolveThemeComponents };
|
|
79
|
+
export { type BreadcrumbItem, type BreadcrumbsProps, type CrosswaveCategory, type CrosswaveCategoryIcon, type CrosswaveCategoryMatch, type CrosswaveColor, type CrosswaveThemeOptions, type DefaultThemeIconSet, type DefaultThemeIconVariant, type DefaultThemeRuntimeConfig, type DirectoryData, type DirwellConfig, type DirwellConfigContext, type DirwellConfigInput, type DirwellThemeComponentOverrides, type DirwellThemeComponents, type EmptyStateProps, type EntryKind, type EntryListItem, type EntryListProps, type EntryNavigation, type EntryRowProps, type ExplorerTheme, type FileMetadata, type FileSystemEntry, type FileTimes, type FooterProps, type GenerateOptions, type IconName, type IconProps, type NameSortMode, type OutputNameResolver, type PageShellProps, type PlainThemeOptions, type RenderedPage, type SortDirection, type SortField, type SortOptions, type SymlinkMetadata, type ThemeContext, type ToolbarProps, defineConfig, loadDirwellConfig, resolveGenerateOptions, resolveThemeComponents };
|
|
59
80
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,8 @@ 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";
|
|
6
|
+
import mime from "mime";
|
|
5
7
|
//#region src/theme-crosswave/icons.ts
|
|
6
8
|
const paths = {
|
|
7
9
|
all: "<rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>",
|
|
@@ -23,6 +25,171 @@ function crosswaveIcon(name) {
|
|
|
23
25
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.45" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${paths[name]}</svg>`;
|
|
24
26
|
}
|
|
25
27
|
//#endregion
|
|
28
|
+
//#region src/theme-crosswave/categories.ts
|
|
29
|
+
const documents = [
|
|
30
|
+
"text/*",
|
|
31
|
+
"application/pdf",
|
|
32
|
+
"application/json",
|
|
33
|
+
"application/*+json",
|
|
34
|
+
"application/xml",
|
|
35
|
+
"application/*+xml",
|
|
36
|
+
"application/msword",
|
|
37
|
+
"application/rtf",
|
|
38
|
+
"application/sql",
|
|
39
|
+
"application/epub+zip",
|
|
40
|
+
"application/vnd.ms-excel",
|
|
41
|
+
"application/vnd.ms-powerpoint",
|
|
42
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
43
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
44
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
45
|
+
];
|
|
46
|
+
/** Default rail definitions. Supply a mapped/filtered copy to change labels, order, or visibility. */
|
|
47
|
+
const defaultCrosswaveCategories = Object.freeze([
|
|
48
|
+
{
|
|
49
|
+
id: "all",
|
|
50
|
+
label: "All files",
|
|
51
|
+
icon: "all",
|
|
52
|
+
match: "all"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "folder",
|
|
56
|
+
label: "Folders",
|
|
57
|
+
icon: "folder",
|
|
58
|
+
match: "folders"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "image",
|
|
62
|
+
label: "Photos",
|
|
63
|
+
icon: "image",
|
|
64
|
+
match: { mimeTypes: ["image/*"] }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "audio",
|
|
68
|
+
label: "Music",
|
|
69
|
+
icon: "audio",
|
|
70
|
+
match: { mimeTypes: ["audio/*", "application/vnd.apple.mpegurl"] }
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "video",
|
|
74
|
+
label: "Videos",
|
|
75
|
+
icon: "video",
|
|
76
|
+
match: { mimeTypes: ["video/*"] }
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "document",
|
|
80
|
+
label: "Documents",
|
|
81
|
+
icon: "document",
|
|
82
|
+
match: { mimeTypes: documents }
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "other",
|
|
86
|
+
label: "Other",
|
|
87
|
+
icon: "other",
|
|
88
|
+
match: "other"
|
|
89
|
+
}
|
|
90
|
+
].map((category) => Object.freeze({
|
|
91
|
+
...category,
|
|
92
|
+
match: typeof category.match === "string" ? category.match : Object.freeze({
|
|
93
|
+
...category.match.extensions ? { extensions: Object.freeze([...category.match.extensions]) } : {},
|
|
94
|
+
...category.match.mimeTypes ? { mimeTypes: Object.freeze([...category.match.mimeTypes]) } : {}
|
|
95
|
+
})
|
|
96
|
+
})));
|
|
97
|
+
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
98
|
+
function strings(value, normalize) {
|
|
99
|
+
if (value === void 0) return void 0;
|
|
100
|
+
if (!Array.isArray(value)) throw new TypeError("Crosswave category rules must be arrays of strings");
|
|
101
|
+
return Object.freeze([...new Set(Array.from(value, (item) => {
|
|
102
|
+
if (typeof item !== "string") throw new TypeError("Crosswave category rules must be arrays of strings");
|
|
103
|
+
return normalize(item);
|
|
104
|
+
}))]);
|
|
105
|
+
}
|
|
106
|
+
function extension(value) {
|
|
107
|
+
const normalized = value.toLowerCase().replace(/^\./, "");
|
|
108
|
+
if (!/^[a-z0-9+_-]+(?:\.[a-z0-9+_-]+)*$/.test(normalized)) throw new TypeError(`Invalid Crosswave extension: ${value}`);
|
|
109
|
+
return normalized;
|
|
110
|
+
}
|
|
111
|
+
function mediaType(value) {
|
|
112
|
+
const normalized = value.toLowerCase();
|
|
113
|
+
if (!/^[a-z0-9!#$&^_.+-]+\/(?:[a-z0-9!#$&^_.+-]+|\*|\*\+[a-z0-9!#$&^_.-]+)$/.test(normalized)) throw new TypeError(`Invalid Crosswave MIME pattern: ${value}`);
|
|
114
|
+
return normalized;
|
|
115
|
+
}
|
|
116
|
+
function match(value) {
|
|
117
|
+
if (value === "all" || value === "folders" || value === "other") return value;
|
|
118
|
+
if (!record(value) || Object.keys(value).some((key) => !["extensions", "mimeTypes"].includes(key))) throw new TypeError("Crosswave match must be all, folders, other, or extension/MIME rules");
|
|
119
|
+
const extensions = strings(value.extensions, extension);
|
|
120
|
+
const mimeTypes = strings(value.mimeTypes, mediaType);
|
|
121
|
+
if (!extensions?.length && !mimeTypes?.length) throw new TypeError("Crosswave category needs at least one matching rule");
|
|
122
|
+
return Object.freeze({
|
|
123
|
+
...extensions ? { extensions } : {},
|
|
124
|
+
...mimeTypes ? { mimeTypes } : {}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function icon(value, rule) {
|
|
128
|
+
if (value === void 0) return crosswaveIcon(rule === "all" ? "all" : rule === "folders" ? "folder" : "other");
|
|
129
|
+
if (typeof value === "string" && isCrosswaveIcon(value)) return crosswaveIcon(value);
|
|
130
|
+
if (record(value) && typeof value.svg === "string" && /^\s*<svg\b/i.test(value.svg) && /<\/svg>\s*$/i.test(value.svg)) return value.svg.replace(/<svg\b/i, "<svg aria-hidden=\"true\"");
|
|
131
|
+
throw new TypeError("Crosswave icon must be a built-in icon name or trusted SVG markup");
|
|
132
|
+
}
|
|
133
|
+
function isCrosswaveIcon(value) {
|
|
134
|
+
return [
|
|
135
|
+
"all",
|
|
136
|
+
"folder",
|
|
137
|
+
"image",
|
|
138
|
+
"audio",
|
|
139
|
+
"video",
|
|
140
|
+
"document",
|
|
141
|
+
"other",
|
|
142
|
+
"link",
|
|
143
|
+
"back",
|
|
144
|
+
"search",
|
|
145
|
+
"pause",
|
|
146
|
+
"play",
|
|
147
|
+
"controller"
|
|
148
|
+
].includes(value);
|
|
149
|
+
}
|
|
150
|
+
function resolveCrosswaveCategories(value) {
|
|
151
|
+
const input = value === void 0 ? defaultCrosswaveCategories : value;
|
|
152
|
+
if (!Array.isArray(input) || !input.length) throw new TypeError("Crosswave categories must be a nonempty array");
|
|
153
|
+
const ids = /* @__PURE__ */ new Set();
|
|
154
|
+
return Object.freeze(Array.from(input).map((category) => {
|
|
155
|
+
if (!record(category) || typeof category.id !== "string" || !/^[a-z][a-z0-9_-]{0,63}$/.test(category.id)) throw new TypeError("Crosswave category id must be a lowercase identifier");
|
|
156
|
+
if (ids.has(category.id)) throw new TypeError(`Duplicate Crosswave category id: ${category.id}`);
|
|
157
|
+
ids.add(category.id);
|
|
158
|
+
if (typeof category.label !== "string" || !category.label.trim()) throw new TypeError("Crosswave category label must be nonempty");
|
|
159
|
+
const rule = match(category.match);
|
|
160
|
+
return Object.freeze({
|
|
161
|
+
id: category.id,
|
|
162
|
+
label: category.label,
|
|
163
|
+
svg: icon(category.icon, rule),
|
|
164
|
+
match: rule
|
|
165
|
+
});
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
function matchesMimeType(pattern, type) {
|
|
169
|
+
if (pattern === type) return true;
|
|
170
|
+
const [group, subtype] = pattern.split("/");
|
|
171
|
+
if (!subtype || !type.startsWith(`${group}/`)) return false;
|
|
172
|
+
return subtype === "*" || subtype.startsWith("*+") && type.endsWith(subtype.slice(1));
|
|
173
|
+
}
|
|
174
|
+
function entryMimeType(entry) {
|
|
175
|
+
if (entry.kind === "directory" || entry.symlink?.targetKind === "directory") return null;
|
|
176
|
+
return mime.getType(`./${entry.name}`) ?? (entry.symlink?.targetKind === "file" && !entry.symlink.isBroken && !entry.symlink.isOutsideRoot && !entry.symlink.isTargetExcluded && entry.symlink.targetRelativePath ? mime.getType(`./${entry.symlink.targetRelativePath}`) : null);
|
|
177
|
+
}
|
|
178
|
+
function entryCategory(entry, type) {
|
|
179
|
+
if (entry.kind === "directory" || entry.symlink?.targetKind === "directory") return "folder";
|
|
180
|
+
if (type?.startsWith("image/")) return "image";
|
|
181
|
+
if (type?.startsWith("audio/") || type === "application/vnd.apple.mpegurl") return "audio";
|
|
182
|
+
if (type?.startsWith("video/")) return "video";
|
|
183
|
+
if (type && documents.some((pattern) => matchesMimeType(pattern, type))) return "document";
|
|
184
|
+
return "other";
|
|
185
|
+
}
|
|
186
|
+
function entryCategories(entry, type, categories) {
|
|
187
|
+
const folder = entry.kind === "directory" || entry.symlink?.targetKind === "directory";
|
|
188
|
+
const filename = entry.name.toLowerCase();
|
|
189
|
+
const specific = categories.filter((category) => typeof category.match !== "string" && !folder && (category.match.extensions?.some((ext) => filename.endsWith(`.${ext}`)) || type && category.match.mimeTypes?.some((pattern) => matchesMimeType(pattern, type))));
|
|
190
|
+
return categories.filter((category) => category.match === "all" || folder && category.match === "folders" || !folder && category.match === "other" && !specific.length || specific.includes(category));
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
26
193
|
//#region src/theme-crosswave/styles.ts
|
|
27
194
|
const crosswaveStyles = String.raw`
|
|
28
195
|
:root{color-scheme:dark;--cw-top:#2b4f85;--cw-bottom:#091a3a;--cw-text:#f7fbff;--cw-muted:#edf4fc;--cw-focus:#fff;--cw-ease:cubic-bezier(.16,1,.3,1);font-family:"Segoe UI",Helvetica,Arial,sans-serif;font-synthesis:none}
|
|
@@ -35,6 +202,7 @@ const crosswaveStyles = String.raw`
|
|
|
35
202
|
.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
203
|
.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
204
|
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}
|
|
205
|
+
.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
206
|
@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
207
|
@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
208
|
@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,63 +213,13 @@ body:not([data-cw-enhanced]) .cw-browser{padding-left:0}body:not([data-cw-enhanc
|
|
|
45
213
|
//#endregion
|
|
46
214
|
//#region src/theme-crosswave.ts
|
|
47
215
|
const runtime = await readFile(new URL("./crosswave-runtime.js", import.meta.url), "utf8");
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
["other", "Other"]
|
|
56
|
-
];
|
|
57
|
-
const extensions = Object.fromEntries([
|
|
58
|
-
...[
|
|
59
|
-
"png",
|
|
60
|
-
"jpg",
|
|
61
|
-
"jpeg",
|
|
62
|
-
"webp",
|
|
63
|
-
"gif",
|
|
64
|
-
"avif",
|
|
65
|
-
"svg",
|
|
66
|
-
"bmp",
|
|
67
|
-
"heic"
|
|
68
|
-
].map((x) => [x, "image"]),
|
|
69
|
-
...[
|
|
70
|
-
"mp3",
|
|
71
|
-
"wav",
|
|
72
|
-
"ogg",
|
|
73
|
-
"flac",
|
|
74
|
-
"m4a",
|
|
75
|
-
"aac",
|
|
76
|
-
"aiff",
|
|
77
|
-
"opus"
|
|
78
|
-
].map((x) => [x, "audio"]),
|
|
79
|
-
...[
|
|
80
|
-
"mp4",
|
|
81
|
-
"webm",
|
|
82
|
-
"mov",
|
|
83
|
-
"mkv",
|
|
84
|
-
"avi",
|
|
85
|
-
"m4v"
|
|
86
|
-
].map((x) => [x, "video"]),
|
|
87
|
-
...[
|
|
88
|
-
"txt",
|
|
89
|
-
"md",
|
|
90
|
-
"pdf",
|
|
91
|
-
"json",
|
|
92
|
-
"csv",
|
|
93
|
-
"html",
|
|
94
|
-
"xml",
|
|
95
|
-
"doc",
|
|
96
|
-
"docx",
|
|
97
|
-
"rtf",
|
|
98
|
-
"yaml",
|
|
99
|
-
"yml"
|
|
100
|
-
].map((x) => [x, "document"])
|
|
101
|
-
]);
|
|
102
|
-
function category(entry) {
|
|
103
|
-
if (entry.kind === "directory" || entry.symlink?.targetKind === "directory") return "folder";
|
|
104
|
-
return extensions[entry.name.split(".").at(-1)?.toLowerCase() ?? ""] ?? "other";
|
|
216
|
+
function pageAsset(relativePath) {
|
|
217
|
+
return `crosswave-page-${createHash("sha256").update(relativePath).digest("hex").slice(0, 24)}.js`;
|
|
218
|
+
}
|
|
219
|
+
function navigationData(context, relativePath, href) {
|
|
220
|
+
const asset = pageAsset(relativePath);
|
|
221
|
+
const dataHref = context.mode === "mpa" ? context.assetHref(asset) : `${href.endsWith("/") ? href : href.slice(0, href.lastIndexOf("/") + 1)}${asset}`;
|
|
222
|
+
return ` data-cw-page="${escapeHtml(dataHref)}" data-cw-path="${escapeHtml(relativePath)}"`;
|
|
105
223
|
}
|
|
106
224
|
function size(bytes) {
|
|
107
225
|
const units = [
|
|
@@ -123,19 +241,22 @@ function status$1(entry) {
|
|
|
123
241
|
if (entry.kind === "directory") return "Folder";
|
|
124
242
|
return "File";
|
|
125
243
|
}
|
|
126
|
-
function row(entry, context, index) {
|
|
244
|
+
function row(entry, context, index, categories) {
|
|
127
245
|
const href = context.hrefFor(entry);
|
|
128
|
-
const
|
|
246
|
+
const mimeType = entryMimeType(entry);
|
|
247
|
+
const type = entryCategory(entry, mimeType);
|
|
248
|
+
const matches = entryCategories(entry, mimeType, categories);
|
|
129
249
|
const icon = entry.kind === "symlink" ? "link" : type;
|
|
250
|
+
const svg = crosswaveIcon(icon);
|
|
130
251
|
const label = `${entry.name}${type === "folder" ? "/" : ""}`;
|
|
131
252
|
const unavailable = entry.symlink?.isOutsideRoot || entry.symlink?.isTargetExcluded || entry.symlink?.isBroken;
|
|
132
253
|
const bytes = type === "folder" ? "Folder" : size(entry.metadata.size);
|
|
133
254
|
const instant = new Date(entry.metadata.times.modifiedAt);
|
|
134
255
|
const modified = Number.isNaN(instant.getTime()) ? "Unknown" : `${instant.toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
135
256
|
const target = entry.symlink === null ? "" : `<span class="cw-target">${escapeHtml(entry.symlink.target)}</span>`;
|
|
136
|
-
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
|
-
const contents = `<span class="cw-entry-icon">${
|
|
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\"" :
|
|
257
|
+
const data = `data-cw-entry data-category="${type}" data-cw-categories="${matches.map((category) => category.id).join(" ")}" data-icon="${icon}" data-name="${escapeHtml(entry.name)}" data-path="${escapeHtml(entry.relativePath)}" data-size="${escapeHtml(bytes)}" data-modified="${modified}" data-status="${escapeHtml(status$1(entry))}" data-exits="${context.exitsExplorerFor(entry)}"`;
|
|
258
|
+
const contents = `<span class="cw-entry-icon">${svg}</span><span class="cw-entry-label"><span class="cw-name">${escapeHtml(label)}</span>${target}<span class="cw-row-meta">${escapeHtml(status$1(entry))}${type === "folder" || unavailable ? "" : ` · ${bytes}`}</span><span class="cw-row-date">Modified ${modified}</span></span>`;
|
|
259
|
+
return `<li class="cw-row${index === 0 ? " cw-selected" : ""}" ${data}>${href === null ? `<span class="cw-entry cw-unavailable">${contents}</span>` : `<a class="cw-entry" href="${escapeHtml(href)}"${context.exitsExplorerFor(entry) ? " target=\"_blank\" rel=\"noopener\"" : ` data-cw-navigation${navigationData(context, entry.symlink?.targetRelativePath ?? entry.relativePath, href)}`}>${contents}</a>`}</li>`;
|
|
139
260
|
}
|
|
140
261
|
/** PSP-inspired media navigation with original wave graphics and ordinary static links. */
|
|
141
262
|
function createCrosswaveTheme(options = {}) {
|
|
@@ -154,6 +275,8 @@ function createCrosswaveTheme(options = {}) {
|
|
|
154
275
|
"gamepad"
|
|
155
276
|
]) if (options[key] !== void 0 && typeof options[key] !== "boolean") throw new TypeError(`${key} must be a boolean`);
|
|
156
277
|
const project = resolveThemeProject(options.project);
|
|
278
|
+
const categories = resolveCrosswaveCategories(options.categories);
|
|
279
|
+
const categoryConfig = createHash("sha256").update(JSON.stringify(categories)).digest("hex").slice(0, 24);
|
|
157
280
|
return {
|
|
158
281
|
name: "crosswave",
|
|
159
282
|
searchIndex: false,
|
|
@@ -162,21 +285,32 @@ function createCrosswaveTheme(options = {}) {
|
|
|
162
285
|
const path = directory.current.relativePath || "/";
|
|
163
286
|
const parent = directory.parent === null ? null : context.hrefForDirectory(directory.parent.relativePath);
|
|
164
287
|
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>");
|
|
166
|
-
const tabs = categories.map((
|
|
167
|
-
|
|
168
|
-
html: `<!doctype html>
|
|
288
|
+
const breadcrumbs = segments.map((segment, i) => i === segments.length - 1 ? `<span aria-current="page">${escapeHtml(segment)}</span>` : `<a data-cw-navigation${navigationData(context, segments.slice(0, i + 1).join("/"), context.hrefForDirectory(segments.slice(0, i + 1).join("/")))} href="${escapeHtml(context.hrefForDirectory(segments.slice(0, i + 1).join("/")))}">${escapeHtml(segment)}</a>`).join("<span class=\"cw-separator\" aria-hidden=\"true\">/</span>");
|
|
289
|
+
const tabs = categories.map(({ id: type, label, svg }, i) => `<button role="tab" id="cw-tab-${type}" aria-selected="${i === 0}" aria-controls="cw-panel" tabindex="${i === 0 ? 0 : -1}" data-cw-category="${type}" class="cw-category${i === 0 ? " cw-active" : ""}">${svg}<span>${escapeHtml(label)}</span><small data-cw-category-count="${type}"></small></button>`).join("");
|
|
290
|
+
const html = `<!doctype html>
|
|
169
291
|
<html lang="en" data-cw-color="${color}" data-cw-motion="${options.backgroundMotion !== false}" data-cw-transitions="${options.pageTransitions !== false}" data-cw-gamepad="${options.gamepad !== false}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">${context.documentBaseHref === null ? "" : `<base href="${escapeHtml(context.documentBaseHref)}">`}<title>${escapeHtml(path)} · ${escapeHtml(project.name)} · Crosswave</title><link rel="stylesheet" href="${escapeHtml(context.assetHref("crosswave.css"))}"><script src="${escapeHtml(context.assetHref("crosswave.js"))}"><\/script></head>
|
|
170
292
|
<body><a class="cw-skip" href="${escapeHtml(context.hrefForDirectory(directory.current.relativePath))}#cw-panel">Skip to files</a><div class="cw-background" aria-hidden="true"><canvas id="cw-wave"></canvas><svg class="cw-fallback-wave" viewBox="0 0 1440 900" preserveAspectRatio="none"><path d="M-80 570C220 300 370 690 760 450S1260 290 1510 510"/><path d="M-80 540C220 640 400 330 820 470S1240 620 1510 360"/></svg></div>
|
|
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>
|
|
174
|
-
<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>
|
|
293
|
+
<header class="cw-top"><a class="cw-brand" data-cw-navigation${navigationData(context, "", context.hrefForDirectory(""))} href="${escapeHtml(context.hrefForDirectory(""))}">${escapeHtml(project.name)}<span>Crosswave</span></a><nav class="cw-location" aria-label="Location"><a data-cw-navigation${navigationData(context, "", context.hrefForDirectory(""))} href="${escapeHtml(context.hrefForDirectory(""))}">Home</a>${breadcrumbs ? `<span class="cw-separator" aria-hidden="true">/</span>${breadcrumbs}` : ""}</nav><time class="cw-clock" aria-label="Current time" hidden></time></header>
|
|
294
|
+
<main class="cw-stage" data-cw-root="${escapeHtml(context.hrefForDirectory(""))}" data-cw-path="${escapeHtml(directory.current.relativePath)}" data-cw-page="${escapeHtml(context.assetHref(pageAsset(directory.current.relativePath)))}" data-cw-file="${escapeHtml(context.outputName)}" data-cw-category-config="${categoryConfig}"><h1 class="cw-sr">Files in ${escapeHtml(path)}</h1><nav class="cw-rail" aria-label="File categories" hidden><div class="cw-categories" role="tablist" aria-label="File categories">${tabs}</div></nav>
|
|
295
|
+
<section class="cw-browser" id="cw-panel" aria-label="Files"><div class="cw-list-head"><div class="cw-directory">${parent === null ? "<span class=\"cw-parent-placeholder\"></span>" : `<a class="cw-parent" data-cw-parent data-cw-navigation${navigationData(context, directory.parent?.relativePath ?? "", parent)} href="${escapeHtml(parent)}" aria-label="Parent directory">${crosswaveIcon("back")}</a>`}<h2 data-cw-heading>All files</h2><span data-cw-count>${directory.entries.length} items</span></div><div class="cw-search" hidden>${crosswaveIcon("search")}<label class="cw-sr" for="cw-search">Search this folder</label><input id="cw-search" type="search" placeholder="Search this folder" autocomplete="off" spellcheck="false"><button type="button" data-cw-clear hidden>Clear</button></div></div>
|
|
296
|
+
<div class="cw-list-scroll"><ul class="cw-files">${directory.entries.map((entry, i) => row(entry, context, i, categories)).join("")}</ul><p class="cw-empty" data-cw-empty${directory.entries.length ? " hidden" : ""}>${directory.entries.length ? "No files match this search." : "This folder is empty."}</p></div><p class="cw-sr" role="status" aria-live="polite" data-cw-status></p></section>
|
|
175
297
|
<aside class="cw-detail" aria-label="Selected file details" hidden><div class="cw-detail-symbol" data-cw-detail-icon></div><h2 data-cw-detail-name></h2><p data-cw-detail-status></p><dl><div><dt>Path</dt><dd data-cw-detail-path></dd></div><div><dt>Size</dt><dd data-cw-detail-size></dd></div><div><dt>Modified</dt><dd data-cw-detail-modified></dd></div></dl><a class="cw-open" data-cw-open>Open file</a></aside></main>
|
|
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
|
|
298
|
+
<footer class="cw-bottom"><span class="cw-key-help" hidden><span><kbd>Arrow keys</kbd> Navigate</span><span><kbd>Enter</kbd> Open</span><span><kbd>Backspace</kbd> Parent</span></span><span class="cw-controller" role="status" hidden>${crosswaveIcon("controller")}<span data-cw-controller-label></span></span><div class="cw-appearance" hidden><label class="cw-sr" for="cw-color">Background color</label><select id="cw-color" aria-label="Background color"><option value="azure">Azure</option><option value="violet">Violet</option><option value="amber">Amber</option><option value="rose">Rose</option><option value="jade">Jade</option><option value="graphite">Graphite</option></select><button data-cw-motion aria-pressed="false" type="button">${crosswaveIcon("pause")}<span>Pause waves</span></button></div></footer><noscript><p class="cw-noscript">All files are available below. Categories, search, animated waves, and controller navigation need JavaScript.</p></noscript></body></html>`;
|
|
299
|
+
const asset = pageAsset(directory.current.relativePath);
|
|
300
|
+
const payload = JSON.stringify({
|
|
301
|
+
version: 1,
|
|
302
|
+
id: asset,
|
|
303
|
+
path: directory.current.relativePath,
|
|
304
|
+
outputName: context.outputName,
|
|
305
|
+
baseHref: context.documentBaseHref,
|
|
306
|
+
html
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
html,
|
|
177
310
|
assets: {
|
|
178
311
|
"crosswave.css": crosswaveStyles,
|
|
179
|
-
"crosswave.js": runtime
|
|
312
|
+
"crosswave.js": runtime,
|
|
313
|
+
[asset]: `window.dispatchEvent(new CustomEvent("dirwell:crosswave-page",{detail:${payload}}));`
|
|
180
314
|
}
|
|
181
315
|
};
|
|
182
316
|
}
|
|
@@ -251,6 +385,6 @@ function createPlainTheme(options = {}) {
|
|
|
251
385
|
};
|
|
252
386
|
}
|
|
253
387
|
//#endregion
|
|
254
|
-
export { compareEntries, createCrosswaveTheme, createDefaultTheme, createExplorerDevServer, createPlainTheme, defaultOutputName, defaultTheme, defaultThemeComponents, defineConfig, escapeHtml, generateExplorer, loadDirwellConfig, readGeneratedPage, resolveGenerateOptions, resolveThemeComponents };
|
|
388
|
+
export { compareEntries, createCrosswaveTheme, createDefaultTheme, createExplorerDevServer, createPlainTheme, defaultCrosswaveCategories, defaultOutputName, defaultTheme, defaultThemeComponents, defineConfig, escapeHtml, generateExplorer, loadDirwellConfig, readGeneratedPage, resolveGenerateOptions, resolveThemeComponents };
|
|
255
389
|
|
|
256
390
|
//# sourceMappingURL=index.mjs.map
|