From f25f8db818b8430d7ca3c94943239f1f5f7fd6fe Mon Sep 17 00:00:00 2001 From: Jake Date: Fri, 24 Apr 2026 12:06:37 +0100 Subject: [PATCH] feat(focus-timer): integrate with float window + add pop-out button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jake's feedback on Phase 1: make the timer pinnable / always-on-top, combined with the existing Now-list pop-out. Two changes: 1. Mount in src/routes/float/+layout@.svelte so the running countdown stays visible in the always-on-top float window alongside the WIP task list. No content change to the float page itself — the timer is a global overlay. 2. Add a pop-out icon to the main-window focus timer that opens the existing /float route via window.open. One click → timer + Now list pinned on top without touching main window focus. Hidden inside the float window itself (detected via URL) so you cannot recursively pop out. Result matches the Todo float-out UX the user already knows: click ExternalLink, you get a small always-on-top window with tasks + a live countdown ring in the top-right. --- src/lib/components/FocusTimer.svelte | 30 +++++++++++++++++++++++++++- src/routes/float/+layout@.svelte | 7 +++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/lib/components/FocusTimer.svelte b/src/lib/components/FocusTimer.svelte index 4fcdc0f..1e8b777 100644 --- a/src/lib/components/FocusTimer.svelte +++ b/src/lib/components/FocusTimer.svelte @@ -15,8 +15,26 @@ // follows the sensory-zone theme switcher in Settings. import { onMount, onDestroy } from "svelte"; - import { X, Plus } from "lucide-svelte"; + import { X, Plus, ExternalLink } from "lucide-svelte"; import { focusTimer } from "$lib/stores/focusTimer.svelte.js"; + import { hasTauriRuntime } from "$lib/utils/runtime.js"; + + // Hide the "pop out" button inside the float window itself — opening + // a second float from a float would be silly and would re-mount the + // same component. Detect via URL rather than a prop so we do not + // have to thread context through every mount site. + let isSecondaryWindow = $state(false); + if (typeof window !== "undefined") { + isSecondaryWindow = window.location.pathname.startsWith("/float") + || window.location.pathname.startsWith("/viewer"); + } + + function handlePopOut() { + // Mirror the button in TasksPage.svelte — opens the always-on-top + // Now list + pinned timer in one floating window. + if (!hasTauriRuntime()) return; + window.open("/float", "_blank", "width=380,height=520"); + } const RING_SIZE = 64; const RING_STROKE = 5; @@ -148,6 +166,16 @@ >