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:
@@ -334,7 +334,7 @@
|
||||
// backend is in right now. The chip also reacts to the $effect
|
||||
// on settings.aiTier below and to explicit mark-generating
|
||||
// calls from DictationPage around cleanup_transcript_text_cmd.
|
||||
refreshLlmStatus(settings.aiTier).catch(() => {});
|
||||
refreshLlmStatus(settings.aiTier, settings.llmModelId).catch(() => {});
|
||||
|
||||
if (settings.prewarmModelOnStartup) {
|
||||
invoke("prewarm_default_model_cmd").catch(() => {});
|
||||
|
||||
@@ -11,15 +11,13 @@
|
||||
applyExternalPreferences,
|
||||
PREFERENCES_CHANGED_EVENT,
|
||||
} from "$lib/stores/preferences.svelte.js";
|
||||
import Titlebar from "$lib/components/Titlebar.svelte";
|
||||
import FocusTimer from "$lib/components/FocusTimer.svelte";
|
||||
import { loadOsInfo, isLinux } from "$lib/utils/osInfo.js";
|
||||
import ResizeHandles from "$lib/components/ResizeHandles.svelte";
|
||||
|
||||
let { children } = $props();
|
||||
let glowing = $state(false);
|
||||
let unlistenFocus = null;
|
||||
let unlistenPrefs = null;
|
||||
let useCustomChrome = $state(false);
|
||||
|
||||
const prefs = getPreferences();
|
||||
|
||||
@@ -44,9 +42,10 @@
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
loadOsInfo()
|
||||
.then(() => { useCustomChrome = !isLinux(); })
|
||||
.catch(() => {});
|
||||
// The float route renders its own pin / close header inside +page.svelte,
|
||||
// so the layout no longer needs to swap in a separate Titlebar component
|
||||
// based on platform. Keeping the loadOsInfo call out entirely simplifies
|
||||
// the chrome — one titlebar source of truth, one place for the pin.
|
||||
try {
|
||||
unlistenFocus = await listen("task-window-focus", () => {
|
||||
glowing = true;
|
||||
@@ -84,14 +83,15 @@
|
||||
<svelte:window onkeydown={handleKeydown} />
|
||||
|
||||
<div class="h-screen w-screen overflow-hidden grain border border-border shadow-xl animate-float-enter flex flex-col {glowing ? 'animate-glow-pulse' : ''}">
|
||||
{#if useCustomChrome}
|
||||
<Titlebar compact />
|
||||
{/if}
|
||||
<div class="flex-1 min-h-0 overflow-hidden">
|
||||
{@render children()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mounted outside the animated/transformed root so fixed-position
|
||||
resize hit zones anchor to the webview viewport. -->
|
||||
<ResizeHandles />
|
||||
|
||||
<!-- Focus timer also visible in the always-on-top float window so a
|
||||
running countdown stays with the Now list. The component is a
|
||||
global overlay (position: fixed) so it pins to the top-right of
|
||||
|
||||
@@ -70,7 +70,21 @@
|
||||
|
||||
async function togglePin() {
|
||||
pinned = !pinned;
|
||||
await getCurrentWindow().setAlwaysOnTop(pinned);
|
||||
const w = getCurrentWindow();
|
||||
await w.setAlwaysOnTop(pinned);
|
||||
if (pinned) {
|
||||
// KWin/Mutter on Wayland routinely drop _NET_WM_STATE_ABOVE when it
|
||||
// arrives post-map for a normal window. Combined with the GTK Utility
|
||||
// hint applied at creation (open_task_window), an unmap/map cycle is
|
||||
// the most reliable way to force the compositor to re-apply state
|
||||
// changes. Hidden for one event-loop tick — visually a flicker, but
|
||||
// the pin actually sticks afterwards.
|
||||
try {
|
||||
await w.hide();
|
||||
await w.show();
|
||||
await w.setFocus();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
function closeWindow() {
|
||||
|
||||
Reference in New Issue
Block a user