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.
100 lines
3.0 KiB
Svelte
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 />
|