diff options
Diffstat (limited to 'llama.cpp/tools/server/webui/vite.config.ts')
| -rw-r--r-- | llama.cpp/tools/server/webui/vite.config.ts | 166 |
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' + } + } +}); |
