mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-07-08 12:10:25 +00:00
ef9c13d4c2
* chore: `npm audit fix --force` * feat: Update sidebar toggle to use Logo * refactor: Clean up favicon SVG * feat: Refactor logo component and implement theme-aware favicon generation * feat: Add configurable padding to generated PWA assets * test: Add unit tests for writeThemeFavicons * refactor: Componentization * feat: WIP * feat: WIP * feat: WIP * feat: Mobile UI * feat: add SEARCH route constant * feat: create SidebarNavigationSearchResults component * refactor: use SidebarNavigationSearchResults in conversation list * feat: enable mobile search navigation in sidebar actions * feat: add mobile search route and page * fix: prevent sidebar overflow on mobile viewports * fix: Mobile sidebar * feat: Mobile Search WIP * feat: Mobile WIP * feat: Add PWA standalone detection and refine mobile UI * feat: Improve mobile layout, sidebar handling, and chat scrolling * feat: Improve mobile sidebar visibility and iOS Safari chat spacing * fix: Disable auto-scroll on mobile * chore: Linting * fix: Wrong condition * feat: Mobile chat scroll * refactor: WIP * fix: Desktop initial scroll always working again * fix: Partial fix for mobile auto-scroll / initial scroll * fix: Desktop auto-scroll on initial load and during streaming * fix: Mobile scrolling logic * refactor: Clean up * feat: Improve start UI * feat: Add `delay` to `fadeInView` * feat: Auto-scroll button * refactor: Cleanup * refactor: Extract chat dialogs and alerts into dedicated component * refactor: Reorganize ChatScreen component structure and initialization * feat: Improve auto-scroll after sending message * feat: UI improvements * fix: Settings link * feat: UI improvements * fix: better UI spacing * fix: Remove unneeded logic * fix: Chat Processing Info UI rendering * feat: Improve mobile UI * feat: UI improvement * fix: Conditional transition delay for Chat Messages based on route from * fix: Delay mobile sidebar collapse for smoother transitions * fix: Mobile scroll down button + sidebar pointer events * fix: Mobile UI * fix: Auto scrolling * fix: Implement dynamic height calculations for chat auto-scroll positioning and UI elements * fix: Retrieve `autofocus` for Chat Form textarea * fix: Use proper class Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * refactor: extract scroll-to-bottom logic and fix message send flow * fix: update viewport store usage and remove conflicting autofocus * feat: add accessibility labels to scroll down button * fix: correct HTML structure in sidebar empty states * fix: dynamically toggle processing info visibility * chore: remove commented exports and fix formatting * fix * fix: Mobile Chat Form Add Action Sheet interactions --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
108 lines
3.5 KiB
TypeScript
108 lines
3.5 KiB
TypeScript
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
const PROJECT_ROOT = resolve(HERE, '..');
|
|
|
|
const DEFAULT_LOGO = resolve(PROJECT_ROOT, 'src/lib/assets/logo.svg');
|
|
const DEFAULT_OUT_DIR = resolve(PROJECT_ROOT, 'static');
|
|
const DEFAULT_OUT_LIGHT = resolve(DEFAULT_OUT_DIR, 'favicon.svg');
|
|
const DEFAULT_OUT_DARK = resolve(DEFAULT_OUT_DIR, 'favicon-dark.svg');
|
|
|
|
const CURRENT_COLOR = 'currentColor';
|
|
|
|
export interface ColorizedFavicon {
|
|
light: string;
|
|
dark: string;
|
|
}
|
|
|
|
export interface WriteThemeFaviconsOptions {
|
|
sourcePath?: string;
|
|
lightOutPath?: string;
|
|
darkOutPath?: string;
|
|
/**
|
|
* Fraction of the icon (0..1) to leave as an even margin on each side.
|
|
* Applied by wrapping the inner content in a `<g transform="...">` so the
|
|
* source `src/lib/assets/logo.svg` is not modified. Pass 0 to disable.
|
|
*/
|
|
padding?: number;
|
|
}
|
|
|
|
/**
|
|
* Replace every `currentColor` occurrence in the SVG with the given color.
|
|
* Pure: no filesystem access, so it is straightforward to unit-test.
|
|
*/
|
|
export function colorizeFaviconSvg(
|
|
svg: string,
|
|
lightColor: string,
|
|
darkColor: string
|
|
): ColorizedFavicon {
|
|
return {
|
|
light: svg.replaceAll(CURRENT_COLOR, lightColor),
|
|
dark: svg.replaceAll(CURRENT_COLOR, darkColor)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Shrink the inner SVG content uniformly and re-center it so `padding` (a
|
|
* 0..1 fraction) is reserved as equal margin on each side. Returns the input
|
|
* unchanged for non-positive padding, missing/invalid `viewBox`, or unexpected
|
|
* markup so the caller always gets a renderable SVG.
|
|
*/
|
|
export function padFaviconSvg(svg: string, padding: number): string {
|
|
if (!(padding > 0) || padding >= 1) return svg;
|
|
|
|
const viewBoxMatch = svg.match(/viewBox\s*=\s*["']([^"']+)["']/i);
|
|
if (!viewBoxMatch) return svg;
|
|
|
|
const parts = viewBoxMatch[1]
|
|
.trim()
|
|
.split(/[\s,]+/)
|
|
.map(Number);
|
|
if (parts.length !== 4 || parts.some((n) => !Number.isFinite(n))) return svg;
|
|
|
|
const [, , width, height] = parts;
|
|
if (width <= 0 || height <= 0) return svg;
|
|
|
|
const scale = 1 - padding;
|
|
const translateX = (padding * width) / 2;
|
|
const translateY = (padding * height) / 2;
|
|
|
|
const openTagStart = svg.search(/<svg\b/i);
|
|
if (openTagStart === -1) return svg;
|
|
const openTagEnd = svg.indexOf('>', openTagStart);
|
|
if (openTagEnd === -1) return svg;
|
|
const closeStart = svg.lastIndexOf('</svg');
|
|
if (closeStart === -1 || closeStart <= openTagEnd) return svg;
|
|
|
|
const openTag = svg.slice(0, openTagEnd + 1);
|
|
const inner = svg.slice(openTagEnd + 1, closeStart);
|
|
const closeTag = svg.slice(closeStart);
|
|
|
|
const group = `<g transform="translate(${translateX} ${translateY}) scale(${scale})">`;
|
|
return `${openTag}${group}${inner}</g>${closeTag}`;
|
|
}
|
|
|
|
/**
|
|
* Read `src/lib/assets/logo.svg`, colorize it for both themes, and write
|
|
* the results to the static directory so the PWA asset generator can consume
|
|
* them. Paths can be overridden for tests.
|
|
*/
|
|
export function writeThemeFavicons(
|
|
lightColor: string,
|
|
darkColor: string,
|
|
{
|
|
sourcePath = DEFAULT_LOGO,
|
|
lightOutPath = DEFAULT_OUT_LIGHT,
|
|
darkOutPath = DEFAULT_OUT_DARK,
|
|
padding = 0
|
|
}: WriteThemeFaviconsOptions = {}
|
|
): void {
|
|
const source = readFileSync(sourcePath, 'utf-8');
|
|
const { light, dark } = colorizeFaviconSvg(source, lightColor, darkColor);
|
|
mkdirSync(dirname(lightOutPath), { recursive: true });
|
|
writeFileSync(lightOutPath, padFaviconSvg(light, padding));
|
|
writeFileSync(darkOutPath, padFaviconSvg(dark, padding));
|
|
}
|