mirror of
https://github.com/lfnovo/open-notebook.git
synced 2026-10-19 09:13:16 +00:00
chore(frontend): typed locales, drop unused deps, fix AGENTS.md drift (#1061)
- Derive TranslationShape from the en-US locale and add 'satisfies TranslationShape' to the other 13 locales so missing/extra i18n keys fail tsc at compile time (previously only the runtime parity test) - Remove unused dependencies next-themes and @monaco-editor/react (zero imports in src/; theming is the hand-rolled zustand theme-store) - Fix frontend/AGENTS.md drift: 14 locales (not 7), dark mode mechanism is the zustand theme-store setting the 'dark' class (not next-themes)
This commit is contained in:
parent
ac30f4d976
commit
97ef02702b
18 changed files with 47 additions and 60 deletions
|
|
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Changed
|
||||
- Re-enabled the ruff rules for unused imports (`F401`), unused local variables (`F841`) and bare `except:` (`E722`) that were ignored to silence legacy Streamlit-era noise, and cleaned up the remaining fallout (10 unused imports, 2 unused test variables; no bare excepts remained)
|
||||
- Chat, source chat, Ask and transformation prompts now steer models to write math as `$$...$$` (display) / `$...$` (inline) so formulas render via KaTeX, reserving fenced `latex` code blocks for when the user explicitly asks for the LaTeX source (#1051)
|
||||
- Frontend locale files are now type-checked at compile time: every non-en-US locale declares `satisfies TranslationShape` (derived from the en-US object), so a missing or extra i18n key fails `tsc` in the editor instead of only the runtime parity test. Also removed two unused frontend dependencies (`next-themes`, `@monaco-editor/react`) and fixed `frontend/AGENTS.md` drift (14 locales, not 7; dark mode is the hand-rolled zustand theme-store, not next-themes)
|
||||
|
||||
### Removed
|
||||
- Dead Streamlit-era service layer (~2,000 lines): `api/client.py` (a synchronous HTTP client that called the app's own API) and 13 `api/*_service.py` wrappers that consumed the app's own HTTP API — none were imported by any router, command or test. Also removed the toy `process_text`/`analyze_data` demo commands (`commands/example_commands.py`) from the background worker
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Normative rules for working on the Next.js frontend. Architecture and flow walkt
|
|||
|
||||
## Hard rules
|
||||
|
||||
- **i18n is mandatory**: every UI string goes through `t('section.key')` and the key must exist in **all 7 locales** (`en-US`, `pt-BR`, `zh-CN`, `zh-TW`, `ja-JP`, `ru-RU`, `bn-IN`) under `src/lib/locales/`. Missing keys fall back to en-US silently — keep locales in sync.
|
||||
- **i18n is mandatory**: every UI string goes through `t('section.key')` and the key must exist in **all locales** under `src/lib/locales/` (currently 14; en-US is the reference). Missing keys fall back to en-US silently — keep locales in sync. Each non-en-US locale ends with `satisfies TranslationShape` (type derived from en-US), so a missing/extra key fails `tsc`; the parity test (`src/lib/locales/index.test.ts`) checks the same at runtime.
|
||||
- All requests go through `apiClient` (`src/lib/api/client.ts`); never create a second axios instance. Auth token is auto-added from localStorage key `auth-storage`.
|
||||
- Data fetching uses TanStack Query hooks in `src/lib/hooks/` with `QUERY_KEYS`; mutations invalidate caches and show toasts (sonner). Follow the existing hook shape.
|
||||
- FormData requests: nested objects/arrays must be `JSON.stringify`-ed before appending; the interceptor strips Content-Type so the browser sets the multipart boundary — don't re-add it.
|
||||
|
|
@ -24,7 +24,7 @@ Normative rules for working on the Next.js frontend. Architecture and flow walkt
|
|||
- SSE (`useAsk`): incomplete lines stay in the buffer between reads; incomplete JSON is silently skipped — don't "simplify" the buffer handling.
|
||||
- `useSourceStatus` polls every 2s while status is `running`/`queued`/`new`.
|
||||
- Cache invalidation is deliberately broad (e.g. `['sources']` hits all source queries) — a simplicity/perf trade-off, be precise only when it matters.
|
||||
- Dark mode requires the `dark` class on the document root (next-themes), not just `prefers-color-scheme`.
|
||||
- Dark mode requires the `dark` class on the document root, not just `prefers-color-scheme`. It's hand-rolled: the zustand `theme-store` (persisted as `theme-storage`) sets the class via `ThemeProvider` — no next-themes.
|
||||
- Dialogs don't auto-reset form state (parent clears it) and auto-focus the first input (layout shift risk with conditional inputs). Fixed overlays use `z-50`.
|
||||
- Provider nesting order in `app/layout.tsx` matters: ErrorBoundary → ThemeProvider → QueryProvider → I18nProvider → ConnectionGuard → Toaster. ErrorBoundary is a class component and uses the raw `enUS` locale object (no hooks).
|
||||
- `i18n` runs with `useSuspense: false` (no SSR for translations).
|
||||
|
|
|
|||
43
frontend/package-lock.json
generated
43
frontend/package-lock.json
generated
|
|
@ -9,7 +9,6 @@
|
|||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.1.1",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
|
|
@ -40,7 +39,6 @@
|
|||
"katex": "^0.16.47",
|
||||
"lucide-react": "^0.525.0",
|
||||
"next": "^16.2.6",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-hook-form": "^7.60.0",
|
||||
|
|
@ -1828,27 +1826,6 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@monaco-editor/loader": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz",
|
||||
"integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==",
|
||||
"dependencies": {
|
||||
"state-local": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@monaco-editor/react": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
|
||||
"integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
|
||||
"dependencies": {
|
||||
"@monaco-editor/loader": "^1.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">= 0.25.0 < 1",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
|
||||
|
|
@ -9876,12 +9853,6 @@
|
|||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.52.2",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
|
||||
"integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz",
|
||||
|
|
@ -9988,15 +9959,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-themes": {
|
||||
"version": "0.4.6",
|
||||
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
|
||||
"integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.47",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz",
|
||||
|
|
@ -11541,11 +11503,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/state-local": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="
|
||||
},
|
||||
"node_modules/std-env": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.1.1",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
|
|
@ -45,7 +44,6 @@
|
|||
"katex": "^0.16.47",
|
||||
"lucide-react": "^0.525.0",
|
||||
"next": "^16.2.6",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-hook-form": "^7.60.0",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const bnIN = {
|
||||
common: {
|
||||
search: "অনুসন্ধান...",
|
||||
|
|
@ -947,4 +949,4 @@ export const bnIN = {
|
|||
goToSettings: "সেটিংসে যান",
|
||||
viewDocs: "ডকুমেন্টেশন দেখুন",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const caES = {
|
||||
common: {
|
||||
search: "Cerca...",
|
||||
|
|
@ -946,4 +948,4 @@ export const caES = {
|
|||
goToSettings: "Ves a la configuració",
|
||||
viewDocs: "Visualitza la documentació",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
// German locale.
|
||||
// Translate values only; do not change keys, placeholders or structure.
|
||||
|
||||
|
|
@ -949,4 +951,4 @@ export const deDE = {
|
|||
goToSettings: "Zu den Einstellungen",
|
||||
viewDocs: "Dokumentation anzeigen",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -948,3 +948,8 @@ export const enUS = {
|
|||
viewDocs: "View docs",
|
||||
},
|
||||
}
|
||||
|
||||
// Compile-time shape of the en-US translations. Every other locale must
|
||||
// `satisfies` this type so missing or extra keys fail `tsc`, not just the
|
||||
// runtime parity test.
|
||||
export type TranslationShape = typeof enUS;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const esES = {
|
||||
common: {
|
||||
search: "Buscar...",
|
||||
|
|
@ -947,4 +949,4 @@ export const esES = {
|
|||
goToSettings: "Ir a configuración",
|
||||
viewDocs: "Ver documentación",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const frFR = {
|
||||
common: {
|
||||
search: "Recherche...",
|
||||
|
|
@ -946,4 +948,4 @@ export const frFR = {
|
|||
goToSettings: "Aller aux paramètres",
|
||||
viewDocs: "Voir la documentation",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const itIT = {
|
||||
common: {
|
||||
search: "Cerca...",
|
||||
|
|
@ -946,4 +948,4 @@ export const itIT = {
|
|||
goToSettings: "Vai alle Impostazioni",
|
||||
viewDocs: "Vedi documentazione",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const jaJP = {
|
||||
common: {
|
||||
search: "検索...",
|
||||
|
|
@ -946,4 +948,4 @@ export const jaJP = {
|
|||
goToSettings: "設定へ移動",
|
||||
viewDocs: "ドキュメントを見る",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const plPL = {
|
||||
common: {
|
||||
search: "Szukaj...",
|
||||
|
|
@ -946,4 +948,4 @@ export const plPL = {
|
|||
goToSettings: "Przejdź do Ustawień",
|
||||
viewDocs: "Zobacz dokumentację",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const ptBR = {
|
||||
common: {
|
||||
search: "Buscar...",
|
||||
|
|
@ -946,4 +948,4 @@ export const ptBR = {
|
|||
goToSettings: "Ir para Configurações",
|
||||
viewDocs: "Ver documentação",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const ruRU = {
|
||||
common: {
|
||||
search: "Поиск...",
|
||||
|
|
@ -946,4 +948,4 @@ export const ruRU = {
|
|||
goToSettings: "Перейти к настройкам",
|
||||
viewDocs: "Документация",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const trTR = {
|
||||
common: {
|
||||
search: "Ara...",
|
||||
|
|
@ -946,4 +948,4 @@ export const trTR = {
|
|||
goToSettings: "Ayarlara Git",
|
||||
viewDocs: "Belgeleri Görüntüle",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const zhCN = {
|
||||
common: {
|
||||
search: "搜索...",
|
||||
|
|
@ -946,4 +948,4 @@ export const zhCN = {
|
|||
goToSettings: "前往设置",
|
||||
viewDocs: "查看文档",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { TranslationShape } from '../en-US';
|
||||
|
||||
export const zhTW = {
|
||||
common: {
|
||||
search: "搜尋...",
|
||||
|
|
@ -946,4 +948,4 @@ export const zhTW = {
|
|||
goToSettings: "前往設定",
|
||||
viewDocs: "查看文件",
|
||||
},
|
||||
}
|
||||
} satisfies TranslationShape;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue