visual continunity
This commit is contained in:
@@ -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('?');
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user