From: Aleksander Grygier Date: Mon, 10 Aug 2026 06:38:37 +0000 (+0200) Subject: ui: Linting & Formatting scripts (#26819) X-Git-Tag: upstream/0.0.10438~98 X-Git-Url: https://git.djapps.eu/?a=commitdiff_plain;h=92d1bb0c996f49bb09561f755ba784565e1aa51c;p=pkg%2Fggml%2Fsources%2Fllama.cpp ui: Linting & Formatting scripts (#26819) --- diff --git a/tools/ui/eslint.config.js b/tools/ui/eslint.config.js index fcbf7ee95..c65484048 100644 --- a/tools/ui/eslint.config.js +++ b/tools/ui/eslint.config.js @@ -1,14 +1,15 @@ // For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format -import storybook from 'eslint-plugin-storybook'; - -import prettier from 'eslint-config-prettier'; +import svelteConfig from './svelte.config.js'; import { includeIgnoreFile } from '@eslint/compat'; import js from '@eslint/js'; +import prettier from 'eslint-config-prettier'; +import perfectionist from 'eslint-plugin-perfectionist'; +import simpleImportSort from 'eslint-plugin-simple-import-sort'; +import storybook from 'eslint-plugin-storybook'; import svelte from 'eslint-plugin-svelte'; import globals from 'globals'; import { fileURLToPath } from 'node:url'; import ts from 'typescript-eslint'; -import svelteConfig from './svelte.config.js'; const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); @@ -21,32 +22,67 @@ export default ts.config( ...svelte.configs.prettier, { languageOptions: { globals: { ...globals.browser, ...globals.node } }, + plugins: { perfectionist, 'simple-import-sort': simpleImportSort }, rules: { - // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. - // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors - 'no-undef': 'off', - 'svelte/no-at-html-tags': 'off', - // This app uses hash-based routing (#/) where resolve() from $app/paths does not apply - 'svelte/no-navigation-without-resolve': 'off', - // Snippet bodies often ignore one or more of the parent's params // (e.g. `{#snippet children(_meta, ctx)}` when only ctx is read). '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' } ], - // Enforce empty line at end of file - 'eol-last': 'error' + 'eol-last': 'error', + // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. + // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + 'no-undef': 'off', + + 'padding-line-between-statements': [ + 'error', + // Blank line between function/class declarations. + { blankLine: 'always', next: ['function', 'class'], prev: ['function', 'class'] }, + // Blank line around if blocks (if/else and else if stay one statement). + { blankLine: 'always', next: '*', prev: 'if' }, + { blankLine: 'always', next: 'if', prev: '*' }, + // Blank line after the last declaration in a group. Because the 'never' + // rules below are scoped per declaration kind, a const group and a let + // group get separated by a blank line, while same-kind declarations stay + // together. + { blankLine: 'always', next: '*', prev: ['const', 'let', 'var'] }, + // No blank line between consecutive declarations of the same kind (kept + // last so each takes precedence over the always rule above for matching + // declaration pairs). + { blankLine: 'never', next: 'const', prev: 'const' }, + { blankLine: 'never', next: 'let', prev: 'let' }, + { blankLine: 'never', next: 'var', prev: 'var' }, + // Blank line before a statement that follows another statement in the block + // (works for return/throw/break/continue). A blank line for a terminal + // statement that opens a block body can't be enforced here: Prettier removes + // the leading blank line of a block, so the two formatters would fight. + { blankLine: 'always', next: ['return', 'throw', 'break', 'continue'], prev: '*' } + ], + + 'perfectionist/sort-objects': ['error', { type: 'natural' }], + + // Alphabetical order for variable declarations and object keys + 'perfectionist/sort-variable-declarations': ['error', { type: 'natural' }], + + // Sort imports alphabetically by module path, and sort named members within + // each statement. A single catch-all group keeps the list flat (no blank-line + // grouping); Prettier normalizes comma spacing afterwards. + 'simple-import-sort/imports': ['error', { groups: [['.*']] }], + 'svelte/no-at-html-tags': 'off', + + // This app uses hash-based routing (#/) where resolve() from $app/paths does not apply + 'svelte/no-navigation-without-resolve': 'off' } }, { files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], languageOptions: { parserOptions: { - projectService: true, extraFileExtensions: ['.svelte'], parser: ts.parser, + projectService: true, svelteConfig } } diff --git a/tools/ui/package-lock.json b/tools/ui/package-lock.json index 976ed050f..f9b793b29 100644 --- a/tools/ui/package-lock.json +++ b/tools/ui/package-lock.json @@ -39,6 +39,8 @@ "dompurify": "3.4.13", "eslint": "9.39.4", "eslint-config-prettier": "10.1.8", + "eslint-plugin-perfectionist": "^5.10.1", + "eslint-plugin-simple-import-sort": "^14.0.0", "eslint-plugin-storybook": "10.5.6", "eslint-plugin-svelte": "3.19.0", "fflate": "0.8.3", @@ -9281,6 +9283,226 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-perfectionist": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.10.1.tgz", + "integrity": "sha512-Kprsp9Us0GqAesYaAIzUViw57xYp5WBqzXrcE0Mtww++E5fexWXYBipMuuD7yvyH4vvpBH0+oJ+OMAmZ0oYXkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.65.0", + "natural-orderby": "^5.0.0" + }, + "engines": { + "node": "^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "eslint": "^8.45.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/project-service": { + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz", + "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.66.0", + "@typescript-eslint/types": "^8.66.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/scope-manager": { + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.66.0.tgz", + "integrity": "sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz", + "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/types": { + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.66.0.tgz", + "integrity": "sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz", + "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.66.0", + "@typescript-eslint/tsconfig-utils": "8.66.0", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/utils": { + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz", + "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.66.0", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.66.0.tgz", + "integrity": "sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.66.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-plugin-simple-import-sort": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-14.0.0.tgz", + "integrity": "sha512-NUJO0+XFCkk+o5EsAJruTgnfMEpeWrPWeJS15UVF60GgXmqz1BJ9/3hzlvG7lkL8Bubzos5cCLptThbFfPnSMQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, "node_modules/eslint-plugin-storybook": { "version": "10.5.6", "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-10.5.6.tgz", @@ -13196,6 +13418,16 @@ "dev": true, "license": "MIT" }, + "node_modules/natural-orderby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz", + "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", diff --git a/tools/ui/package.json b/tools/ui/package.json index 7f042f415..f6d6880d7 100644 --- a/tools/ui/package.json +++ b/tools/ui/package.json @@ -12,7 +12,7 @@ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "reset": "rm -rf .svelte-kit node_modules", - "format": "prettier --write .", + "format": "eslint --fix . && prettier --write .", "lint": "prettier --check . && eslint .", "test": "npm run test:ui -- --run && npm run test:client -- --run && npm run test:unit -- --run && npm run test:e2e", "test:e2e": "playwright test", @@ -36,6 +36,7 @@ "@playwright/test": "1.56.1", "@storybook/addon-a11y": "10.5.6", "@storybook/addon-docs": "10.5.6", + "@storybook/addon-mcp": "0.7.0", "@storybook/addon-svelte-csf": "5.1.2", "@storybook/addon-vitest": "10.5.6", "@storybook/sveltekit": "10.5.6", @@ -57,6 +58,8 @@ "dompurify": "3.4.13", "eslint": "9.39.4", "eslint-config-prettier": "10.1.8", + "eslint-plugin-perfectionist": "^5.10.1", + "eslint-plugin-simple-import-sort": "^14.0.0", "eslint-plugin-storybook": "10.5.6", "eslint-plugin-svelte": "3.19.0", "fflate": "0.8.3", @@ -99,8 +102,7 @@ "vite-plugin-devtools-json": "0.2.1", "vitest": "4.1.10", "vitest-browser-svelte": "2.1.1", - "workbox-window": "7.4.1", - "@storybook/addon-mcp": "0.7.0" + "workbox-window": "7.4.1" }, "overrides": { "cookie": "1.1.1", diff --git a/tools/ui/playwright.config.ts b/tools/ui/playwright.config.ts index 55bf38514..057ed416d 100644 --- a/tools/ui/playwright.config.ts +++ b/tools/ui/playwright.config.ts @@ -1,31 +1,31 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ - testDir: 'tests/e2e', - testMatch: ['**/*.e2e.ts'], - timeout: 30000, expect: { timeout: 5000 }, - fullyParallel: true, forbidOnly: !!process.env.CI, - retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, - reporter: 'line', - use: { - baseURL: 'http://localhost:8181', - trace: 'on-first-retry' - }, + fullyParallel: true, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } } ], + reporter: 'line', + retries: process.env.CI ? 2 : 0, + testDir: 'tests/e2e', + testMatch: ['**/*.e2e.ts'], + timeout: 30000, + use: { + baseURL: 'http://localhost:8181', + trace: 'on-first-retry' + }, webServer: { command: 'npm run build && npx http-server ./dist -p 8181', port: 8181, - timeout: 120000, - reuseExistingServer: !process.env.CI - } + reuseExistingServer: !process.env.CI, + timeout: 120000 + }, + workers: process.env.CI ? 1 : undefined }); diff --git a/tools/ui/pwa-assets-dark.config.ts b/tools/ui/pwa-assets-dark.config.ts index 358c0ebc0..1446b47a8 100644 --- a/tools/ui/pwa-assets-dark.config.ts +++ b/tools/ui/pwa-assets-dark.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from '@vite-pwa/assets-generator/config'; -import { FAVICON_COLORS, PWA_ASSET_GENERATOR } from './src/lib/constants/pwa'; import { writeThemeFavicons } from './scripts/favicon-colorize'; +import { FAVICON_COLORS, PWA_ASSET_GENERATOR } from './src/lib/constants/pwa'; +import { defineConfig } from '@vite-pwa/assets-generator/config'; writeThemeFavicons(FAVICON_COLORS.LIGHT, FAVICON_COLORS.DARK, { padding: PWA_ASSET_GENERATOR.FAVICON_PADDING @@ -10,18 +10,18 @@ export default defineConfig({ headLinkOptions: { preset: '2023' }, + images: ['static/favicon-dark.svg'], preset: { - transparent: { - sizes: [], - favicons: [[48, 'favicon-dark.ico']], - padding: PWA_ASSET_GENERATOR.FAVICON_PADDING + apple: { + sizes: [] }, maskable: { sizes: [] }, - apple: { + transparent: { + favicons: [[48, 'favicon-dark.ico']], + padding: PWA_ASSET_GENERATOR.FAVICON_PADDING, sizes: [] } - }, - images: ['static/favicon-dark.svg'] + } }); diff --git a/tools/ui/pwa-assets.config.ts b/tools/ui/pwa-assets.config.ts index b69884d94..5fed0a595 100644 --- a/tools/ui/pwa-assets.config.ts +++ b/tools/ui/pwa-assets.config.ts @@ -1,3 +1,11 @@ +import { writeThemeFavicons } from './scripts/favicon-colorize'; +import { + FAVICON_COLORS, + PWA_ASSET_GENERATOR, + PWA_GENERATOR_DEVICES, + THEME_COLORS +} from './src/lib/constants/pwa'; +import { SplashOrientation } from './src/lib/enums/splash.enums'; import { combinePresetAndAppleSplashScreens, defineConfig, @@ -5,14 +13,6 @@ import { } from '@vite-pwa/assets-generator/config'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; -import { - THEME_COLORS, - PWA_GENERATOR_DEVICES, - PWA_ASSET_GENERATOR, - FAVICON_COLORS -} from './src/lib/constants/pwa'; -import { SplashOrientation } from './src/lib/enums/splash.enums'; -import { writeThemeFavicons } from './scripts/favicon-colorize'; writeThemeFavicons(FAVICON_COLORS.LIGHT, FAVICON_COLORS.DARK, { padding: PWA_ASSET_GENERATOR.FAVICON_PADDING @@ -22,6 +22,7 @@ export default defineConfig({ headLinkOptions: { preset: PWA_ASSET_GENERATOR.LINK_PRESET }, + images: ['static/favicon.svg'], preset: combinePresetAndAppleSplashScreens( { ...minimal2023Preset, @@ -32,37 +33,37 @@ export default defineConfig({ } }, { - padding: PWA_ASSET_GENERATOR.SPLASH_PADDING, - resizeOptions: { - background: THEME_COLORS.BACKGROUND_LIGHT, - fit: PWA_ASSET_GENERATOR.FIT_MODE - }, - darkResizeOptions: { - background: THEME_COLORS.BACKGROUND_DARK, - fit: PWA_ASSET_GENERATOR.FIT_MODE - }, darkImageResolver: async (imageName: string) => { if (imageName.endsWith('favicon.svg')) { return readFileSync(resolve('static/favicon-dark.svg')); } }, + darkResizeOptions: { + background: THEME_COLORS.BACKGROUND_DARK, + fit: PWA_ASSET_GENERATOR.FIT_MODE + }, linkMediaOptions: { - log: true, addMediaScreen: PWA_ASSET_GENERATOR.ADD_MEDIA_SCREEN, basePath: PWA_ASSET_GENERATOR.BASE_PATH, + log: true, xhtml: PWA_ASSET_GENERATOR.XHTML }, - png: { - compressionLevel: PWA_ASSET_GENERATOR.PNG_COMPRESSION_LEVEL, - quality: PWA_ASSET_GENERATOR.PNG_QUALITY - }, name: (landscape, size, dark) => { const orientation = landscape ? SplashOrientation.LANDSCAPE : SplashOrientation.PORTRAIT; const darkPrefix = dark ? PWA_ASSET_GENERATOR.DARK_PREFIX : ''; + return `apple-splash-${orientation}-${darkPrefix}${size.width}x${size.height}.png`; + }, + padding: PWA_ASSET_GENERATOR.SPLASH_PADDING, + png: { + compressionLevel: PWA_ASSET_GENERATOR.PNG_COMPRESSION_LEVEL, + quality: PWA_ASSET_GENERATOR.PNG_QUALITY + }, + resizeOptions: { + background: THEME_COLORS.BACKGROUND_LIGHT, + fit: PWA_ASSET_GENERATOR.FIT_MODE } }, PWA_GENERATOR_DEVICES - ), - images: ['static/favicon.svg'] + ) }); diff --git a/tools/ui/scripts/favicon-colorize.ts b/tools/ui/scripts/favicon-colorize.ts index e1872b777..54a951296 100644 --- a/tools/ui/scripts/favicon-colorize.ts +++ b/tools/ui/scripts/favicon-colorize.ts @@ -4,12 +4,10 @@ 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 { @@ -39,8 +37,8 @@ export function colorizeFaviconSvg( darkColor: string ): ColorizedFavicon { return { - light: svg.replaceAll(CURRENT_COLOR, lightColor), - dark: svg.replaceAll(CURRENT_COLOR, darkColor) + dark: svg.replaceAll(CURRENT_COLOR, darkColor), + light: svg.replaceAll(CURRENT_COLOR, lightColor) }; } @@ -54,33 +52,40 @@ 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(/', openTagStart); + if (openTagEnd === -1) return svg; + const closeStart = svg.lastIndexOf('`; + return `${openTag}${group}${inner}${closeTag}`; } @@ -93,14 +98,15 @@ export function writeThemeFavicons( lightColor: string, darkColor: string, { - sourcePath = DEFAULT_LOGO, - lightOutPath = DEFAULT_OUT_LIGHT, darkOutPath = DEFAULT_OUT_DARK, - padding = 0 + lightOutPath = DEFAULT_OUT_LIGHT, + padding = 0, + sourcePath = DEFAULT_LOGO }: WriteThemeFaviconsOptions = {} ): void { const source = readFileSync(sourcePath, 'utf-8'); - const { light, dark } = colorizeFaviconSvg(source, lightColor, darkColor); + const { dark, light } = colorizeFaviconSvg(source, lightColor, darkColor); + mkdirSync(dirname(lightOutPath), { recursive: true }); writeFileSync(lightOutPath, padFaviconSvg(light, padding)); writeFileSync(darkOutPath, padFaviconSvg(dark, padding)); diff --git a/tools/ui/scripts/make-icons-circular.js b/tools/ui/scripts/make-icons-circular.js index 7dfd6521e..b4763c625 100644 --- a/tools/ui/scripts/make-icons-circular.js +++ b/tools/ui/scripts/make-icons-circular.js @@ -13,31 +13,28 @@ * maskable-icon and apple-touch-icon are left untouched. */ -import sharp from 'sharp'; import fs from 'fs'; import path from 'path'; +import sharp from 'sharp'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - const STATIC_DIR = path.resolve(__dirname, '..', 'static'); - const paddingPct = process.argv.reduce((acc, arg, i, args) => { if (arg === '--padding-pct' && args[i + 1]) return parseFloat(args[i + 1]); + return acc; }, 0); - // Scale down the source image before cropping to circle const scalePct = process.argv.reduce((acc, arg, i, args) => { if (arg === '--scale-pct' && args[i + 1]) return parseFloat(args[i + 1]); + return acc; }, 85); // default 85% - icon fills 85% of the circular area - // Source for circular icons: the maskable icon (white bg, full logo) const sourceIcon = 'maskable-icon-512x512.png'; const targetIcons = ['pwa-64x64.png', 'pwa-192x192.png', 'pwa-512x512.png']; - // maskable-icon and apple-touch-icon stay square const untouchedIcons = ['maskable-icon-512x512.png', 'apple-touch-icon-180x180.png']; @@ -47,10 +44,13 @@ async function makeCircle(targetFilename) { if (!fs.existsSync(sourcePath)) { console.log(`⏭️ ${sourceIcon} not found, skipping`); + return; } + if (!fs.existsSync(targetPath)) { console.log(`⏭️ ${targetFilename} not found, skipping`); + return; } @@ -58,16 +58,18 @@ async function makeCircle(targetFilename) { const size = Math.max(metadata.width, metadata.height); const radius = Math.floor((size * (1 - paddingPct / 100)) / 2); const center = Math.floor(size / 2); - // Build circular mask as RGBA buffer: white opaque circle on transparent bg const maskBuf = Buffer.alloc(size * size * 4, 0); + for (let y = 0; y < size; y++) { for (let x = 0; x < size; x++) { const dx = x - center; const dy = y - center; const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < radius) { const i = (y * size + x) * 4; + maskBuf[i] = 255; maskBuf[i + 1] = 255; maskBuf[i + 2] = 255; @@ -77,8 +79,9 @@ async function makeCircle(targetFilename) { } const tmpMask = path.join(STATIC_DIR, '.mask-tmp.png'); + await sharp(maskBuf, { - raw: { width: size, height: size, channels: 4 } + raw: { channels: 4, height: size, width: size } }) .png() .toFile(tmpMask); @@ -87,28 +90,26 @@ async function makeCircle(targetFilename) { const circleDiameter = Math.floor(size * (1 - paddingPct / 100)); const scaledSize = Math.floor((circleDiameter * scalePct) / 100); const offset = Math.floor((size - scaledSize) / 2); - const scaledBuf = await sharp(sourcePath) .resize(scaledSize, scaledSize, { - fit: 'cover', - background: { r: 255, g: 255, b: 255, alpha: 1 } + background: { alpha: 1, b: 255, g: 255, r: 255 }, + fit: 'cover' }) .ensureAlpha() .png() .toBuffer(); - // Step 2: Composite scaled image onto white background, then apply circular mask const output = await sharp({ create: { - width: size, - height: size, + background: { alpha: 1, b: 255, g: 255, r: 255 }, channels: 4, - background: { r: 255, g: 255, b: 255, alpha: 1 } + height: size, + width: size } }) .composite([ - { input: scaledBuf, top: offset, left: offset }, - { input: tmpMask, top: 0, left: 0, blend: 'dest-in' } + { input: scaledBuf, left: offset, top: offset }, + { blend: 'dest-in', input: tmpMask, left: 0, top: 0 } ]) .png() .toBuffer(); @@ -130,6 +131,7 @@ async function main() { console.log('\nUnchanged:'); for (const icon of untouchedIcons) { const fp = path.join(STATIC_DIR, icon); + console.log(` ${icon} (${fs.existsSync(fp) ? fs.statSync(fp).size + ' bytes' : 'missing'})`); } } diff --git a/tools/ui/scripts/vite-plugin-build-info.ts b/tools/ui/scripts/vite-plugin-build-info.ts index 972ba3b66..800238630 100644 --- a/tools/ui/scripts/vite-plugin-build-info.ts +++ b/tools/ui/scripts/vite-plugin-build-info.ts @@ -1,7 +1,7 @@ -import { writeFileSync, existsSync } from 'node:fs'; +import { BUILD_CONFIG } from '../src/lib/constants/pwa'; +import { existsSync, writeFileSync } from 'node:fs'; import { resolve } from 'path'; import type { Plugin } from 'vite'; -import { BUILD_CONFIG } from '../src/lib/constants/pwa'; let processed = false; @@ -15,27 +15,29 @@ const OUTPUT_DIR = process.env.LLAMA_UI_OUT_DIR ?? BUILD_CONFIG.OUTPUT_DIR; */ export function buildInfoPlugin(): Plugin { return { - name: 'llamacpp:build-info', apply: 'build', closeBundle() { setTimeout(() => { try { if (processed) return; + processed = true; const buildNumber = process.env.LLAMA_BUILD_NUMBER || 'b0000'; - const outDir = resolve(OUTPUT_DIR); const indexPath = resolve(outDir, 'index.html'); + if (!existsSync(indexPath)) return; const buildJsonPath = resolve(outDir, 'build.json'); + writeFileSync(buildJsonPath, JSON.stringify({ version: buildNumber }), 'utf-8'); console.log(`Created build.json (version: ${buildNumber})`); } catch (error) { console.error('Failed to write build.json:', error); } }, 100); - } + }, + name: 'llamacpp:build-info' }; } diff --git a/tools/ui/scripts/vite-plugin-nerdamer.ts b/tools/ui/scripts/vite-plugin-nerdamer.ts index 218c2fa23..84e463c6d 100644 --- a/tools/ui/scripts/vite-plugin-nerdamer.ts +++ b/tools/ui/scripts/vite-plugin-nerdamer.ts @@ -4,7 +4,6 @@ import { fileURLToPath } from 'url'; import type { Plugin } from 'vite'; const __dirname = dirname(fileURLToPath(import.meta.url)); - const VENDORS_DIR = resolve(__dirname, '../src/lib/vendors'); const VIRTUAL_ID = 'virtual:nerdamer'; const RESOLVED_ID = '\0' + VIRTUAL_ID; @@ -21,29 +20,32 @@ export function nerdamerPlugin(): Plugin { let bundled: string | null = null; return { - name: 'llamacpp:nerdamer', - resolveId(id) { - return id === VIRTUAL_ID ? RESOLVED_ID : undefined; - }, async load(id) { if (id !== RESOLVED_ID) return undefined; + if (bundled === null) { const result = await build({ - entryPoints: [resolve(VENDORS_DIR, 'nerdamer-prime/all.js')], - bundle: true, - minify: true, - format: 'iife', - globalName: 'nerdamer', alias: { 'big-integer': resolve(VENDORS_DIR, 'big-integer/BigInteger.js'), 'decimal.js': resolve(VENDORS_DIR, 'decimal.js/decimal.js') }, - write: false, - logLevel: 'silent' + bundle: true, + entryPoints: [resolve(VENDORS_DIR, 'nerdamer-prime/all.js')], + format: 'iife', + globalName: 'nerdamer', + logLevel: 'silent', + minify: true, + write: false }); + bundled = result.outputFiles[0].text; } + return `export default ${JSON.stringify(bundled)};`; + }, + name: 'llamacpp:nerdamer', + resolveId(id) { + return id === VIRTUAL_ID ? RESOLVED_ID : undefined; } }; } diff --git a/tools/ui/scripts/vite-plugin-relativize-base.ts b/tools/ui/scripts/vite-plugin-relativize-base.ts index ce2f1b6e9..f8eac1d66 100644 --- a/tools/ui/scripts/vite-plugin-relativize-base.ts +++ b/tools/ui/scripts/vite-plugin-relativize-base.ts @@ -1,7 +1,7 @@ -import { readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { BUILD_CONFIG } from '../src/lib/constants/pwa'; +import { existsSync, readFileSync, writeFileSync } from 'node:fs'; import { resolve } from 'path'; import type { Plugin } from 'vite'; -import { BUILD_CONFIG } from '../src/lib/constants/pwa'; let processed = false; @@ -11,11 +11,15 @@ function rewrite(path: string, pairs: [string, string][]): void { if (!existsSync(path)) { return; } + const text = readFileSync(path, 'utf-8'); + let out = text; + for (const [from, to] of pairs) { out = out.split(from).join(to); } + if (out !== text) { writeFileSync(path, out, 'utf-8'); } @@ -32,12 +36,12 @@ function rewrite(path: string, pairs: [string, string][]): void { */ export function relativizeBasePlugin(): Plugin { return { - name: 'llamacpp:relativize-base', apply: 'build', closeBundle() { setTimeout(() => { try { if (processed) return; + processed = true; const outDir = resolve(OUTPUT_DIR); @@ -56,6 +60,7 @@ export function relativizeBasePlugin(): Plugin { console.error('Failed to relativize base refs:', error); } }, 100); - } + }, + name: 'llamacpp:relativize-base' }; } diff --git a/tools/ui/scripts/vite-plugin-splash-screen.ts b/tools/ui/scripts/vite-plugin-splash-screen.ts index 059ce4920..45d931bab 100644 --- a/tools/ui/scripts/vite-plugin-splash-screen.ts +++ b/tools/ui/scripts/vite-plugin-splash-screen.ts @@ -1,10 +1,10 @@ -import { readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs'; -import { resolve } from 'path'; -import type { Plugin } from 'vite'; -import { TAB, NEWLINE } from '../src/lib/constants/code'; +import { NEWLINE, TAB } from '../src/lib/constants/code'; import { APPLE_DEVICES, BUILD_CONFIG, REGEX_PATTERNS, SPLASH_LINK } from '../src/lib/constants/pwa'; -import type { SplashDimensions } from '../src/lib/types'; import { SplashOrientation } from '../src/lib/enums/splash.enums'; +import type { SplashDimensions } from '../src/lib/types'; +import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { resolve } from 'path'; +import type { Plugin } from 'vite'; let processed = false; @@ -16,23 +16,26 @@ const OUTPUT_DIR = process.env.LLAMA_UI_OUT_DIR ?? BUILD_CONFIG.OUTPUT_DIR; */ export function generateSplashScreenLinks(outDir: string): string[] { const files = readdirSync(outDir).filter((f) => f.match(REGEX_PATTERNS.SPLASH_FILE)); + if (files.length === 0) return []; const dimMap = new Map(); + for (const [dims, spec] of Object.entries(APPLE_DEVICES)) { const [w, h] = dims.split('x').map(Number); + // logical-point dimensions - dimMap.set(`${w}x${h}`, { deviceW: spec.width, deviceH: spec.height, dpr: spec.dpr }); - dimMap.set(`${h}x${w}`, { deviceW: spec.width, deviceH: spec.height, dpr: spec.dpr }); + dimMap.set(`${w}x${h}`, { deviceH: spec.height, deviceW: spec.width, dpr: spec.dpr }); + dimMap.set(`${h}x${w}`, { deviceH: spec.height, deviceW: spec.width, dpr: spec.dpr }); // pixel dimensions (used by actual generated splash files) dimMap.set(`${w * spec.dpr}x${h * spec.dpr}`, { - deviceW: spec.width, deviceH: spec.height, + deviceW: spec.width, dpr: spec.dpr }); dimMap.set(`${h * spec.dpr}x${w * spec.dpr}`, { - deviceW: spec.width, deviceH: spec.height, + deviceW: spec.width, dpr: spec.dpr }); } @@ -42,20 +45,23 @@ export function generateSplashScreenLinks(outDir: string): string[] { for (const file of files) { const match = file.match(REGEX_PATTERNS.SPLASH_FILE); + if (!match) continue; + const orientation = match[1] as SplashOrientation; const isDark = !!match[2]; const pixelW = parseInt(match[3]); const pixelH = parseInt(match[4]); - const key = `${pixelW}x${pixelH}`; const spec = dimMap.get(key); + if (!spec) { console.warn(`Unknown splash screen dimensions: ${key} (${file})`); + continue; } - const { deviceW, deviceH, dpr } = spec; + const { deviceH, deviceW, dpr } = spec; const media = `screen and (device-width: ${deviceW}px) and (device-height: ${deviceH}px) and (-webkit-device-pixel-ratio: ${dpr}) and (orientation: ${orientation})`; const href = `./${file}`; @@ -73,16 +79,17 @@ export function generateSplashScreenLinks(outDir: string): string[] { export function splashScreenPlugin(): Plugin { return { - name: 'llamacpp:splash-screen', apply: 'build', closeBundle() { setTimeout(() => { try { if (processed) return; + processed = true; const outDir = resolve(OUTPUT_DIR); const indexPath = resolve(outDir, 'index.html'); + if (!existsSync(indexPath)) return; let content = readFileSync(indexPath, 'utf-8'); @@ -91,9 +98,11 @@ export function splashScreenPlugin(): Plugin { // The @vite-pwa/assets-generator generates apple-splash-*.png files; // this scans them and creates the tags SvelteKit needs. const splashLinks = generateSplashScreenLinks(outDir); + if (splashLinks.length > 0) { console.log(`Generated ${splashLinks.length} apple-splash link tags`); const splashHtml = splashLinks.map((l) => TAB + TAB + l).join(NEWLINE); + content = content.replace( REGEX_PATTERNS.HEAD_CLOSE, splashHtml + NEWLINE + TAB + TAB + '' @@ -110,6 +119,7 @@ export function splashScreenPlugin(): Plugin { console.error('Failed to process build output:', error); } }, 100); - } + }, + name: 'llamacpp:splash-screen' }; } diff --git a/tools/ui/src/app.d.ts b/tools/ui/src/app.d.ts index 814fcf523..6f26214e1 100644 --- a/tools/ui/src/app.d.ts +++ b/tools/ui/src/app.d.ts @@ -3,9 +3,8 @@ import 'vite-plugin-pwa/pwa-assets'; import 'vite-plugin-pwa/svelte'; - +import { ModelModality, ServerModelStatus, ServerRole } from '$lib/enums'; // Import chat types from dedicated module - import type { // API types ApiChatCompletionRequest, @@ -13,59 +12,57 @@ import type { ApiChatCompletionStreamChunk, ApiChatCompletionToolCall, ApiChatCompletionToolCallDelta, - ApiChatMessageData, ApiChatMessageContentPart, + ApiChatMessageData, ApiContextSizeError, ApiErrorResponse, ApiLlamaCppServerProps, ApiModelDataEntry, + ApiModelListResponse, ApiModelLoadStage, - ApiModelsSseProgress, ApiModelsSseData, ApiModelsSseEvent, - ApiModelListResponse, + ApiModelsSseProgress, ApiProcessingState, ApiRouterModelMeta, + ApiRouterModelsListResponse, ApiRouterModelsLoadRequest, ApiRouterModelsLoadResponse, ApiRouterModelsStatusRequest, ApiRouterModelsStatusResponse, - ApiRouterModelsListResponse, ApiRouterModelsUnloadRequest, ApiRouterModelsUnloadResponse, // Chat types ChatAttachmentDisplayItem, + ChatMessagePromptProgress, + ChatMessageSiblingInfo, + ChatMessageTimings, ChatMessageType, ChatRole, ChatUploadedFile, - ChatMessageSiblingInfo, - ChatMessagePromptProgress, - ChatMessageTimings, // Database types DatabaseConversation, DatabaseMessage, DatabaseMessageExtra, DatabaseMessageExtraAudioFile, - DatabaseMessageExtraVideoFile, DatabaseMessageExtraImageFile, - DatabaseMessageExtraTextFile, - DatabaseMessageExtraPdfFile, DatabaseMessageExtraLegacyContext, + DatabaseMessageExtraPdfFile, + DatabaseMessageExtraTextFile, + DatabaseMessageExtraVideoFile, ExportedConversation, ExportedConversations, + ModelLoadProgress, // Model types ModelModalities, ModelOption, - ModelLoadProgress, // Settings types SettingsChatServiceOptions, + SettingsConfigType, SettingsConfigValue, - SettingsFieldConfig, - SettingsConfigType + SettingsFieldConfig } from '$lib/types'; -import { ServerRole, ServerModelStatus, ModelModality } from '$lib/enums'; - declare global { // namespace App { // interface Error {} diff --git a/tools/ui/src/lib/components/app/actions/ActionIcon.svelte b/tools/ui/src/lib/components/app/actions/ActionIcon.svelte index 608ff6fab..e29b5ad67 100644 --- a/tools/ui/src/lib/components/app/actions/ActionIcon.svelte +++ b/tools/ui/src/lib/components/app/actions/ActionIcon.svelte @@ -1,8 +1,8 @@ {#each modalities as modality (modality)} diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte index e74bd8456..36895c8e7 100644 --- a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsList.svelte @@ -28,18 +28,18 @@ } let { - class: className = '', - style = '', + activeModelId, attachments = [], - readonly = false, - onFileRemove, - uploadedFiles = $bindable([]), + class: className = '', // Default to small size for form previews imageClass = '', imageHeight = 'h-24', imageWidth = 'w-auto', limitToSingleRow = false, - activeModelId + onFileRemove, + readonly = false, + style = '', + uploadedFiles = $bindable([]) }: Props = $props(); let carouselRef: HorizontalScrollCarousel | undefined = $state(); @@ -48,7 +48,7 @@ let previewFocusIndex = $state(0); let viewAllDialogOpen = $state(false); - let displayItems = $derived(getAttachmentDisplayItems({ uploadedFiles, attachments })); + let displayItems = $derived(getAttachmentDisplayItems({ attachments, uploadedFiles })); function openPreview(item: ChatAttachmentDisplayItem, event?: MouseEvent) { event?.stopPropagation(); diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte index 143621cd9..ba06e1815 100644 --- a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItem.svelte @@ -2,8 +2,8 @@ import { ChatAttachmentsListItemMcpPrompt, ChatAttachmentsListItemMcpResource, - ChatAttachmentsListItemThumbnailImage, - ChatAttachmentsListItemThumbnailFile + ChatAttachmentsListItemThumbnailFile, + ChatAttachmentsListItemThumbnailImage } from '$lib/components/app'; import { AttachmentType } from '$lib/enums'; import type { @@ -49,10 +49,10 @@ return { id, resource: { - uri: extra.uri, name: extra.name, + serverName: extra.serverName, title: extra.name, - serverName: extra.serverName + uri: extra.uri } }; } @@ -64,12 +64,12 @@ ? (item.attachment as DatabaseMessageExtraMcpPrompt) : item.uploadedFile?.mcpPrompt ? { - type: AttachmentType.MCP_PROMPT as const, + arguments: item.uploadedFile.mcpPrompt.arguments, + content: item.textContent ?? '', name: item.name, - serverName: item.uploadedFile.mcpPrompt.serverName, promptName: item.uploadedFile.mcpPrompt.promptName, - content: item.textContent ?? '', - arguments: item.uploadedFile.mcpPrompt.arguments + serverName: item.uploadedFile.mcpPrompt.serverName, + type: AttachmentType.MCP_PROMPT as const } : null} {#if mcpPrompt} diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte index 636e93f22..f5452aade 100644 --- a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList/ChatAttachmentsListItem/ChatAttachmentsListItemMcpPrompt.svelte @@ -1,8 +1,8 @@
diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte index 7c7cf5120..69b1724fb 100644 --- a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewCurrentItem/ChatAttachmentsPreviewCurrentItemPdf.svelte @@ -1,13 +1,13 @@ {#if show} diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte index 8a85df7d0..994a35f20 100644 --- a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte @@ -1,7 +1,7 @@ diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte index 2d70c302c..bc1befd63 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatForm.svelte @@ -1,4 +1,5 @@ diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContenteditable.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContenteditable.svelte index 18fd24302..98b082c89 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContenteditable.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContenteditable.svelte @@ -1,11 +1,8 @@
diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugeDetails.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugeDetails.svelte index fdec5aca5..ab36e4a43 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugeDetails.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugeDetails.svelte @@ -1,8 +1,8 @@ {#if modelId !== null && !isLoading} diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugePopup.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugePopup.svelte index af9ad010e..db4479b65 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugePopup.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugePopup.svelte @@ -1,15 +1,15 @@ diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte index 04b85b24b..3ebb29b9b 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte @@ -1,9 +1,9 @@ diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte index 0fb0ea0c0..51a51f808 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItem.svelte @@ -12,13 +12,13 @@ } let { + children, class: className = '', - isSelected = false, + dataIndex, disabled = false, + isSelected = false, onclick, - onmouseenter, - dataIndex, - children + onmouseenter }: Props = $props(); diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte index 5a2ab26fc..cbf7b972e 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerListItemSkeleton.svelte @@ -4,7 +4,7 @@ showBadge?: boolean; } - let { titleWidth = 'w-48', showBadge = false }: Props = $props(); + let { showBadge = false, titleWidth = 'w-48' }: Props = $props();
diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte index 24ea8619f..b09d346f1 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerPopover.svelte @@ -1,6 +1,6 @@ diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte index 22ec53119..126d08744 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPickerMcpPrompts.svelte @@ -1,19 +1,19 @@ diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte index 638d10eef..074c69b84 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPickerMcpPrompts/ChatFormPromptPickerArgumentInput.svelte @@ -1,8 +1,8 @@ @@ -66,7 +66,7 @@ {#if isAutocompleteActive && suggestions.length > 0}
{#each suggestions as suggestion, i (suggestion)}