Compare commits
4 Commits
f37cb0128d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
8acc692b08
|
|||
|
557c3166bb
|
|||
|
15027c5231
|
|||
|
0a3bf7779c
|
@@ -3,6 +3,7 @@ ENV CI=true
|
||||
RUN pnpm runtime set node 22 -g
|
||||
RUN apt update
|
||||
RUN apt install git -y
|
||||
RUN apt install ffmpeg -y
|
||||
|
||||
WORKDIR /app
|
||||
COPY pnpm-lock.yaml /app
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
podman-compose
|
||||
];
|
||||
|
||||
env = {
|
||||
UV_THREADPOOL_SIZE = "16";
|
||||
NODE_OPTIONS = "--max-old-space-size=4096";
|
||||
};
|
||||
|
||||
shellHook = ''
|
||||
echo "mreow"
|
||||
'';
|
||||
|
||||
+1
-8
@@ -11,14 +11,6 @@
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
"esbuild",
|
||||
"sharp",
|
||||
"svelte-preprocess"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@sveltejs/enhanced-img": "^0.10.4",
|
||||
@@ -28,6 +20,7 @@
|
||||
"mdsvex": "^0.12.7",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier-plugin-svelte": "^3.5.1",
|
||||
"sass-embedded": "^1.100.0",
|
||||
"sharp": "^0.34.5",
|
||||
"svelte": "^5.55.4",
|
||||
"svelte-check": "^4.4.6",
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
diff --git a/src/vite-plugin.js b/src/vite-plugin.js
|
||||
index c8ccb26b3f17cb69eb1e725bab1cde02c88c36cf..bf32bbd37d85e6d0d003c15e312ba74f7f3daad6 100644
|
||||
--- a/src/vite-plugin.js
|
||||
+++ b/src/vite-plugin.js
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
// TODO: expose this in vite-imagetools rather than duplicating it
|
||||
const OPTIMIZABLE = /^[^?]+\.(avif|heif|gif|jpeg|jpg|png|tiff|webp)(\?.*)?$/;
|
||||
+// Animated formats that should bypass imagetools processing to preserve animation
|
||||
+const ANIMATED = /^[^?]+\.(gif|webp)(\?.*)?$/;
|
||||
|
||||
/**
|
||||
* Creates the Svelte image plugin.
|
||||
@@ -81,7 +83,9 @@
|
||||
const original_url = src_attribute.raw.trim();
|
||||
let url = original_url;
|
||||
|
||||
- if (OPTIMIZABLE.test(url)) {
|
||||
+ const is_animated = ANIMATED.test(original_url);
|
||||
+
|
||||
+ if (OPTIMIZABLE.test(url) && !is_animated) {
|
||||
const sizes = get_attr_value(node, 'sizes');
|
||||
const width = get_attr_value(node, 'width');
|
||||
url += url.includes('?') ? '&' : '?';
|
||||
@@ -110,11 +114,11 @@
|
||||
);
|
||||
}
|
||||
|
||||
- if (OPTIMIZABLE.test(url)) {
|
||||
+ if (OPTIMIZABLE.test(url) && !is_animated) {
|
||||
const image = await process_id(resolved_id, plugin_context, imagetools_plugin);
|
||||
s.update(node.start, node.end, img_to_picture(content, node, image));
|
||||
} else {
|
||||
- const metadata = await sharp(resolved_id).metadata();
|
||||
+ const metadata = await sharp(resolved_id, { animated: true }).metadata();
|
||||
// this must come after the await so that we don't hand off processing between getting
|
||||
// the imports.size and incrementing the imports.size
|
||||
const name = imports.get(original_url) || '__IMPORTED_ASSET_' + imports.size + '__';
|
||||
@@ -0,0 +1,803 @@
|
||||
diff --git a/src/animated-cache.js b/src/animated-cache.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..3ab3e8466866d99c20959638523c00cd6d4ca819
|
||||
--- /dev/null
|
||||
+++ b/src/animated-cache.js
|
||||
@@ -0,0 +1,240 @@
|
||||
+import { createHash } from 'node:crypto';
|
||||
+import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
||||
+import path from 'node:path';
|
||||
+
|
||||
+const CACHE_DIR = path.join(process.cwd(), 'node_modules/.cache/enhanced-img-animated');
|
||||
+
|
||||
+/**
|
||||
+ * @param {string} filepath
|
||||
+ * @returns {string}
|
||||
+ */
|
||||
+function file_hash(filepath) {
|
||||
+ const content = readFileSync(filepath);
|
||||
+ return createHash('sha256').update(content).digest('hex');
|
||||
+}
|
||||
+
|
||||
+// ─── Animated WebP cache ─────────────────────────────────────────────────────
|
||||
+
|
||||
+/**
|
||||
+ * @typedef {{ width: number, height: number, pages: number, loop: number }} AnimatedMeta
|
||||
+ * @returns {{ webp_buf: Buffer, meta: AnimatedMeta } | null}
|
||||
+ */
|
||||
+export function read_animated_cache(filepath) {
|
||||
+ try {
|
||||
+ const dir = path.join(CACHE_DIR, 'animated', file_hash(filepath));
|
||||
+ const webp_path = path.join(dir, 'output.webp');
|
||||
+ const meta_path = path.join(dir, 'meta.json');
|
||||
+ if (!existsSync(webp_path) || !existsSync(meta_path)) return null;
|
||||
+ return {
|
||||
+ webp_buf: readFileSync(webp_path),
|
||||
+ meta: JSON.parse(readFileSync(meta_path, 'utf8'))
|
||||
+ };
|
||||
+ } catch {
|
||||
+ return null;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @param {string} filepath
|
||||
+ * @param {Buffer} webp_buf
|
||||
+ * @param {AnimatedMeta} meta
|
||||
+ */
|
||||
+export function write_animated_cache(filepath, webp_buf, meta) {
|
||||
+ try {
|
||||
+ const dir = path.join(CACHE_DIR, 'animated', file_hash(filepath));
|
||||
+ mkdirSync(dir, { recursive: true });
|
||||
+ writeFileSync(path.join(dir, 'output.webp'), webp_buf);
|
||||
+ writeFileSync(path.join(dir, 'meta.json'), JSON.stringify(meta));
|
||||
+ } catch { /* non-fatal */ }
|
||||
+}
|
||||
+
|
||||
+// ─── MP4 / video cache ───────────────────────────────────────────────────────
|
||||
+
|
||||
+/**
|
||||
+ * @typedef {{ width: number, height: number }} VideoMeta
|
||||
+ * @returns {{ webm_buf: Buffer, meta: VideoMeta } | null}
|
||||
+ */
|
||||
+export function read_video_cache(filepath) {
|
||||
+ try {
|
||||
+ const dir = path.join(CACHE_DIR, 'video', file_hash(filepath));
|
||||
+ const webm_path = path.join(dir, 'output.webm');
|
||||
+ const meta_path = path.join(dir, 'meta.json');
|
||||
+ if (!existsSync(webm_path) || !existsSync(meta_path)) return null;
|
||||
+ return {
|
||||
+ webm_buf: readFileSync(webm_path),
|
||||
+ meta: JSON.parse(readFileSync(meta_path, 'utf8'))
|
||||
+ };
|
||||
+ } catch {
|
||||
+ return null;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @param {string} filepath
|
||||
+ * @param {Buffer} webm_buf
|
||||
+ * @param {VideoMeta} meta
|
||||
+ */
|
||||
+export function write_video_cache(filepath, webm_buf, meta) {
|
||||
+ try {
|
||||
+ const dir = path.join(CACHE_DIR, 'video', file_hash(filepath));
|
||||
+ mkdirSync(dir, { recursive: true });
|
||||
+ writeFileSync(path.join(dir, 'output.webm'), webm_buf);
|
||||
+ writeFileSync(path.join(dir, 'meta.json'), JSON.stringify(meta));
|
||||
+ } catch { /* non-fatal */ }
|
||||
+}
|
||||
+
|
||||
+// ─── General imagetools cache ─────────────────────────────────────────────────
|
||||
+// Each cache entry stores the emitted asset buffers plus the module code template
|
||||
+// with __VITE_ASSET__ refs replaced by stable placeholder keys ($REF[key]).
|
||||
+// On cache hit the buffers are re-emitted (producing fresh ref IDs) and the
|
||||
+// placeholders are substituted with the new IDs before the module is returned.
|
||||
+
|
||||
+/**
|
||||
+ * @typedef {{ key: string, name: string, source: Buffer }} CachedAsset
|
||||
+ * @typedef {{ assets: CachedAsset[], template: string }} ImgCacheEntry
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * @param {string} filepath
|
||||
+ * @param {string} query raw query string (everything after '?')
|
||||
+ * @returns {ImgCacheEntry | null}
|
||||
+ */
|
||||
+export function read_img_cache(filepath, query) {
|
||||
+ try {
|
||||
+ const hash = createHash('sha256')
|
||||
+ .update(file_hash(filepath))
|
||||
+ .update('\0')
|
||||
+ .update(normalise_query(query))
|
||||
+ .digest('hex');
|
||||
+ const dir = path.join(CACHE_DIR, 'img', hash);
|
||||
+ const index_path = path.join(dir, 'index.json');
|
||||
+ if (!existsSync(index_path)) return null;
|
||||
+ const index = JSON.parse(readFileSync(index_path, 'utf8'));
|
||||
+ const assets = index.assets.map((/** @type {any} */ a) => ({
|
||||
+ key: a.key,
|
||||
+ name: a.name,
|
||||
+ source: readFileSync(path.join(dir, a.file))
|
||||
+ }));
|
||||
+ return { assets, template: index.template };
|
||||
+ } catch {
|
||||
+ return null;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @param {string} filepath
|
||||
+ * @param {string} query
|
||||
+ * @param {ImgCacheEntry} entry
|
||||
+ */
|
||||
+export function write_img_cache(filepath, query, entry) {
|
||||
+ try {
|
||||
+ const hash = createHash('sha256')
|
||||
+ .update(file_hash(filepath))
|
||||
+ .update('\0')
|
||||
+ .update(normalise_query(query))
|
||||
+ .digest('hex');
|
||||
+ const dir = path.join(CACHE_DIR, 'img', hash);
|
||||
+ mkdirSync(dir, { recursive: true });
|
||||
+ const index_assets = entry.assets.map((a, i) => {
|
||||
+ const file = `asset_${i}_${a.name}`;
|
||||
+ writeFileSync(path.join(dir, file), a.source);
|
||||
+ return { key: a.key, name: a.name, file };
|
||||
+ });
|
||||
+ writeFileSync(path.join(dir, 'index.json'), JSON.stringify({
|
||||
+ assets: index_assets,
|
||||
+ template: entry.template
|
||||
+ }));
|
||||
+ } catch { /* non-fatal */ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Normalises a query string so key order doesn't matter for cache keying.
|
||||
+ * @param {string} query
|
||||
+ * @returns {string}
|
||||
+ */
|
||||
+function normalise_query(query) {
|
||||
+ const p = new URLSearchParams(query);
|
||||
+ return [...p.entries()].sort().map(([k, v]) => `${k}=${v}`).join('&');
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Wraps a Vite plugin's load hook with the image cache.
|
||||
+ * Intercepts emitFile calls during the original load to capture asset buffers,
|
||||
+ * then writes them to cache. On subsequent builds, re-emits cached buffers and
|
||||
+ * reconstructs the module code.
|
||||
+ *
|
||||
+ * @param {import('vite').Plugin} plugin the imagetools plugin instance
|
||||
+ * @returns {import('vite').Plugin}
|
||||
+ */
|
||||
+export function with_img_cache(plugin) {
|
||||
+ const original_hook = plugin.load;
|
||||
+ if (!original_hook) return plugin;
|
||||
+ const original_fn = typeof original_hook === 'object' ? original_hook.handler : original_hook;
|
||||
+
|
||||
+ /**
|
||||
+ * @this {import('vite').Rollup.PluginContext}
|
||||
+ * @param {string} id
|
||||
+ */
|
||||
+ async function cached_load(id) {
|
||||
+ // Skip cache entirely in dev/watch mode — emitFile semantics differ there.
|
||||
+ if (this.meta?.watchMode) return original_fn.call(this, id);
|
||||
+
|
||||
+ const q = id.indexOf('?');
|
||||
+ if (q < 0) return original_fn.call(this, id);
|
||||
+ const query = id.slice(q + 1);
|
||||
+ const params = new URLSearchParams(query);
|
||||
+ if (!params.has('enhanced')) return original_fn.call(this, id);
|
||||
+
|
||||
+ const filepath = id.slice(0, q);
|
||||
+
|
||||
+ const cached = read_img_cache(filepath, query);
|
||||
+ if (cached) {
|
||||
+ /** @type {Record<string, string>} */
|
||||
+ const new_refs = {};
|
||||
+ for (const asset of cached.assets) {
|
||||
+ new_refs[asset.key] = this.emitFile({ type: 'asset', name: asset.name, source: asset.source });
|
||||
+ }
|
||||
+ return cached.template.replace(/\$REF\[([^\]]+)\]/g, (_, k) => `__VITE_ASSET__${new_refs[k]}__`);
|
||||
+ }
|
||||
+
|
||||
+ // Cache miss — call original, intercept emitFile to capture assets.
|
||||
+ /** @type {CachedAsset[]} */
|
||||
+ const emitted = [];
|
||||
+ const original_emit = this.emitFile.bind(this);
|
||||
+
|
||||
+ const proxy_ctx = new Proxy(this, {
|
||||
+ get(target, prop) {
|
||||
+ if (prop === 'emitFile') {
|
||||
+ return (/** @type {any} */ opts) => {
|
||||
+ const ref = original_emit(opts);
|
||||
+ if (opts.type === 'asset' && opts.source) {
|
||||
+ emitted.push({ key: ref, name: opts.name ?? '', source: Buffer.from(opts.source) });
|
||||
+ }
|
||||
+ return ref;
|
||||
+ };
|
||||
+ }
|
||||
+ return /** @type {any} */ (target)[prop];
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ const result = await original_fn.call(proxy_ctx, id);
|
||||
+
|
||||
+ if (result && emitted.length > 0) {
|
||||
+ let template = typeof result === 'string' ? result : result.code;
|
||||
+ for (const asset of emitted) {
|
||||
+ template = template.replaceAll(`__VITE_ASSET__${asset.key}__`, `$REF[${asset.key}]`);
|
||||
+ }
|
||||
+ write_img_cache(filepath, query, { assets: emitted, template });
|
||||
+ }
|
||||
+
|
||||
+ return result;
|
||||
+ }
|
||||
+
|
||||
+ if (typeof original_hook === 'object') {
|
||||
+ plugin.load = { ...original_hook, handler: cached_load };
|
||||
+ } else {
|
||||
+ plugin.load = cached_load;
|
||||
+ }
|
||||
+
|
||||
+ return plugin;
|
||||
+}
|
||||
diff --git a/src/index.js b/src/index.js
|
||||
index 013b9405436dd61d02d947a49c56ae1d2cad9f57..2f9205521c0bd01646f00cd051f158360322d4de 100644
|
||||
--- a/src/index.js
|
||||
+++ b/src/index.js
|
||||
@@ -1,17 +1,131 @@
|
||||
import process from 'node:process';
|
||||
+import path from 'node:path';
|
||||
+import sharp from 'sharp';
|
||||
import { imagetools } from 'vite-imagetools';
|
||||
import { image_plugin } from './vite-plugin.js';
|
||||
+import { read_animated_cache, write_animated_cache, read_video_cache, write_video_cache, with_img_cache } from './animated-cache.js';
|
||||
+import { video_dimensions, mp4_to_webm } from './video-convert.js';
|
||||
|
||||
/**
|
||||
* @returns {import('vite').Plugin[]}
|
||||
*/
|
||||
export function enhancedImages() {
|
||||
- const imagetools_instance = imagetools_plugin();
|
||||
+ const imagetools_instance = with_img_cache(imagetools_plugin());
|
||||
+ const animated_instance = animated_image_plugin();
|
||||
return !process.versions.webcontainer
|
||||
- ? [image_plugin(imagetools_instance), imagetools_instance]
|
||||
+ ? [animated_instance, image_plugin(imagetools_instance), imagetools_instance]
|
||||
: [];
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Vite plugin (enforce: 'pre') that intercepts animated GIF/WebP imports that
|
||||
+ * carry ?enhanced before imagetools can strip their frames. Produces optimised
|
||||
+ * animated WebP and emits them as build assets. In dev mode the original file
|
||||
+ * is served unmodified.
|
||||
+ * @returns {import('vite').Plugin}
|
||||
+ */
|
||||
+function animated_image_plugin() {
|
||||
+ /** @type {import('vite').ResolvedConfig} */
|
||||
+ let vite_config;
|
||||
+
|
||||
+ return {
|
||||
+ name: 'vite-plugin-enhanced-img-animated',
|
||||
+ enforce: /** @type {'pre'} */ ('pre'),
|
||||
+ configResolved(config) {
|
||||
+ vite_config = config;
|
||||
+ },
|
||||
+ async load(id) {
|
||||
+ const q = id.indexOf('?');
|
||||
+ const params = new URLSearchParams(q >= 0 ? id.slice(q + 1) : '');
|
||||
+ if (!params.has('enhanced')) return null;
|
||||
+
|
||||
+ const filepath = q >= 0 ? id.slice(0, q) : id;
|
||||
+ const ext = path.extname(filepath).toLowerCase();
|
||||
+
|
||||
+ // ── MP4 → WebM + MP4 (for <enhanced:video>) ──────────────────────
|
||||
+ if (ext === '.mp4') {
|
||||
+ const stem = path.basename(filepath, ext);
|
||||
+
|
||||
+ if (vite_config.command === 'serve') {
|
||||
+ const rel = '/' + path.relative(vite_config.root, filepath).replace(/\\/g, '/');
|
||||
+ const { width, height } = await video_dimensions(filepath);
|
||||
+ return (
|
||||
+ `export default { sources: { webm: ${JSON.stringify(rel)} },` +
|
||||
+ ` video: { src: ${JSON.stringify(rel)}, w: ${width}, h: ${height} } }`
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ let cached_v = read_video_cache(filepath);
|
||||
+ let webm_buf, vid_w, vid_h;
|
||||
+ if (cached_v) {
|
||||
+ ({ webm_buf, meta: { width: vid_w, height: vid_h } } = cached_v);
|
||||
+ } else {
|
||||
+ const dims = await video_dimensions(filepath);
|
||||
+ vid_w = dims.width;
|
||||
+ vid_h = dims.height;
|
||||
+ webm_buf = await mp4_to_webm(filepath);
|
||||
+ write_video_cache(filepath, webm_buf, { width: vid_w, height: vid_h });
|
||||
+ }
|
||||
+
|
||||
+ const webm_ref = this.emitFile({ type: 'asset', name: `${stem}.webm`, source: webm_buf });
|
||||
+
|
||||
+ return [
|
||||
+ `export default {`,
|
||||
+ ` sources: { webm: "__VITE_ASSET__${webm_ref}__" },`,
|
||||
+ ` video: { src: "__VITE_ASSET__${webm_ref}__", w: ${vid_w}, h: ${vid_h} }`,
|
||||
+ `}`
|
||||
+ ].join('\n');
|
||||
+ }
|
||||
+
|
||||
+ if (ext !== '.gif' && ext !== '.webp') return null;
|
||||
+
|
||||
+ const sharp_meta = await sharp(filepath, { animated: true }).metadata();
|
||||
+ if (!sharp_meta.pages || sharp_meta.pages <= 1) return null; // not animated; let imagetools handle it
|
||||
+
|
||||
+ const stem = path.basename(filepath, ext);
|
||||
+
|
||||
+ if (vite_config.command === 'serve') {
|
||||
+ // Dev: serve the original file directly — it is already animated.
|
||||
+ const rel = '/' + path.relative(vite_config.root, filepath).replace(/\\/g, '/');
|
||||
+ const w = sharp_meta.width ?? 0;
|
||||
+ const h = sharp_meta.pageHeight ?? sharp_meta.height ?? 0;
|
||||
+ return (
|
||||
+ `export default { sources: { ${JSON.stringify(ext.slice(1))}: ${JSON.stringify(rel)} },` +
|
||||
+ ` img: { src: ${JSON.stringify(rel)}, w: ${w}, h: ${h} } }`
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ // Build: check cache first, convert only on miss.
|
||||
+ let cached = read_animated_cache(filepath);
|
||||
+ let webp_buf, width, height;
|
||||
+ if (cached) {
|
||||
+ ({ webp_buf, meta: { width, height } } = cached);
|
||||
+ } else {
|
||||
+ webp_buf = await sharp(filepath, { animated: true })
|
||||
+ .webp({ effort: 6, loop: sharp_meta.loop ?? 0 })
|
||||
+ .toBuffer();
|
||||
+ width = sharp_meta.width ?? 0;
|
||||
+ height = sharp_meta.pageHeight ?? sharp_meta.height ?? 0;
|
||||
+ write_animated_cache(filepath, webp_buf, {
|
||||
+ width,
|
||||
+ height,
|
||||
+ pages: sharp_meta.pages,
|
||||
+ loop: sharp_meta.loop ?? 0
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ const webp_ref = this.emitFile({ type: 'asset', name: `${stem}.webp`, source: webp_buf });
|
||||
+
|
||||
+ return [
|
||||
+ `export default {`,
|
||||
+ ` sources: { webp: "__VITE_ASSET__${webp_ref}__" },`,
|
||||
+ ` img: { src: "__VITE_ASSET__${webp_ref}__", w: ${width}, h: ${height} }`,
|
||||
+ `}`
|
||||
+ ].join('\n');
|
||||
+ }
|
||||
+ };
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* @param {import('sharp').Metadata} meta
|
||||
* @returns {string}
|
||||
diff --git a/src/video-convert.js b/src/video-convert.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0f2ce0d75e71eee14dea3efc82cb7a7851a9f7d8
|
||||
--- /dev/null
|
||||
+++ b/src/video-convert.js
|
||||
@@ -0,0 +1,61 @@
|
||||
+import process from 'node:process';
|
||||
+import { execFile } from 'node:child_process';
|
||||
+import { readFileSync, unlinkSync } from 'node:fs';
|
||||
+import os from 'node:os';
|
||||
+import path from 'node:path';
|
||||
+
|
||||
+/**
|
||||
+ * @param {string} cmd
|
||||
+ * @param {string[]} args
|
||||
+ * @param {number} [timeout]
|
||||
+ * @returns {Promise<string>}
|
||||
+ */
|
||||
+function run(cmd, args, timeout = 120000) {
|
||||
+ return new Promise((resolve, reject) => {
|
||||
+ execFile(cmd, args, { timeout }, (err, stdout, stderr) => {
|
||||
+ if (err) reject(new Error(`${cmd} failed: ${(stderr || '').trim().split('\n').pop()}`));
|
||||
+ else resolve(stdout.trim());
|
||||
+ });
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+/** @param {string} ext */
|
||||
+function tmp_path(ext) {
|
||||
+ return path.join(os.tmpdir(), `enhanced_img_${process.hrtime.bigint()}.${ext}`);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Returns pixel dimensions of the first video stream via ffprobe.
|
||||
+ * @param {string} filepath
|
||||
+ * @returns {Promise<{ width: number, height: number }>}
|
||||
+ */
|
||||
+export async function video_dimensions(filepath) {
|
||||
+ const out = await run('ffprobe', [
|
||||
+ '-v', 'error',
|
||||
+ '-select_streams', 'v:0',
|
||||
+ '-show_entries', 'stream=width,height',
|
||||
+ '-of', 'csv=p=0',
|
||||
+ filepath
|
||||
+ ], 10000);
|
||||
+ const [w, h] = out.split(',').map(Number);
|
||||
+ return { width: w || 0, height: h || 0 };
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Converts an MP4 to a VP9+Opus WebM buffer (constant-quality, audio preserved).
|
||||
+ * @param {string} filepath
|
||||
+ * @returns {Promise<Buffer>}
|
||||
+ */
|
||||
+export async function mp4_to_webm(filepath) {
|
||||
+ const out = tmp_path('webm');
|
||||
+ await run('ffmpeg', [
|
||||
+ '-i', filepath,
|
||||
+ '-c:v', 'libvpx-vp9',
|
||||
+ '-b:v', '0', '-crf', '33',
|
||||
+ '-c:a', 'libopus', '-b:a', '128k',
|
||||
+ '-y', out
|
||||
+ ]);
|
||||
+ const buf = readFileSync(out);
|
||||
+ try { unlinkSync(out); } catch { /* ignore */ }
|
||||
+ return buf;
|
||||
+}
|
||||
diff --git a/src/vite-plugin.js b/src/vite-plugin.js
|
||||
index c8ccb26b3f17cb69eb1e725bab1cde02c88c36cf..08392ceb8d2788917c1557ce81161982d97ea60e 100644
|
||||
--- a/src/vite-plugin.js
|
||||
+++ b/src/vite-plugin.js
|
||||
@@ -1,6 +1,8 @@
|
||||
/** @import { AST } from 'svelte/compiler' */
|
||||
import { existsSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
+import { read_animated_cache, write_animated_cache, read_video_cache, write_video_cache, read_img_cache, write_img_cache } from './animated-cache.js';
|
||||
+import { video_dimensions, mp4_to_webm } from './video-convert.js';
|
||||
import MagicString from 'magic-string';
|
||||
import sharp from 'sharp';
|
||||
import { parse } from 'svelte-parse-markup';
|
||||
@@ -8,6 +10,97 @@ import { walk } from 'zimmerframe';
|
||||
|
||||
// TODO: expose this in vite-imagetools rather than duplicating it
|
||||
const OPTIMIZABLE = /^[^?]+\.(avif|heif|gif|jpeg|jpg|png|tiff|webp)(\?.*)?$/;
|
||||
+// Formats that may carry animation — checked before handing off to imagetools.
|
||||
+const ANIMATED_EXT = /\.(gif|webp)$/i;
|
||||
+// Video MIME types (use video/* instead of image/* in <source> elements).
|
||||
+const VIDEO_FORMATS = new Set(['webm', 'mp4']);
|
||||
+
|
||||
+/**
|
||||
+ * Process an animated GIF/WebP into optimised animated WebP assets.
|
||||
+ * In dev mode returns the original file path so the browser plays it directly.
|
||||
+ * @param {string} filepath Absolute path to the source file (no query params).
|
||||
+ * @param {import('sharp').Metadata} meta Sharp metadata (animated: true).
|
||||
+ * @param {import('vite').Rollup.PluginContext} plugin_context
|
||||
+ * @param {import('vite').ResolvedConfig} vite_config
|
||||
+ * @returns {Promise<import('vite-imagetools').Picture>}
|
||||
+ */
|
||||
+async function process_animated(filepath, meta, plugin_context, vite_config) {
|
||||
+ const width = meta.width ?? 0;
|
||||
+ const height = meta.pageHeight ?? meta.height ?? 0;
|
||||
+ const ext = path.extname(filepath);
|
||||
+ const stem = path.basename(filepath, ext);
|
||||
+
|
||||
+ if (vite_config.command === 'serve') {
|
||||
+ // Dev: serve the original file — already animated, no optimisation needed.
|
||||
+ const rel = '/' + path.relative(vite_config.root, filepath).replace(/\\/g, '/');
|
||||
+ return /** @type {any} */ ({
|
||||
+ sources: { [ext.slice(1)]: rel },
|
||||
+ img: { src: rel, w: width, h: height }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ // Build: check cache first, convert only on miss.
|
||||
+ let cached_anim = read_animated_cache(filepath);
|
||||
+ let webp_buf;
|
||||
+ if (cached_anim) {
|
||||
+ ({ webp_buf } = cached_anim);
|
||||
+ } else {
|
||||
+ webp_buf = await sharp(filepath, { animated: true })
|
||||
+ .webp({ effort: 6, loop: meta.loop ?? 0 })
|
||||
+ .toBuffer();
|
||||
+ write_animated_cache(filepath, webp_buf, {
|
||||
+ width,
|
||||
+ height,
|
||||
+ pages: meta.pages ?? 1,
|
||||
+ loop: meta.loop ?? 0
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ const webp_ref = plugin_context.emitFile({ type: 'asset', name: `${stem}.webp`, source: webp_buf });
|
||||
+
|
||||
+ return /** @type {any} */ ({
|
||||
+ sources: { webp: `__VITE_ASSET__${webp_ref}__` },
|
||||
+ img: { src: `__VITE_ASSET__${webp_ref}__`, w: width, h: height }
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Process an MP4 source into optimised WebM + MP4 assets.
|
||||
+ * In dev mode returns the original file so the browser can play it directly.
|
||||
+ * @param {string} filepath
|
||||
+ * @param {import('vite').Rollup.PluginContext} plugin_context
|
||||
+ * @param {import('vite').ResolvedConfig} vite_config
|
||||
+ * @returns {Promise<{ sources: Record<string,string>, video: { src: string, w: number, h: number } }>}
|
||||
+ */
|
||||
+async function process_video(filepath, plugin_context, vite_config) {
|
||||
+ const ext = path.extname(filepath);
|
||||
+ const stem = path.basename(filepath, ext);
|
||||
+
|
||||
+ if (vite_config.command === 'serve') {
|
||||
+ const rel = '/' + path.relative(vite_config.root, filepath).replace(/\\/g, '/');
|
||||
+ const { width, height } = await video_dimensions(filepath);
|
||||
+ return { sources: { webm: rel }, video: { src: rel, w: width, h: height } };
|
||||
+ }
|
||||
+
|
||||
+ let cached_v = read_video_cache(filepath);
|
||||
+ let webm_buf, width, height;
|
||||
+ if (cached_v) {
|
||||
+ ({ webm_buf, meta: { width, height } } = cached_v);
|
||||
+ } else {
|
||||
+ const dims = await video_dimensions(filepath);
|
||||
+ width = dims.width;
|
||||
+ height = dims.height;
|
||||
+ webm_buf = await mp4_to_webm(filepath);
|
||||
+ write_video_cache(filepath, webm_buf, { width, height });
|
||||
+ }
|
||||
+
|
||||
+ const webm_ref = plugin_context.emitFile({ type: 'asset', name: `${stem}.webm`, source: webm_buf });
|
||||
+
|
||||
+ return {
|
||||
+ sources: { webm: `__VITE_ASSET__${webm_ref}__` },
|
||||
+ video: { src: `__VITE_ASSET__${webm_ref}__`, w: width, h: height }
|
||||
+ };
|
||||
+}
|
||||
|
||||
/**
|
||||
* Creates the Svelte image plugin.
|
||||
@@ -38,7 +131,7 @@ export function image_plugin(imagetools_plugin) {
|
||||
transform: {
|
||||
order: 'pre', // puts it before vite-plugin-svelte:compile
|
||||
filter: {
|
||||
- code: /<enhanced:img/ // code filter must match in addition to the id filter set in configResolved hook above
|
||||
+ code: /<enhanced:(img|video)/ // code filter must match in addition to the id filter set in configResolved hook above
|
||||
},
|
||||
|
||||
async handler(content, filename) {
|
||||
@@ -110,8 +203,21 @@ export function image_plugin(imagetools_plugin) {
|
||||
);
|
||||
}
|
||||
|
||||
+ // For GIF/WebP, check animation before handing off to imagetools.
|
||||
+ if (ANIMATED_EXT.test(original_url)) {
|
||||
+ const filepath = resolved_id.includes('?')
|
||||
+ ? resolved_id.slice(0, resolved_id.indexOf('?'))
|
||||
+ : resolved_id;
|
||||
+ const anim_meta = await sharp(filepath, { animated: true }).metadata();
|
||||
+ if (anim_meta.pages && anim_meta.pages > 1) {
|
||||
+ const image = await process_animated(filepath, anim_meta, plugin_context, vite_config);
|
||||
+ s.update(node.start, node.end, img_to_picture(content, node, image));
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (OPTIMIZABLE.test(url)) {
|
||||
- const image = await process_id(resolved_id, plugin_context, imagetools_plugin);
|
||||
+ const image = await process_id(resolved_id, plugin_context, imagetools_plugin, vite_config);
|
||||
s.update(node.start, node.end, img_to_picture(content, node, image));
|
||||
} else {
|
||||
const metadata = await sharp(resolved_id).metadata();
|
||||
@@ -132,20 +238,58 @@ export function image_plugin(imagetools_plugin) {
|
||||
}
|
||||
|
||||
/**
|
||||
- * @type {Array<ReturnType<typeof update_element>>}
|
||||
+ * Handles a static or dynamic <enhanced:video src=...> element.
|
||||
+ * @param {import('svelte/compiler').AST.RegularElement} node
|
||||
+ * @param {AST.Text | AST.ExpressionTag} src_attribute
|
||||
+ */
|
||||
+ async function update_video_element(node, src_attribute) {
|
||||
+ if (src_attribute.type === 'ExpressionTag') {
|
||||
+ const start =
|
||||
+ 'end' in src_attribute.expression
|
||||
+ ? src_attribute.expression.end
|
||||
+ : src_attribute.expression.range?.[0];
|
||||
+ const end =
|
||||
+ 'start' in src_attribute.expression
|
||||
+ ? src_attribute.expression.start
|
||||
+ : src_attribute.expression.range?.[1];
|
||||
+ if (typeof start !== 'number' || typeof end !== 'number') {
|
||||
+ throw new Error('ExpressionTag has no range');
|
||||
+ }
|
||||
+ const src_var_name = content.substring(start, end).trim();
|
||||
+ s.update(node.start, node.end, dynamic_video_to_video(content, node, src_var_name));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const original_url = src_attribute.raw.trim();
|
||||
+ const resolved_id = (await plugin_context.resolve(original_url, filename))?.id;
|
||||
+ if (!resolved_id) {
|
||||
+ throw new Error(`Could not locate ${original_url} for <enhanced:video>`);
|
||||
+ }
|
||||
+ const filepath = resolved_id.includes('?')
|
||||
+ ? resolved_id.slice(0, resolved_id.indexOf('?'))
|
||||
+ : resolved_id;
|
||||
+ const video = await process_video(filepath, plugin_context, vite_config);
|
||||
+ s.update(node.start, node.end, video_to_video(content, node, video));
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * @type {Array<ReturnType<typeof update_element | typeof update_video_element>>}
|
||||
*/
|
||||
const pending_ast_updates = [];
|
||||
|
||||
walk(/** @type {import('svelte/compiler').AST.TemplateNode} */ (ast), null, {
|
||||
RegularElement(node, { next }) {
|
||||
if ('name' in node && node.name === 'enhanced:img') {
|
||||
- // Compare node tag match
|
||||
const src = get_attr_value(node, 'src');
|
||||
-
|
||||
if (!src || typeof src === 'boolean') return;
|
||||
-
|
||||
pending_ast_updates.push(update_element(node, src));
|
||||
+ return;
|
||||
+ }
|
||||
|
||||
+ if ('name' in node && node.name === 'enhanced:video') {
|
||||
+ const src = get_attr_value(node, 'src');
|
||||
+ if (!src || typeof src === 'boolean') return;
|
||||
+ pending_ast_updates.push(update_video_element(node, src));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -172,7 +316,9 @@ export function image_plugin(imagetools_plugin) {
|
||||
|
||||
if (ast.css) {
|
||||
const css = content.substring(ast.css.start, ast.css.end);
|
||||
- const modified = css.replaceAll('enhanced\\:img', 'img');
|
||||
+ const modified = css
|
||||
+ .replaceAll('enhanced\\:img', 'img')
|
||||
+ .replaceAll('enhanced\\:video', 'video');
|
||||
if (modified !== css) {
|
||||
s.update(ast.css.start, ast.css.end, modified);
|
||||
}
|
||||
@@ -194,18 +340,70 @@ export function image_plugin(imagetools_plugin) {
|
||||
* @param {import('vite').Plugin} imagetools_plugin
|
||||
* @returns {Promise<import('vite-imagetools').Picture>}
|
||||
*/
|
||||
-async function process_id(resolved_id, plugin_context, imagetools_plugin) {
|
||||
+async function process_id(resolved_id, plugin_context, imagetools_plugin, vite_config) {
|
||||
if (!imagetools_plugin.load) {
|
||||
throw new Error('Invalid instance of vite-imagetools. Could not find load method.');
|
||||
}
|
||||
const hook = imagetools_plugin.load;
|
||||
const handler = typeof hook === 'object' ? hook.handler : hook;
|
||||
- const module_info = await handler.call(plugin_context, resolved_id);
|
||||
+
|
||||
+ // Cache keyed by file content + query params (build only).
|
||||
+ const q = resolved_id.indexOf('?');
|
||||
+ const filepath = q >= 0 ? resolved_id.slice(0, q) : resolved_id;
|
||||
+ const query = q >= 0 ? resolved_id.slice(q + 1) : '';
|
||||
+
|
||||
+ if (vite_config?.command === 'serve') {
|
||||
+ const module_info = await handler.call(plugin_context, resolved_id);
|
||||
+ if (!module_info) throw new Error(`Could not load ${resolved_id}`);
|
||||
+ const code = typeof module_info === 'string' ? module_info : module_info.code;
|
||||
+ return parse_object(code.replace('export default', '').replace(/;$/, '').trim());
|
||||
+ }
|
||||
+
|
||||
+ const cached = read_img_cache(filepath, query);
|
||||
+ if (cached) {
|
||||
+ /** @type {Record<string, string>} */
|
||||
+ const new_refs = {};
|
||||
+ for (const asset of cached.assets) {
|
||||
+ new_refs[asset.key] = plugin_context.emitFile({ type: 'asset', name: asset.name, source: asset.source });
|
||||
+ }
|
||||
+ const code = cached.template.replace(/\$REF\[([^\]]+)\]/g, (_, k) => `__VITE_ASSET__${new_refs[k]}__`);
|
||||
+ return parse_object(code.replace('export default', '').replace(/;$/, '').trim());
|
||||
+ }
|
||||
+
|
||||
+ // Cache miss — intercept emitFile to capture assets for caching.
|
||||
+ /** @type {{ key: string, name: string, source: Buffer }[]} */
|
||||
+ const emitted = [];
|
||||
+ const original_emit = plugin_context.emitFile.bind(plugin_context);
|
||||
+ const proxy_ctx = new Proxy(plugin_context, {
|
||||
+ get(target, prop) {
|
||||
+ if (prop === 'emitFile') {
|
||||
+ return (/** @type {any} */ opts) => {
|
||||
+ const ref = original_emit(opts);
|
||||
+ if (opts.type === 'asset' && opts.source) {
|
||||
+ emitted.push({ key: ref, name: opts.name ?? '', source: Buffer.from(opts.source) });
|
||||
+ }
|
||||
+ return ref;
|
||||
+ };
|
||||
+ }
|
||||
+ return /** @type {any} */ (target)[prop];
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ const module_info = await handler.call(proxy_ctx, resolved_id);
|
||||
if (!module_info) {
|
||||
throw new Error(`Could not load ${resolved_id}`);
|
||||
}
|
||||
- const code = typeof module_info === 'string' ? module_info : module_info.code;
|
||||
- return parse_object(code.replace('export default', '').replace(/;$/, '').trim());
|
||||
+ const raw_code = typeof module_info === 'string' ? module_info : module_info.code;
|
||||
+
|
||||
+ if (emitted.length > 0) {
|
||||
+ let template = raw_code;
|
||||
+ for (const asset of emitted) {
|
||||
+ template = template.replaceAll(`__VITE_ASSET__${asset.key}__`, `$REF[${asset.key}]`);
|
||||
+ }
|
||||
+ write_img_cache(filepath, query, { assets: emitted, template });
|
||||
+ }
|
||||
+
|
||||
+ return parse_object(raw_code.replace('export default', '').replace(/;$/, '').trim());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,6 +543,59 @@ function to_value(src) {
|
||||
return src.startsWith('__VITE_ASSET__') ? `{"${src}"}` : `"${src}"`;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Generates a static <video> element from a processed video object.
|
||||
+ * Emits WebM first (better compression) then MP4 (Safari fallback).
|
||||
+ * @param {string} content
|
||||
+ * @param {import('svelte/compiler').AST.RegularElement} node
|
||||
+ * @param {{ sources: Record<string,string>, video: { src: string, w: number, h: number } }} video
|
||||
+ */
|
||||
+function video_to_video(content, node, video) {
|
||||
+ const skip = new Set(['src', 'width', 'height']);
|
||||
+ const has = (/** @type {string} */ name) =>
|
||||
+ node.attributes.some((a) => 'name' in a && /** @type {any} */ (a).name === name);
|
||||
+ const pass_through = node.attributes
|
||||
+ .filter((a) => 'name' in a && !skip.has(/** @type {any} */ (a).name))
|
||||
+ .map((a) => content.substring(a.start, a.end))
|
||||
+ .join(' ');
|
||||
+ const extra = pass_through ? ' ' + pass_through : '';
|
||||
+ const preload = has('preload') ? '' : ' preload="metadata"';
|
||||
+ const controls = has('controls') ? '' : ' controls';
|
||||
+ let res = `<video width=${video.video.w} height=${video.video.h}${preload}${controls}${extra}>`;
|
||||
+ for (const [format, src] of Object.entries(video.sources)) {
|
||||
+ res += `<source src=${to_value(src)} type="video/${format}" />`;
|
||||
+ }
|
||||
+ return res + '</video>';
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Generates a dynamic {#if} block for <enhanced:video src={expr} />.
|
||||
+ * @param {string} content
|
||||
+ * @param {import('svelte/compiler').AST.RegularElement} node
|
||||
+ * @param {string} src_var_name
|
||||
+ */
|
||||
+function dynamic_video_to_video(content, node, src_var_name) {
|
||||
+ const skip = new Set(['src', 'width', 'height']);
|
||||
+ const has = (/** @type {string} */ name) =>
|
||||
+ node.attributes.some((a) => 'name' in a && /** @type {any} */ (a).name === name);
|
||||
+ const pass_through = node.attributes
|
||||
+ .filter((a) => 'name' in a && !skip.has(/** @type {any} */ (a).name))
|
||||
+ .map((a) => content.substring(a.start, a.end))
|
||||
+ .join(' ');
|
||||
+ const extra = pass_through ? ' ' + pass_through : '';
|
||||
+ const preload = has('preload') ? '' : ' preload="metadata"';
|
||||
+ const controls = has('controls') ? '' : ' controls';
|
||||
+ return `{#if typeof ${src_var_name} === 'string'}
|
||||
+ <video src={${src_var_name}}${preload}${controls}${extra}></video>
|
||||
+{:else}
|
||||
+ <video width={${src_var_name}.video.w} height={${src_var_name}.video.h}${preload}${controls}${extra}>
|
||||
+ {#each Object.entries(${src_var_name}.sources) as [format, src]}
|
||||
+ <source {src} type={'video/' + format} />
|
||||
+ {/each}
|
||||
+ </video>
|
||||
+{/if}`;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* For images like `<img src={manually_imported} />`
|
||||
* @param {string} content
|
||||
Generated
+338
-77
@@ -5,9 +5,7 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
patchedDependencies:
|
||||
'@sveltejs/enhanced-img':
|
||||
hash: 3870abe8d0cf58437ab9e677757ee9dc3f035f804ffee5c41d3be06cab748492
|
||||
path: patches/@sveltejs__enhanced-img.patch
|
||||
'@sveltejs/enhanced-img@0.10.4': e313100e058c1ce98f02a1eeb196881f22fcb6af5d992973eaae763037b27509
|
||||
|
||||
importers:
|
||||
|
||||
@@ -15,55 +13,61 @@ importers:
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-static':
|
||||
specifier: ^3.0.10
|
||||
version: 3.0.10(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))
|
||||
version: 3.0.10(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(svelte@5.55.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))
|
||||
'@sveltejs/enhanced-img':
|
||||
specifier: ^0.10.4
|
||||
version: 0.10.4(patch_hash=3870abe8d0cf58437ab9e677757ee9dc3f035f804ffee5c41d3be06cab748492)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(rollup@4.60.2)(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
version: 0.10.4(patch_hash=e313100e058c1ce98f02a1eeb196881f22fcb6af5d992973eaae763037b27509)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(rollup@4.60.2)(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
'@sveltejs/kit':
|
||||
specifier: ^2.57.1
|
||||
version: 2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
version: 2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(svelte@5.55.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
'@sveltejs/vite-plugin-svelte':
|
||||
specifier: ^6.2.4
|
||||
version: 6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
version: 6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
'@types/node':
|
||||
specifier: ^25.9.1
|
||||
version: 25.9.1
|
||||
mdsvex:
|
||||
specifier: ^0.12.7
|
||||
version: 0.12.7(svelte@5.55.4(@typescript-eslint/types@8.58.0))
|
||||
version: 0.12.7(svelte@5.55.4)
|
||||
prettier:
|
||||
specifier: ^3.8.3
|
||||
version: 3.8.3
|
||||
prettier-plugin-svelte:
|
||||
specifier: ^3.5.1
|
||||
version: 3.5.1(prettier@3.8.3)(svelte@5.55.4(@typescript-eslint/types@8.58.0))
|
||||
version: 3.5.1(prettier@3.8.3)(svelte@5.55.4)
|
||||
sass-embedded:
|
||||
specifier: ^1.100.0
|
||||
version: 1.100.0
|
||||
sharp:
|
||||
specifier: ^0.34.5
|
||||
version: 0.34.5
|
||||
svelte:
|
||||
specifier: ^5.55.4
|
||||
version: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
version: 5.55.4
|
||||
svelte-check:
|
||||
specifier: ^4.4.6
|
||||
version: 4.4.6(picomatch@4.0.4)(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3)
|
||||
version: 4.4.6(picomatch@4.0.4)(svelte@5.55.4)(typescript@5.9.3)
|
||||
svelte-preprocess:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3(postcss@8.5.10)(sass@1.99.0)(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3)
|
||||
version: 6.0.3(postcss@8.5.10)(sass@1.100.0)(svelte@5.55.4)(typescript@5.9.3)
|
||||
svelte-preprocess-cssmodules:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1(svelte@5.55.4(@typescript-eslint/types@8.58.0))
|
||||
version: 3.0.1(svelte@5.55.4)
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
vite:
|
||||
specifier: ^7.3.2
|
||||
version: 7.3.2(@types/node@25.9.1)(sass@1.99.0)
|
||||
version: 7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^6.1.1
|
||||
version: 6.1.1(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
version: 6.1.1(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
|
||||
packages:
|
||||
|
||||
'@bufbuild/protobuf@2.12.0':
|
||||
resolution: {integrity: sha512-B/XlCaFIP8LOwzo+bz5uFzATYokcwCKQcghqnlfwSmM5eX/qTkvDBnDPs+gXtX/RyjxJ4DRikECcPJbyALA8FA==}
|
||||
|
||||
'@emnapi/runtime@1.10.0':
|
||||
resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
|
||||
|
||||
@@ -699,10 +703,6 @@ packages:
|
||||
'@types/unist@2.0.11':
|
||||
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
||||
|
||||
'@typescript-eslint/types@8.58.0':
|
||||
resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
acorn@8.16.0:
|
||||
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@@ -723,10 +723,17 @@ packages:
|
||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
|
||||
chokidar@5.0.0:
|
||||
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
clsx@2.1.1:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
colorjs.io@0.5.2:
|
||||
resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==}
|
||||
|
||||
cookie@0.6.0:
|
||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -787,12 +794,16 @@ packages:
|
||||
globrex@0.1.2:
|
||||
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
|
||||
|
||||
has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
imagetools-core@9.1.0:
|
||||
resolution: {integrity: sha512-xQjs+2vrxLnAjCq+omuNkd5UQTld9/bP8+YT0LyYTlKfuSQtgUBvqhUwGugzSAh6sCdN+LnROMuLswn5hZ9Fhg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
immutable@5.1.5:
|
||||
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
|
||||
immutable@5.1.6:
|
||||
resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
@@ -878,18 +889,142 @@ packages:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
|
||||
readdirp@5.0.0:
|
||||
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
rollup@4.60.2:
|
||||
resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
rxjs@7.8.2:
|
||||
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||
|
||||
sade@1.8.1:
|
||||
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
sass@1.99.0:
|
||||
resolution: {integrity: sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==}
|
||||
sass-embedded-all-unknown@1.100.0:
|
||||
resolution: {integrity: sha512-auFtXY/kwYILmSVjtBDwyj0axcLbYYiffOKWoaXHnI5bsYwiRbBh3EneR1rpbX2ZIZCrwX93i5pxKLTZF/662Q==}
|
||||
cpu: ['!arm', '!arm64', '!riscv64', '!x64']
|
||||
|
||||
sass-embedded-android-arm64@1.100.0:
|
||||
resolution: {integrity: sha512-W+Ru9JwTnfU0UX3jSZcbqFdtKFMcYdfFwytc57h2DgnqCOIiAqI2E06mABZBZC+r3LwXCBuS5GbXAGeVgvVDkA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
sass-embedded-android-arm@1.100.0:
|
||||
resolution: {integrity: sha512-70f3HgX2pFNmzpGQ86n5e6QfWn2fP4QUQGfFQK0P1XH73ZLIzLo2YqygrGKGKeeqtc5eU2Wl1/xQzhzuKnO4kw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
sass-embedded-android-riscv64@1.100.0:
|
||||
resolution: {integrity: sha512-icU3o0V/uCSytSpf+tX5Lf51BvyQEbLzDUJfUi9etSauYBGHpPKkdtdZH0si4v98phq11Kl8rSV1SggksxF1Hg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [riscv64]
|
||||
os: [android]
|
||||
|
||||
sass-embedded-android-x64@1.100.0:
|
||||
resolution: {integrity: sha512-mevF9VQk6gEYByy8+jusaHGmd7Usb2ytX/DsEOd0JtOGCtcf1kh575xJ6OUBDIcJ15uLnbau/0iy1eP6WVBvWA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
sass-embedded-darwin-arm64@1.100.0:
|
||||
resolution: {integrity: sha512-1PVlYi61POo93IT/FfrG1mc1tAHxeSTyUALF2aOFmXGWjVXr3bQzEQiBGCOvQbj/ix+5hNyXFXcEMEyKvtUJJA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
sass-embedded-darwin-x64@1.100.0:
|
||||
resolution: {integrity: sha512-x97o3JnGyImZNCIVs9wQHJUE5QCvmVIKaH1cwrz/5dK7OT1FpeNiW+u9TUomP9hG6Ekjd8EL8NBHpxTfIhdjmg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
sass-embedded-linux-arm64@1.100.0:
|
||||
resolution: {integrity: sha512-Dwjmj8Z6VRy7rAi53JAdEwIyUjpfl7PhpSc2/LpQPQx+aO5Dp7Spaipkax0ufJl1SoDUdchCsM4y/88YaluorQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: glibc
|
||||
|
||||
sass-embedded-linux-arm@1.100.0:
|
||||
resolution: {integrity: sha512-9Ul7O1eKrc5YlhwWjkp8tZPSe3UEwSZ1uwUZOQom1HL0pRlBA6F/IlGZYFTLwnHMIP1fc77MMNaBRfc05mKMpw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: glibc
|
||||
|
||||
sass-embedded-linux-musl-arm64@1.100.0:
|
||||
resolution: {integrity: sha512-XpACJB2KjSLjf2e9uuvGVdOURsoNrFqgRiihhXyUHK9W0t3LIHb7z5MA/7XGPIT9bWSOO2zyw+rH/FHtDV/Yrg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: musl
|
||||
|
||||
sass-embedded-linux-musl-arm@1.100.0:
|
||||
resolution: {integrity: sha512-sl0JgbGloPyJg66XXx5UDSDScZ0oU85DpMQU4JU/sCUCFj1Z8zZ69SJWKTCNE4/jwnce7WI2zPCV5AG+RHOZJw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: musl
|
||||
|
||||
sass-embedded-linux-musl-riscv64@1.100.0:
|
||||
resolution: {integrity: sha512-ShvI0Kx04mwoCARwZ0UjiT97isQvzO80tAt91zmFyHLN9kelc/IrQi940farSm2xQVPCKdeVyeG0ekBsokSpYQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: musl
|
||||
|
||||
sass-embedded-linux-musl-x64@1.100.0:
|
||||
resolution: {integrity: sha512-TDBCRWNuS4RDLQXvRc1gjZlWiWTWaWGp0Bwu/IKwJxov81lsvrCs3TihTyNXtW7V5aoN4Ky3r0QOkNb3mwmBnA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: musl
|
||||
|
||||
sass-embedded-linux-riscv64@1.100.0:
|
||||
resolution: {integrity: sha512-j4ENJGOheO+fm3j/yorLxCjBP6/XskrZx7dTLlT+lXYwN/qqCqoA/gsNLI0McS3DFM6GBwPiffzWsdWS8t6sEQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: glibc
|
||||
|
||||
sass-embedded-linux-x64@1.100.0:
|
||||
resolution: {integrity: sha512-0vUSN8j0WGtCJIOPh//EmUvYGHW0QOe5iul8qyhPk50MAcw49MA0r34AhftjDdx94ILPF6vApFs0gwHPQRlpVA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: glibc
|
||||
|
||||
sass-embedded-unknown-all@1.100.0:
|
||||
resolution: {integrity: sha512-c+naBgWId4MIpToXcI0DgqetjdAkwTTAxFAuOaBz7HUXLdyG1oZRrEvSsbe41nEdQOKH0vgofVFCeSQgoXOG9A==}
|
||||
os: ['!android', '!darwin', '!linux', '!win32']
|
||||
|
||||
sass-embedded-win32-arm64@1.100.0:
|
||||
resolution: {integrity: sha512-iE+yxj+hUXwwbqpHkXxgAWTzeRfcWxJ7SSTQEPMk48lwq3oCrWLlz5sQuWHbuTK/i0GKQfROdP+hOmPi89yjUg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
sass-embedded-win32-x64@1.100.0:
|
||||
resolution: {integrity: sha512-qI4F8MI7/KYoy9NdjJfhSspG42WPkADSNDvwEV7qWvCSFC83koJssRsKO2/PfY+niZz6BG65Ic/D+A11h959hw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
sass-embedded@1.100.0:
|
||||
resolution: {integrity: sha512-Ut8wlQSk19tm7jMK6mz6cF1+e+E7tUnW2tM02zQDPnOTcVbV8qCQG8UWxZkkNlY50+hV3hqP24OOkUlMz8xBpw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
hasBin: true
|
||||
|
||||
sass@1.100.0:
|
||||
resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
hasBin: true
|
||||
|
||||
semver@7.7.4:
|
||||
@@ -916,6 +1051,10 @@ packages:
|
||||
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
||||
deprecated: Please use @jridgewell/sourcemap-codec instead
|
||||
|
||||
supports-color@8.1.1:
|
||||
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
svelte-check@4.4.6:
|
||||
resolution: {integrity: sha512-kP1zG81EWaFe9ZyTv4ZXv44Csi6Pkdpb7S3oj6m+K2ec/IcDg/a8LsFsnVLqm2nxtkSwsd5xPj/qFkTBgXHXjg==}
|
||||
engines: {node: '>= 18.0.0'}
|
||||
@@ -975,6 +1114,14 @@ packages:
|
||||
resolution: {integrity: sha512-q8DFohk6vUswSng95IZb9nzWJnbINZsK7OiM1snAa3qCjJBL0ZQpvMyAaVXjUukdM75J/m8UE8xwqat8Ors/zQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
sync-child-process@1.0.2:
|
||||
resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
sync-message-port@1.2.0:
|
||||
resolution: {integrity: sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
tinyglobby@0.2.16:
|
||||
resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -1016,6 +1163,9 @@ packages:
|
||||
unist-util-visit@2.0.3:
|
||||
resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==}
|
||||
|
||||
varint@6.0.0:
|
||||
resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
|
||||
|
||||
vfile-message@2.0.4:
|
||||
resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==}
|
||||
|
||||
@@ -1081,6 +1231,8 @@ packages:
|
||||
|
||||
snapshots:
|
||||
|
||||
'@bufbuild/protobuf@2.12.0': {}
|
||||
|
||||
'@emnapi/runtime@1.10.0':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@@ -1431,28 +1583,28 @@ snapshots:
|
||||
dependencies:
|
||||
acorn: 8.16.0
|
||||
|
||||
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))':
|
||||
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(svelte@5.55.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))':
|
||||
dependencies:
|
||||
'@sveltejs/kit': 2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
'@sveltejs/kit': 2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(svelte@5.55.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
|
||||
'@sveltejs/enhanced-img@0.10.4(patch_hash=3870abe8d0cf58437ab9e677757ee9dc3f035f804ffee5c41d3be06cab748492)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(rollup@4.60.2)(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))':
|
||||
'@sveltejs/enhanced-img@0.10.4(patch_hash=e313100e058c1ce98f02a1eeb196881f22fcb6af5d992973eaae763037b27509)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(rollup@4.60.2)(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
magic-string: 0.30.21
|
||||
sharp: 0.34.5
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
svelte-parse-markup: 0.1.5(svelte@5.55.4(@typescript-eslint/types@8.58.0))
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass@1.99.0)
|
||||
svelte: 5.55.4
|
||||
svelte-parse-markup: 0.1.5(svelte@5.55.4)
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)
|
||||
vite-imagetools: 9.0.3(rollup@4.60.2)
|
||||
zimmerframe: 1.1.4
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
||||
'@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))':
|
||||
'@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(svelte@5.55.4)(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.1.0
|
||||
'@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0)
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
'@types/cookie': 0.6.0
|
||||
acorn: 8.16.0
|
||||
cookie: 0.6.0
|
||||
@@ -1463,27 +1615,27 @@ snapshots:
|
||||
mrmime: 2.0.1
|
||||
set-cookie-parser: 3.1.0
|
||||
sirv: 3.0.2
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass@1.99.0)
|
||||
svelte: 5.55.4
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
'@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))':
|
||||
'@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
obug: 2.1.1
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass@1.99.0)
|
||||
svelte: 5.55.4
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)
|
||||
|
||||
'@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))':
|
||||
'@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)))(svelte@5.55.4(@typescript-eslint/types@8.58.0))(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
'@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)))(svelte@5.55.4)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
deepmerge: 4.3.1
|
||||
magic-string: 0.30.21
|
||||
obug: 2.1.1
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass@1.99.0)
|
||||
vitefu: 1.1.3(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0))
|
||||
svelte: 5.55.4
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)
|
||||
vitefu: 1.1.3(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0))
|
||||
|
||||
'@types/cookie@0.6.0': {}
|
||||
|
||||
@@ -1501,9 +1653,6 @@ snapshots:
|
||||
|
||||
'@types/unist@2.0.11': {}
|
||||
|
||||
'@typescript-eslint/types@8.58.0':
|
||||
optional: true
|
||||
|
||||
acorn@8.16.0: {}
|
||||
|
||||
aria-query@5.3.1: {}
|
||||
@@ -1516,8 +1665,15 @@ snapshots:
|
||||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
|
||||
chokidar@5.0.0:
|
||||
dependencies:
|
||||
readdirp: 5.0.0
|
||||
optional: true
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
colorjs.io@0.5.2: {}
|
||||
|
||||
cookie@0.6.0: {}
|
||||
|
||||
debug@4.4.3:
|
||||
@@ -1561,11 +1717,9 @@ snapshots:
|
||||
|
||||
esm-env@1.2.2: {}
|
||||
|
||||
esrap@2.2.5(@typescript-eslint/types@8.58.0):
|
||||
esrap@2.2.5:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/types': 8.58.0
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
@@ -1578,10 +1732,11 @@ snapshots:
|
||||
|
||||
globrex@0.1.2: {}
|
||||
|
||||
has-flag@4.0.0: {}
|
||||
|
||||
imagetools-core@9.1.0: {}
|
||||
|
||||
immutable@5.1.5:
|
||||
optional: true
|
||||
immutable@5.1.6: {}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
optional: true
|
||||
@@ -1607,13 +1762,13 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
mdsvex@0.12.7(svelte@5.55.4(@typescript-eslint/types@8.58.0)):
|
||||
mdsvex@0.12.7(svelte@5.55.4):
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
'@types/unist': 2.0.11
|
||||
prism-svelte: 0.4.7
|
||||
prismjs: 1.30.0
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
svelte: 5.55.4
|
||||
unist-util-visit: 2.0.3
|
||||
vfile-message: 2.0.4
|
||||
|
||||
@@ -1640,10 +1795,10 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
prettier-plugin-svelte@3.5.1(prettier@3.8.3)(svelte@5.55.4(@typescript-eslint/types@8.58.0)):
|
||||
prettier-plugin-svelte@3.5.1(prettier@3.8.3)(svelte@5.55.4):
|
||||
dependencies:
|
||||
prettier: 3.8.3
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
svelte: 5.55.4
|
||||
|
||||
prettier@3.8.3: {}
|
||||
|
||||
@@ -1653,6 +1808,9 @@ snapshots:
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
readdirp@5.0.0:
|
||||
optional: true
|
||||
|
||||
rollup@4.60.2:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@@ -1684,14 +1842,105 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc': 4.60.2
|
||||
fsevents: 2.3.3
|
||||
|
||||
rxjs@7.8.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
sade@1.8.1:
|
||||
dependencies:
|
||||
mri: 1.2.0
|
||||
|
||||
sass@1.99.0:
|
||||
sass-embedded-all-unknown@1.100.0:
|
||||
dependencies:
|
||||
chokidar: 4.0.3
|
||||
immutable: 5.1.5
|
||||
sass: 1.100.0
|
||||
optional: true
|
||||
|
||||
sass-embedded-android-arm64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-android-arm@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-android-riscv64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-android-x64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-darwin-arm64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-darwin-x64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-linux-arm64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-linux-arm@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-linux-musl-arm64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-linux-musl-arm@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-linux-musl-riscv64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-linux-musl-x64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-linux-riscv64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-linux-x64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-unknown-all@1.100.0:
|
||||
dependencies:
|
||||
sass: 1.100.0
|
||||
optional: true
|
||||
|
||||
sass-embedded-win32-arm64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded-win32-x64@1.100.0:
|
||||
optional: true
|
||||
|
||||
sass-embedded@1.100.0:
|
||||
dependencies:
|
||||
'@bufbuild/protobuf': 2.12.0
|
||||
colorjs.io: 0.5.2
|
||||
immutable: 5.1.6
|
||||
rxjs: 7.8.2
|
||||
supports-color: 8.1.1
|
||||
sync-child-process: 1.0.2
|
||||
varint: 6.0.0
|
||||
optionalDependencies:
|
||||
sass-embedded-all-unknown: 1.100.0
|
||||
sass-embedded-android-arm: 1.100.0
|
||||
sass-embedded-android-arm64: 1.100.0
|
||||
sass-embedded-android-riscv64: 1.100.0
|
||||
sass-embedded-android-x64: 1.100.0
|
||||
sass-embedded-darwin-arm64: 1.100.0
|
||||
sass-embedded-darwin-x64: 1.100.0
|
||||
sass-embedded-linux-arm: 1.100.0
|
||||
sass-embedded-linux-arm64: 1.100.0
|
||||
sass-embedded-linux-musl-arm: 1.100.0
|
||||
sass-embedded-linux-musl-arm64: 1.100.0
|
||||
sass-embedded-linux-musl-riscv64: 1.100.0
|
||||
sass-embedded-linux-musl-x64: 1.100.0
|
||||
sass-embedded-linux-riscv64: 1.100.0
|
||||
sass-embedded-linux-x64: 1.100.0
|
||||
sass-embedded-unknown-all: 1.100.0
|
||||
sass-embedded-win32-arm64: 1.100.0
|
||||
sass-embedded-win32-x64: 1.100.0
|
||||
|
||||
sass@1.100.0:
|
||||
dependencies:
|
||||
chokidar: 5.0.0
|
||||
immutable: 5.1.6
|
||||
source-map-js: 1.2.1
|
||||
optionalDependencies:
|
||||
'@parcel/watcher': 2.5.6
|
||||
@@ -1742,39 +1991,43 @@ snapshots:
|
||||
|
||||
sourcemap-codec@1.4.8: {}
|
||||
|
||||
svelte-check@4.4.6(picomatch@4.0.4)(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3):
|
||||
supports-color@8.1.1:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
svelte-check@4.4.6(picomatch@4.0.4)(svelte@5.55.4)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
chokidar: 4.0.3
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picocolors: 1.1.1
|
||||
sade: 1.8.1
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
svelte: 5.55.4
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- picomatch
|
||||
|
||||
svelte-parse-markup@0.1.5(svelte@5.55.4(@typescript-eslint/types@8.58.0)):
|
||||
svelte-parse-markup@0.1.5(svelte@5.55.4):
|
||||
dependencies:
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
svelte: 5.55.4
|
||||
|
||||
svelte-preprocess-cssmodules@3.0.1(svelte@5.55.4(@typescript-eslint/types@8.58.0)):
|
||||
svelte-preprocess-cssmodules@3.0.1(svelte@5.55.4):
|
||||
dependencies:
|
||||
acorn: 8.16.0
|
||||
big.js: 6.2.2
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.25.9
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
svelte: 5.55.4
|
||||
|
||||
svelte-preprocess@6.0.3(postcss@8.5.10)(sass@1.99.0)(svelte@5.55.4(@typescript-eslint/types@8.58.0))(typescript@5.9.3):
|
||||
svelte-preprocess@6.0.3(postcss@8.5.10)(sass@1.100.0)(svelte@5.55.4)(typescript@5.9.3):
|
||||
dependencies:
|
||||
svelte: 5.55.4(@typescript-eslint/types@8.58.0)
|
||||
svelte: 5.55.4
|
||||
optionalDependencies:
|
||||
postcss: 8.5.10
|
||||
sass: 1.99.0
|
||||
sass: 1.100.0
|
||||
typescript: 5.9.3
|
||||
|
||||
svelte@5.55.4(@typescript-eslint/types@8.58.0):
|
||||
svelte@5.55.4:
|
||||
dependencies:
|
||||
'@jridgewell/remapping': 2.3.5
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -1787,7 +2040,7 @@ snapshots:
|
||||
clsx: 2.1.1
|
||||
devalue: 5.7.1
|
||||
esm-env: 1.2.2
|
||||
esrap: 2.2.5(@typescript-eslint/types@8.58.0)
|
||||
esrap: 2.2.5
|
||||
is-reference: 3.0.3
|
||||
locate-character: 3.0.0
|
||||
magic-string: 0.30.21
|
||||
@@ -1795,6 +2048,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@typescript-eslint/types'
|
||||
|
||||
sync-child-process@1.0.2:
|
||||
dependencies:
|
||||
sync-message-port: 1.2.0
|
||||
|
||||
sync-message-port@1.2.0: {}
|
||||
|
||||
tinyglobby@0.2.16:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
@@ -1806,8 +2065,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
tslib@2.8.1:
|
||||
optional: true
|
||||
tslib@2.8.1: {}
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
@@ -1830,6 +2088,8 @@ snapshots:
|
||||
unist-util-is: 4.1.0
|
||||
unist-util-visit-parents: 3.1.1
|
||||
|
||||
varint@6.0.0: {}
|
||||
|
||||
vfile-message@2.0.4:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
@@ -1843,17 +2103,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
||||
vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)):
|
||||
vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)):
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.6(typescript@5.9.3)
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass@1.99.0)
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite@7.3.2(@types/node@25.9.1)(sass@1.99.0):
|
||||
vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0):
|
||||
dependencies:
|
||||
esbuild: 0.27.7
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
@@ -1864,10 +2124,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 25.9.1
|
||||
fsevents: 2.3.3
|
||||
sass: 1.99.0
|
||||
sass: 1.100.0
|
||||
sass-embedded: 1.100.0
|
||||
|
||||
vitefu@1.1.3(vite@7.3.2(@types/node@25.9.1)(sass@1.99.0)):
|
||||
vitefu@1.1.3(vite@7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)):
|
||||
optionalDependencies:
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass@1.99.0)
|
||||
vite: 7.3.2(@types/node@25.9.1)(sass-embedded@1.100.0)(sass@1.100.0)
|
||||
|
||||
zimmerframe@1.1.4: {}
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
onlyBuiltDependencies:
|
||||
- "@parcel/watcher"
|
||||
- cwebp-bin
|
||||
- es5-ext
|
||||
- esbuild
|
||||
@@ -16,4 +17,4 @@ allowBuilds:
|
||||
vite-plugin-image-tools: true
|
||||
|
||||
patchedDependencies:
|
||||
'@sveltejs/enhanced-img': patches/@sveltejs__enhanced-img.patch
|
||||
'@sveltejs/enhanced-img@0.10.4': patches/@sveltejs__enhanced-img@0.10.4.patch
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
let meow = "";
|
||||
</script>
|
||||
|
||||
{#snippet StoreButton(store: string, url: string)}
|
||||
<a aria-label="Store Page Button" href={url}"></a>
|
||||
{/snippet}
|
||||
|
||||
<style lang="scss">
|
||||
.StoreButton {
|
||||
display: block;
|
||||
width: fit-content;
|
||||
height: 30px;
|
||||
border: 2px outset #1da1f2;
|
||||
background: #1da1f2;
|
||||
margin: 2px;
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
.StoreButton:hover {
|
||||
filter: brightness(110%);
|
||||
}
|
||||
|
||||
.StoreButton:active {
|
||||
border-style: inset;
|
||||
|
||||
img {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,6 +7,7 @@
|
||||
<nav class="nav">
|
||||
<a href="/">Home</a>
|
||||
<a href="/posts">Posts</a>
|
||||
<a href="/store">Store</a>
|
||||
<a href="/changelog">Changelog</a>
|
||||
</nav>
|
||||
{/snippet}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import json from "./dic.json";
|
||||
import "./webButton.scss";
|
||||
const imageModules = import.meta.glob(
|
||||
"./imgs/*.{avif,AVIF,heif,HEIF,jpeg,JPEG,jpg,JPG,png,PNG,tiff,TIFF,webp,WEBP}",
|
||||
"./imgs/*.{avif,AVIF,heif,HEIF,jpeg,JPEG,jpg,JPG,png,PNG,tiff,TIFF,webp,WEBP,gif,GIF}",
|
||||
{
|
||||
eager: true,
|
||||
import: "default",
|
||||
@@ -11,13 +11,13 @@
|
||||
},
|
||||
},
|
||||
);
|
||||
const gifModules = import.meta.glob("./imgs/*.{gif,GIF}", {
|
||||
eager: true,
|
||||
import: "default",
|
||||
});
|
||||
// const gifModules = import.meta.glob("./imgs/*.{gif,GIF}", {
|
||||
// eager: true,
|
||||
// import: "default",
|
||||
// });
|
||||
function getImageUrl(name: string): any {
|
||||
let query = "./imgs/" + name;
|
||||
return imageModules[query] || gifModules[query];
|
||||
return imageModules[query];
|
||||
}
|
||||
export { WebButtons };
|
||||
</script>
|
||||
|
||||
@@ -30,7 +30,7 @@ export type commit = {
|
||||
export async function load() {
|
||||
const raw = execSync(GITLOGCMD).toString();
|
||||
const lines = raw.split('\n');
|
||||
// console.log(lines);
|
||||
console.log(lines);
|
||||
const commits: commit[] = []
|
||||
for (var x of lines) {
|
||||
const [hash, unix, title, auther, email, add, del] = x.split("|")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script>
|
||||
import { commitCard } from "./commitCard.svelte";
|
||||
|
||||
let { data } = $props();
|
||||
let commits = data.data.commits;
|
||||
console.log(commits);
|
||||
import "./card.scss";
|
||||
import { commitCard } from "./commitCard.svelte";
|
||||
</script>
|
||||
|
||||
<table class="commitContainer">
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<script lang="ts">
|
||||
const videos = import.meta.glob("./*.mp4", {
|
||||
eager: true,
|
||||
import: "default",
|
||||
query: { enhanced: true },
|
||||
});
|
||||
console.log(videos);
|
||||
const scub = videos["./scub.mp4"];
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<meta property="og:type" content="video.other" />
|
||||
<meta property="og:video" content="https://doloro.co.uk{scub.video.src}" />
|
||||
<meta property="og:video:type" content="video/mp4" />
|
||||
<meta property="og:video:width" content="1920" />
|
||||
<meta property="og:video:height" content="1080" />
|
||||
<meta property="og:title" content="Scublings" />
|
||||
<!-- Optional but helps Discord show a thumbnail -->
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://yourdomain.com/scub-thumbnail.jpg"
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<div class="video-container">
|
||||
<enhanced:video src={scub}> </enhanced:video>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.video-container {
|
||||
display: flex;
|
||||
height: auto;
|
||||
aspect-ratio: 16/9;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-width: 3px;
|
||||
border-color: var(--primary);
|
||||
border-style: ridge;
|
||||
video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
export const prerender = true;
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export type StoreEntity = {
|
||||
image: string,
|
||||
name: string,
|
||||
short: string, // Short Description
|
||||
gumroad?: string,
|
||||
jinxxy?: string
|
||||
}
|
||||
|
||||
const Catalog: StoreEntity[] = [
|
||||
{ name: "Magic Circle", image: "MagicCircle", short: "", gumroad: undefined, jinxxy: "" } // Placeholder
|
||||
];
|
||||
|
||||
export async function load() {
|
||||
return {
|
||||
catalog: Catalog
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
let { data } = $props();
|
||||
import { snackProduct } from "./snacks.svelte";
|
||||
</script>
|
||||
|
||||
{#each data.catalog as item}
|
||||
{@render snackProduct(item)}
|
||||
{/each}
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="ts" module>
|
||||
const imgs = import.meta.glob(
|
||||
"./imgs/*.{avif,AVIF,heif,HEIF,jpeg,JPEG,jpg,JPG,png,PNG,tiff,TIFF,webp,WEBP,svg,SVG,gif,GIF}",
|
||||
{
|
||||
eager: true,
|
||||
import: "default",
|
||||
query: {
|
||||
enhanced: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
let trimmedImgs: Record<string, unknown> = {};
|
||||
Object.entries(imgs).forEach(([key, value]) => {
|
||||
let trimmedString = key.replace("./imgs/", "");
|
||||
let trimmedString2 = trimmedString.replace(/(.*)\.[^.]+$/, "$1");
|
||||
trimmedImgs[trimmedString2] = value;
|
||||
});
|
||||
|
||||
export function snackImage(img: string) {
|
||||
return trimmedImgs[img];
|
||||
}
|
||||
</script>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.9 MiB |
@@ -0,0 +1,32 @@
|
||||
.SnackProductContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
border-width: 3px;
|
||||
border-style: groove;
|
||||
border-color: blue;
|
||||
|
||||
width: 300px;
|
||||
height: 500px;
|
||||
|
||||
picture {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.SnackProductDetails {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.SnackProductButtons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<script lang="ts" module>
|
||||
import { type StoreEntity } from "./+page.server.ts";
|
||||
import { snackImage } from "./func.svelte";
|
||||
import "./snacks.scss";
|
||||
export { snackProduct };
|
||||
</script>
|
||||
|
||||
{#snippet snackProduct(product: StoreEntity)}
|
||||
<div class="SnackProductContainer">
|
||||
<enhanced:img src={snackImage(product.image)} />
|
||||
<div class="SnackProductDetails">
|
||||
<h1>{product.name}</h1>
|
||||
<div class="SnackProductButtons"></div>
|
||||
</div>
|
||||
</div>
|
||||
{/snippet}
|
||||
+4
-1
@@ -5,7 +5,10 @@ import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
export default defineConfig({
|
||||
// publicDir: "./meowmeowmeow",
|
||||
plugins: [enhancedImages(), sveltekit(), tsconfigPaths()],
|
||||
plugins: [
|
||||
sveltekit(),
|
||||
enhancedImages(),
|
||||
tsconfigPaths()],
|
||||
build: {
|
||||
},
|
||||
resolve: {
|
||||
|
||||
Reference in New Issue
Block a user