visual continunity

This commit is contained in:
Bradley Nelson
2026-06-17 22:07:18 -06:00
parent daa3010458
commit 89e14f8f9e
89 changed files with 19249 additions and 1367 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
/** Display helpers shared across list views. */
/**
* Map a legacy `icon_class` (e.g. "fas fa-folder", "fa-file-pdf") to an icon
* Map an `icon_class` (e.g. "fas fa-folder", "fa-file-pdf") to an icon
* registry name (the FA token without the `fa-` prefix).
*/
export function iconNameFromClass(iconClass: string | undefined | null): string {
@@ -0,0 +1,34 @@
import { describe, expect, it } from 'vitest';
import { hashUrlToPath } from './hashRedirect';
describe('hashUrlToPath', () => {
it('maps root and files', () => {
expect(hashUrlToPath('#/')).toBe('/files');
expect(hashUrlToPath('#/files')).toBe('/files');
});
it('maps folder deep links to the new path', () => {
expect(hashUrlToPath('#/files/folder/abc')).toBe('/files/abc');
expect(hashUrlToPath('#/files/folder/abc/def')).toBe('/files/abc/def');
});
it('maps the named sections', () => {
expect(hashUrlToPath('#/shared')).toBe('/shared');
expect(hashUrlToPath('#/sharedwithme')).toBe('/shared-with-me');
expect(hashUrlToPath('#/recent')).toBe('/recent');
expect(hashUrlToPath('#/favorites')).toBe('/favorites');
expect(hashUrlToPath('#/trash')).toBe('/trash');
expect(hashUrlToPath('#/photos')).toBe('/photos');
expect(hashUrlToPath('#/music')).toBe('/music');
});
it('ignores query strings in the hash', () => {
expect(hashUrlToPath('#/recent?foo=bar')).toBe('/recent');
});
it('returns null for non-legacy or unknown hashes', () => {
expect(hashUrlToPath('')).toBeNull();
expect(hashUrlToPath('#section')).toBeNull();
expect(hashUrlToPath('#/unknown')).toBeNull();
});
});
@@ -1,10 +1,10 @@
/**
* Translate a legacy hash route (`#/...` from the vanilla app) into the new
* SvelteKit path, so old bookmarks and external links keep working.
* Translate an old hash route (`#/...` from the vanilla frontend) into the new
* SvelteKit path, so existing bookmarks and external links keep working.
*
* Returns the new pathname, or null when the hash isn't a legacy route.
* Returns the new pathname, or null when the hash isn't a recognised route.
*/
export function legacyHashToPath(hash: string): string | null {
export function hashUrlToPath(hash: string): string | null {
if (!hash.startsWith('#/')) return null;
const raw = hash.slice(1); // drop the '#'
const [pathPart] = raw.split('?');
+38
View File
@@ -0,0 +1,38 @@
/**
* Downscale an image File to a data URI for avatar upload. Ported from the
* original utils/imageResize.js: never upscales, caps the longest edge at
* `maxDim`, prefers WebP with a JPEG fallback.
*/
function readAsDataUrl(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const fr = new FileReader();
fr.onload = () => resolve(fr.result as string);
fr.onerror = () => reject(fr.error);
fr.readAsDataURL(file);
});
}
function loadImage(src: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error('image load failed'));
img.src = src;
});
}
export async function resizeImageToDataUrl(file: File, maxDim = 512): Promise<string> {
const dataUrl = await readAsDataUrl(file);
const img = await loadImage(dataUrl);
const scale = Math.min(1, maxDim / Math.max(img.width, img.height));
const w = Math.round(img.width * scale);
const h = Math.round(img.height * scale);
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
if (!ctx) return dataUrl;
ctx.drawImage(img, 0, 0, w, h);
const webp = canvas.toDataURL('image/webp', 0.85);
return webp.startsWith('data:image/webp') ? webp : canvas.toDataURL('image/jpeg', 0.85);
}
-34
View File
@@ -1,34 +0,0 @@
import { describe, expect, it } from 'vitest';
import { legacyHashToPath } from './legacyHash';
describe('legacyHashToPath', () => {
it('maps root and files', () => {
expect(legacyHashToPath('#/')).toBe('/files');
expect(legacyHashToPath('#/files')).toBe('/files');
});
it('maps folder deep links to the new path', () => {
expect(legacyHashToPath('#/files/folder/abc')).toBe('/files/abc');
expect(legacyHashToPath('#/files/folder/abc/def')).toBe('/files/abc/def');
});
it('maps the named sections', () => {
expect(legacyHashToPath('#/shared')).toBe('/shared');
expect(legacyHashToPath('#/sharedwithme')).toBe('/shared-with-me');
expect(legacyHashToPath('#/recent')).toBe('/recent');
expect(legacyHashToPath('#/favorites')).toBe('/favorites');
expect(legacyHashToPath('#/trash')).toBe('/trash');
expect(legacyHashToPath('#/photos')).toBe('/photos');
expect(legacyHashToPath('#/music')).toBe('/music');
});
it('ignores query strings in the hash', () => {
expect(legacyHashToPath('#/recent?foo=bar')).toBe('/recent');
});
it('returns null for non-legacy or unknown hashes', () => {
expect(legacyHashToPath('')).toBeNull();
expect(legacyHashToPath('#section')).toBeNull();
expect(legacyHashToPath('#/unknown')).toBeNull();
});
});