summaryrefslogtreecommitdiff
path: root/llama.cpp/tools/server/webui/vite.config.ts
diff options
context:
space:
mode:
Diffstat (limited to 'llama.cpp/tools/server/webui/vite.config.ts')
-rw-r--r--llama.cpp/tools/server/webui/vite.config.ts166
1 files changed, 166 insertions, 0 deletions
diff --git a/llama.cpp/tools/server/webui/vite.config.ts b/llama.cpp/tools/server/webui/vite.config.ts
new file mode 100644
index 0000000..5183c09
--- /dev/null
+++ b/llama.cpp/tools/server/webui/vite.config.ts
@@ -0,0 +1,166 @@
+import tailwindcss from '@tailwindcss/vite';
+import { sveltekit } from '@sveltejs/kit/vite';
+import * as fflate from 'fflate';
+import { readFileSync, writeFileSync, existsSync } from 'fs';
+import { resolve } from 'path';
+import { defineConfig } from 'vite';
+import devtoolsJson from 'vite-plugin-devtools-json';
+import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
+
+const GUIDE_FOR_FRONTEND = `
+<!--
+ This is a single file build of the frontend.
+ It is automatically generated by the build process.
+ Do not edit this file directly.
+ To make changes, refer to the "Web UI" section in the README.
+-->
+`.trim();
+
+const MAX_BUNDLE_SIZE = 2 * 1024 * 1024;
+
+/**
+ * the maximum size of an embedded asset in bytes,
+ * e.g. maximum size of embedded font (see node_modules/katex/dist/fonts/*.woff2)
+ */
+const MAX_ASSET_SIZE = 32000;
+
+/** public/index.html.gz minified flag */
+const ENABLE_JS_MINIFICATION = true;
+
+function llamaCppBuildPlugin() {
+ return {
+ name: 'llamacpp:build',
+ apply: 'build' as const,
+ closeBundle() {
+ // Ensure the SvelteKit adapter has finished writing to ../public
+ setTimeout(() => {
+ try {
+ const indexPath = resolve('../public/index.html');
+ const gzipPath = resolve('../public/index.html.gz');
+
+ if (!existsSync(indexPath)) {
+ return;
+ }
+
+ let content = readFileSync(indexPath, 'utf-8');
+
+ const faviconPath = resolve('static/favicon.svg');
+ if (existsSync(faviconPath)) {
+ const faviconContent = readFileSync(faviconPath, 'utf-8');
+ const faviconBase64 = Buffer.from(faviconContent).toString('base64');
+ const faviconDataUrl = `data:image/svg+xml;base64,${faviconBase64}`;
+
+ content = content.replace(/href="[^"]*favicon\.svg"/g, `href="${faviconDataUrl}"`);
+
+ console.log('✓ Inlined favicon.svg as base64 data URL');
+ }
+
+ content = content.replace(/\r/g, '');
+ content = GUIDE_FOR_FRONTEND + '\n' + content;
+
+ const compressed = fflate.gzipSync(Buffer.from(content, 'utf-8'), { level: 9 });
+
+ compressed[0x4] = 0;
+ compressed[0x5] = 0;
+ compressed[0x6] = 0;
+ compressed[0x7] = 0;
+ compressed[0x9] = 0;
+
+ if (compressed.byteLength > MAX_BUNDLE_SIZE) {
+ throw new Error(
+ `Bundle size is too large (${Math.ceil(compressed.byteLength / 1024)} KB).\n` +
+ `Please reduce the size of the frontend or increase MAX_BUNDLE_SIZE in vite.config.ts.\n`
+ );
+ }
+
+ writeFileSync(gzipPath, compressed);
+ console.log('✓ Created index.html.gz');
+ } catch (error) {
+ console.error('Failed to create gzip file:', error);
+ }
+ }, 100);
+ }
+ };
+}
+
+export default defineConfig({
+ resolve: {
+ alias: {
+ 'katex-fonts': resolve('node_modules/katex/dist/fonts')
+ }
+ },
+ build: {
+ assetsInlineLimit: MAX_ASSET_SIZE,
+ chunkSizeWarningLimit: 3072,
+ minify: ENABLE_JS_MINIFICATION
+ },
+ css: {
+ preprocessorOptions: {
+ scss: {
+ additionalData: `
+ $use-woff2: true;
+ $use-woff: false;
+ $use-ttf: false;
+ `
+ }
+ }
+ },
+ plugins: [tailwindcss(), sveltekit(), devtoolsJson(), llamaCppBuildPlugin()],
+ test: {
+ projects: [
+ {
+ extends: './vite.config.ts',
+ test: {
+ name: 'client',
+ environment: 'browser',
+ browser: {
+ enabled: true,
+ provider: 'playwright',
+ instances: [{ browser: 'chromium' }]
+ },
+ include: ['tests/client/**/*.svelte.{test,spec}.{js,ts}'],
+ setupFiles: ['./vitest-setup-client.ts']
+ }
+ },
+ {
+ extends: './vite.config.ts',
+ test: {
+ name: 'unit',
+ environment: 'node',
+ include: ['tests/unit/**/*.{test,spec}.{js,ts}']
+ }
+ },
+ {
+ extends: './vite.config.ts',
+ test: {
+ name: 'ui',
+ environment: 'browser',
+ browser: {
+ enabled: true,
+ provider: 'playwright',
+ instances: [{ browser: 'chromium', headless: true }]
+ },
+ include: ['tests/stories/**/*.stories.{js,ts,svelte}'],
+ setupFiles: ['./.storybook/vitest.setup.ts']
+ },
+ plugins: [
+ storybookTest({
+ storybookScript: 'pnpm run storybook --no-open'
+ })
+ ]
+ }
+ ]
+ },
+
+ server: {
+ proxy: {
+ '/v1': 'http://localhost:8080',
+ '/props': 'http://localhost:8080',
+ '/models': 'http://localhost:8080'
+ },
+ headers: {
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
+ 'Cross-Origin-Opener-Policy': 'same-origin'
+ }
+ }
+});