Skip to content

l10n(zh-CN): full Chinese localization + Windows compat + backend fixes#66

Open
SummerSec wants to merge 43 commits into
debpalash:mainfrom
SummerSec:fix/windows-triton-torch-compile
Open

l10n(zh-CN): full Chinese localization + Windows compat + backend fixes#66
SummerSec wants to merge 43 commits into
debpalash:mainfrom
SummerSec:fix/windows-triton-torch-compile

Conversation

@SummerSec
Copy link
Copy Markdown

@SummerSec SummerSec commented May 16, 2026

Overview

1. Chinese (zh-CN) Localization

Every visible page in the frontend now supports Simplified Chinese:

  • NavRail — sidebar navigation labels
  • Launchpad — hero, action cards, project sections, empty states
  • Clone / Design — prompt, language, categories (Gender/Age/Pitch/Style), personality chips, production overrides, all buttons
  • Settings — all 8 tabs: General (language/theme/proxy), Models, Engines, Capture, Credentials, Logs, About, Privacy
  • Transcriptions — header, search, timestamps, copy/delete/export
  • Voice Gallery — categories (Disney/Anime/…), search, cards, empty state
  • DubTab — upload area, CAST, Language, Style, Transcript, Generate Dub, export formats

2. Bug Fixes

Fix Detail
General tab missing TABS array omitted general — component was unreachable
Language reset on reload useEffect empty deps [], persisted locale never synced to i18n
Personality validation error Backend personalities had natural language in instruct — added client-side filter
NavRail React warning key prop spread into JSX elements
Emoji duplication Personality translations duplicated the API icon field
Importlib.util import import importlibimport importlib.util (real bug masked by try/except)

3. Backend Reliability

Change Detail
FFmpeg detection on Windows Added D:\ffmpeg\bin\ffmpeg.exe and other common Windows paths
yt-dlp timeout Socket timeout 30s → 120s, added retries/fragment_retries
Error messages Empty errors now show exit code or exception type instead of ""
Download proxy Proxy setting in Settings → General. Supports HTTP/HTTPS/SOCKS5. Sets HTTP_PROXY, HTTPS_PROXY, ALL_PROXY

Files changed

frontend/src/App.jsx, frontend/src/components/Header.jsx, frontend/src/components/NavRail.jsx, frontend/src/i18n/locales/en.json, frontend/src/i18n/locales/zh-CN.json (new), frontend/src/pages/CloneDesignTab.jsx, frontend/src/pages/DubTab.jsx, frontend/src/pages/Launchpad.jsx, frontend/src/pages/Settings.jsx, frontend/src/pages/Transcriptions.jsx, frontend/src/pages/VoiceGallery.jsx, backend/services/ffmpeg_utils.py, backend/services/dub_pipeline.py, backend/services/model_manager.py, backend/api/routers/system.py, frontend/src/utils/constants.js

SummerSec

@
fix: skip torch.compile on Windows where Triton is unavailable

torch.compile with mode="reduce-overhead" depends on Triton, which has no
official Windows support. On Windows the compile call succeeds but generates
code paths that crash at inference time with an OOM-like error
("Cannot find a working triton installation").

Check for Triton availability before compiling so TTS gracefully falls back
to eager mode on platforms without Triton.

Closes debpalash#65
SummerSec
@
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Model loading in backend/services/model_manager.py now conditionally applies torch.compile only when the triton module is available. If Triton is missing, the code logs a skip; otherwise it compiles with mode="reduce-overhead" and logs success. Frontend changes add react-i18next, wire runtime language switching from persisted locale, localize many components/pages, and add/expand English and zh-CN locale files.

Changes

Triton-gated torch.compile

Layer / File(s) Summary
Conditional torch.compile on Triton availability
backend/services/model_manager.py
Synchronous model loading checks importlib.util.find_spec("triton") and applies torch.compile(..., mode="reduce-overhead") only if Triton is installed; logs appropriately when skipped or applied.

Frontend i18n integration

Layer / File(s) Summary
App initialization and theme/locale effect
frontend/src/App.jsx
Adds i18n import and a useEffect that sets data-theme and calls i18n.changeLanguage(locale) when locale or theme changes.
Header and NavRail localization
frontend/src/components/Header.jsx, frontend/src/components/NavRail.jsx
Header uses useTranslation for the DEV reload button; NavRail replaces static label arrays with translation-key defs and memoized localized items.
Launchpad page localization
frontend/src/pages/Launchpad.jsx
Uses Trans and useTranslation to localize hero, callouts, project headings, badges, and action buttons.
Clone design page localization
frontend/src/pages/CloneDesignTab.jsx
Replaces many hardcoded strings with t(...) keys across the clone design UI, localizes production override labels/options, and MicButton states.
Settings page localization and GeneralTab
frontend/src/pages/Settings.jsx
Refactors tab defs, adds GeneralTab for theme/language, introduces LOG_SOURCE_DEFS and CREDENTIAL_DEFS, and localizes Models, Engines, Logs, About, Privacy, Capture, Hotkey, and Credentials UI and flows.
Dub page localization
frontend/src/pages/DubTab.jsx
Localizes DubTab idle/upload copy and ingest controls to translation keys.
Transcriptions page localization
frontend/src/pages/Transcriptions.jsx
Adds i18n to Transcriptions for UI text, toasts, and localized relative-time formatting.
VoiceGallery localization
frontend/src/pages/VoiceGallery.jsx
Replaces hardcoded headings, placeholders, counts, and action tooltips with translation keys.
English locale additions
frontend/src/i18n/locales/en.json
Expands en.json with common, nav, launchpad, clone, settings, about, privacy, credentials, engines, capture, logs, and new top-level pages/sections.
Chinese locale (zh-CN) file
frontend/src/i18n/locales/zh-CN.json
Adds full zh-CN.json locale with translations for UI sections including clone, settings, about, privacy, credentials, engines, capture, logs, and pages.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • debpalash/OmniVoice-Studio#37: Also modifies backend/services/model_manager.py in the model-loading path and adds model loading progress/status fields.

Poem

I checked for Triton before a try,
So compiles won't make your app cry,
I hopped through keys in English and 中文 too,
I nudged the theme and locale for you,
A rabbit sings — translations done, huzzah! 🐇✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title mentions Chinese localization and Windows compatibility, which are major themes, but omits the backend fixes and torch.compile fix entirely, making it only partially accurate. Consider revising the title to better reflect all major changes (e.g., 'Add zh-CN localization, fix Windows torch.compile, and backend reliability improvements').
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering the overview, bug fixes, backend reliability improvements, and affected files with clear details and tables.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/services/model_manager.py`:
- Line 229: Replace the Chinese inline comment "Windows 没有 Triton——torch.compile
可能不报错但推理时炸显存" in backend/services/model_manager.py with an English equivalent;
locate the comment string as it appears near the model initialization/inference
logic in model_manager.py and change it to something like "On Windows Triton is
not available — torch.compile may not error but can cause OOM during inference"
(adjust wording as preferred) so the codebase uses English comments
consistently.
- Around line 230-231: The code currently does "import importlib" then calls
importlib.util.find_spec("triton"), which raises AttributeError because
importlib.util isn't imported; update the import so the util submodule is
available (e.g., add "import importlib.util" or "from importlib import util as
importlib_util") and then call util.find_spec("triton") (or
importlib_util.find_spec) in the Triton detection branch; ensure the change
prevents the AttributeError that the existing exception handler would otherwise
mask (references: the import statement and the call to
importlib.util.find_spec("triton")).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 22c2daf4-bfe7-4f55-9abb-098691ac7ac5

📥 Commits

Reviewing files that changed from the base of the PR and between ba4cf6a and 505531e.

📒 Files selected for processing (1)
  • backend/services/model_manager.py

Comment thread backend/services/model_manager.py Outdated
Comment thread backend/services/model_manager.py Outdated
…igation

Add full Chinese (zh-CN) translation support across the frontend:
- NavRail, Launchpad, Clone/Design tabs, Settings (all tabs)
- Sidebar navigation labels, hero text, action cards, section headings
- Fix: Settings missing General tab in TABS array
- Fix: i18n locale not persisted after page reload (useEffect deps)
- Fix: NavRail key prop spreading into JSX elements

Co-authored-by: SummerSec
@SummerSec SummerSec changed the title fix: skip torch.compile on Windows where Triton is unavailable fix: skip torch.compile on Windows + Chinese (zh-CN) localization May 16, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/i18n/locales/en.json (1)

32-32: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove "any of" for better grammar.

The phrase "dub a video into any of <1>{{count}} languages</1>" is grammatically awkward. It should be either "into <1>{{count}} languages</1>" or "into any of <1>the {{count}} languages</1>". The Chinese translation correctly omits "any of" and reads more naturally.

✏️ Proposed fix
-    "hero_desc": "Clone a voice, design a new one, or dub a video into any of <1>{{count}} languages</1>. Built for creators who care how it sounds.",
+    "hero_desc": "Clone a voice, design a new one, or dub a video into <1>{{count}} languages</1>. Built for creators who care how it sounds.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/i18n/locales/en.json` at line 32, The string value for the
"hero_desc" key contains the awkward phrase "dub a video into any of
<1>{{count}} languages</1>"; update that value to remove "any of" so it reads
"dub a video into <1>{{count}} languages</1>" (preserving the existing HTML-like
tags and {{count}} interpolation) to match the Chinese translation and correct
the grammar.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/App.jsx`:
- Around line 80-87: The useEffect handling themes doesn't clear the data-theme
when switching back to the default 'gruvbox', so update the effect in the
useEffect that references theme/locale (the hook using theme, locale,
i18n.changeLanguage, and document.documentElement.setAttribute) to remove the
data-theme attribute when theme is 'gruvbox' or falsy (use
removeAttribute('data-theme')) instead of leaving the old attribute; keep the
existing branch that sets data-theme when theme && theme !== 'gruvbox' and
preserve the locale i18n.changeLanguage call.

In `@frontend/src/i18n/locales/zh-CN.json`:
- Line 14: The translation for the "reload" key currently reads "刷新界面" which
loses the stronger "Force" meaning from the English "Force Reload UI"; if this
action is indeed a hard/forceful reload (clearing state/session), update the
"reload" value to "强制刷新界面" (or another Chinese phrase that preserves "force") so
the UI semantics match the English source; locate the "reload" key in the zh-CN
locale file and replace its string accordingly.

In `@frontend/src/pages/Settings.jsx`:
- Around line 1339-1354: CREDENTIAL_DEFS currently stores placeholderKey strings
but the component uses them directly (placeholder={field.placeholderKey}), so
placeholders aren't localized; update the Settings component to pass translated
placeholders instead by calling the i18n translator with the placeholderKey
(e.g., useTranslation hook and replace placeholder={field.placeholderKey} with
placeholder={t(field.placeholderKey)}) wherever CREDENTIAL_DEFS entries are
rendered (also fix the same pattern referenced around the other occurrence for
TRANSLATE_API_KEY).

---

Outside diff comments:
In `@frontend/src/i18n/locales/en.json`:
- Line 32: The string value for the "hero_desc" key contains the awkward phrase
"dub a video into any of <1>{{count}} languages</1>"; update that value to
remove "any of" so it reads "dub a video into <1>{{count}} languages</1>"
(preserving the existing HTML-like tags and {{count}} interpolation) to match
the Chinese translation and correct the grammar.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d2caa55-2d93-4891-a7ce-f0a787402841

📥 Commits

Reviewing files that changed from the base of the PR and between 505531e and 4e7397b.

📒 Files selected for processing (8)
  • frontend/src/App.jsx
  • frontend/src/components/Header.jsx
  • frontend/src/components/NavRail.jsx
  • frontend/src/i18n/locales/en.json
  • frontend/src/i18n/locales/zh-CN.json
  • frontend/src/pages/CloneDesignTab.jsx
  • frontend/src/pages/Launchpad.jsx
  • frontend/src/pages/Settings.jsx

Comment thread frontend/src/App.jsx
"refresh": "刷新",
"close": "关闭",
"back": "返回",
"reload": "刷新界面",
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Semantic mismatch: "Force" is lost in translation.

The English version uses "Force Reload UI" (line 14 in en.json) which implies a stronger, potentially disruptive action. The Chinese "刷新界面" translates to just "Refresh UI" without conveying the forceful nature. If this action truly performs a hard/forceful reload (clearing state, etc.), consider using "强制刷新界面" to preserve the semantic meaning.

📝 Proposed fix if this action is truly forceful
-    "reload": "刷新界面",
+    "reload": "强制刷新界面",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"reload": "刷新界面",
"reload": "强制刷新界面",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/i18n/locales/zh-CN.json` at line 14, The translation for the
"reload" key currently reads "刷新界面" which loses the stronger "Force" meaning
from the English "Force Reload UI"; if this action is indeed a hard/forceful
reload (clearing state/session), update the "reload" value to "强制刷新界面" (or
another Chinese phrase that preserves "force") so the UI semantics match the
English source; locate the "reload" key in the zh-CN locale file and replace its
string accordingly.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⏭️ Skipped intentionally. The user explicitly asked to remove '强制' and use just '刷新界面' — it's a dev-only button.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SummerSec Acknowledged—will leave zh-CN common.reload as “刷新界面” per the product request, given it’s a dev-only button. No change needed here. If you ever want to align semantics across locales, we could optionally soften the English to “Reload UI” in a follow-up, but fine to keep as-is. Want me to resolve this thread?


✏️ Learnings added
Learnt from: SummerSec
Repo: debpalash/OmniVoice-Studio PR: 66
File: frontend/src/i18n/locales/zh-CN.json:16-16
Timestamp: 2026-05-16T18:09:07.853Z
Learning: OmniVoice-Studio i18n preference: For the dev-only reload UI button, zh-CN locale key `common.reload` should be "刷新界面" (not "强制刷新界面") per product direction; do not re-flag the lack of "force" in future reviews.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread frontend/src/pages/Settings.jsx Outdated
Comment on lines 1339 to 1354
const CREDENTIAL_DEFS = [
{
key: 'HF_TOKEN',
label: 'HuggingFace Token',
placeholder: 'hf_xxxxxxxxxxxx',
help: 'Required for speaker diarization and faster model downloads. Get yours at huggingface.co/settings/tokens.',
labelKey: 'credentials.hf_token',
placeholderKey: 'hf_xxxxxxxxxxxx',
helpKey: 'credentials.hf_help',
link: 'https://huggingface.co/settings/tokens',
},
{
key: 'TRANSLATE_API_KEY',
label: 'Translation API Key',
placeholder: 'API key',
help: 'Optional — for DeepL, OpenAI, or paid translation providers. Not needed for Google Translate (free tier).',
labelKey: 'credentials.translate_key',
placeholderKey: 'API key',
helpKey: 'credentials.translate_help',
link: null,
},
];
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Credentials placeholders are not localized.

The placeholder pipeline bypasses translation (placeholder={field.placeholderKey}), so localized UIs still show English placeholder text.

Suggested fix
 const CREDENTIAL_DEFS = [
   {
     key: 'HF_TOKEN',
     labelKey: 'credentials.hf_token',
-    placeholderKey: 'hf_xxxxxxxxxxxx',
+    placeholderKey: 'credentials.hf_placeholder',
     helpKey: 'credentials.hf_help',
     link: 'https://huggingface.co/settings/tokens',
   },
   {
     key: 'TRANSLATE_API_KEY',
     labelKey: 'credentials.translate_key',
-    placeholderKey: 'API key',
+    placeholderKey: 'credentials.translate_placeholder',
     helpKey: 'credentials.translate_help',
     link: null,
   },
 ];
@@
             <input
               type="password"
               className="settings-credential__input"
-              placeholder={field.placeholderKey}
+              placeholder={t(field.placeholderKey)}
               value={values[field.key] || ''}

Also applies to: 1567-1571

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/Settings.jsx` around lines 1339 - 1354, CREDENTIAL_DEFS
currently stores placeholderKey strings but the component uses them directly
(placeholder={field.placeholderKey}), so placeholders aren't localized; update
the Settings component to pass translated placeholders instead by calling the
i18n translator with the placeholderKey (e.g., useTranslation hook and replace
placeholder={field.placeholderKey} with placeholder={t(field.placeholderKey)})
wherever CREDENTIAL_DEFS entries are rendered (also fix the same pattern
referenced around the other occurrence for TRANSLATE_API_KEY).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⏭️ Skipped intentionally. The placeholders (e.g. \hf_xxxxxxxxxxxx) are example token formats, not translatable UI text.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SummerSec Got it—keeping these as literal example formats (not translatable UI) makes sense.

Optional tiny cleanup to prevent future confusion (no behavioral change):

  • Rename placeholderKey to placeholder to convey it’s a literal, not an i18n key.
  • Reuse a shared constant for the HF token example so ModelStoreTab and CredentialsTab stay in sync.
diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx
@@
 const LOG_SOURCE_DEFS = [
   { value: 'backend',  key: 'backend' },
   { value: 'frontend', key: 'frontend' },
   { value: 'tauri',    key: 'tauri' },
 ];
+
+// Intentionally not localized — serves as an example token format.
+const HF_PLACEHOLDER = 'hf_xxxxxxxxxxxx';
@@
-const CREDENTIAL_DEFS = [
+const CREDENTIAL_DEFS = [
   {
     key: 'HF_TOKEN',
     labelKey: 'credentials.hf_token',
-    placeholderKey: 'hf_xxxxxxxxxxxx',
+    placeholder: HF_PLACEHOLDER,
     helpKey: 'credentials.hf_help',
     link: 'https://huggingface.co/settings/tokens',
   },
   {
     key: 'TRANSLATE_API_KEY',
     labelKey: 'credentials.translate_key',
-    placeholderKey: 'API key',
+    placeholder: 'API key',
     helpKey: 'credentials.translate_help',
     link: null,
   },
 ];
@@
-            <input
+            <input
               type="password"
               className="settings-credential__input"
-              placeholder={field.placeholderKey}
+              placeholder={field.placeholder}
               value={values[field.key] || ''}
               onChange={e => setValues(prev => ({ ...prev, [field.key]: e.target.value }))}
               onKeyDown={e => e.key === 'Enter' && save(field.key)}
             />

And (same file) in the Models toolbar HF token inline input:

- placeholder="hf_xxxxxxxxxxxx"
+ placeholder={HF_PLACEHOLDER}

If you prefer, I can mark my earlier comment as “resolved/won’t fix.” Just say the word and I’ll take care of it.


✏️ Learnings added
Learnt from: SummerSec
Repo: debpalash/OmniVoice-Studio PR: 66
File: frontend/src/pages/Settings.jsx:1339-1354
Timestamp: 2026-05-16T18:08:54.448Z
Learning: OmniVoice Studio i18n policy: In frontend/src/pages/Settings.jsx, credential input placeholders (e.g., "hf_xxxxxxxxxxxx" for HF_TOKEN and "API key" for TRANSLATE_API_KEY) are intentional example formats and should remain literal (not localized) across CredentialsTab and ModelStoreTab.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/pages/CloneDesignTab.jsx`:
- Around line 230-231: The placeholder for the style input is hardcoded ("e.g.
whisper") and must be localized; update the input in CloneDesignTab.jsx (the
input with value={instruct} and onChange={e => setInstruct(e.target.value)}) to
use the i18n translator (t) instead of the literal string — e.g. replace
placeholder="e.g. whisper" with placeholder={t('clone.stylePlaceholder')} (and
add the corresponding translation key in your locale files).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7bfaae68-ddff-4b17-924a-de1244604014

📥 Commits

Reviewing files that changed from the base of the PR and between 4e7397b and ec6d016.

📒 Files selected for processing (3)
  • frontend/src/i18n/locales/en.json
  • frontend/src/i18n/locales/zh-CN.json
  • frontend/src/pages/CloneDesignTab.jsx
✅ Files skipped from review due to trivial changes (2)
  • frontend/src/i18n/locales/en.json
  • frontend/src/i18n/locales/zh-CN.json

Comment thread frontend/src/pages/CloneDesignTab.jsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
frontend/src/pages/CloneDesignTab.jsx (2)

301-308: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate select option labels to match chip button behavior.

The select dropdown (used when many > 6) renders options with raw values at line 307, but the chip buttons (lines 311-325) translate their labels using the same optLabel pattern. This inconsistency leaves dropdown options in English even when the UI is in zh-CN.

🌐 Proposed fix
                     <select
                       className="input-base"
                       value={vdStates[key]}
                       onChange={e => setVdStates({ ...vdStates, [key]: e.target.value })}
                     >
-                      {options.map(opt => <option key={opt} value={opt}>{opt}</option>)}
+                      {options.map(opt => <option key={opt} value={opt}>{optLabel(opt)}</option>)}
                     </select>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/CloneDesignTab.jsx` around lines 301 - 308, The select
dropdown renders raw option values instead of localized labels, causing
inconsistency with the chip buttons; update the <select> options rendering to
keep option value={opt} but display the translated label using the same optLabel
logic used by the chips (i.e., use optLabel(opt) or the equivalent
mapping/function) so dropdown labels match the chip button translations; locate
the code around variables many, options, vdStates and setVdStates and replace
the option inner text with the optLabel call.

286-330: ⚠️ Potential issue | 🔴 Critical

Add missing translation keys for EnglishAccent and ChineseDialect options.

The code dynamically constructs translation keys for EnglishAccent accents and ChineseDialect dialects, but these keys are missing from both en.json and zh-CN.json. When a user selects options like "british accent" or "四川话", the UI will silently fall back to untranslated raw values, breaking the localized experience.

Missing keys:

  • EnglishAccent: opt_american_accent, opt_british_accent, opt_australian_accent, opt_canadian_accent, opt_indian_accent, opt_chinese_accent, opt_korean_accent, opt_japanese_accent, opt_portuguese_accent, opt_russian_accent
  • ChineseDialect: opt_河南话, opt_陕西话, opt_四川话, opt_贵州话, opt_云南话, opt_桂林话, opt_济南话, opt_石家庄话, opt_甘肃话, opt_宁夏话, opt_青岛话, opt_东北话
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/CloneDesignTab.jsx` around lines 286 - 330, The UI falls
back to raw option values because translation keys built in CloneDesignTab.jsx
via t(`clone.opt_${val.replace(/[ -]/g, '_')}`) are missing for EnglishAccent
and ChineseDialect; add the listed keys to both en.json and zh-CN.json under the
"clone" namespace (e.g. "opt_american_accent", "opt_british_accent", ..., and
"opt_河南话", "opt_陕西话", ...), providing correct English and Chinese translations
so t(optTKey) returns a localized string for the CATEGORIES options used by
vdStates and rendered by the optLabel/optTl logic.
♻️ Duplicate comments (1)
frontend/src/pages/CloneDesignTab.jsx (1)

230-231: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the style placeholder (duplicate of previous review).

Line 231 still hardcodes "e.g. whisper" as flagged in the prior review. This placeholder remains untranslated in zh-CN mode.

🌐 Suggested fix
-                <input type="text" className="input-base" value={instruct} onChange={e => setInstruct(e.target.value)} placeholder="e.g. whisper" />
+                <input type="text" className="input-base" value={instruct} onChange={e => setInstruct(e.target.value)} placeholder={t('clone.style_placeholder')} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/CloneDesignTab.jsx` around lines 230 - 231, The
placeholder for the style input is hardcoded ("e.g. whisper"); update the input
element in CloneDesignTab.jsx to use the i18n translator (t) instead of a
literal string (e.g. placeholder={t('clone.stylePlaceholder')}), and add the
corresponding "clone.stylePlaceholder" entry to the locale files (including
zh-CN) with the translated text; keep the existing value/instruct and
onChange/setInstruct logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@frontend/src/pages/CloneDesignTab.jsx`:
- Around line 301-308: The select dropdown renders raw option values instead of
localized labels, causing inconsistency with the chip buttons; update the
<select> options rendering to keep option value={opt} but display the translated
label using the same optLabel logic used by the chips (i.e., use optLabel(opt)
or the equivalent mapping/function) so dropdown labels match the chip button
translations; locate the code around variables many, options, vdStates and
setVdStates and replace the option inner text with the optLabel call.
- Around line 286-330: The UI falls back to raw option values because
translation keys built in CloneDesignTab.jsx via t(`clone.opt_${val.replace(/[
-]/g, '_')}`) are missing for EnglishAccent and ChineseDialect; add the listed
keys to both en.json and zh-CN.json under the "clone" namespace (e.g.
"opt_american_accent", "opt_british_accent", ..., and "opt_河南话", "opt_陕西话",
...), providing correct English and Chinese translations so t(optTKey) returns a
localized string for the CATEGORIES options used by vdStates and rendered by the
optLabel/optTl logic.

---

Duplicate comments:
In `@frontend/src/pages/CloneDesignTab.jsx`:
- Around line 230-231: The placeholder for the style input is hardcoded ("e.g.
whisper"); update the input element in CloneDesignTab.jsx to use the i18n
translator (t) instead of a literal string (e.g.
placeholder={t('clone.stylePlaceholder')}), and add the corresponding
"clone.stylePlaceholder" entry to the locale files (including zh-CN) with the
translated text; keep the existing value/instruct and onChange/setInstruct logic
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cbd3bc35-73fa-4883-989c-83fa16783a8d

📥 Commits

Reviewing files that changed from the base of the PR and between ec6d016 and f678e33.

📒 Files selected for processing (3)
  • frontend/src/i18n/locales/en.json
  • frontend/src/i18n/locales/zh-CN.json
  • frontend/src/pages/CloneDesignTab.jsx
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/i18n/locales/zh-CN.json

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/Transcriptions.jsx (1)

96-108: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add t to exportAll callback dependencies.

exportAll uses t('transcriptions.exported') (line 108) but omits t from the dependency list. This causes the callback to retain a stale reference to the translation function after locale changes, displaying the toast in the previous language.

Proposed fix
-  }, [transcriptions]);
+  }, [transcriptions, t]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/Transcriptions.jsx` around lines 96 - 108, The exportAll
useCallback captures the translation function t but its dependency array only
lists transcriptions, causing stale translations after locale changes; update
the exportAll hook (the useCallback that defines exportAll which calls
t('transcriptions.exported') and toast.success) to include t (and any other
external values used) in its dependency array so the callback is recreated when
the translation function changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/pages/VoiceGallery.jsx`:
- Around line 288-290: The header currently renders the raw category name
(categories.find(c => c.id === selectedCategory)?.name) without localization;
update the selectedCategory branch to pass the found category through the
translation helper (t) instead of rendering cat.name directly — e.g. lookup the
category via categories.find(...) and call t(...) with either a category
translation key property (e.g. c.i18nKey or c.labelKey) if available, or with
the category name string if your translations are keyed by name; modify the JSX
in VoiceGallery.jsx where selectedCategory is rendered to use t(...) so the
selected category label is localized consistently with the chips.

---

Outside diff comments:
In `@frontend/src/pages/Transcriptions.jsx`:
- Around line 96-108: The exportAll useCallback captures the translation
function t but its dependency array only lists transcriptions, causing stale
translations after locale changes; update the exportAll hook (the useCallback
that defines exportAll which calls t('transcriptions.exported') and
toast.success) to include t (and any other external values used) in its
dependency array so the callback is recreated when the translation function
changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 94501b70-26b8-4969-aa01-85771940e849

📥 Commits

Reviewing files that changed from the base of the PR and between f678e33 and fb732c1.

📒 Files selected for processing (6)
  • frontend/src/i18n/locales/en.json
  • frontend/src/i18n/locales/zh-CN.json
  • frontend/src/pages/CloneDesignTab.jsx
  • frontend/src/pages/DubTab.jsx
  • frontend/src/pages/Transcriptions.jsx
  • frontend/src/pages/VoiceGallery.jsx
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/pages/CloneDesignTab.jsx
  • frontend/src/i18n/locales/zh-CN.json

Comment thread frontend/src/pages/VoiceGallery.jsx
@SummerSec SummerSec changed the title fix: skip torch.compile on Windows + Chinese (zh-CN) localization feat: Chinese (zh-CN) localization for frontend May 16, 2026
@SummerSec SummerSec changed the title feat: Chinese (zh-CN) localization for frontend l10n(zh-CN): comprehensive Chinese localization + Windows Triton compat fix + UI bugfixes May 16, 2026
@SummerSec
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

debpalash added a commit that referenced this pull request May 16, 2026
… OOS deferrals

- GATE-06: mark #53 + #61 merged (2026-05-16); add #62 (Wave 1 quick wins) to gate set
- INST-01: note PR #62 implements setuptools pin (closes #58)
- INST-04: note PR #62 lands README docs for #56 workaround
- INST-12: new requirement for #65 Windows Triton/torch.compile OOM (filed post-planning)
- Out of Scope: defer #67/PR #68 (audio effects), #64 (custom model dir),
  PR #66 zh-CN (i18n milestone), #63 (empty-template bug)

PR #62 is the user's own Wave 1 work landed as a separate PR while
GSD planning ran in parallel. Merging it eliminates duplicate work
in Phase 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
debpalash added a commit that referenced this pull request May 17, 2026
…ase smoke (#71)

* docs: initialize OmniVoice stabilization milestone project

* chore: add project config (yolo + balanced)

* docs: domain research for stabilization milestone

* docs: define v1 requirements for stabilization milestone

* docs: add GGUF + singing engine spike requirements (Phase 4 new)

* docs: roadmap revision + CLAUDE.md (7 phases, 62 reqs, +GGUF/SING spikes)

* docs(phase-0): add Gates phase RESEARCH.md

Phase 0 research synthesizes the cross-platform CI matrix, frozen
omnivoice_data fixture, installer post-build smoke, SHA-256 checksum
publishing, and PR-template extension into copy-paste-ready YAML and
Python snippets composed entirely from existing in-repo patterns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(phase-0): add Gates phase CONTEXT, PATTERNS, and PLAN

Phase 0 — Gates is the hard pre-condition for v0.3.x stabilization.
Lays cross-platform CI matrix (macos-14/windows-2022/ubuntu-22.04),
regression fixture (≤200 KB), installer smoke on tag push, SHA-256
checksums in release body + per-OS SHA256SUMS-*.txt assets, PR
template with RC cadence + fixture line, and the open-PR landing
for #51.

Plan covers GATE-01..06; structured into 7 slices (A–G) with explicit
Slice C → Slice G dependency reordering so the new smoke-matrix lands
on main before PR #51 (CONTEXT.md L86 interleave decision).

Plan-checker iteration 2: APPROVED — all 3 BLOCKERs + 3 MAJORs from
iteration 1 resolved (file truncation/Slice-G missing, GATE-06 sibling
PR verification, Slice C ordering, Truth #5 wording, macOS Tauri
WebView avoidance per Pitfall #5, Windows taskkill per Pitfall #2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(00-gates): seed regression fixture (GATE-01)

- scripts/seed-test-fixture.py — deterministic builder for tests/fixtures/omnivoice_data/
  - wipes + rebuilds; fixed created_at=1700000000.0; all-zero PCM for byte-deterministic diffs
  - calls backend.core.db.init_db() directly (alembic versions/ is empty — see CONTEXT.md)
  - checkpoints WAL → DELETE on close so no -shm/-wal sidecars pollute git status
  - exits non-zero if fixture > 200 KB
- tests/fixtures/omnivoice_data/{omnivoice.db, README.md} — 8-table empty DB + 1 voice_profiles row
- tests/fixtures/omnivoice_data/voices/test-voice/{profile.json, sample.wav} — 1-sec 24 kHz mono silence
- .gitignore — explicit allow-list (!tests/fixtures/omnivoice_data/**) so the existing
  omnivoice_data/, *.db, *.wav patterns don't hide the fixture from git

Verifies: du = 144 KB on disk; sqlite_master lists 8 init_db tables + sqlite_sequence;
voice_profiles has exactly 1 row id='test-voice'; 0 rows in generation_history.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(00-gates): add tests/smoke/test_boot_smoke.py (GATE-01)

- tests/smoke/__init__.py — package marker so pytest treats tests/smoke/ as a module
- tests/smoke/test_boot_smoke.py — 4 in-process FastAPI TestClient smoke tests:
    * test_health_returns_ok — /health returns 200 + {status:ok, device:...}
    * test_profiles_endpoint_lists_fixture_voice — /profiles surfaces the seeded
      test-voice row (validates OMNIVOICE_DATA_DIR wiring → DB_PATH → init_db schema)
    * test_system_info_includes_data_dir — /system/info resolves data_dir
    * test_history_endpoint_empty — /history reaches DB and returns []
  Test isolation env vars (OMNIVOICE_MODEL=test, OMNIVOICE_DISABLE_FILE_LOG=1)
  set at module top BEFORE any backend import — pattern from tests/test_router_smoke.py.
  Fixture is copied to a per-session temp dir so the test never mutates the
  checked-in artifact (SQLite file-change counter + runtime subdirs like dub_jobs/
  would otherwise dirty `git status` after every run).
  Failure mode: if tests/fixtures/omnivoice_data/ is missing, pytest.fail at
  import time with the regenerate command.
- .gitignore — tighten the GATE-01 allow-list to ONLY the seed-produced files
  (README.md, omnivoice.db, voices/test-voice/profile.json, sample.wav).
  Prevents future runtime subdirs the backend may create under the fixture
  from being accidentally committed.

Verifies: `uv run pytest tests/smoke/ -q --tb=short` → 4 passed in 1.31 s
(target was < 30 s). `git status` clean after a test run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(triage): record post-planning GitHub state — PR #62, new issues, OOS deferrals

- GATE-06: mark #53 + #61 merged (2026-05-16); add #62 (Wave 1 quick wins) to gate set
- INST-01: note PR #62 implements setuptools pin (closes #58)
- INST-04: note PR #62 lands README docs for #56 workaround
- INST-12: new requirement for #65 Windows Triton/torch.compile OOM (filed post-planning)
- Out of Scope: defer #67/PR #68 (audio effects), #64 (custom model dir),
  PR #66 zh-CN (i18n milestone), #63 (empty-template bug)

PR #62 is the user's own Wave 1 work landed as a separate PR while
GSD planning ran in parallel. Merging it eliminates duplicate work
in Phase 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci(00-gates): add cross-platform smoke matrix (GATE-02)

- New smoke-matrix job on macos-14, windows-2022, ubuntu-22.04
- needs: test, fail-fast: false, timeout-minutes: 10
- Pinned actions: checkout@v4, setup-python@v5, setup-uv@v3 (cache enabled)
- Per-OS ffmpeg + libsndfile install (brew/choco/apt via awalsh128 cache)
- UV_HTTP_TIMEOUT=120, UV_HTTP_RETRIES=5 for restricted-network resilience
- Narrow scope: uv run pytest tests/smoke/ -q --tb=short
- Existing `test` and `tauri-cross-platform` jobs untouched

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: add workflow_dispatch to ci.yml so smoke-matrix can run on feature branches

* feat(00-gates): add --health-check CLI flag to backend entrypoint (GATE-03)

- argparse on __main__ block; --health-check boots uvicorn in a daemon
  thread and polls http://127.0.0.1:3900/health every 5s for up to 60s.
- Prints 'OK — /health responded 200 after Ns' and exits 0 on first 200.
- Prints 'FAIL — /health did not respond 200 within 60s' to stderr and
  exits 1 on timeout. Default invocation behavior unchanged.
- No new deps (stdlib argparse/threading/time/urllib.request/sys + uvicorn).
- Consumed by per-OS installer-smoke step in .github/workflows/release.yml.

Verified locally: exits 0 in 5s against tests/fixtures/omnivoice_data/.

* ci(00-gates): add per-OS installer smoke to release.yml (GATE-03)

Adds three matrix-leg-specific steps after 'Build + release (Tauri)',
each gated by runner.os with timeout-minutes: 5:

- macOS (macos-14): hdiutil attach DMG → locate bundled Python backend
  inside *.app/Contents (NOT the Tauri WebView shell — RESEARCH Pitfall
  #5: WebView hangs on headless runners) → invoke --health-check →
  hdiutil detach. Falls back to *.app/Contents/Resources and hard-fails
  with a directory listing if no backend binary found.

- Windows (windows-2022): msiexec /quiet install → find backend.exe
  under 'C:/Program Files/OmniVoice Studio' → invoke --health-check in
  background, wait, then taskkill //F //T //PID to cleanup orphaned
  PyInstaller child processes on port 3900 (RESEARCH Pitfall #2).

- Linux (ubuntu-22.04): --appimage-extract (no FUSE on GH runners),
  locate binary or AppRun, run under xvfb-run -a.

Bundle-only regressions (PyInstaller missing-module, Tauri sidecar
path mismatch) are invisible to ci.yml's in-process smoke matrix —
this step closes that gap before any release is published.

Verified: YAML parses; all three steps present; gating + timeout
correct; Pitfall #2/#5 mitigations preserved.

* ci(00-gates): publish SHA-256 checksums in release body + as asset (GATE-05)

- Add 'Compute SHA-256 checksums' step writing SHA256SUMS-<label>.txt
  per matrix leg using native shasum/sha256sum (Git Bash on Windows).
- Add 'Append checksums to release + attach SHA256SUMS file' step using
  softprops/action-gh-release@v2 with append_body: true so the hashes
  land in the release body alongside tauri-action's content (not
  replacing it) and the file is uploaded as a release asset for
  'shasum -c SHA256SUMS-<label>.txt' verification.
- Both steps gated by 'github.event_name == push && refs/tags/v*' so
  workflow_dispatch dry-runs do not attempt to attach to a non-existent
  release (per CONTEXT.md L70 + RESEARCH Pitfall #7 deferral of any
  aggregate cross-leg SHA256SUMS job).
- fail_on_unmatched_files: true to surface path-resolution errors loudly.

* docs(00-gates): document RC cadence + regression-fixture check in PR template (GATE-04)

* docs(setup): add HF token persistence guide for macOS/Windows/Linux (DOCS-05)

Covers two persistent paths:
- Method A — canonical ~/.cache/huggingface/token via huggingface-cli login
- Method B — shell env var (~/.zshrc / ~/.bashrc / Windows User scope)

Documents the v0.2.7 "session only" in-app behavior + notes that
Phase 1 AUTH-03 will make in-app pastes write to the canonical file.

Bundled with Phase 0 PR per user request. Strictly DOCS-05 scope —
zero code changes, no engine touches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* spec(auth): redesign HF token resolution as 3-source cascade with fallback (AUTH-01..06)

Replaces the env_store.py file-based design with a SQLite-backed app
store + cascade resolver that checks app → env var → ~/.cache/huggingface/token
in priority order, with automatic fallback to next source on HTTP 401.

User-explicit design decision:
- App-stored token (SQLite settings table, AES-GCM encrypted) wins
- Env var ($HF_TOKEN) second
- Global huggingface-cli login file third
- All three sources visible in Settings → API Keys with "Active" badge
- Save action populates BOTH app store AND canonical HF file (defense in depth)

New requirement:
- AUTH-06 — on 401, auto-retry next source in cascade before erroring

Also: traceability count corrected (62 → 74 — undercount at planning +
INST-12 + AUTH-06 added post-planning). All 74 v1 reqs mapped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(auth): backend recognizes HF token from canonical file, not just env var

Two call sites were only checking $HF_TOKEN env var, missing the canonical
~/.cache/huggingface/token file written by `huggingface-cli login` (or the
app's future Save action):

- system.py `/system/info` `has_hf_token` flag — UI showed "No HF token"
  even when `huggingface-cli login` had populated the file.
- model_manager.get_diarization_pipeline — pyannote diarization silently
  returned None when only the canonical file was set. This is the bug
  behind issue #35 (speaker diarization setup failure).

Both fixes use the same pattern: env var > huggingface_hub.get_token()
(which reads the canonical file). Adds a local _has_hf_token() helper
to system.py with a comment marking it as prelude to the AUTH-01..06
cascade (Phase 1 token_resolver.py will layer SQLite app-store on top).

Closes #35 sub-issue (canonical token invisible to diarization).
Cross-cuts AUTH-02 + AUTH-06 design for Phase 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(dictation): make pill-widget mode reachable from GUI + scripts (INST-13)

The dictation widget infrastructure shipped in PR #40 but was only reachable
via the undocumented --pill CLI flag. Adds three discovery paths:

1. Tray menu: "Switch to Dictation Widget" (studio mode) — saves
   launch_as_widget=true to config, relaunches with --pill, exits current.
   Mirrors the existing "Open Studio" path in pill-mode tray.

2. Persistent config: AppConfig.launch_as_widget (bool, default false). Read
   at startup via load_config_pre_app() (uses dirs-next, no AppHandle
   required). CLI --pill still takes precedence when explicitly passed.

3. Tauri commands: get_launch_as_widget / set_launch_as_widget for the
   Phase 2 Settings UI to bind a checkbox to.

4. Scripts: bun desktop-prod:pill / desktop-prod:run:pill — forward --pill
   to the bundled app launch. macOS uses `open -n --args` to spawn fresh
   instance with the flag.

Closes the GUI half of INST-13. Phase 2 closes the Settings UI half.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(dictation): show widget unconditionally on pill-mode launch + visible Suspense fallback

Before: pill mode set up correctly but the widget window stayed hidden
until ⌘⇧Space was pressed. New users saw absolutely nothing on launch
(no main window, no dock icon, hidden widget) and assumed the app
failed. If global-shortcut Accessibility permission wasn't granted,
they had no path to discover the widget at all.

Two changes:

1. lib.rs: in pill_mode_setup, explicitly show + position + focus the
   widget window after hiding main. With per-call error logging so we
   can diagnose failures (and a clear error log if widget window
   wasn't created at all — points at tauri.conf.json regression).

2. main-app.jsx: Suspense fallback was `null`, which combined with
   widget's transparent+decorations:false config made any lazy-import
   delay or failure invisible. Now renders a dark pill saying
   "Loading dictation…" so even if CaptureWidget lazy-import stalls,
   the user sees the window exists.

Studio mode behavior unchanged — widget stays hidden until hotkey
or tray click triggers it (existing show() call in the shortcut/
menu handlers is preserved).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(dictation): create widget window programmatically; Tauri 2 silently dropped config-array creation

Root cause: declaring the widget window in tauri.conf.json's app.windows[]
silently failed in Tauri 2 — get_webview_window("widget") returned None
even though the config was syntactically valid. Probable culprit was the
transparent + decorations:false + visible:false combo, but Tauri offered
no error message either at startup or via webview_windows() enumeration.

Diagnosed by adding webview_windows() enumeration logging at setup start
(only ["main"] ever appeared) and a programmatic WebviewWindowBuilder
fallback that surfaces real Result errors.

Fix:
- tauri.conf.json: widget entry now has `create: false` to make the
  config-vs-programmatic handoff explicit.
- lib.rs setup(): call WebviewWindowBuilder::new(app, "widget", ...).build()
  with the exact same surface attributes the config used to declare.
- capabilities/default.json: include "widget" in windows array so the new
  window inherits the same Tauri permissions as main.
- tauri.conf.json: remove the invalid `"url": "/?window=widget"` field —
  WebviewUrl::App takes a path only, query strings aren't supported.
  Both windows now load index.html.
- main-app.jsx: replace URL-query-based widget detection with
  getCurrentWindow().label === 'widget' via @tauri-apps/api/window. This
  is the Tauri 2-recommended pattern for multi-window apps and works
  regardless of URL routing.

Closes the immediate UX bug behind the dictation widget being invisible.
Builds cleanly + manually verified: pill widget visible on screen at
top-center after `bun desktop-prod:pill`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SummerSec added 8 commits May 17, 2026 15:06
…L/Microsoft keys, add glossary max-height scroll and collapse

- /system/set-env writes env.* to prefs.json via prefs.set_()/delete()
- Backend startup reads env.* from prefs.json into os.environ (.setdefault)
- PERSISTENT_KEYS covers proxy, FFmpeg, LLM, DeepL/Microsoft keys
- DeepL uses DEEPL_API_KEY, Microsoft uses MICROSOFT_API_KEY (fallback TRANSLATE_API_KEY)
- DeepL/Microsoft _build_translator reads DEEPL_BASE_URL/MICROSOFT_BASE_URL
- Google/MyMemory/Microsoft translators bypass Windows registry proxy
- Frontend CREDENTIAL_GROUPS splits into 4 groups with password/text fields
- Glossary panel body max-height: 35vh + overflow-y: auto
- Glossary panel can be collapsed via ChevronDown button
- queryClient.invalidateQueries after save for immediate refresh
- SystemInfoResponse adds proxy_url, ffmpeg_ok, ffmpeg_path
- Add useTranslation + replace ~50 hardcoded strings with t() calls
- Add exportModal namespace to en.json and zh-CN.json
- Cover presets, tracks, tabs, video/audio/subs/package tabs, license notice, and output footer
@debpalash
Copy link
Copy Markdown
Owner

@SummerSec that's a substantial amount of commits, thanks for interest.

Some of your commits are already handled in other PRs. To avoid redundancy, it would be great if we communicate about your efforts on specific issues you like to address.

Let me know your thoughts!

@debpalash
Copy link
Copy Markdown
Owner

Hi @SummerSec — thanks for this PR. The zh-CN localization is a substantial body of work and we want it in. The Triton guard at backend/services/model_manager.py:228-237 is the exact fix for #65, _WindowsSafeRotatingFileHandler cleanly handles a real Windows pain point, and the translation-engine threaded subprocess fallback for the Windows event-loop issue is well-targeted. That whole block of Windows compat is the kind of contribution this project needs.

The security pass surfaced three things we need landed before merge. None of them touch the parts of your PR we like — they're about how the project wants sensitive config and supply-chain handled. Listing each with a concrete fix so you can pick this back up quickly.


1. uv.lock — please regenerate against pypi.org

Every package URL in uv.lock was rewritten from pypi.org to mirrors.tuna.tsinghua.edu.cn (3,628 substitutions). SHA256 hashes are preserved, so this isn't artifact tampering — but it silently shifts the trust anchor for every dependency download from PyPI to a single third-party mirror. Combined with dev:api flipping to uv sync --frozen + uv run --no-sync, that mirror gets locked in for every developer and CI run on the project.

We want mirror configuration to live in environment variables, not in the lockfile itself. CLAUDE.md's Capability 3 has the full pattern (UV_DEFAULT_INDEX, UV_PYTHON_INSTALL_MIRROR, etc.). To regenerate the lockfile:

UV_DEFAULT_INDEX=https://pypi.org/simple uv lock --refresh

Same package set, same hashes, just the URLs restored. If PyPI was slow/unreachable from your network when generating the lock, set the mirror at install time only:

UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple uv sync

That affects only your local fetches and leaves the lockfile clean for the rest of the project.


2. HF_TOKEN — route through huggingface_hub.login()

HF_TOKEN is currently in PERSISTENT_KEYS (backend/api/routers/system.py:530-548) and gets written plaintext to omnivoice_data/prefs.json. We want HF auth to go through the official Hugging Face path:

from huggingface_hub import login
login(token=value, add_to_git_credential=False)

This writes to $HF_HOME/token with the right permissions and integrates with the rest of the HF stack — it's what CLAUDE.md Capability 1 specifies.

The other API keys in PERSISTENT_KEYS (DEEPL_API_KEY, MICROSOFT_API_KEY, TRANSLATE_API_KEY) can stay in prefs.json for now — just add an explicit os.chmod(prefs_path, 0o600) after os.replace() in the prefs save path so the file's mode is restricted regardless of umask.


3. /system/set-env needs a loopback check

The endpoint binds 0.0.0.0:3900 and has no origin/auth check. Pre-PR this only persisted into the running process, so blast radius was bounded by uptime. Post-PR it writes to disk — which means any LAN host (or any local process on a multi-tenant machine) can overwrite a user's stored token by POSTing to /system/set-env.

This is technically a pre-existing issue on main for in-memory state, but your PR widens it. We just opened PR #81 for the loopback check on our side — the fix is small:

host = request.client.host if request.client else None
if host not in ("127.0.0.1", "::1", "localhost"):
    raise HTTPException(status_code=403, detail="set-env requires loopback origin")

Once #81 merges, rebasing onto main will pick it up automatically — no action needed from you on this one. If you'd like to review or comment on it, the PR is at #81.


What's clean (please leave as-is)

  • Triton guard in model_manager.py — closes [Bug] torch.compile on Windows causes TTS OOM due to missing Triton #65
  • _WindowsSafeRotatingFileHandler (tiny nit: a logger.warning("log rotation failed: %s", e) inside the bare-except blocks so rotation failures still surface in logs, but not blocking)
  • FFMPEG_PATH validation
  • Translation engine threaded subprocess fallback
  • The entire zh-CN locale + JSX call-site wiring
  • dev:api switching to --frozen is fine — --no-sync becomes fine again once the lockfile URLs are restored

Re-review path

Once (1), (2), and the prefs.json chmod are pushed, ping me and I'll re-run the security pass. The Triton guard alone closes a HIGH-priority Windows bug, so there's real motivation on our side to land this — just need to keep the supply-chain and secret-storage surfaces clean.

Thanks again for the Windows compat work.

debpalash added a commit that referenced this pull request May 18, 2026
Adds `request.client.host` allow-list check (`127.0.0.1` / `::1` / `localhost`) to `POST /system/set-env`. Non-loopback callers receive `403` instead of being able to mutate `os.environ` for HF_TOKEN / TRANSLATE_API_KEY.

Surfaced during security review of PR #66, which widens the pre-existing window by persisting these keys to disk via prefs.json. This fix closes the underlying vulnerability so PR #66's revision lands onto a clean base.

Defensive `request.client is None` branch handles ASGI middleware that strips client info. Three new tests cover non-loopback reject, loopback allow, and allow-list still validated on loopback.

Follow-up: `260518-ivy-deferred-items.md` enumerates 5 sibling POST routes in `system.py` that share the same gap — separate PR.
- Regenerate uv.lock against pypi.org (remove TUNA mirror URLs)
- Route HF_TOKEN through huggingface_hub.login() instead of prefs.json
- Add os.chmod(prefs_path, 0o600) for restricted file permissions
- Add warning logs to _WindowsSafeRotatingFileHandler bare except blocks
@SummerSec
Copy link
Copy Markdown
Author

@debpalash Thanks for the thorough security review. Here's what's been pushed:

  1. uv.lock — Regenerated via uv lock --refresh against pypi.org. All 3,628 TUNA mirror URLs restored to pypi.org.
  2. HF_TOKEN — Removed from PERSISTENT_KEYS. Now persisted via huggingface_hub.login(token=value, add_to_git_credential=False).
  3. prefs.json chmod — Added os.chmod(_PREFS_PATH, 0o600) after os.replace() in core/prefs.py:_save().
  4. Log rotation — Added logger.warning calls in the bare except blocks of _WindowsSafeRotatingFileHandler.doRollover() so rotation failures surface in logs.

All points (1), (2), and the prefs.json chmod are addressed. After rebasing onto main, the /system/set-env loopback check from #81 will be picked up automatically.

@debpalash
Copy link
Copy Markdown
Owner

Quick follow-up @SummerSec — and good news on one front.

Fixed already (no action needed from you): the loopback check on /system/set-env I flagged as ask #3 just landed on main via PR #81. Once you rebase, that piece is just there.

Still need from you for this PR:

  1. Regenerate uv.lock against pypi.orgUV_DEFAULT_INDEX=https://pypi.org/simple uv lock --refresh. The Tsinghua mirror is a legitimate install-time choice; pinning it in the lockfile shifts every developer/CI trust anchor.
  2. Route HF_TOKEN through huggingface_hub.login() instead of plaintext prefs.json. The other API keys (DEEPL_API_KEY, etc.) can stay if you add os.chmod(prefs_path, 0o600) after the os.replace call.

Bigger picture proposal — your call: This PR bundles three independently-valuable changes (Triton guard for #65, zh-CN localization, security-touched items). The Triton fix is exactly right and fixes a HIGH-priority Windows bug right now. If easier on you, I'd happily cherry-pick the Triton commit (model_manager.py:228-237 and the related log handler) into a new PR today with you as co-author, so #65 stops biting Windows users while you take your time revising the rest. The zh-CN localization is the biggest i18n contribution this project has ever received and deserves its own clean review on its own clock.

Three paths:

  • (a) You handle all three asks in this PR — happy to re-review on push
  • (b) I cherry-pick the Triton fix as a co-authored standalone PR today; this PR keeps the rest
  • (c) You split into 3 PRs at your pace

Let me know which works. Thanks again for the Windows compat depth — that's exactly what this project needed.

@SummerSec
Copy link
Copy Markdown
Author

@debpalash Everything you listed in items (1) and (2) is already pushed:

  1. uv.lock — Regenerated against pypi.org via \uv lock --refresh. All 3,628 Tsinghua mirror URLs restored.
  2. HF_TOKEN — Removed from PERSISTENT_KEYS, now routes through \huggingface_hub.login(token=value, add_to_git_credential=False).
  3. prefs.json chmod — \os.chmod(_PREFS_PATH, 0o600)\ added after \os.replace()\ in \core/prefs.py:_save().
  4. Also fixed the bare \except: pass\ in _WindowsSafeRotatingFileHandler.doRollover()\ per your earlier nit — now logs rotation failures.
  5. Added \README_CN.md\ (full Simplified Chinese translation of the README) with cross-links between both READMEs.

For the bigger picture: I'm fine with path (a) — all asks are addressed in this PR already. Ready for re-review whenever you get a chance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants