Files
Lumotia/src/routes/float/+layout@.svelte
Jake f25f8db818 feat(focus-timer): integrate with float window + add pop-out button
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 <FocusTimer /> 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.
2026-04-24 12:06:37 +01:00

100 lines
3.0 KiB
Svelte

<script lang="ts">
// @ts-nocheck
import "../../app.css";
import { onMount, onDestroy } from "svelte";
import { listen } from "@tauri-apps/api/event";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { settings } from "$lib/stores/page.svelte.js";
import {
getPreferences,
updatePreferences,
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";
let { children } = $props();
let glowing = $state(false);
let unlistenFocus = null;
let unlistenPrefs = null;
let useCustomChrome = $state(false);
const prefs = getPreferences();
// Theme — sync legacy settings to preferences store (same as main layout)
$effect(() => {
const legacyTheme = settings.theme;
const mapped = legacyTheme === "Light" ? "light" : legacyTheme === "Dark" ? "dark" : "system";
if (prefs.theme !== mapped) {
updatePreferences({ theme: mapped });
}
});
// Listen for settings changes from main window
if (typeof window !== "undefined") {
window.addEventListener("storage", (e) => {
if (e.key === "kon_settings" && e.newValue) {
try {
Object.assign(settings, JSON.parse(e.newValue));
} catch {}
}
});
}
onMount(async () => {
loadOsInfo()
.then(() => { useCustomChrome = !isLinux(); })
.catch(() => {});
try {
unlistenFocus = await listen("task-window-focus", () => {
glowing = true;
setTimeout(() => { glowing = false; }, 700);
// Auto-focus quick-add input
const input = document.querySelector("[data-quick-add]");
if (input) input.focus();
});
} catch {}
try {
let ownLabel = null;
try { ownLabel = getCurrentWindow().label; } catch {}
unlistenPrefs = await listen(PREFERENCES_CHANGED_EVENT, (event) => {
const payload = event?.payload;
if (!payload || payload.source === ownLabel) return;
applyExternalPreferences(payload.prefs);
});
} catch {}
});
onDestroy(() => {
if (unlistenFocus) unlistenFocus();
if (unlistenPrefs) unlistenPrefs();
});
// Escape to close
function handleKeydown(e) {
if (e.key === "Escape") {
getCurrentWindow().close();
}
}
</script>
<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>
<!-- 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
this window independent of the Tasks content below. -->
<FocusTimer />