agent: dogfood polish 2026/04/19 — Linux native chrome + History redesign + mic picker cleanup
Second dogfood sprint. Headline fix: Linux now uses native KWin/Mutter decorations instead of fragile frameless `startResizeDragging`, which collapsed diagonal corner resize to a single axis and made drag feel laggy. macOS / Windows keep custom chrome via `useCustomChrome` gate. Other changes: - Cross-window preferences sync via `kon:preferences-changed` Tauri event — theme and font changes propagate live to float/viewer. - Hotkey recorder rewritten to use capture-phase document listener gated by $effect; button focus was unreliable in webkit2gtk. - History page redesigned for cognitive-load hygiene: title-first compact row, inline title input, Edit popout opening /viewer in edit mode, clipboard export as .md with YAML frontmatter, manual tag chips + + Add tag input, header tag filter (cap 7), global Starred filter, `tag:xyz` search syntax. - `deriveAutoTags` kept as empty hook for post-Task-7 LLM topic tags; research found all previous auto-tag chips redundant with row UI. - Viewer window adds edit mode with debounced-save textarea; native title renamed to "Kon - Transcription Editor". - Window minimums updated per GNOME HIG + WCAG reflow research: main 960x600, float 360x480, editor 560x520. - Microphone picker filters raw ALSA strings (hw:, plughw:, front:, sysdefault:, null) and dedupes by CARD=X. New `description` field on DeviceInfo reads /proc/asound/cards so Blue Yeti shows as "Blue Microphones" instead of the short "Microphones" card name. - GPU reporting fixed: get_runtime_capabilities now returns accelerators=[cpu,vulkan] and whisper.supports_gpu=true, matching the transcribe-rs whisper-vulkan feature linked unconditionally. - ResizeHandles kept for macOS/Windows frameless: 12px edges, 20px corners via CSS vars, pointerdown + setPointerCapture, corners above edges in z-order, rendered as sibling (not child) of the animated layout root so `position: fixed` is viewport-relative. - Dueling drag-region handlers removed — `data-tauri-drag-region` and manual `startDragging()` were stacked on the same elements; kept the manual handler which has the button/input early-return logic. See HANDOVER.md for the full session log and deferred items. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
141
src/lib/components/ResizeHandles.svelte
Normal file
141
src/lib/components/ResizeHandles.svelte
Normal file
@@ -0,0 +1,141 @@
|
||||
<script>
|
||||
// Invisible resize-handle overlays around each Kon window edge.
|
||||
// Needed because Kon runs with `decorations: false`, so the WM
|
||||
// provides no resize affordance on KDE/GNOME Wayland.
|
||||
//
|
||||
// Architecture: eight fixed-position divs are the click target for
|
||||
// resize. Because the divs themselves are the event target (not any
|
||||
// ancestor), Tauri's `data-tauri-drag-region` delegated handler walks
|
||||
// the div's ancestors, finds no drag-region, and does not start a
|
||||
// window move. This gives us a clean separation: resize wins on the
|
||||
// edges, drag wins on the titlebar.
|
||||
//
|
||||
// Hit zone sizes come from CSS custom properties so every Kon window
|
||||
// across the app stays identical. See `--kon-resize-edge` and
|
||||
// `--kon-resize-corner` in app.css.
|
||||
//
|
||||
// Placement requirement: instances of this component MUST be mounted
|
||||
// as a sibling of the animated/transformed layout root (not a child),
|
||||
// or `position: fixed` becomes relative to that transformed ancestor
|
||||
// instead of the viewport.
|
||||
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { hasTauriRuntime } from "$lib/utils/runtime.js";
|
||||
|
||||
const enabled = hasTauriRuntime();
|
||||
|
||||
async function startResize(e, direction) {
|
||||
if (!enabled) return;
|
||||
if (e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Lock the pointer to this handle BEFORE asking Wayland to begin a
|
||||
// resize. Without capture, KWin's xdg_toplevel.resize grab arrives
|
||||
// between mousedown synthesis and pointer movement, often losing the
|
||||
// diagonal component and collapsing a corner drag to a single axis.
|
||||
// pointerdown + setPointerCapture keeps the direction pinned.
|
||||
try {
|
||||
e.currentTarget?.setPointerCapture?.(e.pointerId);
|
||||
} catch {}
|
||||
try {
|
||||
await getCurrentWindow().startResizeDragging(direction);
|
||||
} catch {}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if enabled}
|
||||
<!-- Edge strips. Inset by corner size so edge and corner zones don't overlap. -->
|
||||
<div class="kon-rh kon-rh-n" onpointerdown={(e) => startResize(e,"North")}></div>
|
||||
<div class="kon-rh kon-rh-s" onpointerdown={(e) => startResize(e,"South")}></div>
|
||||
<div class="kon-rh kon-rh-w" onpointerdown={(e) => startResize(e,"West")}></div>
|
||||
<div class="kon-rh kon-rh-e" onpointerdown={(e) => startResize(e,"East")}></div>
|
||||
|
||||
<!-- Corner hit zones, larger for easier diagonal targeting. -->
|
||||
<div class="kon-rh kon-rh-nw" onpointerdown={(e) => startResize(e,"NorthWest")}></div>
|
||||
<div class="kon-rh kon-rh-ne" onpointerdown={(e) => startResize(e,"NorthEast")}></div>
|
||||
<div class="kon-rh kon-rh-sw" onpointerdown={(e) => startResize(e,"SouthWest")}></div>
|
||||
<div class="kon-rh kon-rh-se" onpointerdown={(e) => startResize(e,"SouthEast")}></div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.kon-rh {
|
||||
position: fixed;
|
||||
z-index: 2147483646;
|
||||
background: transparent;
|
||||
pointer-events: auto;
|
||||
/* Prevent the browser from consuming the pointerdown for its own
|
||||
defaults (text selection, drag scroll) before we capture it. */
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
/* Corners stack above edges so, at the 1-2 px of overlap on some
|
||||
compositors, a corner click is never mistaken for an edge click. */
|
||||
.kon-rh-nw,
|
||||
.kon-rh-ne,
|
||||
.kon-rh-sw,
|
||||
.kon-rh-se {
|
||||
z-index: 2147483647;
|
||||
}
|
||||
|
||||
/* Edges */
|
||||
.kon-rh-n {
|
||||
top: 0;
|
||||
left: var(--kon-resize-corner);
|
||||
right: var(--kon-resize-corner);
|
||||
height: var(--kon-resize-edge);
|
||||
cursor: n-resize;
|
||||
}
|
||||
.kon-rh-s {
|
||||
bottom: 0;
|
||||
left: var(--kon-resize-corner);
|
||||
right: var(--kon-resize-corner);
|
||||
height: var(--kon-resize-edge);
|
||||
cursor: s-resize;
|
||||
}
|
||||
.kon-rh-w {
|
||||
top: var(--kon-resize-corner);
|
||||
bottom: var(--kon-resize-corner);
|
||||
left: 0;
|
||||
width: var(--kon-resize-edge);
|
||||
cursor: w-resize;
|
||||
}
|
||||
.kon-rh-e {
|
||||
top: var(--kon-resize-corner);
|
||||
bottom: var(--kon-resize-corner);
|
||||
right: 0;
|
||||
width: var(--kon-resize-edge);
|
||||
cursor: e-resize;
|
||||
}
|
||||
|
||||
/* Corners */
|
||||
.kon-rh-nw {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: var(--kon-resize-corner);
|
||||
height: var(--kon-resize-corner);
|
||||
cursor: nw-resize;
|
||||
}
|
||||
.kon-rh-ne {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: var(--kon-resize-corner);
|
||||
height: var(--kon-resize-corner);
|
||||
cursor: ne-resize;
|
||||
}
|
||||
.kon-rh-sw {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: var(--kon-resize-corner);
|
||||
height: var(--kon-resize-corner);
|
||||
cursor: sw-resize;
|
||||
}
|
||||
.kon-rh-se {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: var(--kon-resize-corner);
|
||||
height: var(--kon-resize-corner);
|
||||
cursor: se-resize;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user