diff --git a/.editorconfig b/.editorconfig index 86c714b7..f5fa787b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,16 +5,18 @@ charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true +indent_style = space +tab_width = 8 [*.py] indent_style = space indent_size = 4 -tab_width = 4 +tab_width = 8 [*.{js,ts,jsx,tsx,json,css,scss,html,md}] indent_style = space indent_size = 2 -tab_width = 2 +tab_width = 8 [Makefile] indent_style = tab diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 87ed1cee..6117b8df 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,156 +1,22 @@ # Copilot Instructions -## Repository overview +This file is intentionally concise to reduce token overhead. -- AugmentedQuill is a web-based GUI for AI-assisted prose writing, with a FastAPI backend and a React + TypeScript SPA frontend (Vite). -- Backend lives in src/augmentedquill/, frontend in src/frontend/, tests in tests/. -- Python 3.12+ required (CI uses 3.12). Node.js 24+ required (CI uses 24). -- The backend serves the built frontend from static/dist; frontend build output must be present for production-like runs. +## Canonical policy source -## High-level structure and key files +- The canonical, comprehensive agent policy for this repository is `AGENTS.md` at the repository root. +- Apply `AGENTS.md` as the source of truth for architecture boundaries, style, testing, migration rules, and done criteria. -- Backend entrypoint: src/augmentedquill/main.py (FastAPI app factory, CLI entrypoint augmentedquill). -- Backend config utilities: src/augmentedquill/core/config.py (config paths and env interpolation). -- API layer: src/augmentedquill/api/v1/; services: src/augmentedquill/services/. -- Frontend entrypoints: src/frontend/index.tsx and src/frontend/App.tsx. -- Frontend API layer: src/frontend/services/api.ts and src/frontend/services/apiClients/. -- Build/lint/test config: - - pyproject.toml (pytest, ruff, black, packaging). - - src/frontend/package.json (npm scripts). - - src/frontend/eslint.config.cjs, src/frontend/vite.config.ts, src/frontend/vitest.config.ts. - - .pre-commit-config.yaml (ruff, black, prettier, eslint). - - .github/workflows/code-quality.yml (CI checks). +## Always-enforced highlights -## Runtime configuration and data +- Keep all application LLM instructions separated from code and stored in `resources/config/instructions.json`. +- Project/story file format changes must be backward compatible or include explicit schema/version bumps with automatic chainable migrations. +- Prioritize maintainable, compact code with strict separation of concerns and minimal duplication. +- Code must be professional and production-quality; avoid shortcuts and temporary hacks. -- Config files are read from resources/config/ (machine.json, story.json). Examples in resources/config/examples/. -- JSON schema contracts live in resources/schemas/. -- Runtime data and logs are stored under data/projects/ and data/logs/. -- Environment overrides: OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL, OPENAI_TIMEOUT_S. +## Type discipline (Python + TypeScript) -## Validated commands (run in this repo) - -Backend setup (Python 3.12.3, venv): - -- Always activate the venv before running any Python commands: - - source venv/bin/activate -- python -m pip install -e ".[dev]" - -Backend lint and tests (with venv active): - -- ruff check . -- black --check . -- python -m pytest - -Frontend setup (Node 18+ expected; npm install required before any frontend script): - -- cd src/frontend && npm install - - Note: npm reports 13 vulnerabilities (1 moderate, 12 high). This does not fail npm install or CI. - -Frontend lint, tests, build: - -- cd src/frontend && npm run lint -- cd src/frontend && npm run test -- cd src/frontend && npm run build - - Build outputs to static/dist and is required for production-like runs. - -## How to run - -Production-like (serves built frontend): - -- cd src/frontend && npm run build -- augmentedquill --host 127.0.0.1 --port 8000 - -Dev mode (hot reload + proxy): - -- Terminal 1: augmentedquill --reload --host 127.0.0.1 --port 28000 -- Terminal 2: cd src/frontend && npm run dev -- Vite dev server runs on http://127.0.0.1:28001 and proxies /api and /static to 28000. - -## Required hygiene for new or modified files - -- All .py/.ts/.tsx/.js files must include a GPL header and a Purpose line. -- Use python tools/enforce_code_hygiene.py . to normalize headers (this can modify files). -- Use python tools/check_copyright.py . to validate headers (fails if missing). - -## CI and pre-commit parity - -CI runs in .github/workflows/code-quality.yml: - -- Backend: ruff check ., black --check, pytest. -- Frontend: npm run lint, npx prettier --check ., npm run test, npm run build. - Pre-commit mirrors these via .pre-commit-config.yaml. - -## Root inventory (important files and directories) - -Files at repo root: - -- CODE_OF_CONDUCT.md, CONTRIBUTING.md, LICENSE, README.md, SECURITY.md, pyproject.toml, tools.json, .pre-commit-config.yaml - Directories at repo root: -- src/, tests/, tools/, docs/, resources/, static/, data/, .github/, AugmentedQuill.egg-info/ - -Top-level directories (one level down): - -- src/augmentedquill/ (backend app) -- src/frontend/ (frontend app) -- tests/unit/ (backend tests) -- tools/ (repo scripts) -- resources/config/ and resources/schemas/ (config + schemas) -- static/ (images + built frontend output) -- data/ (local runtime data) - -## Key README points (summary) - -- Python 3.11+ and Node 18+ are required. -- Build frontend before running the app. -- Dev workflow: backend reload + Vite dev server. -- Config uses JSON with env overrides; samples in resources/config/examples/. - -## Guidance - -- Prefer editing backend logic in services/ and keep API routes thin. -- Prefer frontend changes inside features// and keep API calls in services/. -- Trust these instructions and only search the repo if something is missing or contradicts these notes. - -## Test Data Safety (Mandatory) - -- Never read from or write to real user runtime files under `data/config/`, `data/projects/`, or `data/logs/` when running tests. -- For all automated tests, force the app to use a temporary user data root by setting `AUGQ_USER_DATA_DIR` to a temp directory (for example under `/tmp`). -- Tests and AI-generated test code must isolate runtime paths via environment variables before importing app modules so default config constants resolve into temp paths. -- When tests need projects/registry overrides, use temp values for `AUGQ_PROJECTS_ROOT` and `AUGQ_PROJECTS_REGISTRY` inside that same temp root. - -## Branching and Release Policy - -The repository uses the following branch layout by default: - -- `main` — stable, reflects the last tagged release. Protected and only updated via PRs that have passed CI and reviews. -- `develop` — integration branch for active development. Feature branches should branch from and be merged into `develop`. - -Release and hotfix branches follow `release/vX.Y` and `hotfix/vX.Y.Z` naming; tags should use semantic versions like `v1.2.3`. - -When preparing a new release, create `release/vX.Y` from `develop`, finish testing, then merge into `main` and tag. Merge the release branch back into `develop` afterwards. - -## Chat Tools (LLM Function Calling) - -To add a new chat tool: - -1. Define a Pydantic model for parameters in the appropriate tool file (e.g., `src/augmentedquill/services/chat/chat_tools/project_tools.py`) -2. Decorate an async function with `@chat_tool(description="...")` -3. Implement: `async def tool_name(params: ParamsModel, payload: dict, mutations: dict) -> dict` -4. Return a dictionary (auto-wrapped as tool message) - -Example: - -```python -from pydantic import BaseModel, Field -from augmentedquill.services.chat.chat_tool_decorator import chat_tool - -class MyParams(BaseModel): - name: str = Field(..., description="Description") - -@chat_tool(description="Does something") -async def my_tool(params: MyParams, payload: dict, mutations: dict): - return {"result": "value"} -``` - -Tool files: `src/augmentedquill/services/chat/chat_tools/{project,story,chapter,sourcebook,image,order}_tools.py` +- All new/modified Python and TypeScript code must be typed. +- Shared/library helpers should use reasonably wide reusable types where safety is preserved. +- Application/domain functions should use the strictest minimal types possible to encode invariants. +- Convert from wider boundary types to stricter internal types at module boundaries. diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index a67c3afe..a18bc5dd 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -31,7 +31,7 @@ jobs: run: pytest - name: Run pip-audit - run: pip-audit --ignore-vuln CVE-2026-4539 + run: pip-audit --ignore-vuln CVE-2026-4539 --ignore-vuln CVE-2026-3219 frontend-checks: runs-on: ubuntu-latest @@ -46,11 +46,19 @@ jobs: - name: Install dependencies run: | cd src/frontend - npm ci + npm ci --legacy-peer-deps - name: Run ESLint run: | cd src/frontend npm run lint + - name: Run TypeScript Check + run: | + cd src/frontend + npm run typecheck + - name: Check generated API types are up-to-date + run: | + cd src/frontend + npm run check:generated-types - name: Run Prettier run: | cd src/frontend diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5319486a..2032c99b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,3 +36,5 @@ jobs: - name: Perform CodeQL analysis uses: github/codeql-action/analyze@v3 + with: + skip-queries: true diff --git a/.vscode/launch.json b/.vscode/launch.json index 293430ba..b654034a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "args": ["--host", "127.0.0.1", "--port", "28000", "--reload", "--llm-dump"], "console": "integratedTerminal", "justMyCode": true, - "preLaunchTask": "backend: clear-llm-log", + "preLaunchTask": "backend: prepare", "env": { "AUGQ_LLM_DUMP_LEVEL": "normal" } @@ -18,7 +18,7 @@ "name": "Frontend (Vite 28001)", "type": "node-terminal", "request": "launch", - "preLaunchTask": "frontend: install", + "preLaunchTask": "frontend: prepare", "command": "npm run dev -- --port 28001 --strictPort", "cwd": "${workspaceFolder}/src/frontend", "env": { diff --git a/.vscode/settings.json b/.vscode/settings.json index 8131eb21..c935a767 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,6 @@ { - "python-envs.pythonProjects": [], "python.terminal.activateEnvironment": true, - "python.defaultInterpreterPath": "./venv/bin/python", + "python.defaultInterpreterPath": "${workspaceFolder}/venv/bin/python", "python.testing.pytestArgs": ["tests"], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true diff --git a/.vscode/tasks.json b/.vscode/tasks.json index fee4f68c..09034c9e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -5,7 +5,7 @@ "label": "frontend: install", "type": "shell", "command": "npm", - "args": ["install"], + "args": ["install", "--legacy-peer-deps"], "options": { "cwd": "${workspaceFolder}/src/frontend" }, @@ -14,6 +14,18 @@ "reveal": "silent" } }, + { + "label": "frontend: generate-types", + "type": "shell", + "command": "npm", + "args": ["run", "generate:types"], + "options": { + "cwd": "${workspaceFolder}/src/frontend" + }, + "presentation": { + "reveal": "silent" + } + }, { "label": "backend: clear-llm-log", "type": "shell", @@ -22,6 +34,25 @@ "reveal": "silent", "close": true } + }, + { + "label": "backend: export-openapi", + "type": "shell", + "command": "${workspaceFolder}/venv/bin/python", + "args": ["tools/export_openapi.py"], + "presentation": { + "reveal": "silent" + } + }, + { + "label": "backend: prepare", + "dependsOn": ["backend: clear-llm-log", "backend: export-openapi"], + "dependsOrder": "sequence" + }, + { + "label": "frontend: prepare", + "dependsOn": ["frontend: install", "frontend: generate-types"], + "dependsOrder": "sequence" } ] } diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..83a897cf --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,244 @@ +# AGENTS.md + +Agent operating guide for AugmentedQuill. + +This file is intentionally optimized for coding agents: high-signal, executable guidance only. + +## 1. Purpose And Scope + +- Use this file as the primary agent playbook for this repository. +- Apply these rules for all files unless a more local AGENTS.md exists in a subdirectory. +- Prefer minimal, targeted changes. Avoid repo-wide refactors unless explicitly requested. + +## 2. Project Snapshot + +- Product: local-first AI writing assistant. +- Backend: FastAPI + Python (`src/augmentedquill`). +- Frontend: React + TypeScript + Vite (`src/frontend`). +- Tests: backend tests in `tests/unit`, frontend tests co-located with features. +- Production serving model: backend serves built frontend from `static/dist`. + +## 3. Environment And Setup + +### Required versions + +- Python >= 3.12 (CI uses 3.12) +- Node >= 24 (CI uses 24) + +### Backend setup (repo root) + +```bash +source venv/bin/activate +python -m pip install -e ".[dev]" +``` + +### Frontend setup + +```bash +cd src/frontend +npm install --legacy-peer-deps +``` + +## 4. Run Commands + +### Dev mode (recommended) + +Terminal 1: + +```bash +source venv/bin/activate +augmentedquill --reload --host 127.0.0.1 --port 28000 +``` + +Terminal 2: + +```bash +cd src/frontend +npm run dev +``` + +- Vite dev server defaults to `http://127.0.0.1:28001` and proxies backend/static. + +### Production-like local run + +```bash +cd src/frontend && npm run build +cd ../.. +source venv/bin/activate +augmentedquill --host 127.0.0.1 --port 8000 +``` + +## 5. Mandatory Validation (Before Finishing) + +Run the smallest relevant subset first, then broaden if needed. + +### Backend checks + +```bash +source venv/bin/activate +ruff check . +black --check . +python -m pytest +``` + +### Frontend checks + +```bash +cd src/frontend +npm run lint +npm run typecheck +npm run test +npm run build +``` + +### Generated API types drift check + +```bash +cd src/frontend +npm run check:generated-types +``` + +## 6. Architecture Boundaries (Do Not Cross) + +### Backend + +- Keep route handlers in `src/augmentedquill/api/v1` thin. +- Put business logic in `src/augmentedquill/services/`. +- Use shared response helpers in `src/augmentedquill/api/v1/http_responses.py` where applicable. +- Use project dependency resolution (`ProjectDep`) from `src/augmentedquill/api/v1/dependencies.py` for project-scoped routes. +- Keep all application LLM instructions separated from code and stored in `resources/config/instructions.json` so instruction sets can be project-language specific. + +### Frontend + +- Keep feature logic in `src/frontend/features/`. +- Keep reusable UI primitives in `src/frontend/components/ui`. +- Use API facade/client modules in `src/frontend/services`. +- Do not introduce ad-hoc fetch calls in features when an API client exists. + +## 7. Style And Conventions + +### Required file headers + +- Every new/modified `.py`, `.ts`, `.tsx`, `.js` file must include: + - GPL header + - Purpose line/docstring style used by the repository + +Use: + +```bash +python tools/enforce_code_hygiene.py . +python tools/check_copyright.py . +``` + +### Python + +- Formatter/linter: Black + Ruff +- Line length: 88 +- Favor typed signatures and small service helpers over oversized route functions. +- Code changes must be professional and solid; avoid shortcuts and temporary hacks. +- Use type annotations for all new/modified Python code (function params, return types, and important variables where needed for clarity). + +### TypeScript/React + +- Linting via `src/frontend/eslint.config.cjs` +- Key enforced rules include: + - single quotes + - no explicit `any` + - prefer typed parameters + - accessibility-focused jsx-a11y rules +- Code changes must be professional and solid; avoid shortcuts and temporary hacks. +- Use explicit, precise TypeScript types for all new/modified code; avoid implicit `any` and weakly typed shapes. + +### Type design strategy (library-wide vs app-specific) + +- Library/shared helper functions should accept reasonably wide, reusable input types where this does not reduce safety. +- Program/application functions should use the strictest minimal types possible to encode concrete invariants. +- Prefer converting from wide boundary types to strict internal types at module boundaries. +- Keep type contracts explicit; do not rely on ambiguous unions when domain types are known. + +### Maintainability priorities (highest priority) + +- Optimize first for maintainable and compact code. +- Avoid duplication; prefer shared helpers over copy-pasted logic. +- Enforce strict separation of concerns across route/service/model/UI layers. + +### i18n and language handling (strict) + +- Never hardcode user-facing English strings in UI components. +- Use `react-i18next` translation keys. +- Inputs/textareas that edit story text must set `lang={storyLanguage || 'en'}` (or equivalent) and keep spellcheck behavior correct. + +## 8. Test Data Safety (Strict) + +- Never use real runtime data directories in tests: + - `data/config` + - `data/projects` + - `data/logs` +- Tests must isolate runtime paths via temp environment variables before importing app modules: + - `AUGQ_USER_DATA_DIR` + - `AUGQ_PROJECTS_ROOT` + - `AUGQ_PROJECTS_REGISTRY` + - `AUGQ_MACHINE_CONFIG_PATH` (when needed) +- Follow `tests/conftest.py` pattern for session-scoped temporary directories. + +## 9. Generated Artifacts Rules + +- Treat these as generated artifacts and do not hand-edit unless explicitly required: + - `openapi.json` (from backend export tool) + - `src/frontend/types/api.generated.ts` (from OpenAPI generation) +- If backend API contracts change, regenerate and validate generated type files. + +## 9.5 Project File Schema Evolution Rules + +- Changes to project/story files must either be backward compatible or explicitly bump the config/schema version. +- Any version bump must include an automatic conversion function from the prior version. +- Conversions must be chainable step-by-step (for example v2 -> v3 -> v4) so older versions can be upgraded safely through intermediate migrations. +- Never introduce a breaking project-file format change without a tested migration path. + +## 10. Security And Operational Guardrails + +- App is local-first and not designed for public internet exposure without external access controls. +- Prefer local loopback bindings for development (`127.0.0.1`). +- Be careful with LLM request/response logs and secrets in environment variables. + +## 11. Agent Workflow Heuristics + +- Read only what is needed for the current task. +- Prefer surgical edits over broad rewrites. +- After edits, run relevant checks and report concrete outcomes. +- If failures are unrelated to your change, report them clearly instead of silently changing unrelated code. +- Preserve existing style and naming in touched files. + +## 12. Token Budget Rules (What To Include vs Omit) + +Use this section to avoid context waste in future agent updates. + +### Keep in AGENTS.md + +- Stable, repo-wide commands and constraints. +- Non-obvious pitfalls that frequently cause regressions. +- Hard requirements (test isolation, i18n constraints, generated file rules). +- Architectural boundaries that prevent misplaced code. + +### Keep out of AGENTS.md + +- Exhaustive file trees and long inventories. +- Full architecture narratives already covered in docs. +- Rare one-off historical incidents. +- Verbose style philosophy not tied to actionable checks. +- Duplicate instructions that are already enforced by tooling, unless the failure cost is high. + +## 13. Fast Task Routing + +- API endpoint behavior change: edit route in `api/v1`, implement logic in `services/`, add/update backend tests. +- Frontend feature/UI change: edit `features/`, keep strings in i18n resources, add/update component tests. +- Config/path behavior: inspect `src/augmentedquill/core/config.py` and corresponding tests in `tests/unit/core`. +- Chat tool change: update tool models/functions under `src/augmentedquill/services/chat/chat_tools` and validate tool schema behavior. + +## 14. Definition Of Done For Agent Changes + +- Code compiles/lints/tests for the touched area. +- Required generated artifacts updated (if applicable). +- No hardcoded UI strings introduced. +- No real user runtime data touched by tests. +- Diff is minimal and scoped to the request. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f672b77d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,57 @@ +# Changelog + +## [0.9.0] - 2026-04-26 + +### Added + +- **Search and Replace**: Full search & replace functionality including title search, relation handling, and conflict highlighting +- **Attachments**: Drag-and-drop file attachments in chat, nice confirmation dialogs +- **Scratchpad Dialog**: Dedicated dialog to show the scratchpad +- **Internationalization (i18n)**: Initial i18n setup with multiple language support +- **Gemma 4 Preset**: New model preset for Gemma 4 +- **Provider Config**: Allow tweaking of Provider configuration +- **Paragraph Suggestion Modes**: Different modes for suggesting next paragraphs with regenerate button +- **Undo/Redo**: Undo/redo buttons in metadata and sourcebook editors +- **Diff View Toggle**: GUI option to toggle diff view in editor and dialog titles +- **Screenshots**: First round of documentation screenshots + +### Changed + +- **Accessibility**: Major accessibility improvements (focus indicators, keyboard navigation, ARIA) +- **UI Style**: Style unification, better resizeable indicators, pointer cursors on interactive elements +- **Diff Display**: Enhanced diff display, show what was changed by the LLM, better whitespace highlighting +- **Typography**: Enforced typographic quotes support in chapter and story content writing +- **Tailwind CSS**: Migrated to Tailwind CSS v4 + +### Fixed + +- Diff view issues (whitespace highlighting, loss during mode switching, project switching) +- Project switch not changing prose +- Scratchpad display on browser reload +- White space handling and display modes +- Image display in editor +- Scroll away errors +- Sourcebook handling with undo/redo +- Story summary generation +- Metadata diff view +- Search/replace dialog and functionality +- React infinite rerender loops in Settings +- Story continuation through chatting +- Chapter requirement for short stories without chapters +- Language settings display +- EDITING tool calling detection +- Gemini 4 tool calling detection +- LLM model selection +- Rename of sourcebook entries + +### Performance + +- Streaming text content intake and scrolling optimized +- Reduced editor lag +- Optimized main text area handling +- Performance improvements by decoupling React updates +- React separation for sourcebook + +## [v0.1.0-alpha] - 2026-03-29 + +- Initial public relase diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index e9a377ed..526a2618 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - You are the author in the driver seat: your story is your story, and the AI is a creative partner (from brainstorm buddy to ghostwriter-style assistant) that supports your voice and choices. - Join the community: [r/AugmentedQuill](https://www.reddit.com/r/AugmentedQuill/) -> **Screenshot placeholder:** Insert your app screenshot here (e.g., `docs/assets/screenshot.png`). +![Main screen of AugmentedQuill](docs/user_manual/screenshots/main.png) --- @@ -106,6 +106,7 @@ The complete user guide is in `docs/user_manual/`: - `black --check .` - `python -m pytest` - Frontend: `cd src/frontend && npm run lint && npm run test && npm run build` +- Accessibility validation: `cd src/frontend && npm run test:accessibility` (new) - Quick run: `augmentedquill --reload --host 127.0.0.1 --port 28000` ### Configuration paths @@ -143,5 +144,8 @@ Model endpoint variables: ## 🧩 Known limitations - No multi-user access controls. -- Limited accessibility support. - No real-time external editor sync. + +## Multiple Languages + +AugmentedQuill natively supports multiple languages for both the application interface (GUI) and your story formatting. Use `Settings > General > GUI Language` to configure the application locale, and the language will automatically be respected. diff --git a/docs/ORGANIZATION.md b/docs/ORGANIZATION.md index e34b0fb9..161f463f 100644 --- a/docs/ORGANIZATION.md +++ b/docs/ORGANIZATION.md @@ -92,3 +92,20 @@ All `*.py`, `*.ts`, `*.tsx`, and `*.js` files are expected to start with: 2. A one-line `Purpose:` header describing why the file exists. Use `python tools/enforce_code_hygiene.py .` to normalize headers and validate compliance. + +## Internationalization (i18n) + +The application uses `react-i18next` for GUI internationalization and standard HTML `lang` attributes for project-specific language routing (like browser spellchecking). + +**GUI Language:** +The GUI locale defines the language of the application interface. It defaults to the browser's `navigator.language` and can be manually set via `Settings > General > GUI Language`. State lives in `AppSettings.guiLanguage`. +To add new GUI languages: + +1. Update `src/frontend/features/app/i18n.ts` and add a new dictionary under `resources`. +2. Add the language to the `