fix(ai): download progress bytes/speed + voice progress-scale bug (#333 item 1) - #346
Conversation
… item 1)
Voice model download (VoiceModelDownloadModal, transformers.js pipeline):
transformers.js's own onProgress payload is `{ progress: <0-100>, loaded:
<bytes>, total: <bytes> }` (verified against its readResponse() source —
progress = loaded/total*100). voiceCommandService.ts treated `progress` as if
it were already a 0-1 fraction, so the existing `Math.min(0.95, pct)` safety
clamp kicked in almost immediately (any progress value over 0.95%, i.e. after
the very first chunk) and stayed there for the rest of the download, making
the progress bar look stuck. Now derives the fraction from the payload's real
loaded/total byte counts (falling back to progress/100 only when absent), and
threads the real bytes through to the modal for a live "X MB of Y MB" + speed
display.
WebLLM text-model download (LocalAiDownloadProgress, services/localAiFacade):
the installed @mlc-ai/web-llm's own progress callback exposes only a 0-1
fraction, no structured byte counts. Adds a machine-readable per-model size
table (WEBLLM_MODEL_APPROX_MB, mirroring the free-text GB figures already in
WEBLLM_SUPPORTED_MODELS' labels) and derives an approximate downloaded/total
MB + speed from progress × that table — clearly labeled as an estimate, not
measured telemetry, since no real byte-level data is obtainable here without
patching library internals.
Both UIs share services/downloadProgressFormat.ts for MB/speed formatting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
🤖 CodeAnt AI — Review Status
|
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Reviewer's GuideImplements byte-level progress and speed display for both voice and local AI model downloads, and fixes a mis-scaled transformers.js progress handler that made the voice download bar appear stuck around 95%. Sequence diagram for WebLLM text model download approximate size and speedsequenceDiagram
participant User
participant LocalAiDownloadProgress
participant localAiFacade
participant WebLlmLibrary
participant inferenceProgressEmitter
participant aiCore
User->>LocalAiDownloadProgress: open Settings → AI
LocalAiDownloadProgress->>localAiFacade: preloadLocalModel(modelId) / generateLocalText
localAiFacade->>WebLlmLibrary: start model download with reportProgress
WebLlmLibrary-->>localAiFacade: reportProgress({ progress, text })
localAiFacade->>inferenceProgressEmitter: reportWebLlmProgress(progress, text, workerModelId/modelId)
inferenceProgressEmitter->>aiCore: WEBLLM_MODEL_APPROX_MB[modelId]
aiCore-->>inferenceProgressEmitter: approxMb
inferenceProgressEmitter->>inferenceProgressEmitter: compute totalBytes, loadedBytes, bytesPerSecond
inferenceProgressEmitter-->>LocalAiDownloadProgress: WebLlmLoadProgress snapshot
LocalAiDownloadProgress-->>User: show percent + ETA + "≈X MB of Y MB" + "≈Z MB/s"
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in:15 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (51)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughWebLLM and voice model downloads now report byte totals and transfer speed. Voice progress uses byte ratios with percentage fallback. The UI, translations, generated locale bundles, documentation, audit records, and unit tests were updated. ChangesDownload progress reporting
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- The new
reportWebLlmProgress(progress, text, modelId?)signature is now a bit brittle and loosely typed (accepting any string and casting toWebLlmModelId); consider tightening this to aWebLlmModelId | undefinedor an options object so the model id domain is explicit and mismatches are caught at compile time. - The size/speed UI blocks in
LocalAiDownloadProgressandVoiceModelDownloadModalduplicate very similar layout and conditional logic; you might factor a small shared presentational component that takes the formatted strings to keep the download progress UI consistent and easier to adjust across both surfaces.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments- The new `reportWebLlmProgress(progress, text, modelId?)` signature is now a bit brittle and loosely typed (accepting any string and casting to `WebLlmModelId`); consider tightening this to a `WebLlmModelId | undefined` or an options object so the model id domain is explicit and mismatches are caught at compile time.
- The size/speed UI blocks in `LocalAiDownloadProgress` and `VoiceModelDownloadModal` duplicate very similar layout and conditional logic; you might factor a small shared presentational component that takes the formatted strings to keep the download progress UI consistent and easier to adjust across both surfaces.
## Individual Comments### Comment 1
<locationpath="services/ai/inferenceProgressEmitter.ts"line_range="56-58" />
<code_context>
}
- reportWebLlmProgress(progress: number, text: string): void {
+ // QNBS-v3 (#333 item 1): modelId is optional (existing callers with no known model id keep
+ // working; loadedBytes/totalBytes/bytesPerSecond simply stay null when it's absent or unknown).
+ reportWebLlmProgress(progress: number, text: string, modelId?: string): void {
if (this.snapshot.state !== 'loading') {
this.loadStartMs = Date.now();
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Use the specific WebLlmModelId type for modelId instead of string to avoid unsafe casts.
`reportWebLlmProgress` takes `modelId?: string` and then casts to `WebLlmModelId` when indexing `WEBLLM_MODEL_APPROX_MB`. If only known WebLlm IDs are expected here, update the signature to `modelId?: WebLlmModelId` so you can drop the `as WebLlmModelId` cast and get compile-time checks. If arbitrary strings must be supported, validate or guard the ID before indexing instead of casting.
Suggested implementation:
```typescript// QNBS-v3 (#333 item 1): modelId is optional (existing callers with no known model id keep// working; loadedBytes/totalBytes/bytesPerSecond simply stay null when it's absent or unknown).reportWebLlmProgress(progress: number, text: string, modelId?:WebLlmModelId): void {
``````typescriptconst approxMb =modelId?WEBLLM_MODEL_APPROX_MB[modelId] :undefined;
```1. Ensure `WebLlmModelId` is imported or declared in `services/ai/inferenceProgressEmitter.ts` (e.g., `import { WebLlmModelId } from'...';`) in line with existing type imports in this file.2. Verify all call sites of `reportWebLlmProgress` now pass a `WebLlmModelId` (or `undefined`) rather than a `string`; adjust their types/values accordingly.</issue_to_address>### Comment 2<location path="services/downloadProgressFormat.ts" line_range="5-7" /><code_context>+// (LocalAiDownloadProgress, VoiceModelDownloadModal) — kept as pure functions so both can render+// identical "X MB of Y MB" / "Z MB/s" text without duplicating the rounding/formatting logic.++/** Formats a byte count as whole megabytes, e.g. `formatMegabytes(43_000_000)` → `"41"`. */+export function formatMegabytes(bytes: number): string {+ return (bytes / (1024 * 1024)).toFixed(0);+}+</code_context><issue_to_address>**question:** Clarify whether the UI intends MiB or decimal MB, and adjust the divisor accordingly.This helper divides by `1024*1024` (MiB), while the UI and comments refer to MB/GB (e.g. `"~0.4 GB"`). Please confirm whether the product intends decimal MB or binary MiB and align the divisor, labels, and possibly the function name (`formatMebibytes` for binary) to avoid unit confusion and small discrepancies with external size references.</issue_to_address>### Comment 3<location path="tests/unit/components/voice/VoiceModelDownloadModal.test.tsx" line_range="175-184" /><code_context> progress: 0, estimatedSecondsRemaining: null as number | null, text: '',+ // QNBS-v3 (#333 item 1)+ loadedBytes: null as number | null,+ totalBytes: null as number | null,</code_context><issue_to_address>**suggestion (testing):** Byte-level tests cover size but not the new speed text; consider a test that exercises the MB/s displayThe existing progress-display tests exercise the MB text and `isDownloading` wiring, but not the new `speedText` (`voice.modelDownload.speed`). Since the `useEffect` derives `bytesPerSecond` from `loadedBytes` and `Date.now`, add a test that:- uses `vi.useFakeTimers()` / `vi.setSystemTime()` to simulate elapsed time > 0.5s,- initializes `mockVoiceState.wasmModelDownloadLoadedBytes` before render and advances time so the effect re-runs,- asserts that the expected "X.Y MB/s" speed label is rendered.This will cover the MB/s path and guard against regressions in timing/formatting logic for the download UX.</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
PR Summary by QodoFix AI/voice model download progress with bytes + speed metrics
AI Description
Diagram
High-Level Assessment
Files changed (54) |
Uh oh!
There was an error while loading. Please reload this page.
🏁 CodeAnt Quality Gate ResultsCommit: ✅ Overall Status: PASSEDQuality Gate Details
|
Code Review by Qodo
1. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
packages/ai-core/src/index.ts (1)
185-189: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueKeep each
QNBS-v3comment on one physical line.The new comments wrap across multiple physical lines. Replace each with one concise why-comment.
packages/ai-core/src/index.ts#L185-L189: collapse the model-size rationale into one line.services/ai/inferenceProgressEmitter.ts#L14-L17: collapse the derived-byte rationale into one line.services/ai/inferenceProgressEmitter.ts#L56-L57: collapse the optional-model-ID rationale into one line.services/localAiFacade.ts#L159-L161: collapse theworkerModelIdrationale into one line.types.ts#L612-L614: collapse the byte-source rationale into one line.services/voice/voiceCommandService.ts#L608-L613: collapse the progress-scale rationale into one line.As per coding guidelines, “For every non-trivial code change, add one single-line
QNBS-v3comment explaining why” and “never wrap the comment across physical lines.”🤖 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 `@packages/ai-core/src/index.ts` around lines 185 - 189, Collapse each multi-line QNBS-v3 rationale into one concise physical-line why-comment: packages/ai-core/src/index.ts lines 185-189 for model-size metadata; services/ai/inferenceProgressEmitter.ts lines 14-17 for derived byte estimates and lines 56-57 for the optional model ID; services/localAiFacade.ts lines 159-161 for workerModelId; types.ts lines 612-614 for the byte source; and services/voice/voiceCommandService.ts lines 608-613 for the progress scale. Preserve each comment’s rationale and the required QNBS-v3 prefix; make no other changes.Source: Coding guidelines
services/downloadProgressFormat.ts (1)
1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the required single-line QNBS-v3 rationale format.
The changed TypeScript and TSX surfaces either omit the required rationale or split it across physical lines. Normalize each location to one single-line
// QNBS-v3: [Grund / Impact / Kreativer Mehrwert]comment.
services/downloadProgressFormat.ts#L1-L3: collapse the shared-formatting rationale into one line.tests/unit/services/downloadProgressFormat.test.ts#L1-L29: add one QNBS-v3 rationale before the imports.components/voice/VoiceModelDownloadModal.tsx#L35-L36: collapse the byte-selector rationale into one line.components/voice/VoiceModelDownloadModal.tsx#L43-L44: collapse the speed-calculation rationale into one line.components/voice/VoiceModelDownloadModal.tsx#L113-L114: collapse the display-formatting rationale into one line.components/settings/LocalAiDownloadProgress.tsx#L50-L51: collapse the approximate-byte rationale into one line.As per coding guidelines, every non-trivial TypeScript or TSX change requires one single-line QNBS-v3 comment in the specified format.
🤖 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 `@services/downloadProgressFormat.ts` around lines 1 - 3, Normalize the QNBS-v3 rationale comments to one physical line using the exact // QNBS-v3: [Grund / Impact / Kreativer Mehrwert] format. Update services/downloadProgressFormat.ts (lines 1-3), VoiceModelDownloadModal.tsx (lines 35-36, 43-44, and 113-114), and LocalAiDownloadProgress.tsx (lines 50-51) by collapsing each existing rationale; add one rationale before imports in tests/unit/services/downloadProgressFormat.test.ts (lines 1-29).Source: Coding guidelines
🤖 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 `@components/voice/VoiceModelDownloadModal.tsx`:
- Around line 43-60: Update the speed-tracking effect using downloadStartMsRef
and bytesPerSecond so it stores the previous loadedBytes value and timestamp,
resets both when isDownloading becomes false, and computes bytes per second from
consecutive byte and time deltas after samples begin. Avoid dividing cumulative
loadedBytes by the total startup-inclusive elapsed time, while preserving the
existing null/initial-sample handling.
In `@public/locales/el/bundle.json`:
- Around line 1808-1809: Translate the download size and speed labels,
preserving the {{loaded}}, {{total}}, and {{speed}} interpolation tokens, in
public/locales/el/bundle.json lines 1808-1809 and 2470-2472 into Greek;
public/locales/eu/bundle.json lines 1808-1809 and 2470-2472 into Basque;
public/locales/fa/bundle.json lines 1808-1809 and 2470-2472 into Persian; and
public/locales/fi/bundle.json lines 1808-1809 and 2470-2472 into Finnish. Update
both local AI and voice entries at every listed site, with no English
user-facing values remaining.
In `@public/locales/pt/bundle.json`:
- Around line 1808-1809: Translate the English “of” connector in the affected
progress strings, updating the source locale values for
settings.ai.localAi.downloadSize and voice.modelDownload.progressBytes in
public/locales/pt/bundle.json (1808-1809, 2470-2472),
public/locales/ru/bundle.json (1808-1809, 2470-2472),
public/locales/sv/bundle.json (1808-1809, 2470-2472), and
public/locales/zh/bundle.json (1808-1809, 2470-2472); then regenerate each
corresponding runtime bundle.
In `@services/downloadProgressFormat.ts`:
- Around line 10-13: Update formatMegabytesPerSecond to accept the active locale
and format the megabytes value with Intl.NumberFormat using one fractional digit
instead of toFixed(1). Update both callers to pass their locale through, and add
a test covering comma-decimal formatting for a non-English locale.
In `@services/voice/voiceCommandService.ts`:
- Around line 621-630: Update the voice download flow around setVoiceSettings to
reset wasmModelDownloadLoadedBytes and wasmModelDownloadTotalBytes when starting
a download, and clear them again on cancellation, including byte-less downloads.
Preserve byte progress updates when p.loaded and p.total are available, and add
a test covering sequential downloads where the later download has no byte
metrics.
---
Nitpick comments:
In `@packages/ai-core/src/index.ts`:
- Around line 185-189: Collapse each multi-line QNBS-v3 rationale into one
concise physical-line why-comment: packages/ai-core/src/index.ts lines 185-189
for model-size metadata; services/ai/inferenceProgressEmitter.ts lines 14-17 for
derived byte estimates and lines 56-57 for the optional model ID;
services/localAiFacade.ts lines 159-161 for workerModelId; types.ts lines
612-614 for the byte source; and services/voice/voiceCommandService.ts lines
608-613 for the progress scale. Preserve each comment’s rationale and the
required QNBS-v3 prefix; make no other changes.
In `@services/downloadProgressFormat.ts`:
- Around line 1-3: Normalize the QNBS-v3 rationale comments to one physical line
using the exact // QNBS-v3: [Grund / Impact / Kreativer Mehrwert] format. Update
services/downloadProgressFormat.ts (lines 1-3), VoiceModelDownloadModal.tsx
(lines 35-36, 43-44, and 113-114), and LocalAiDownloadProgress.tsx (lines 50-51)
by collapsing each existing rationale; add one rationale before imports in
tests/unit/services/downloadProgressFormat.test.ts (lines 1-29).
🪄 Autofix
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
Run ID: 91834030-57e4-4d9b-b8a3-69d188612a99
📒 Files selected for processing (54)
CHANGELOG.mdREADME.mdcomponents/settings/LocalAiDownloadProgress.tsxcomponents/voice/VoiceModelDownloadModal.tsxlocales/ar/settings.jsonlocales/de/settings.jsonlocales/el/settings.jsonlocales/en/settings.jsonlocales/es/settings.jsonlocales/eu/settings.jsonlocales/fa/settings.jsonlocales/fi/settings.jsonlocales/fr/settings.jsonlocales/he/settings.jsonlocales/hu/settings.jsonlocales/is/settings.jsonlocales/it/settings.jsonlocales/ja/settings.jsonlocales/ko/settings.jsonlocales/pt/settings.jsonlocales/ru/settings.jsonlocales/sv/settings.jsonlocales/zh/settings.jsonpackages/ai-core/src/index.tspublic/locales/ar/bundle.jsonpublic/locales/de/bundle.jsonpublic/locales/el/bundle.jsonpublic/locales/en/bundle.jsonpublic/locales/es/bundle.jsonpublic/locales/eu/bundle.jsonpublic/locales/fa/bundle.jsonpublic/locales/fi/bundle.jsonpublic/locales/fr/bundle.jsonpublic/locales/he/bundle.jsonpublic/locales/hu/bundle.jsonpublic/locales/is/bundle.jsonpublic/locales/it/bundle.jsonpublic/locales/ja/bundle.jsonpublic/locales/ko/bundle.jsonpublic/locales/pt/bundle.jsonpublic/locales/ru/bundle.jsonpublic/locales/sv/bundle.jsonpublic/locales/zh/bundle.jsonservices/ai/inferenceProgressEmitter.tsservices/downloadProgressFormat.tsservices/localAiFacade.tsservices/voice/voiceCommandService.tstests/unit/LocalAiDownloadProgress.test.tsxtests/unit/components/voice/VoiceModelDownloadModal.test.tsxtests/unit/localAiFacade.test.tstests/unit/services/downloadProgressFormat.test.tstests/unit/services/inferenceProgressEmitter.test.tstests/unit/services/voice/voiceCommandService.test.tstypes.ts
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…able) Freshly published/reviewed 2026-08-12; latest extract-zip release (2.0.1) has no patched version to override to. Transitive devDependency of @puppeteer/browsers (Playwright's browser-binary downloader) — only ever extracts Playwright/Chromium's own CDN-hosted zip releases, never a user- or attacker-supplied archive, and ships in no production bundle. Documented in src-tauri/osv-scanner.toml's IgnoredVulns list (existing pattern for unfixable transitive findings) and AUDIT.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Addresses all 14 CodeRabbit/CodeAnt/Qodo/Sourcery review findings on PR #346: - services/voice/voiceCommandService.ts + components/voice/VoiceModelDownloadModal.tsx: explicitly clear wasmModelDownloadLoadedBytes/wasmModelDownloadTotalBytes at every download lifecycle boundary (start, cancel, error, completion) — setVoiceSettings merges partial state, so a prior attempt's bytes previously lingered in Redux until the next byte-bearing progress tick arrived, showing stale size/speed on retry or when switching models. types.ts: widened both fields to `number | undefined` so they can be explicitly cleared under exactOptionalPropertyTypes. - components/voice/VoiceModelDownloadModal.tsx: bytesPerSecond is now delta-based (bytes and timestamp of the previous sample, reset when downloading stops) instead of cumulative-loaded-bytes / elapsed-since-start, which included startup time and smoothed real throughput changes into a misleading average presented as a live rate. - services/downloadProgressFormat.ts: formatMegabytesPerSecond renamed to megabytesPerSecond and now returns a number instead of a toFixed() string — the decimal separator is locale-dependent (comma vs period), so callers now format it via formatNumber() from useTranslation() instead of a hardcoded period. - packages/ai-core/src/index.ts / services/ai/localModelStorageService.ts: removed the duplicate WEBLLM_MODEL_APPROX_MB literal from localModelStorageService — it now re-exports the canonical ai-core table instead of maintaining a second, driftable copy of the same model-id-to-size mapping. - services/ai/inferenceProgressEmitter.ts: replaced an unsafe `as WebLlmModelId` cast with a proper type guard (isKnownWebLlmModel) — modelId stays `string` since real call sites in localAiFacade.ts pass broader adaptive/fallback model ids, not just the curated WebLlmModelId union, so narrowing the param type would have broken them. - components/settings/LocalAiDownloadProgress.tsx: prefixes WebLLM's derived/estimated byte and speed values with "~" so users can distinguish them from the voice download UI's measured bytes, without adding new i18n keys (reuses the tilde convention already used in WEBLLM_SUPPORTED_MODELS' own size labels). - 8 locales (el, eu, fa, fi, pt, ru, sv, zh): translated the "of" connector in settings.ai.localAi.downloadSize / voice.modelDownload.progressBytes, mirroring this file's own already-translated storageUsage key's per-locale phrasing exactly. MB/s stays untranslated — a universal unit abbreviation, same as every other locale including production ones (de/es/fr/it/ar/he/...) already leave it. - New/updated tests: voiceCommandService (3 new lifecycle-boundary byte-clearing cases), VoiceModelDownloadModal (delta-speed test, byte-clear-on-cancel coverage), downloadProgressFormat (renamed function, plain-number-return assertion). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
qnbs
commented
Aug 13, 2026
@coderabbitai review |
|
Real conflicts resolved: - CHANGELOG.md: three genuinely distinct "### Fixed" entries from different PRs (#341/#344's AI Writing Studio readability fixes, already in main, vs. this branch's own voice-download-progress-scale fix) — kept both. - locales/pt/settings.json + its bundle: same providerStatusReady ("Preparar" infinitive vs. the already-fixed "Pronto") conflict already resolved on the other branches this session — took main's already-correct side, since this branch never touched that key. - README.md: stale i18n key-count/test-file-count badges — regenerated via `node scripts/sync-readme-metrics.mjs` post-merge rather than guessing (2918 keys × 19 locales, 547 test files). - src-tauri/osv-scanner.toml: cosmetic-only comment-header conflict (both sides already had the identical extract-zip IgnoredVulns entry) — kept the more current review-date comment. Rebuilt all 19 public/locales/*/bundle.json bundles fresh via `pnpm run i18n:check` rather than resolving bundle.json conflicts by hand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CHANGELOG.md: two genuinely distinct "### Added" entries (#332's manual reduced-transparency toggle, already in main, vs. this branch's own download-progress bytes/speed entry) — kept both. README.md: stale i18n key-count/test-file-count badges across 5 locations — regenerated via `node scripts/sync-readme-metrics.mjs` post-merge (2919 keys x 19 locales, 549 test files) rather than guessing. All 19 locale source files and public/locales/*/bundle.json merged/rebuilt cleanly with no manual conflict resolution needed this time. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
…reliability fixes (#351) * docs(deepsource): log re-surfaced JS-0440 finding (local-only, not for push) Co-Authored-By: GitHub Copilot (Claude Sonnet 5) <noreply@github.com> * docs(deepsource): correct stale JS-0440 disposition, re-surfaced 2026-08-01 (local-only) Co-Authored-By: GitHub Copilot (Claude Sonnet 5) <noreply@github.com> * release: v1.27.0 — Phase 4 encryption production wiring + desktop/AI reliability fixes Closes out issue #338 (Phase 4 of the at-rest encryption lifecycle): disable encryption and passphrase rotation are now live in Settings › Privacy, backed by the durable resumable migration journal built in the prior release. Also ships three independently-diagnosed reliability/UX fixes: - Tauri desktop cold boot never read persisted state back (#332) — every desktop launch loaded as a brand-new user regardless of what was actually saved to disk; boot hydration now mirrors the already-correct save path. Quitting also now awaits any pending debounced autosave instead of risking a mid-debounce data loss. - AI Writing Studio manuscript text was unreadable, with the caret/selection visually drifting from the real text (#341) — a blur/font-mismatch/ scroll-desync defect in the invisible-input-over-visible-mirror rendering pattern used by both Writer Studio and the main manuscript editor. - Voice and WebLLM model download progress bars showed real byte counts and transfer speed instead of a bare percentage, and the voice download bar's progress-scale bug (stuck at ~95% for most of the download) is fixed (#333 item 1). All 5 correction-loop PRs (#342-#346) ran to full quiescence before merging — every CodeRabbit/CodeAnt/Qodo/Sourcery finding fixed or justified with evidence, 0 unresolved review threads, full CI green (Quality Gate, E2E, E2E Deep Coverage, Build, Storybook, Lighthouse, Visual Regression) — including a genuine data-integrity bug found and fixed during that loop: a shared try/catch in the rekey-resume recovery path could, on a crash immediately after `commitRekeyMigration`, misinterpret that crash as "already committed" and clear the migration journal while the durable sentinel still held the old passphrase — leaving neither passphrase able to unlock the library. 2919 i18n keys × 19 locales. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix: address PR #351 review findings — stale CHANGELOG claim + self-contradictory local-only doc - CHANGELOG.md: the [1.27.0] section never documented its own headline feature (#342/#343's disable-encryption/passphrase-rotation production wiring) and still carried the prior release's "intentionally unavailable ... see issue #338" caveat, directly contradicting what shipped in this same version (chatgpt-codex-connector). Added the missing entry, qualified the stale caveat as describing that point in time, and corrected the Docs section's "opened issue #338" line to note it closed in this release. - docs/DEEPSOURCE-REVIEW-LOOP.md: two restored local-only tracking commits (originally discarded by an earlier `git reset --hard`, recovered via cherry-pick) carried "LOCAL-ONLY — not pushed to remote" / "do not push this entry upstream" wording that became false the moment they were committed to a pushed branch (qodo-code-review, coderabbitai). Removed the contradictory local-only framing, keeping the content as a normal dated log entry. Also softened the JS-0440 remediation TODO's "consider a rule-level ignore repo-wide" suggestion per coderabbitai's security concern — a blanket ignore would hide future unsafe `dangerouslySetInnerHTML` uses, not just the one reviewed occurrence. The public/sw.js QNBS-v3-comment findings (qodo-code-review, coderabbitai) were false positives — the existing single-line QNBS-v3 comment on the line directly above APP_VERSION was already unchanged by this PR's one-line version-bump diff; verified via `git diff main~1 -- public/sw.js`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: GitHub Copilot (Claude Sonnet 5) <noreply@github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
User description
Summary
Addresses #333 item 1 (local AI / voice model download UX should show bytes/speed, not just a bare percentage), with a genuine root-cause bug fix discovered while investigating.
Voice model download (
VoiceModelDownloadModal.tsx,services/voice/voiceCommandService.ts):onProgresspayload is{ progress: <0-100>, loaded: <bytes>, total: <bytes> }— verified directly against itsreadResponse()source (progress = loaded/total*100). The existing code treatedprogressas if it were already a 0-1 fraction, so the safety clampMath.min(0.95, pct)kicked in almost immediately (any progress value over 0.95%, i.e. after the very first chunk of the download) and stayed there for the rest of the download — the progress bar looked stuck/frozen the whole time. This plausibly contributes to the "local AI download appears frozen" complaint in Stability issues with Local AI, Gemini API keys, LM Studio and UI + workflow suggestions #333.loaded/totalbyte counts (falling back toprogress / 100only when those are genuinely absent), and threads the real bytes through Redux to the modal for a live "X MB of Y MB" display plus a client-side-computed transfer speed.WebLLM text-model download (
LocalAiDownloadProgress.tsx,services/localAiFacade.ts,services/ai/inferenceProgressEmitter.ts):@mlc-ai/web-llm's own progress callback exposes only a 0-1 fraction + a status string — no structured byte counts (confirmed against its installed.d.ts). Real byte-level progress isn't obtainable here without patching library internals.WEBLLM_MODEL_APPROX_MBinpackages/ai-core), mirroring the free-text GB figures already embedded inWEBLLM_SUPPORTED_MODELS' labels, and derive an approximate downloaded/total MB + speed fromprogress × totalBytes. This is clearly labeled as an estimate in code comments and is not presented as measured telemetry.Both UIs share a small
services/downloadProgressFormat.tsfor consistent MB/speed formatting.Test plan
pnpm run typecheck(exact CI command) — cleanpnpm run lint(full project,--error-on-warnings) — cleanpnpm run i18n:check— 19 locales, 2908 keys, parity OK; bundles rebuiltinferenceProgressEmitter(both existing suites + new byte-metric cases),downloadProgressFormat(new),localAiFacade(2 pre-existing assertions updated for the newmodelIdarg),voiceCommandService(3 new cases pinning the progress-scale fix and byte threading),LocalAiDownloadProgress(2 new size/speed display cases),VoiceModelDownloadModal(2 new size-display cases)CLAUDE.md)🤖 Generated with Claude Code
Summary by Sourcery
Improve AI model download UX by showing byte-level progress, estimated size, and speed for both voice and WebLLM text models, while fixing the mis-scaled voice download progress reporting.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests:
CodeAnt-AI Description
Fix model download progress and show transfer details
What Changed
Impact
✅ Voice downloads no longer appear frozen✅ Clearer model download progress✅ Visible download speed💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation