feat(focus-timer): Phase 1 — visual countdown ring + just-start timer
Closes the Core MVP gap in docs/brief/feature-set.md ("visual time
representation") and wires the dangling kon:start-timer emit that
MicroSteps.svelte has been firing into the void since the stub was
written. Implements phase 1 of the 2026-04-23 feature-complete
roadmap.
New:
- src/lib/stores/focusTimer.svelte.ts — singleton timer store with
localStorage persistence so a timer started in Dictation survives
page nav, window close, and reopen. Gentle WebAudio chime at
completion (no bundled asset). 250 ms tick. Completion flash for
3 s before auto-clear.
- src/lib/components/FocusTimer.svelte — floating top-right overlay
with SVG progress ring (shrinking colour: accent -> warning in
the final 15% -> success on completion). Cancel + "+1 min" on
hover. Renders nothing when idle.
Wired in +layout.svelte next to ToastViewport.
Two triggers now in the app:
- MicroSteps row 2-min button (pre-existing emit, previously no
listener)
- WipTaskList row 5-min button (new; the brief's "just-start"
from the Now column)
Respects prefers-reduced-motion via the existing
[data-reduce-motion] attribute.
Out of scope, carried to later phases: rhythmic voice anchoring
(Phase 6), custom-duration picker, multi-timer UI, native OS
notification (deferred to Phase 6 with the full nudge pipeline).
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { tasks, addTask, completeTask, uncompleteTask, deleteTask } from '$lib/stores/page.svelte.js';
|
||||
import MicroSteps from '$lib/components/MicroSteps.svelte';
|
||||
import { ChevronDown, ChevronRight } from 'lucide-svelte';
|
||||
import { ChevronDown, ChevronRight, Timer } from 'lucide-svelte';
|
||||
|
||||
function startFocusTimer(task: { id: string; text: string }) {
|
||||
window.dispatchEvent(new CustomEvent('kon:start-timer', {
|
||||
detail: { taskId: task.id, seconds: 300, label: task.text }
|
||||
}));
|
||||
}
|
||||
|
||||
let { wipLimit = 3 } = $props();
|
||||
|
||||
@@ -68,6 +74,16 @@
|
||||
aria-label="Complete task"
|
||||
></button>
|
||||
<span class="text-[13px] text-text flex-1 min-w-0 truncate">{task.text}</span>
|
||||
<!-- 5-min focus timer — the "just-start" button from the brief -->
|
||||
<button
|
||||
class="opacity-0 group-hover:opacity-100 text-text-tertiary hover:text-accent"
|
||||
onclick={() => startFocusTimer(task)}
|
||||
aria-label="Start 5-minute focus timer for this task"
|
||||
title="Start 5-minute focus timer"
|
||||
style="transition: opacity var(--duration-ui)"
|
||||
>
|
||||
<Timer size={12} aria-hidden="true" />
|
||||
</button>
|
||||
<!-- Expand/collapse micro-steps toggle -->
|
||||
<button
|
||||
class="opacity-0 group-hover:opacity-100 text-text-tertiary hover:text-accent"
|
||||
|
||||
Reference in New Issue
Block a user