Spaces:
Running
Running
Commit History
27 days ago
-
Merge fix/usage-by-language-caption into main Running
d8c0a8f -
Add caption clarifying 'Usage by language' only covers post-rollout events
The lang column was added after ~490 events were already logged without it, so the per-language counts will look tiny next to the all-time feature-usage totals for a while. Without an explanation that reads as broken data rather than expected backfill behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>9178511 -
Merge feature/track-language-usage into main
41b5270 -
Merge fix/ipa-box-html-escaping into main
a87fb57 -
Escape untrusted phoneme/IPA text before HTML interpolation
Re-applies the never-merged fix from fix/security-review-findings (unescaped fallback span in _ipa_box_html), plus two more instances of the same pattern found while re-investigating: a duplicate fallback span in render_alignment_html's text-only path, and the root cause shared by both -- _ipa_span itself returning non-reference phonemes raw instead of escaped. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>798c6e7 -
Merge fix/similarity-highlight-timeout: remove 20s cutoff on page-load JS polling
556d461 -
Remove 20s cutoff on page-load JS polling
Confirmed broken via both a user's manual test and my own: keyboard similarity highlighting never fired because trySetupSimilarity's target (the Edit IPA textarea) only exists after Analyze completes, and a real Analyze call -- especially the first one in a container, while models load -- regularly took well past 20 seconds. The other two setup functions (uid box, Mac tooltip) target elements that exist from page load, so they were never affected by this cutoff; only similarity highlighting depended on timing that doesn't hold up in practice. Fix: removed the setTimeout(() => clearInterval(iv), 20000) entirely. Each trySetup* function already guards on "target missing" or "already attached" and returns almost instantly otherwise, so polling for the lifetime of the page has no measurable cost -- there's no reason to guess at a "long enough" timeout when there's no real downside to not having one. Verified: JS syntax valid (parsed via JavaScriptCore), no leftover references to the remo…9a8863a -
Merge fix/uid-box-gradio6-visibility: fix uid-box not mounting under Gradio 6
a7201d3 -
Fix uid-box not mounting under Gradio 6
Confirmed via live browser testing after the Gradio 6 upgrade: a component with visible=False from the very start never gets its DOM element mounted at all in Gradio 6 (not even the wrapper div) -- unlike 4.7.1, where the element existed in the DOM and was just CSS-hidden. This silently broke the anonymous per-browser uid used for the Usage tab's unique-user count: events kept logging fine, just with no uid, so that count would undercount or sit at zero. Fix: uid_box is now visible=True (so Gradio actually mounts it) and hidden visually instead via a #ipa-uid-box rule in _GLOBAL_CSS, using the standard "visually-hidden but present" pattern (1px clipped box, absolute positioning) rather than display:none, which could reintroduce the same non-mounting risk in some frameworks. Verified locally (stub-level only -- same caveat as the main Gradio 6 upgrade, this needs real-Space confirmation): app.py still imports and builds, and the new CSS rule is present in _GLOBAL_CSS. Co-Authored-By:…05d9976 -
Merge feature/upgrade-gradio-6: upgrade Gradio 4.7.1 -> 6.30.0 to fix known CVEs
8e137e4 -
Upgrade Gradio 4.7.1 -> 6.30.0 to fix known CVEs
4.7.1 is confirmed affected by at least CVE-2023-51449 and CVE-2024-1561 (arbitrary file read via path traversal), and the ecosystem's current safe floor is 6.16+ -- there's no safe way to stay on a patched 4.x release, since a later session-fixation CVE (CVE-2026-48545) spans the entire 3.18.0-6.14.0 range. 6.30.0 is the current latest stable release. This is a 2-major-version jump, not a version-string bump -- three concrete breaking changes applied, cross-checked against Gradio's own 6.0 migration guide and current docs: 1. theme=/css=/js= moved from the Blocks() constructor to launch(). Confirmed via the official migration guide (these became app-level settings since Blocks can now be nested). Restructured build_ui()'s Blocks() call and both launch() call sites accordingly. 2. File serving moved from /file=<path> to /gradio_api/file=<path> -- part of the same security work that fixed the path-traversal CVEs this upgrade addresses. Fixed both hardcoded URL-construc…18ed945 -
Track which languages are used on the Usage tab
Adds a lang column to usage_tracking.py (migrated in-place for existing DBs, same pattern as the earlier uid migration), storing the human-readable language label (e.g. "Japanese") rather than the internal eSpeak code, since it's only ever used for display. Threaded through every tracked event that has language context: analyze(), get_alternate_options(), run_allosaurus(), and keyboard-open (which didn't have a language input before -- added the language dropdown to kb_btn.click's inputs). get_summary() now returns a "languages" breakdown (all distinct languages used, not capped like top_words since there are only ~22 possible values), surfaced as a new "Usage by language" table on the Usage tab. CSV export includes the new column too. Verified locally: migration from a pre-lang-column DB, language-count aggregation, blank/whitespace lang values correctly excluded (same normalization as text/uid), CSV export header, and a full analyze()-to -rendered-HTML smoke test confirming the lan…5bf9426 -
Merge feature/japanese-kanji-target-ipa: convert kanji to kana before eSpeak for correct Target IPA
53a8213 -
Convert Japanese kanji to kana before eSpeak for correct Target IPA
eSpeak-ng's ja voice can't do kanji reading disambiguation on its own (confirmed empirically: hiragana input transcribes correctly, kanji input just gets described character-by-character instead of pronounced -- e.g. "Chinese letter" per character for 日本語). Since pyopenjtalk (already added last round for Piper's Japanese audio synthesis) does real dictionary-based kanji->kana conversion, this reuses it as a preprocessing step before the EXISTING, already-working eSpeak kana pipeline, rather than building a new phoneme-mapping system from scratch. core.py: new _prepare_text_for_espeak(text, lang) -- no-op for every language except "ja", where it runs pyopenjtalk.g2p(text, kana=True) and falls back to the original text on any failure (not installed, no dictionary, unexpected input). Wired into the three Target-IPA eSpeak call sites that matter: analyze() (via a separate espeak_text variable so the original kanji still shows as "Text used for target IPA" and still gets passed to audio sy…270a491 -
Merge fix/japanese-synthesis-crash: fix crash when synthesizing Japanese text audio
3843168 -
Fix crash when synthesizing Japanese text audio
Root cause (from the Space's own traceback): Piper's Japanese voice needs pyopenjtalk internally to phonemize text, which wasn't declared as a dependency. The resulting ModuleNotFoundError happened mid-way through `with wave.open(...) as wav_file:`, so closing that empty WAV file raised a *second*, unrelated-looking exception ("# channels not specified") that masked the real cause. Neither was caught anywhere -- synthesize() had no error handling, and analyze() only wraps engine.analyze() (the Target IPA step), not the later engine.synthesize() call for "Listen to pronunciation" -- so the whole Gradio event crashed uncaught, which is why every output component showed an Error badge instead of just the audio player. requirements.txt: added pyopenjtalk-plus, not the original pyopenjtalk -- the original only ships a source distribution requiring a C++ build and has no confirmed Python 3.12+ wheels, while this Space runs Python 3.13. pyopenjtalk-plus is a confirmed drop-in replacement (sa…25733b9 -
Merge feature/japanese-piper-voice: add Japanese Piper voice for higher-quality audio synthesis
09078b3 -
Add Japanese Piper voice for higher-quality audio synthesis
rhasspy/piper-voices added a Japanese voice (renamed ja_JA -> ja_JP) since this dict was last written -- verified the exact model path (ja/ja_JP/hi_fi_captain/medium/ja_JP-hi_fi_captain-medium.onnx) exists in the repo and that _load_piper_voice's locale/speaker/quality path construction resolves to it correctly. Japanese audio synthesis now uses Piper's neural voice instead of falling back to eSpeak-ng. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>4fde829
-
Merge fix/human-readable-codepoint: show human-readable Unicode names instead of raw codepoints
ea35099 -
Show human-readable Unicode names instead of raw codepoints
An editor asked what "(U+0329)" meant -- a raw hex codepoint isn't self-explanatory to anyone without Unicode reference tables handy. Both the summary line and the per-character tooltip now use unicodedata.name() to show e.g. "combining vertical line below" instead, falling back to the hex codepoint only for the rare character with no assigned Unicode name. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>5e9def2 -
Merge fix/remove-marker-glyph: drop the removal-marker glyph from the highlight
7bab727 -
Drop the removal-marker glyph from the highlight
The colored+underlined base character was rendering correctly, but the appended marker looked broken/garbled in that context -- likely the underline decoration cutting across its boxy glyph shape, even though the same character renders fine elsewhere in the app (the keyboard's actual Backspace key). Rather than chase the exact rendering quirk, drop the marker: the color+underline on the affected letter already conveys the highlight on its own, and the tooltip still gives the exact codepoint on hover. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>0c62c28 -
Merge fix/highlight-combining-marks: fix invisible combining-mark highlighting in removed-symbols note
4039f8f -
Fix invisible combining-mark highlighting in the removed-symbols note
The previous version wrapped each removed character in its own colored/ underlined span, which looked broken for words like "button" -- the actual removed symbol there is a combining diacritic (U+0329) with no visible width of its own, so highlighting it alone rendered as nothing. Now removed marks are grouped with their preceding base character (clustered by Unicode category Mn/Mc/Me) before styling, so the highlight always has a visible glyph to attach to -- "button" now shows the trailing "n" underlined in red with a small removal marker (⌫) and a tooltip giving the exact codepoint. Falls back to "∅⌫" for the rare case where the removed character has no preceding base of its own. Verified locally: the exact reported case (trailing combining mark on "n"), a mid-word combining mark, a standalone non-combining removed character, and a full analyze()-to-rendered-HTML smoke test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>b28cece -
Merge feature/highlight-removed-symbols: highlight removed symbol(s) in the affected word(s) with codepoint tooltips
d25a6bd -
Highlight the specific removed symbol(s) in the affected word(s)
The TTS-compatibility note only ever listed distinct removed symbol types (deduplicated), with no indication of which word(s) they came from or how many occurrences were affected -- confusing when several words share the same invisible combining mark, since the note reads "1 symbol removed" regardless of whether that happened once or five times. core.py: filter_to_supported_ipa now also returns affected_words -- for each word that had a removal, the word as it looked right before filtering (stress repositioned) paired with a keep-mask. All call sites updated to the 3-tuple return. app.py: _removed_symbols_note renders each affected word inline with the removed character(s) underlined in red and a hover tooltip showing the Unicode codepoint -- since many removed symbols are combining marks that render as blank on their own (exactly what caused confusion in the most recent user report). Also added codepoints to the summary line's symbol list for the same reason. Wired through both the …55e3626
-
Merge fix/narrow-mic-agc-override: fix chipmunk-pitch regression by narrowing mic override to autoGainControl only
118e435 -
Narrow mic override to autoGainControl only
Forcing echoCancellation/noiseSuppression off alongside autoGainControl caused a real regression: a Mac user's voice came out pitched up (chipmunk-style) on both his built-in and external mic. Disabling echoCancellation is independently documented to make Chrome/Safari stop reliably honoring sampleRate/channelCount, which is the textbook cause of that kind of sample-rate-mismatch pitch shift. Now only autoGainControl is forced off. This may be less effective at the original hot-mic/clipping complaint for some setups, but that failure mode already has a clear warning + Mac-specific remediation (core.py's clipping check from the previous round) -- a known, explained problem beats an unexplained, unusable one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>3eec11f
-
Merge feature/mac-mic-fixes: address hot/clipped self-recordings reported by Mac users
cc2b62f -
Address hot/clipped self-recordings reported by Mac users
Mac users reported self-recordings clipping and being unusable, while PC users don't see it -- a known cross-platform quirk where Chrome's automatic gain control runs hotter on macOS than Windows for the same mic and speaking volume, on top of macOS's own input-gain slider often running high by default. core.py: _assess_audio_quality now flags clipping (>0.5% of samples pinned near full scale) with Mac-specific remediation text, instead of silently feeding distorted audio into Wav2Vec2. Added a shared _load_audio helper that peak-normalizes the waveform (to -1 dBFS) before recognition -- this can't undo genuine clipping distortion, but brings borderline-hot (not fully clipped) recordings to a consistent level, and both audio_to_ipa and _wav2vec2_log_probs now go through it instead of calling librosa.load directly. app.py: experimental page-load JS patches navigator.mediaDevices. getUserMedia to force autoGainControl/echoCancellation/noiseSuppression off for every recording -- Chrome …8d6b0c2
-
Merge feature/unique-user-tracking: track unique users on the Usage tab
8925984 -
Track unique users on the Usage tab
Adds an anonymous, per-browser id (a UUID generated client-side and stored in localStorage, via the same gr.Blocks(js=...) page-load mechanism already used for keyboard-similarity highlighting) so the Usage tab can report distinct users rather than just event counts. Threaded through every tracked action (analyze, keyboard open, alternate options, Allosaurus) via a hidden textbox input. usage_tracking.py gains a uid column (migrated in-place for existing DBs), an all-time unique-users count, and a per-day unique-users column. No real identity is ever collected — the id ties to nothing but itself, and is simply absent for any browser that blocks localStorage. Note: the JS->hidden-textbox mechanism is the standard, well-documented Gradio pattern for this, but local live-browser verification wasn't possible this round due to a sandbox restriction on spinning up a test server -- worth a quick manual click-through on the deployed Space to confirm the uid box populates as expected. Co-Aut…aff1e48
-
Merge feature/usage-tracking: track feature adoption, words tried, and sync to a private HF dataset for durability
0ad4d66
-
Sync usage stats to a private HF dataset repo for durability
The local SQLite file doesn't survive a Space restart on its own. When the HF_TOKEN Space secret is configured, usage_tracking.py now restores it from ZacDLX1915/ipa-translator-usage-stats at cold start (only if no local DB exists yet) and pushes it back after each new event, throttled to once per 5 minutes. Entirely optional and best-effort — with no HF_TOKEN set, everything behaves exactly as before (local-only). Also adds a "Sync to HF now" button on the Usage tab for a forced, unthrottled push plus a plain-language status message, so the sync can be verified/triggered on demand rather than only waiting for the next throttle window. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>632020d -
Add Usage tab: track feature adoption and words tried
Logs analyze() calls (split by typed text / self-recording / uploaded recording via the Audio component's stop_recording/upload events), IPA keyboard opens, alternate-options requests, and Allosaurus runs to a local SQLite file, plus the word/phrase each event was for. New "Usage" tab shows counts by feature, a day-by-day activity table, and a most-tried-words list, with a CSV export for backup since the DB resets on container restart. All logging is best-effort and never raises, so a stats problem can't affect the app people rely on. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>a97aba3 -
Merge feature/always-open-edit-box: remove the Modify step, edit box and keyboard button open by default
7f62d0a -
Remove the "Modify" step — edit box and keyboard button open by default
Previously: Analyze -> click "Modify" to reveal the edit box -> click "IPA Keyboard". Now: Analyze -> edit box and keyboard button are already there, pre-filled with the current IPA. One click removed from the most common editing path. modify_btn is gone entirely. ipa_edit_box/apply_btn/kb_btn now become visible directly from analyze()'s output instead of being gated behind a Modify click. Apply no longer collapses the edit UI afterward either — editing is an ongoing activity now, not a one-shot session, so it just re-renders and stays open for further edits. Since the edit box is now always visible instead of being freshly populated each time Modify is clicked, anything that changes the active IPA needs to keep it in sync explicitly: use_alt_option ("Use this" in Alternate Options) now also updates ipa_edit_box in text mode, where it previously only touched ipa_out/ipa_text_state — otherwise picking an alternate would silently leave the visible edit box showing stale text. Caught a…7315fd5 -
Merge feature/fill-missing-ipa-reference: fill in the 39 keyboard symbols missing from IPA_REFERENCE
64cd0f1 -
Fill in the 39 keyboard symbols missing from IPA_REFERENCE
Symbols like ɴ, ɳ, ɱ (uvular/retroflex/labiodental nasals) had no tooltip or click-to-hear audio because they don't occur in English — IPA_REFERENCE was built entirely around English example words, so any sound without one had no entry at all. Adds accurate linguistic descriptions for all 39 missing symbols (spanning stops, affricates, nasals, fricatives, approximants, laterals, and vowels), each referencing where the sound actually occurs (Hindi, Arabic, French, Mandarin, etc.) since there's no English word to point to. These entries use "word": None to mark "no natural English example" explicitly, rather than omitting the symbol entirely. For audio, adds a second synthesis pipeline (_ensure_phoneme_audio) that synthesizes the isolated phoneme itself via the existing synthesize_ipa() (already used elsewhere for arbitrary IPA strings) instead of a whole word — the only option when no example word exists. Kept as a separate _PHONEME_AUDIO_URLS cache (keyed by symbol, not word) so call…b1373d7 -
Merge feature/keyboard-hover-tooltip: add hover tooltips to the on-screen IPA keyboard
9da8e13 -
Add hover tooltips to the on-screen IPA keyboard
Keyboard keys now show the same rich hover tooltip (description + example word) already used for phonemes in the analysis results, adapted for buttons: symbol, description, example text, and a hint clarifying what clicking does. Directly addresses reported confusion — clicking a key plays its audio "as heard in an example word" (e.g. "p" plays "pat"), which wasn't obvious without this context. The hint text differs depending on whether audio is actually wired up for that key: "insert & hear it as in the example above" when synthesized audio exists, "insert this symbol" when it doesn't (e.g. before the first Analyze, or for the 39 keyboard symbols with no IPA_REFERENCE entry). CSS is self-contained in the keyboard's own <style> block (position: relative on .ipa-k plus the tooltip styles) rather than depending on the phoneme-tooltip CSS happening to already be on the page. Verified in a real browser (standalone harness using the actual generated keyboard HTML): tooltip renders correct…9e6373f
-
Merge feature/keyboard-click-audio: play audio when clicking IPA keyboard symbols
8993961 -
Merge feature/tooltip-piper-audio: serve pre-synthesized Piper audio for IPA tooltips
996e4bf -
Play audio when clicking a symbol on the on-screen IPA keyboard
Reuses the same pre-synthesized Piper audio the phoneme tooltips just gained: keyboard keys for symbols with a known example word now play that word's audio in addition to their existing insert-at-cursor behavior, so editors can hear a symbol while browsing the keyboard, not just after it's already been placed. _IPA_KEYBOARD_HTML (built at import time, before any Analyze call) is now paired with a lazily-rebuilt _get_ipa_keyboard_html(), refreshed once real tooltip audio exists and cached thereafter — the keyboard initially shows with no playback (since no audio has been generated yet), then analyze() swaps in the audio-aware version once _ensure_tooltip_audio has run, same as it already does for phoneme tooltips elsewhere on the page. Keyboard symbols with no IPA_REFERENCE example word (39 of the ~90 on the keyboard, mostly rarer non-English symbols) are unaffected — insert-only, exactly as before. Verified: keyboard has no audio calls before first Analyze, rebuilds with audio afte…78f0cda -
Serve pre-synthesized Piper audio for IPA tooltip click-to-hear
The tooltip's "click to hear an example" used the browser's own speechSynthesis (Web Speech API), whose quality is inconsistent and often poor depending on each user's OS/browser voices — entirely outside the app's control. Adds _ensure_tooltip_audio(), which lazily batch-synthesizes each of the ~54 distinct IPA_REFERENCE example words via the app's own Piper voice (same engine already used for "Listen to pronunciation" elsewhere) on the first Analyze call, writing them into Gradio's own managed cache directory. Confirmed empirically (installed gradio==4.7.1 in an isolated venv, tested against a live server with curl) that files placed there are servable via /file=<path> with no allowed_paths config needed. Deliberately lazy, not eager-at-import: eager synthesis would be the first-ever eager ML/network work in this app (even Whisper+Wav2Vec2 load lazily via get_engine()) and would block every Space cold start on a Piper voice download. Instead it rides along on the same first-Analyze…6a81e31 -
Merge feature/hide-allosaurus-text-mode: hide Allosaurus section in text-only mode
ee8b72e -
Merge feature/alternate-options-variance: increase candidate variance
d9ffd3d -
Hide the Allosaurus comparison section in text-only mode
Allosaurus is a speech-recognition model with no text-only equivalent — it always required real recorded/uploaded audio, but the button and intro text stayed visible even after a text-only analysis, so clicking it just produced a "record or upload audio" message. That's expected behavior, not a bug, but confusing to hit unprompted. The section (both its intro Markdown and the button) is now hidden by default and only revealed by analyze() when audio was actually used — mirrors how modify_btn/kb_btn/alt_options_btn already toggle visibility per mode.95c815d -
Increase variance in Alternate options candidates
Feedback: candidates were "just British pronunciations and very similar spellings." Three contributing causes, all fixed: 1. _ESPEAK_ACCENT_VARIANTS for English only offered en-gb/en-gb-x-rp — two accents that sound very close to each other. Swapped in Scottish and Caribbean English, which diverge audibly from US/RP English. 2. _perturb_ipa always took the first same-row neighbor for a given phone (e.g. "p" always became "b", every time). Now tries up to 2 distinct neighbors per position, spreading across more of each row. 3. generate_alternate_ipas built the candidate list accent-variants- first, so a language with several accent variants could crowd every perturbation candidate off the final list. Now interleaves the two sources so both are represented. Verified: perturbation pool now yields multiple distinct swaps per position (e.g. "pæt" -> "pæp" and "pæb" from the same word-final position), all 3 new accent voices get queried, and the final list mixes accent…81b42e5 -
Merge feature/alternate-ipa-options: candidate Target IPA spellings with confidence
1ff25cd