diff --git a/.github/workflows/backend_ci.yml b/.github/workflows/backend_ci.yml new file mode 100644 index 00000000..cb590b0f --- /dev/null +++ b/.github/workflows/backend_ci.yml @@ -0,0 +1,39 @@ +name: Check for project +description: Check for type errors and linting errors in the project. + +on: + pull_request: + paths: + - "backend/**" + - ".github/workflows/**" + - "pnpm-lock.yaml" + workflow_dispatch: +permissions: + contents: read + +jobs: + ci: + name: Backend CI + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + package_json_file: "package.json" + run_install: false + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version-file: "package.json" + cache: "pnpm" + cache-dependency-path: "pnpm-lock.yaml" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Build backend + run: pnpm run --filter backend build # If this fails, the PR should not be merged. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 4249ebbb..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,145 +0,0 @@ -name: Check for project -description: Check for type errors and linting errors in the project. - -on: - pull_request: - branches: - - main - - kei - workflow_dispatch: -permissions: - contents: read - -jobs: - buildable: - timeout-minutes: 10 - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - package_json_file: "package.json" - run_install: false - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version-file: "package.json" - cache: "pnpm" - cache-dependency-path: "pnpm-lock.yaml" - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - name: Build frontend - run: pnpm run --filter frontend build # If this fails, the PR should not be merged. - - name: Build backend - run: pnpm run --filter backend build # If this fails, the PR should not be merged. - ci: - timeout-minutes: 15 - runs-on: ubuntu-latest - container: - image: mcr.microsoft.com/playwright:v1.57.0 - options: --user 1001 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - package_json_file: "package.json" - run_install: false - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version-file: "package.json" - cache: "pnpm" - cache-dependency-path: "pnpm-lock.yaml" - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Check type errors - timeout-minutes: 10 # It takes long time due to svelte type checking - run: pnpm run --filter frontend check:types - - - name: Run unit tests - run: pnpm run --filter frontend test --reporter=dot - timeout-minutes: 5 - - - name: Run browser tests - run: pnpm run --filter frontend test:browser --reporter=dot - timeout-minutes: 10 # Browser is slow - - - name: Check linting - run: pnpm run --filter frontend lint - timeout-minutes: 5 - coverage: - timeout-minutes: 15 - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - statuses: write - container: - image: mcr.microsoft.com/playwright:v1.57.0 - options: --user 1001 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - package_json_file: "package.json" - run_install: false - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version-file: "package.json" - cache: "pnpm" - cache-dependency-path: "pnpm-lock.yaml" - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Run coverage check - run: pnpm run --filter frontend test:coverage - timeout-minutes: 5 - - - name: Update Coverage Status - if: always() - uses: actions/github-script@v7 - with: - script: | - // 1. read coverage summary - const fs = require('fs'); - const summaryPath = './frontend/coverage/coverage-summary.json'; - - if (!fs.existsSync(summaryPath)) { - console.log('No coverage summary found.'); - return; - } - - const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8')); - - // 2. Total coverage % calc - const total = summary.total.lines.pct; - const color = total >= 75 ? 'success' : 'failure'; // Green if >= 75% - const description = `Lines: ${total}% | Statements: ${summary.total.statements.pct}%`; - - // 3. Github PR checklist update - await github.rest.repos.createCommitStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - sha: context.sha, - state: color, - context: 'Vitest Coverage', // List name - description: description, // e.g. "Lines: 85%" - target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}` // Click to see details - }); \ No newline at end of file diff --git a/.github/workflows/flaky_test_check.yml b/.github/workflows/flaky_test_check.yml index a14acdee..8d5e6ac3 100644 --- a/.github/workflows/flaky_test_check.yml +++ b/.github/workflows/flaky_test_check.yml @@ -1,68 +1,66 @@ -name: Flaky Test Stress Test - -on: - workflow_dispatch: - inputs: - iterations: - description: | - Number of parallel iterations to run. - Note that iterations above 20 require GitHub Pro or better to run in parallel. - Range: 1..200 - required: true - default: 10 - type: number -permissions: - contents: read -jobs: - setup-matrix: - runs-on: ubuntu-latest - outputs: - indices: ${{ steps.set-matrix.outputs.indices }} - steps: - - id: set-matrix - run: | - ITER="${{ github.event.inputs.iterations }}" - if [ "$ITER" -lt 1 ] || [ "$ITER" -gt 200 ]; then - echo "Error: iterations must be between 1 and 200." - exit 1 - fi - # Generate a JSON array [1, 2, ..., N] - INDICES=$(node -e "console.log(JSON.stringify(Array.from({length: $ITER}, (_, i) => i + 1)))") - echo "indices=$INDICES" >> $GITHUB_OUTPUT - - run-flaky-check: - needs: setup-matrix - runs-on: ubuntu-latest - timeout-minutes: 30 - strategy: - fail-fast: false # Don't stop other jobs if one fails, we want to see all fails - matrix: - index: ${{ fromJson(needs.setup-matrix.outputs.indices) }} - container: - image: mcr.microsoft.com/playwright:v1.57.0 - options: --user 1001 - steps: - - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - package_json_file: "package.json" - run_install: false - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version-file: "package.json" - cache: "pnpm" - cache-dependency-path: "pnpm-lock.yaml" - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Run Tests (Iteration ${{ matrix.index }}) - run: | - pnpm -F frontend test:browser - pnpm -F frontend test:coverage --coverage.reporters=text-summary - pnpm -F frontend test - timeout-minutes: 10 +name: Flaky Test Stress Test + +on: + workflow_dispatch: + inputs: + iterations: + description: | + Number of parallel iterations to run. + Note that iterations above 20 require GitHub Pro or better to run in parallel. + Range: 1..200 + required: true + default: 10 + type: number +permissions: + contents: read +jobs: + setup-matrix: + runs-on: ubuntu-latest + outputs: + indices: ${{ steps.set-matrix.outputs.indices }} + steps: + - id: set-matrix + run: | + ITER="${{ github.event.inputs.iterations }}" + if [ "$ITER" -lt 1 ] || [ "$ITER" -gt 200 ]; then + echo "Error: iterations must be between 1 and 200." + exit 1 + fi + # Generate a JSON array [1, 2, ..., N] + INDICES=$(node -e "console.log(JSON.stringify(Array.from({length: $ITER}, (_, i) => i + 1)))") + echo "indices=$INDICES" >> $GITHUB_OUTPUT + + run-flaky-check: + needs: setup-matrix + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false # Don't stop other jobs if one fails, we want to see all fails + matrix: + index: ${{ fromJson(needs.setup-matrix.outputs.indices) }} + container: + image: mcr.microsoft.com/playwright:v1.57.0 + options: --user 1001 + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + package_json_file: "package.json" + run_install: false + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version-file: "package.json" + cache: "pnpm" + cache-dependency-path: "pnpm-lock.yaml" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run Tests (Iteration ${{ matrix.index }}) + run: | + pnpm -F frontend test --coverage.reporters=text-summary + timeout-minutes: 10 diff --git a/.github/workflows/frontend_ci.yml b/.github/workflows/frontend_ci.yml new file mode 100644 index 00000000..d87a76c3 --- /dev/null +++ b/.github/workflows/frontend_ci.yml @@ -0,0 +1,171 @@ +name: Frontend CI +on: + pull_request: + paths: + - "frontend/**" + - ".github/workflows/**" + - "pnpm-lock.yaml" + +jobs: + setup: + name: Setup Cache + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + package_json_file: frontend/package.json + run_install: false + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version-file: frontend/package.json + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: frontend + + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-browsers-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Install Playwright browsers + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: pnpm exec playwright install + working-directory: frontend + + lint: + name: Frontend Lint + needs: [setup] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + package_json_file: frontend/package.json + run_install: false + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version-file: frontend/package.json + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: frontend + + - name: Lint + run: pnpm -F frontend run lint + + - name: Type check + run: pnpm -F frontend run check:types + + tests: + name: Frontend Tests + needs: [setup] + permissions: + contents: read + runs-on: ubuntu-latest + strategy: + matrix: + shardIndex: [1, 2, 3, 4, 5] + shardTotal: [5] + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + package_json_file: frontend/package.json + run_install: false + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version-file: frontend/package.json + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: frontend + + - name: Restore Playwright browsers + uses: actions/cache/restore@v4 + with: + path: ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-browsers-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Install Playwright system dependencies + run: pnpm exec playwright install-deps + working-directory: frontend + + - name: Run tests + run: pnpm -F frontend run test --reporter=blob --coverage.enabled --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} + + - name: Upload blob report + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: blob-report-${{ matrix.shardIndex }} + path: frontend/.vitest-reports/* + include-hidden-files: true + retention-days: 1 + + merge-reports: + name: Merge Reports + if: ${{ !cancelled() }} + needs: [tests] + permissions: + pull-requests: write + statuses: write + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + package_json_file: frontend/package.json + run_install: false + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version-file: frontend/package.json + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: frontend + + - name: Download blob reports + uses: actions/download-artifact@v4 + with: + path: frontend/.vitest-reports + pattern: blob-report-* + merge-multiple: true + + - name: Merge reports + run: pnpm -F frontend run test --merge-reports --reporter=github-actions --reporter=default --coverage.reporter=text --coverage.reporter=json-summary + + - name: Update Coverage Status + if: ${{ github.event_name == 'pull_request' && always() }} + uses: concertypin/action/report-vitest-coverage@master + with: + json-file: frontend/coverage/coverage-summary.json + threshold: 80 \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 13a2e6e9..2b01a393 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ "svelte.svelte-vscode", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", - "vitest.explorer" + "vitest.explorer", + "oxc.oxc-vscode" ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 56e0e4f1..bc5fb756 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,27 +9,28 @@ }, "[svelte]": { "editor.tabSize": 4, - "editor.defaultFormatter": "svelte.svelte-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.tabSize": 4 }, + "[github-actions-workflow]": { + "editor.tabSize": 2 + }, "typescript.preferences.preferTypeOnlyAutoImports": true, "js/ts.implicitProjectConfig.target": "ESNext", "typescript.preferences.importModuleSpecifier": "non-relative", "javascript.preferences.importModuleSpecifier": "non-relative", "javascript.preferences.importModuleSpecifierEnding": "minimal", "typescript.preferences.importModuleSpecifierEnding": "minimal", - "vitest.nodeEnv": { - // Enabling browser test on VSC vitest, - // since it will not run browser tests by default - "npm_lifecycle_event": "coverage", - "IS_IDE": "vscode" - }, - "eslint.workingDirectories": [ - { - "directory": "./frontend/", - } - ] + "oxc.typeAware": true, + "eslint.validate": [ + "javascript", + "typescript", + "svelte" + ], + "files.exclude": { + "coverage": true + } } \ No newline at end of file diff --git a/frontend/.oxlintrc.json b/frontend/.oxlintrc.json new file mode 100644 index 00000000..c93f6e30 --- /dev/null +++ b/frontend/.oxlintrc.json @@ -0,0 +1,35 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "extends": [ + "./scripts/linter/oxlint-eslint-error.json", + "./scripts/linter/oxlint-eslint-warn.json", + "./scripts/linter/oxlint-svelte.json" + ], + "ignorePatterns": [ + "**/node_modules/**", + "**/dist/**", + "**/coverage/**", + "**/.cache/**" + ], + "overrides": [ + { + "files": [ + "**/*.d.ts" + ], + "rules": { + "no-unused-vars": "off" + } + }, + { + "files": [ + "**/test/**", + "**/*.test.ts", + "**/*.spec.ts" + ], + "rules": { + "jest/require-to-throw-message": "off", + "jest/expect-expect": "off" + } + } + ] +} \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md index f563e205..ba45341f 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,63 +1,63 @@ -# ArisuTalk Frontend ๐ŸŽจ - -This is the frontend for ArisuTalk, built with **Svelte 5** and lots of love! โœจ - -## ๐ŸŒธ Project Kei - -This frontend is currently being rebuilt as part of **Project Kei**! ๐Ÿš€ We're using modern tools and a clean architecture to provide the best possible roleplay experience. - -## ๐Ÿ› ๏ธ Tech Stack - -- **Framework:** Svelte 5 (Runes) -- **Build Tool:** Vite -- **Styling:** Tailwind CSS + DaisyUI -- **Icons:** `@lucide/svelte` -- **Storage:** Dexie.js (IndexedDB) -- **Workers:** Comlink - -## ๐Ÿš€ Development - -### Prerequisites - -- Node.js (>20) -- pnpm (10.14.0+) - -### Setup - -```bash -pnpm install -``` - -### Run Dev Server - -```bash -pnpm run dev -``` - -The server will start at `http://localhost:5173` ๐Ÿš€ - -### Testing - -We take testing seriously! ๐Ÿงช - -- **Unit Tests:** `pnpm run test` -- **Browser Tests:** `pnpm run test:browser` -- **Coverage:** `pnpm run test:coverage` - -### Linting & Formatting - -```bash -pnpm run lint -pnpm run format -``` - -## ๐Ÿ“‚ Structure - -- `src/components`: UI components ๐Ÿงฉ -- `src/features`: Feature-based logic (character, chat, persona) ๐ŸŒŸ -- `src/lib`: Shared utilities, adapters, and stores ๐Ÿ› ๏ธ -- `worker/`: Web workers for heavy tasks ๐Ÿ‘ทโ€โ™€๏ธ - ---- - -Happy coding! ๐ŸŽ€ +# ArisuTalk Frontend ๐ŸŽจ + +This is the frontend for ArisuTalk, built with **Svelte 5** and lots of love! โœจ + +## ๐ŸŒธ Project Kei + +This frontend is currently being rebuilt as part of **Project Kei**! ๐Ÿš€ We're using modern tools and a clean architecture to provide the best possible roleplay experience. + +## ๐Ÿ› ๏ธ Tech Stack + +- **Framework:** Svelte 5 (Runes) +- **Build Tool:** Vite +- **Styling:** Tailwind CSS + DaisyUI +- **Icons:** `@lucide/svelte` +- **Storage:** Dexie.js (IndexedDB) +- **Workers:** Comlink + +## ๐Ÿš€ Development + +### Prerequisites + +- Node.js (>=22.18.0 <23.0.0 || >=23.6.0), which supports type stripping without flag. +- pnpm (10.14.0+) + +### Setup + +```bash +pnpm install +``` + +### Run Dev Server + +```bash +pnpm run dev +``` + +The server will start at `http://localhost:5173` ๐Ÿš€ + +### Testing + +We take testing seriously! ๐Ÿงช + +- **Unit Tests:** `pnpm run test` +- **Browser Tests:** `pnpm run test:browser` +- **Coverage:** `pnpm run test:coverage` + +### Linting & Formatting + +```bash +pnpm run lint +pnpm run format +``` + +## ๐Ÿ“‚ Structure + +- `src/components`: UI components ๐Ÿงฉ +- `src/features`: Feature-based logic (character, chat, persona) ๐ŸŒŸ +- `src/lib`: Shared utilities, adapters, and stores ๐Ÿ› ๏ธ +- `worker/`: Web workers for heavy tasks ๐Ÿ‘ทโ€โ™€๏ธ + +--- + +Happy coding! ๐ŸŽ€ diff --git a/frontend/common/logger/LogBridge.ts b/frontend/common/logger/LogBridge.ts index ccb2adf9..d328ad61 100644 --- a/frontend/common/logger/LogBridge.ts +++ b/frontend/common/logger/LogBridge.ts @@ -1,5 +1,6 @@ +// oxlint-disable no-console // It's logger, so we should use console -/* eslint-disable no-console */ + import type { StandardLogEntry, LogLevel, StructuredLogEntry } from "./Logger"; import type { StructuredLogLevel } from "./LogType"; diff --git a/frontend/docs/rules/tools.md b/frontend/docs/rules/tools.md new file mode 100644 index 00000000..05cb155e --- /dev/null +++ b/frontend/docs/rules/tools.md @@ -0,0 +1,89 @@ +# Tools Configuration + +This document provides guidelines for configuring the linting and formatting tools in the ArisuTalk frontend project. + +## Linter Configuration + +This project uses **oxlint** as the primary linter for fast, type-aware linting, with **ESLint** as a fallback for rules that oxlint doesn't support yet. + +### Oxlint + +Fast linter for TypeScript and JavaScript, built on the Oxc compiler stack. + +**Supports:** + +- Most typescript-eslint rules, including type-aware rules +- ESLint's JS plugins via the `jsPlugins` feature +- Plugins: `typescript`, `unicorn`, `import`, `vitest`, `promise` + +**Doesn't support:** + +- Custom file formats and parsers (Svelte, Vue, Angular templates) +- Some HTML-superset code (only checks ` diff --git a/frontend/src/components/settingSubpage/LLMSetting/GenerationParameters.svelte b/frontend/src/components/settingSubpage/LLMSetting/GenerationParameters.svelte index 42791766..60f25468 100644 --- a/frontend/src/components/settingSubpage/LLMSetting/GenerationParameters.svelte +++ b/frontend/src/components/settingSubpage/LLMSetting/GenerationParameters.svelte @@ -10,7 +10,7 @@ id: number; }; - let { config = $bindable(), id }: Props = $props(); + const { config = $bindable(), id }: Props = $props(); /** * Creates a proxy object to bind a checkbox to the presence of a field in a target object. @@ -48,22 +48,22 @@ let isActive = $derived(settings.value.activeLLMConfigId === config.id); - const modelProxy = $derived(createFieldProxy(config, "model")); - const keyProxy = $derived(createFieldProxy(config, "apiKey")); - const urlProxy = $derived(createFieldProxy(config, "baseURL")); - const tempProxy = $derived(createFieldProxy(config.generationParameters, "temperature", 1)); - const maxInProxy = $derived( + let modelProxy = $derived(createFieldProxy(config, "model")); + let keyProxy = $derived(createFieldProxy(config, "apiKey")); + let urlProxy = $derived(createFieldProxy(config, "baseURL")); + let tempProxy = $derived(createFieldProxy(config.generationParameters, "temperature", 1)); + let maxInProxy = $derived( createFieldProxy(config.generationParameters, "maxInputTokens", 1024) ); - const maxOutProxy = $derived( + let maxOutProxy = $derived( createFieldProxy(config.generationParameters, "maxOutputTokens", 1024) ); - const topPProxy = $derived(createFieldProxy(config.generationParameters, "topP", 0.95)); - const topKProxy = $derived(createFieldProxy(config.generationParameters, "topK", 40)); - const freqPenProxy = $derived( + let topPProxy = $derived(createFieldProxy(config.generationParameters, "topP", 0.95)); + let topKProxy = $derived(createFieldProxy(config.generationParameters, "topK", 40)); + let freqPenProxy = $derived( createFieldProxy(config.generationParameters, "frequencyPenalty", 0) ); - const presPenProxy = $derived( + let presPenProxy = $derived( createFieldProxy(config.generationParameters, "presencePenalty", 0) ); diff --git a/frontend/src/components/ui/Button.svelte b/frontend/src/components/ui/Button.svelte index 182253d7..e9599d3a 100644 --- a/frontend/src/components/ui/Button.svelte +++ b/frontend/src/components/ui/Button.svelte @@ -20,7 +20,7 @@ children: Snippet; }; - let { + const { variant = "primary", size = "md", disabled = false, diff --git a/frontend/src/features/character/adapters/storage/LocalStorageAdapter.ts b/frontend/src/features/character/adapters/storage/LocalStorageAdapter.ts index 1ac35b90..38d3ccc3 100644 --- a/frontend/src/features/character/adapters/storage/LocalStorageAdapter.ts +++ b/frontend/src/features/character/adapters/storage/LocalStorageAdapter.ts @@ -154,7 +154,7 @@ export class LocalStorageAdapter { } } catch (e) { Logger.error("Failed to import data", e); - throw new Error("Invalid data format"); + throw new Error("Invalid data format", { cause: e }); } } } diff --git a/frontend/src/features/character/components/CharacterCard.svelte b/frontend/src/features/character/components/CharacterCard.svelte index 62e41632..9a43aa23 100644 --- a/frontend/src/features/character/components/CharacterCard.svelte +++ b/frontend/src/features/character/components/CharacterCard.svelte @@ -17,7 +17,7 @@ isFirst?: boolean; isLast?: boolean; }; - let { + const { character, onEdit, onDelete, diff --git a/frontend/src/features/character/components/CharacterForm.svelte b/frontend/src/features/character/components/CharacterForm.svelte index e5749e80..ebca6c48 100644 --- a/frontend/src/features/character/components/CharacterForm.svelte +++ b/frontend/src/features/character/components/CharacterForm.svelte @@ -10,7 +10,7 @@ onSubmit?: (character: Character) => void; }; - let { character = undefined, onSave, onCancel, onSubmit }: Props = $props(); + const { character = undefined, onSave, onCancel, onSubmit }: Props = $props(); let name = $state(""); let description = $state(""); diff --git a/frontend/src/features/character/components/CharacterLayout.svelte b/frontend/src/features/character/components/CharacterLayout.svelte index cfe542b7..d1200ec4 100644 --- a/frontend/src/features/character/components/CharacterLayout.svelte +++ b/frontend/src/features/character/components/CharacterLayout.svelte @@ -15,7 +15,7 @@ children?: import("svelte").Snippet; }; - let { children }: Props = $props(); + const { children }: Props = $props(); let selectedCharacterId = $state(null); let dialog = $state(); diff --git a/frontend/src/features/character/components/CharacterList.svelte b/frontend/src/features/character/components/CharacterList.svelte index e4ceb2ba..de301f0d 100644 --- a/frontend/src/features/character/components/CharacterList.svelte +++ b/frontend/src/features/character/components/CharacterList.svelte @@ -12,8 +12,8 @@ onEdit: (index: number) => void; }; - let { onEdit }: Props = $props(); - let worker = getCardParseWorker(); + const { onEdit }: Props = $props(); + const worker = getCardParseWorker(); async function handleDelete(index: number) { const modal = document.getElementById("delete-confirm-modal") as HTMLDialogElement; diff --git a/frontend/src/features/character/components/CharacterSidebar.svelte b/frontend/src/features/character/components/CharacterSidebar.svelte index 87445be4..becbc184 100644 --- a/frontend/src/features/character/components/CharacterSidebar.svelte +++ b/frontend/src/features/character/components/CharacterSidebar.svelte @@ -15,7 +15,7 @@ onPersona: () => void; }; - let { selectedCharacterId, onSelect, onAdd, onPersona }: Props = $props(); + const { selectedCharacterId, onSelect, onAdd, onPersona }: Props = $props(); const flipDurationMs = 200; diff --git a/frontend/src/features/character/components/CharacterSidebarItem.svelte b/frontend/src/features/character/components/CharacterSidebarItem.svelte index 2c3e2730..35fafc39 100644 --- a/frontend/src/features/character/components/CharacterSidebarItem.svelte +++ b/frontend/src/features/character/components/CharacterSidebarItem.svelte @@ -9,7 +9,7 @@ onClick: () => void; }; - let { character, active, onClick }: Props = $props(); + const { character, active, onClick }: Props = $props(); // Generate initials from name let initials = $derived(character.name.substring(0, 2).toUpperCase()); diff --git a/frontend/src/features/character/components/settingsSubpage/CharacterAssetsSettings.svelte b/frontend/src/features/character/components/settingsSubpage/CharacterAssetsSettings.svelte index 1ede81a7..9b4d6cc8 100644 --- a/frontend/src/features/character/components/settingsSubpage/CharacterAssetsSettings.svelte +++ b/frontend/src/features/character/components/settingsSubpage/CharacterAssetsSettings.svelte @@ -20,13 +20,13 @@ onChange: (character: Character) => void; }; - let { character, onChange }: Props = $props(); + const { character, onChange }: Props = $props(); let fileInput = $state(); let expandOtherAssets = $state(false); let duplicateNameError = $state(null); let draggedIndex = $state(null); - let assetPreviews = new SvelteMap(); + const assetPreviews = new SvelteMap(); const assetStorage = getAssetStorage(); @@ -48,15 +48,15 @@ void loadPreviews(); }); - const imageAssets = $derived( + let imageAssets = $derived( character.assets.assets.filter((a) => a.mimeType.startsWith("image/")) ); - const otherAssets = $derived( + let otherAssets = $derived( character.assets.assets.filter((a) => !a.mimeType.startsWith("image/")) ); - async function handleFileUpload(e: Event) { - const target = e.target as HTMLInputElement; + async function handleFileUpload(ev: Event) { + const target = ev.target as HTMLInputElement; const files = target.files; if (!files || files.length === 0) return; diff --git a/frontend/src/features/character/components/settingsSubpage/CharacterBasicSettings.svelte b/frontend/src/features/character/components/settingsSubpage/CharacterBasicSettings.svelte index e7ea2123..9d6e6ff6 100644 --- a/frontend/src/features/character/components/settingsSubpage/CharacterBasicSettings.svelte +++ b/frontend/src/features/character/components/settingsSubpage/CharacterBasicSettings.svelte @@ -16,13 +16,13 @@ onChange: (character: Character) => void; }; - let { character, onChange }: Props = $props(); + const { character, onChange }: Props = $props(); const assetStorage = getAssetStorage(); - let assetPreviews = new SvelteMap(); + const assetPreviews = new SvelteMap(); let showManualUrl = $state(false); - const imageAssets = $derived( + let imageAssets = $derived( character.assets.assets.filter((a) => a.mimeType.startsWith("image/")) ); diff --git a/frontend/src/features/character/components/settingsSubpage/CharacterHooksSettings.svelte b/frontend/src/features/character/components/settingsSubpage/CharacterHooksSettings.svelte index ceb75338..3a23b120 100644 --- a/frontend/src/features/character/components/settingsSubpage/CharacterHooksSettings.svelte +++ b/frontend/src/features/character/components/settingsSubpage/CharacterHooksSettings.svelte @@ -21,7 +21,7 @@ onChange: (character: Character) => void; }; - let { character, onChange }: Props = $props(); + const { character, onChange }: Props = $props(); let activeHookType = $state("display"); let expandedHookIndex = $state(null); diff --git a/frontend/src/features/character/components/settingsSubpage/CharacterLorebookSettings.svelte b/frontend/src/features/character/components/settingsSubpage/CharacterLorebookSettings.svelte index 7213b09c..d0d8d67c 100644 --- a/frontend/src/features/character/components/settingsSubpage/CharacterLorebookSettings.svelte +++ b/frontend/src/features/character/components/settingsSubpage/CharacterLorebookSettings.svelte @@ -19,7 +19,7 @@ onChange: (character: Character) => void; }; - let { character, onChange }: Props = $props(); + const { character, onChange }: Props = $props(); let expandedEntryId = $state(null); diff --git a/frontend/src/features/character/components/settingsSubpage/CharacterMetadataSettings.svelte b/frontend/src/features/character/components/settingsSubpage/CharacterMetadataSettings.svelte index 6f7bfbd1..449dafcb 100644 --- a/frontend/src/features/character/components/settingsSubpage/CharacterMetadataSettings.svelte +++ b/frontend/src/features/character/components/settingsSubpage/CharacterMetadataSettings.svelte @@ -11,7 +11,7 @@ onChange: (character: Character) => void; }; - let { character, onChange }: Props = $props(); + const { character, onChange }: Props = $props(); /** Common license options for autocomplete */ const licenseOptions = [ diff --git a/frontend/src/features/character/components/settingsSubpage/CharacterPromptSettings.svelte b/frontend/src/features/character/components/settingsSubpage/CharacterPromptSettings.svelte index 27ba0104..3cf76a75 100644 --- a/frontend/src/features/character/components/settingsSubpage/CharacterPromptSettings.svelte +++ b/frontend/src/features/character/components/settingsSubpage/CharacterPromptSettings.svelte @@ -14,7 +14,7 @@ onChange: (character: Character) => void; }; - let { character, onChange }: Props = $props(); + const { character, onChange }: Props = $props(); let isDescriptionExpanded = $state(false); let isAuthorsNoteExpanded = $state(false); diff --git a/frontend/src/features/character/stores/characterStore.svelte.ts b/frontend/src/features/character/stores/characterStore.svelte.ts index 15af8762..ffc89cbf 100644 --- a/frontend/src/features/character/stores/characterStore.svelte.ts +++ b/frontend/src/features/character/stores/characterStore.svelte.ts @@ -44,11 +44,12 @@ export class CharacterStore { // Sort by saved order const order = this.getOrder(); if (order.length > 0) { - // eslint-disable-next-line svelte/prefer-svelte-reactivity - const orderMap = new Map(order.map((id, index) => [id, index])); + const orderMap: Record = Object.fromEntries( + order.map((id, index) => [id, index]) + ); chars.sort((a, b) => { - const idxA = orderMap.get(a.id); - const idxB = orderMap.get(b.id); + const idxA = orderMap[a.id]; + const idxB = orderMap[b.id]; // If both present, sort by index if (idxA !== undefined && idxB !== undefined) return idxA - idxB; // If only one present, present goes first? Or last? @@ -131,14 +132,13 @@ export class CharacterStore { success: true, }); return { success: true }; - } else { - Logger.structured("character.import", { - format, - success: false, - errorMessage: "Failed to parse character", - }); - return { success: false, error: "Failed to parse character" }; } + Logger.structured("character.import", { + format, + success: false, + errorMessage: "Failed to parse character", + }); + return { success: false, error: "Failed to parse character" }; } catch (e) { Logger.error(e); Logger.structured("character.import", { diff --git a/frontend/src/features/chat/components/ChatList.svelte b/frontend/src/features/chat/components/ChatList.svelte index d0d5d207..63292b97 100644 --- a/frontend/src/features/chat/components/ChatList.svelte +++ b/frontend/src/features/chat/components/ChatList.svelte @@ -8,7 +8,7 @@ characterId: string; }; - let { characterId }: Props = $props(); + const { characterId }: Props = $props(); let chats = $derived(chatStore.chats.filter((c) => c.characterId === characterId)); let activeChatId = $derived(chatStore.activeChatId); diff --git a/frontend/src/features/chat/stores/chatStore.svelte.ts b/frontend/src/features/chat/stores/chatStore.svelte.ts index 0b4e8d9c..6dc4b375 100644 --- a/frontend/src/features/chat/stores/chatStore.svelte.ts +++ b/frontend/src/features/chat/stores/chatStore.svelte.ts @@ -195,7 +195,7 @@ export class ChatStore { async setProvider( type: T, - settings: CommonChatSettings & ProviderSettings[T] + providerSettings: CommonChatSettings & ProviderSettings[T] ) { if (this.activeProvider) { await this.activeProvider.disconnect(); @@ -203,28 +203,29 @@ export class ChatStore { Logger.structured("llm.request.start", { provider: type, - model: settings.model || "default", // Should check if model is in settings type + model: providerSettings.model || "default", // Should check if model is in settings type }); switch (type) { case "ANTHROPIC": { - this.activeProvider = await AnthropicChatProvider.factory.connect(settings); + this.activeProvider = await AnthropicChatProvider.factory.connect(providerSettings); break; } case "GEMINI": { - this.activeProvider = await GeminiChatProvider.factory.connect(settings); + this.activeProvider = await GeminiChatProvider.factory.connect(providerSettings); break; } case "MOCK": { - this.activeProvider = await MockChatProvider.factory.connect(settings); + this.activeProvider = await MockChatProvider.factory.connect(providerSettings); break; } case "OPENAI": { - this.activeProvider = await OpenAIChatProvider.factory.connect(settings); + this.activeProvider = await OpenAIChatProvider.factory.connect(providerSettings); break; } case "OPENROUTER": { - this.activeProvider = await OpenRouterChatProvider.factory.connect(settings); + this.activeProvider = + await OpenRouterChatProvider.factory.connect(providerSettings); break; } default: { diff --git a/frontend/src/features/persona/components/PersonaForm.svelte b/frontend/src/features/persona/components/PersonaForm.svelte index 237315f5..832f47ce 100644 --- a/frontend/src/features/persona/components/PersonaForm.svelte +++ b/frontend/src/features/persona/components/PersonaForm.svelte @@ -15,7 +15,7 @@ onCancel: () => void; }; - let { persona = undefined, onSave, onCancel }: Props = $props(); + const { persona = undefined, onSave, onCancel }: Props = $props(); let name = $state(""); let description = $state(""); diff --git a/frontend/src/features/persona/components/PersonaList.svelte b/frontend/src/features/persona/components/PersonaList.svelte index 0cb896f2..78c2f669 100644 --- a/frontend/src/features/persona/components/PersonaList.svelte +++ b/frontend/src/features/persona/components/PersonaList.svelte @@ -12,7 +12,7 @@ onEdit: (persona: Persona) => void; }; - let { onEdit }: Props = $props(); + const { onEdit }: Props = $props(); function handleDelete(id: string) { if (confirm("Delete this persona?")) { diff --git a/frontend/src/features/sns/SNSFeedCard.svelte b/frontend/src/features/sns/SNSFeedCard.svelte index b198c8df..90cf48a3 100644 --- a/frontend/src/features/sns/SNSFeedCard.svelte +++ b/frontend/src/features/sns/SNSFeedCard.svelte @@ -24,7 +24,7 @@ tags?: string[]; }; - let { + const { content = "Hello from the SNS mode!", author = "User", timestamp = "1m ago", diff --git a/frontend/src/lib/adapters/storage/chat/LocalStorageChatAdapter.ts b/frontend/src/lib/adapters/storage/chat/LocalStorageChatAdapter.ts index 8a1eb0e2..b072d911 100644 --- a/frontend/src/lib/adapters/storage/chat/LocalStorageChatAdapter.ts +++ b/frontend/src/lib/adapters/storage/chat/LocalStorageChatAdapter.ts @@ -234,7 +234,7 @@ export class LocalStorageChatAdapter implements IChatStorageAdapter { } } catch (e) { Logger.error("Failed to import data", e); - throw new Error("Invalid data format"); + throw new Error("Invalid data format", { cause: e }); } } } diff --git a/frontend/src/lib/interfaces/IHookSystem.ts b/frontend/src/lib/interfaces/IHookSystem.ts index a23c9f99..5322a51c 100644 --- a/frontend/src/lib/interfaces/IHookSystem.ts +++ b/frontend/src/lib/interfaces/IHookSystem.ts @@ -5,7 +5,7 @@ */ export type ScriptContext = { // Script can access properties dynamically - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // oxlint-disable-next-line typescript/no-explicit-any [key: string]: any; }; diff --git a/frontend/src/lib/providers/chat/LangChainBaseProvider.ts b/frontend/src/lib/providers/chat/LangChainBaseProvider.ts index f691d2e6..7fe3efa8 100644 --- a/frontend/src/lib/providers/chat/LangChainBaseProvider.ts +++ b/frontend/src/lib/providers/chat/LangChainBaseProvider.ts @@ -37,6 +37,7 @@ export abstract class LangChainBaseProvider< latencyMs: Date.now() - startTime, }); if (typeof content === "string") return content; + // oxlint-disable-next-line eqeqeq if (content == null) return ""; try { return JSON.stringify(content); diff --git a/frontend/src/lib/services/HookService.ts b/frontend/src/lib/services/HookService.ts index 53d4dd13..6d19e668 100644 --- a/frontend/src/lib/services/HookService.ts +++ b/frontend/src/lib/services/HookService.ts @@ -118,27 +118,22 @@ export class HookService { } return await regexWorker.replace(content, pattern, replacement, hook.meta.flag); - } else { - // String replacement - if (hook.meta.isOutputScripted) { - const scriptResult = await scriptingWorker.execute(replacement, { - context: this.createContext(content, character, type, persona, role), - allowNetwork, - characterId: character.id, - }); - if (scriptResult.result !== undefined && scriptResult.result !== null) { - replacement = String(scriptResult.result as unknown); - } - } + } - const flags = hook.meta.caseSensitive ? "g" : "gi"; - return await regexWorker.replace( - content, - this.escapeRegExp(pattern), - replacement, - flags - ); + // String replacement + if (hook.meta.isOutputScripted) { + const scriptResult = await scriptingWorker.execute(replacement, { + context: this.createContext(content, character, type, persona, role), + allowNetwork, + characterId: character.id, + }); + if (scriptResult.result !== undefined && scriptResult.result !== null) { + replacement = String(scriptResult.result as unknown); + } } + + const flags = hook.meta.caseSensitive ? "g" : "gi"; + return await regexWorker.replace(content, this.escapeRegExp(pattern), replacement, flags); } private createContext( diff --git a/frontend/src/lib/types/IDataModel.ts b/frontend/src/lib/types/IDataModel.ts index 8d01d24f..923c8074 100644 --- a/frontend/src/lib/types/IDataModel.ts +++ b/frontend/src/lib/types/IDataModel.ts @@ -56,7 +56,7 @@ const BaseLLMConfigSchema = z.object({ /** * Unique identifier for the LLM configuration. */ - id: z.string().default(() => "LLMconfig-" + crypto.randomUUID()), + id: z.string().default(() => `LLMconfig-${crypto.randomUUID()}`), /** * Human-readable name for the LLM configuration. * Just for easier identification. diff --git a/frontend/src/lib/workers/workerClient.ts b/frontend/src/lib/workers/workerClient.ts index 75014bb8..04948e6e 100644 --- a/frontend/src/lib/workers/workerClient.ts +++ b/frontend/src/lib/workers/workerClient.ts @@ -29,8 +29,9 @@ function createWorkerApi(worker: Worker): WorkerApi { // Automatically set up logging if the worker supports it if ("setLogReceiver" in api && typeof api.setLogReceiver === "function") { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - void api.setLogReceiver(Comlink.proxy(logReceiver)); + void (api.setLogReceiver as (receiver: typeof logReceiver) => Promise)( + Comlink.proxy(logReceiver) + ); } // Proxy api again @@ -46,7 +47,7 @@ function createWorkerApi(worker: Worker): WorkerApi { if (prop === "disabled") { return false; } - if (typeof prop == "symbol") return Reflect.get(target, prop); + if (typeof prop === "symbol") return Reflect.get(target, prop); return target[prop as keyof T]; }, }); diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts index 62ff9ec6..4b9b307d 100644 --- a/frontend/src/vite-env.d.ts +++ b/frontend/src/vite-env.d.ts @@ -39,5 +39,7 @@ interface SharedArrayBuffer { readonly [Symbol.toStringTag]: "SharedArrayBuffer"; } type ArrayBufferLike = ArrayBuffer; -// eslint-disable-next-line no-var + +// lib.dom.d.ts declared as var +// oxlint-disable-next-line no-var declare var SharedArrayBuffer: never; diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index 5ac18645..c45c4bda 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -1,15 +1,15 @@ -import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; - -/** @type {import('@sveltejs/vite-plugin-svelte').SvelteConfig}*/ -export default { - // Consult https://svelte.dev/docs#compile-time-svelte-preprocess - // for more information about preprocessors - preprocess: vitePreprocess(), - vitePlugin: { - inspector: { - showToggleButton: "always", - toggleButtonPos: "bottom-right", - }, - }, - compilerOptions: { runes: true }, -}; +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; + +/** @type {import('@sveltejs/vite-plugin-svelte').SvelteConfig}*/ +export default { + // Consult https://svelte.dev/docs#compile-time-svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), + vitePlugin: { + inspector: { + showToggleButton: "always", + toggleButtonPos: "bottom-right", + }, + }, + compilerOptions: { runes: true }, +}; diff --git a/frontend/test/browser/features/character/CharacterHooksSettings.test.ts b/frontend/test/browser/features/character/CharacterHooksSettings.test.ts index 464624d5..f8b17e0f 100644 --- a/frontend/test/browser/features/character/CharacterHooksSettings.test.ts +++ b/frontend/test/browser/features/character/CharacterHooksSettings.test.ts @@ -82,17 +82,17 @@ describe("CharacterHooksSettings Component", () => { await expect.element(memInput).toBeVisible(); await memInput.fill("1024"); - /* eslint-disable @typescript-eslint/no-unsafe-assignment */ expect(onChangeSpy).toHaveBeenCalledWith( expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment executables: expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment runtimeSetting: expect.objectContaining({ mem: 1024, }), }), }) ); - /* eslint-enable @typescript-eslint/no-unsafe-assignment */ }); test("switches between hook tabs", async () => { @@ -161,11 +161,14 @@ describe("CharacterHooksSettings Component", () => { const inputField = getByLabelText("Input Pattern"); await expect.element(inputField).toBeVisible(); await inputField.fill("baz"); - /* eslint-disable @typescript-eslint/no-unsafe-assignment */ + expect(onChangeSpy).toHaveBeenCalledWith( expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment executables: expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment replaceHooks: expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment display: expect.arrayContaining([ expect.objectContaining({ input: "baz" } as const), ]), @@ -173,7 +176,6 @@ describe("CharacterHooksSettings Component", () => { }), }) ); - /* eslint-enable @typescript-eslint/no-unsafe-assignment */ }); test("deletes a hook", async () => { @@ -204,13 +206,13 @@ describe("CharacterHooksSettings Component", () => { expect(onChangeSpy).toHaveBeenCalledWith( expect.objectContaining({ - /* eslint-disable @typescript-eslint/no-unsafe-assignment */ + // oxlint-disable-next-line typescript/no-unsafe-assignment executables: expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment replaceHooks: expect.objectContaining({ display: [], }), }), - /* eslint-enable @typescript-eslint/no-unsafe-assignment */ }) ); }); @@ -248,17 +250,19 @@ describe("CharacterHooksSettings Component", () => { expect(onChangeSpy).toHaveBeenCalledWith( expect.objectContaining({ - /* eslint-disable @typescript-eslint/no-unsafe-assignment */ + // oxlint-disable-next-line typescript/no-unsafe-assignment executables: expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment replaceHooks: expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment display: expect.arrayContaining([ expect.objectContaining({ + // oxlint-disable-next-line typescript/no-unsafe-assignment meta: expect.objectContaining({ type: "regex" }), }), ]), }), }), - /* eslint-enable @typescript-eslint/no-unsafe-assignment */ }) ); }); diff --git a/frontend/test/browser/features/character/CharacterList.test.ts b/frontend/test/browser/features/character/CharacterList.test.ts index b38b633c..bb91f5c2 100644 --- a/frontend/test/browser/features/character/CharacterList.test.ts +++ b/frontend/test/browser/features/character/CharacterList.test.ts @@ -17,7 +17,7 @@ vi.mock("@/features/character/adapters/assetStorage/OpFSAssetStorageAdapter", () getAssetUrl: vi.fn().mockResolvedValue(null), }; return { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // oxlint-disable-next-line typescript/no-explicit-any OpFSAssetStorageAdapter: vi.fn().mockImplementation(function (this: any) { return mockAdapter; }), @@ -29,7 +29,7 @@ vi.mock( "comlink", () => ({ - // eslint-disable-next-line @typescript-eslint/no-unsafe-return + // oxlint-disable-next-line typescript/no-unsafe-return transfer: vi.fn((data, _transferables) => data) satisfies (typeof comlink)["transfer"], }) satisfies Partial ); diff --git a/frontend/test/browser/features/persona/PersonaList.test.ts b/frontend/test/browser/features/persona/PersonaList.test.ts index 61ce4668..52e4ec6d 100644 --- a/frontend/test/browser/features/persona/PersonaList.test.ts +++ b/frontend/test/browser/features/persona/PersonaList.test.ts @@ -11,7 +11,7 @@ vi.mock("@/features/character/adapters/assetStorage/OpFSAssetStorageAdapter", () getAssetUrl: vi.fn().mockResolvedValue(null), }; return { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // oxlint-disable-next-line typescript/no-explicit-any OpFSAssetStorageAdapter: vi.fn().mockImplementation(function (this: any) { return mockAdapter; }), diff --git a/frontend/test/browser/features/settings/GenerationParameters.test.ts b/frontend/test/browser/features/settings/GenerationParameters.test.ts index aba88758..45690740 100644 --- a/frontend/test/browser/features/settings/GenerationParameters.test.ts +++ b/frontend/test/browser/features/settings/GenerationParameters.test.ts @@ -1,7 +1,7 @@ /// import { test, expect, describe, vi, beforeEach } from "vitest"; import { render } from "vitest-browser-svelte"; -import Wrapper from "../../wrappers/GenerationParametersTestWrapper.svelte"; +import Wrapper from "@test/browser/wrappers/GenerationParametersTestWrapper.svelte"; import { settings } from "@/lib/stores/settings.svelte"; import type { LLMConfig } from "@/lib/types/IDataModel"; diff --git a/frontend/test/browser/integration/ChatPersion.test.ts b/frontend/test/browser/integration/ChatPersion.test.ts index 2c0330da..179e7c3f 100644 --- a/frontend/test/browser/integration/ChatPersion.test.ts +++ b/frontend/test/browser/integration/ChatPersion.test.ts @@ -27,8 +27,9 @@ describe("Persona and Chat interactions", () => { // Mock chatStore.waitForSettings to avoid delays const { chatStore } = await import("@/features/chat/stores/chatStore.svelte"); + // Cast chatStore to access all private members - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // oxlint-disable-next-line typescript/no-explicit-any vi.spyOn(chatStore as any, "waitForSettings").mockResolvedValue(undefined); await chatStore.initPromise; await chatStore.setProvider("MOCK", { diff --git a/frontend/test/browser/wrappers/ButtonTestWrapper.svelte b/frontend/test/browser/wrappers/ButtonTestWrapper.svelte index 4fe25bf3..8f7c25f4 100644 --- a/frontend/test/browser/wrappers/ButtonTestWrapper.svelte +++ b/frontend/test/browser/wrappers/ButtonTestWrapper.svelte @@ -10,7 +10,7 @@ label: string; } - let { variant, size, disabled, type, onclick, label }: Props = $props(); + const { variant, size, disabled, type, onclick, label }: Props = $props();