feat(ux): dogfood pass — onboarding, tasks, LLM chip, float popout
Bundles a session of dogfood UX feedback plus the two Cursor Bugbot
findings on the auto-titles branch.
Onboarding (FirstRunPage):
- Welcome leads with "Set up automatically"; system breakdown and the
full model list move behind a "Choose manually" disclosure
- Morning / evening / autostart modal copy trimmed to one short
sentence each; CTAs shortened
- "Corbie" autostart string reverted to "Kon"
- Already-downloaded models are clickable in the picker so the
Settings → About → Replay onboarding flow doesn't re-download
- Autostart "No thanks" now does isEnabled() → disable() to actually
remove the OS login item when replaying after a previous "Yes"
Tasks page:
- Bucket nav (All / Inbox / Today / Soon / Later) now a horizontal
pill row; was stacking because nav was block-level
- List sidebar sized to content via self-start max-h-full instead of
stretching to viewport when sparse
- Energy chip surfaces at opacity-60 when unset (was opacity-0,
hidden until hover) so the affordance is discoverable
- "Brain-Dead" energy label → "Zero" everywhere user-facing; enum
stays brain_dead to avoid a destructive DB migration
LLM status chip (llmStatus.svelte.ts + Dictation/Settings):
- Chip no longer auto-warms when the engine isn't loaded; it's hidden
unless ready / generating / loading / error
- refreshLlmStatus takes { force: true } so post-load reconcile clears
stale "warming"; ambient refreshes still preserve in-flight state
- markError exported; failed loads surface "AI error" with detail
rather than silently going to off
- check_llm_model is the source of truth (replaces the bool-only
get_llm_status path in the store)
Float popout window:
- Native decorations off — was stacking two titlebars + two close X's
on KWin, one of which silently failed
- ResizeHandles mounted outside the animate-float-enter wrapper so
fixed-position handles anchor to the viewport, not the transformed
root; secondary-windows capability gains
core:window:allow-start-resize-dragging for tasks-float
- GTK Utility WindowTypeHint applied pre-map (mirroring the preview
window) so KWin Wayland honours always-on-top reliably
- visible_on_all_workspaces(true) so the pinned tasks list follows
workspace switches
- togglePin does hide()+show()+focus() on re-pin to nudge the
compositor into re-evaluating window state
- Pop-out / Edit / Open viewer buttons hidden on Android via
isAndroid() — the multi-window Tauri commands stub out there
Build / Bugbot:
- src-tauri Cargo.toml: whisper feature now chains whisper-vulkan, so
the dev runner's --no-default-features --features whisper
invocation actually pulls Vulkan acceleration instead of silently
falling back to CPU-only
- jsconfig.json's inherited "types": ["node"] fixed by adding
@types/node; corresponding @ts-expect-error in vite.config.js
removed now that process is a known global
Verification: svelte-check + cargo check pass clean. Manual
device-side validation still pending for float resize and replay
autostart "No thanks" — those are the only remaining confidence items.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,13 +10,22 @@ name = "kon_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
# Default build includes the Whisper backend. Disabling this feature
|
||||
# also drops it from kon-transcription (see Cargo.toml in that crate)
|
||||
# so a --no-default-features workspace build does not pull whisper-rs-sys.
|
||||
# load_model_from_disk returns a runtime error for Engine::Whisper when
|
||||
# this feature is off; Parakeet continues to work.
|
||||
# Default build includes the Whisper backend with Vulkan GPU acceleration.
|
||||
#
|
||||
# Vulkan is chained into `whisper` (rather than being a separate top-level
|
||||
# default) because Tauri's dev runner invokes
|
||||
# `cargo run --no-default-features --features whisper`, which would
|
||||
# otherwise drop the vulkan feature and silently fall back to CPU-only
|
||||
# inference. The Bugbot finding is satisfied: desktop builds get GPU
|
||||
# acceleration by default, while the workspace-level kon-transcription
|
||||
# crate still keeps `whisper` and `whisper-vulkan` separable for
|
||||
# CPU-only-capable targets that build the crate directly.
|
||||
#
|
||||
# `whisper-vulkan` is kept as an aliased explicit-opt-in for callers who
|
||||
# want to spell out the dependency.
|
||||
default = ["whisper"]
|
||||
whisper = ["kon-transcription/whisper"]
|
||||
whisper = ["kon-transcription/whisper", "whisper-vulkan"]
|
||||
whisper-vulkan = ["kon-transcription/whisper-vulkan"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
@@ -41,7 +50,7 @@ kon-llm = { path = "../crates/llm" }
|
||||
# autostart plugins are desktop-only — gated below under
|
||||
# cfg(not(target_os = "android")). The dialog, opener, and notification
|
||||
# plugins all support Android natively so live in the unconditional list.
|
||||
tauri = { version = "2" }
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-opener = "2"
|
||||
tauri-plugin-dialog = "2"
|
||||
# Phase 6 nudges: OS-native notifications via the frontend-owned nudge
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"permissions": [
|
||||
"core:event:default",
|
||||
"core:window:allow-start-dragging",
|
||||
"core:window:allow-start-resize-dragging",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-hide",
|
||||
"core:window:allow-show",
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"main":{"identifier":"main","description":"Main window capability for user-initiated app control.","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-start-dragging","core:window:allow-set-always-on-top","core:window:allow-minimize","core:window:allow-toggle-maximize","core:window:allow-is-maximized","core:window:allow-close","core:window:allow-hide","core:window:allow-show","core:window:allow-set-focus","opener:default","dialog:default","global-shortcut:allow-register","global-shortcut:allow-unregister","autostart:allow-enable","autostart:allow-disable","autostart:allow-is-enabled","notification:allow-is-permission-granted","notification:allow-request-permission","notification:allow-notify"]},"secondary-windows":{"identifier":"secondary-windows","description":"Narrow capability for passive secondary windows.","local":true,"windows":["tasks-float","transcript-viewer","transcription-preview"],"permissions":["core:event:default","core:window:allow-start-dragging","core:window:allow-close","core:window:allow-hide","core:window:allow-show","core:window:allow-set-focus"]}}
|
||||
{"main":{"identifier":"main","description":"Main window capability for user-initiated app control.","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-start-dragging","core:window:allow-set-always-on-top","core:window:allow-minimize","core:window:allow-toggle-maximize","core:window:allow-is-maximized","core:window:allow-close","core:window:allow-hide","core:window:allow-show","core:window:allow-set-focus","opener:default","dialog:default","global-shortcut:allow-register","global-shortcut:allow-unregister","autostart:allow-enable","autostart:allow-disable","autostart:allow-is-enabled","notification:allow-is-permission-granted","notification:allow-request-permission","notification:allow-notify"]},"secondary-windows":{"identifier":"secondary-windows","description":"Narrow capability for passive secondary windows.","local":true,"windows":["tasks-float","transcript-viewer","transcription-preview"],"permissions":["core:event:default","core:window:allow-start-dragging","core:window:allow-start-resize-dragging","core:window:allow-close","core:window:allow-hide","core:window:allow-show","core:window:allow-set-focus"]}}
|
||||
@@ -58,14 +58,33 @@ pub async fn open_task_window(app: tauri::AppHandle) -> Result<(), String> {
|
||||
// custom frameless chrome drawn by the Titlebar component.
|
||||
let use_native_decorations = cfg!(target_os = "linux");
|
||||
|
||||
// Built hidden so the GTK utility type hint can be applied pre-map on
|
||||
// Linux — see the preview window for the same pattern. KWin/Mutter on
|
||||
// Wayland reliably keep utility-class windows above normal windows
|
||||
// (and respect runtime keep-above toggles for them); for normal
|
||||
// windows the same hint requests are flaky post-map.
|
||||
//
|
||||
// Decorations are off for this window: the float route renders its own
|
||||
// titlebar with the pin / close controls, and stacking native KDE
|
||||
// decorations on top of that produced two titlebars and two close X's
|
||||
// (the native one didn't even close the window because the in-page
|
||||
// chrome captured the click first). Custom drag is wired via
|
||||
// handleDragStart (startDragging) and ResizeHandles in the route.
|
||||
let _ = use_native_decorations; // keep the OS detection for future use
|
||||
let mut builder =
|
||||
WebviewWindowBuilder::new(&app, "tasks-float", WebviewUrl::App("/float".into()))
|
||||
.title("Kon Tasks")
|
||||
.inner_size(480.0, 520.0)
|
||||
.min_inner_size(360.0, 480.0)
|
||||
.always_on_top(true)
|
||||
.decorations(use_native_decorations)
|
||||
.resizable(true);
|
||||
// Pin across virtual desktops so users who flip workspaces
|
||||
// mid-task don't lose the floating tasks list. Combined with
|
||||
// always_on_top + utility hint, this matches what users
|
||||
// expect from a "pin" button on KDE Plasma and GNOME.
|
||||
.visible_on_all_workspaces(true)
|
||||
.decorations(false)
|
||||
.resizable(true)
|
||||
.visible(false);
|
||||
|
||||
// Inject preferences before Svelte mounts
|
||||
if let Some(script) = app.try_state::<PreferencesScript>() {
|
||||
@@ -74,8 +93,23 @@ pub async fn open_task_window(app: tauri::AppHandle) -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
|
||||
builder.build().map_err(|e| e.to_string())?;
|
||||
let window = builder.build().map_err(|e| e.to_string())?;
|
||||
|
||||
// Apply the GTK Utility type hint before the window maps. On X11 and
|
||||
// XWayland the hint is honoured immediately; on native Wayland-only
|
||||
// compositors GTK uses the closest semantic equivalent. Must happen
|
||||
// before show() per GTK3 docs.
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use gdk::WindowTypeHint;
|
||||
use gtk::prelude::GtkWindowExt;
|
||||
if let Ok(gtk_window) = window.gtk_window() {
|
||||
gtk_window.set_type_hint(WindowTypeHint::Utility);
|
||||
}
|
||||
}
|
||||
|
||||
window.show().map_err(|e| e.to_string())?;
|
||||
window.set_focus().map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user