These keys are called with t() but are missing from public/locales/en.json, which is the fallback language, so the UI shows the key path instead of text.
- New public/locales/he.json, full key parity with en.json
- 'he' added to supportedLngs and the NavBar language menu
- Document dir/lang follow the active language, so Hebrew renders RTL
- MUI theme direction follows suit, so components anchor correctly
Closes#1136
Add full Korean translation for the UI (public/locales/ko.json, 405
keys, full parity with en.json) and register it:
- src/i18n.ts supportedLngs
- NavBar.tsx language menu (desktop + mobile)
- language_menu entries added to the other 6 locale files (en, es,
ja, zh, de, tr) so each language's switcher can display 'Korean' in
its own language, matching the existing convention.
- Added RECORDING_TIMEOUT_MS and recordingTimeouts Map to track sessions
- Timer starts after browser is added to pool; emits recording-timeout
socket event then destroys the remote browser after 1s delay (gives
frontend time to handle the event before socket teardown)
- clearRecordingTimeout() exported and called in destroyRemoteBrowser
and the /stop/:browserId route to cancel timer on normal save/discard
- RecordingPage listens for recording-timeout: broadcasts via
BroadcastChannel then calls window.close(); falls back to
navigate('/robots') if close is blocked by the browser
- PageWrapper (always mounted in original tab) listens on
BroadcastChannel, navigates back to the page the recording was
started from (stored in sessionStorage), and shows warning notification
- RobotCreate stores current pathname in sessionStorage before opening
the recording tab so PageWrapper can navigate back on timeout
- Added timeout_discarded translation key to all 6 locale files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add isRobotNameTaken() helper with case-insensitive, trimmed comparison
using lower(trim(recording_meta->>'name')) in Sequelize queries
- Add 409 uniqueness checks in POST /recordings/scrape, /llm, /crawl,
/search, PUT /recordings/:id (edit), and POST /recordings/:id/duplicate
- Trim all robot names before uniqueness check and before saving
- Add DB migration: unique index robot_user_name_unique on
("userId", lower(trim(recording_meta->>'name')))
- Generator.ts: trim fileName, case-insensitive conflict check before
creating new robot, emit fileSaved { actionType: 'nameExists' } on conflict
- Frontend API: re-throw server error messages for all create/update/
duplicate functions so callers surface the conflict message
- RobotCreate: check for duplicate name before optimistic update + navigation
for LLM robot flow; try-catch for scrape/crawl/search flows
- RobotEditPage: use error.message in catch to surface conflict
- RobotDuplicatePage: add Robot Name field (pre-filled, auto-updates with URL)
- SaveRecording: replace confirm-to-overwrite with hard block using
case-insensitive trimmed check; handle nameExists socket action
- Add name_exists and robot_duplication name translations for all 6 locales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>