qa: restore boot, wire dead error rx, harden storage and config
Front-to-back QC pass turned up two independent missing-module
showstoppers (workspace did not compile; frontend did not load) plus a
handful of HANDOVER-claimed features that were wired but dead. Fixes:
P0 — gets the app booting again:
- Add the never-committed src/lib/utils/runtime.js (hasTauriRuntime
detection); 5 imports were resolving to nothing.
- Add the never-committed crates/audio/src/streaming_resample.rs
(rubato-backed StreamingResampler with new/push_samples/flush);
declared in lib.rs and used 3x by live.rs but had no impl.
- Drop the duplicate hasTauriRuntime import in routes/+layout.svelte.
- Allow the transcript-viewer window to use the default capability
(was missing from capabilities/default.json:windows, so the viewer
window could open but not invoke any Tauri command).
P1 — features documented as working but actually dead:
- Pump MicrophoneCapture::take_error_rx() into LiveStatusMessage::
Warning each loop iteration in commands/live.rs. The HANDOVER
promised cpal stream errors would surface as toasts; the channel
was created and never read.
- Replace .expect() on the WebKit media-permission setup with a
logged warning. Failure no longer aborts the whole process.
- Toast on save_preferences failure (preferences.svelte.js had a
silent console.error — now warns once per failure run via the
existing toasts store).
P2 — correctness/robustness:
- add_dictionary_entry: switch INSERT OR IGNORE to ON CONFLICT
DO UPDATE ... RETURNING id so duplicate terms get the real row id
instead of a stale auto-increment.
- search_transcripts: qualify ORDER BY fts.rank.
- InsertTranscriptParams + TranscriptRow: bump sample_rate /
audio_channels from i32 to i64 to match the Tauri DTO and avoid
silent truncation at the boundary.
- Drop the unused tauri-plugin-mcp dependency.
- Promote sqlx in src-tauri/Cargo.toml from linux-only to
unconditional (lib.rs names sqlx::SqlitePool unconditionally —
macOS/Windows builds were latently broken).
- hotkey/linux.rs: stop panicking the hotplug task on inotify
failure; degrade to "no hotplug" with a stderr warning.
- layout.svelte: store the global error/unhandledrejection handler
refs and remove them in onDestroy so HMR/window teardown doesn't
leak listeners.
Verified: cargo check -p kon-core -p kon-storage -p kon-cloud-providers
passes. cargo check on src-tauri/kon-audio/kon-hotkey requires alsa +
gtk system libs not present in this sandbox; their changes are
syntactically and type-checked against the rest of the workspace.
svelte-check requires npm install which is not available here.
https://claude.ai/code/session_018ozAs4UcRC8jbJbddqJtEw
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// src/lib/stores/preferences.svelte.js
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { toasts } from './toasts.svelte.js';
|
||||
|
||||
const DEFAULTS = {
|
||||
theme: 'dark',
|
||||
@@ -74,13 +75,25 @@ function applyToDOM(prefs) {
|
||||
}
|
||||
|
||||
let saveTimeout = null;
|
||||
// Show the failure toast at most once per process so a stuck SQLite path
|
||||
// doesn't spam the user every time they nudge a slider.
|
||||
let saveFailureToastShown = false;
|
||||
function persistToSQLite(prefs) {
|
||||
clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(async () => {
|
||||
try {
|
||||
await invoke('save_preferences', { preferences: JSON.stringify(prefs) });
|
||||
saveFailureToastShown = false;
|
||||
} catch (e) {
|
||||
console.error('Failed to save preferences:', e);
|
||||
if (!saveFailureToastShown) {
|
||||
const msg = typeof e === 'string' ? e : (e?.message ?? String(e));
|
||||
toasts.warn(
|
||||
'Could not save preferences',
|
||||
`${msg}. Your changes still apply for this session.`,
|
||||
);
|
||||
saveFailureToastShown = true;
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
18
src/lib/utils/runtime.js
Normal file
18
src/lib/utils/runtime.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// Detects whether the Tauri runtime is present in the current window.
|
||||
//
|
||||
// In Tauri v2 the bootstrap script injects `window.__TAURI_INTERNALS__`
|
||||
// before the page script runs, and also sets `window.isTauri = true` as
|
||||
// a convenience marker. Either is sufficient evidence we are inside a
|
||||
// Tauri webview.
|
||||
//
|
||||
// In a plain browser (vite preview, `npm run dev` outside `tauri dev`)
|
||||
// neither global is set, so `hasTauriRuntime()` returns false. Callers
|
||||
// use this to gate `invoke()` calls and fall back to localStorage-only
|
||||
// behaviour for browser-preview mode.
|
||||
|
||||
export function hasTauriRuntime() {
|
||||
if (typeof window === 'undefined') return false;
|
||||
if (window.__TAURI_INTERNALS__) return true;
|
||||
if (window.isTauri === true) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
import { loadOsInfo } from "$lib/utils/osInfo.js";
|
||||
import { page, settings, saveSettings } from "$lib/stores/page.svelte.js";
|
||||
import { getPreferences, updatePreferences } from "$lib/stores/preferences.svelte.js";
|
||||
import { hasTauriRuntime } from "$lib/utils/runtime.js";
|
||||
|
||||
import { page as sveltePage } from "$app/stores";
|
||||
|
||||
@@ -171,6 +170,9 @@
|
||||
// log_frontend_error. Best-effort: never let the error handler itself
|
||||
// throw, never crash the app over a logging failure.
|
||||
// (Diagnostics layer 1 — local only, never transmitted)
|
||||
let onWindowError = null;
|
||||
let onUnhandledRejection = null;
|
||||
|
||||
function installGlobalErrorCapture() {
|
||||
if (!hasTauriRuntime()) return;
|
||||
|
||||
@@ -181,19 +183,22 @@
|
||||
} catch { /* same */ }
|
||||
};
|
||||
|
||||
window.addEventListener("error", (ev) => {
|
||||
onWindowError = (ev) => {
|
||||
safeLog(
|
||||
"window.onerror",
|
||||
ev?.message || ev?.error?.message || "Unknown error",
|
||||
ev?.error?.stack || null,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener("unhandledrejection", (ev) => {
|
||||
onUnhandledRejection = (ev) => {
|
||||
const reason = ev?.reason;
|
||||
const msg = (reason && (reason.message || String(reason))) || "Unhandled rejection";
|
||||
safeLog("unhandledrejection", msg, reason?.stack || null);
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener("error", onWindowError);
|
||||
window.addEventListener("unhandledrejection", onUnhandledRejection);
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
@@ -229,6 +234,8 @@
|
||||
|
||||
onDestroy(() => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
if (onWindowError) window.removeEventListener("error", onWindowError);
|
||||
if (onUnhandledRejection) window.removeEventListener("unhandledrejection", onUnhandledRejection);
|
||||
if (!tauriRuntimeAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user