feat: tooltips, two-column timer, font selector, tray behavior, icons, readme
- Custom tooltip directive (WCAG AAA) on every button in the app - Two-column timer layout with sticky hero and recent entries sidebar - Timer font selector with 16 monospace Google Fonts and live preview - UI font selector with 15+ Google Fonts - Close-to-tray and minimize-to-tray settings - New app icons (no-glow variants), platform icon set - Mini timer pop-out window - Favorites strip with drag-reorder and inline actions - Comprehensive README with feature documentation - Remove tracked files that belong in gitignore
This commit is contained in:
@@ -3,11 +3,13 @@ import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useTimerStore } from '../stores/timer'
|
||||
import { useProjectsStore } from '../stores/projects'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
|
||||
const appWindow = getCurrentWindow()
|
||||
const isMaximized = ref(false)
|
||||
const timerStore = useTimerStore()
|
||||
const projectsStore = useProjectsStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
onMounted(async () => {
|
||||
isMaximized.value = await appWindow.isMaximized()
|
||||
@@ -20,7 +22,11 @@ function getProjectName(projectId: number | null): string {
|
||||
}
|
||||
|
||||
async function minimize() {
|
||||
await appWindow.minimize()
|
||||
if (settingsStore.settings.minimize_to_tray === 'true') {
|
||||
await appWindow.hide()
|
||||
} else {
|
||||
await appWindow.minimize()
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleMaximize() {
|
||||
@@ -43,7 +49,7 @@ async function handleDoubleClick() {
|
||||
|
||||
<template>
|
||||
<header
|
||||
class="h-10 flex items-center justify-between px-4 bg-bg-surface border-b border-border-subtle select-none shrink-0"
|
||||
class="h-11 flex items-center justify-between px-4 bg-bg-surface border-b border-border-subtle select-none shrink-0"
|
||||
@mousedown.left="startDrag"
|
||||
@dblclick="handleDoubleClick"
|
||||
>
|
||||
@@ -58,11 +64,18 @@ async function handleDoubleClick() {
|
||||
|
||||
<!-- Center: Running timer status -->
|
||||
<div
|
||||
role="status"
|
||||
aria-live="off"
|
||||
class="flex items-center gap-3 transition-opacity duration-150"
|
||||
:class="timerStore.isRunning ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
||||
:class="timerStore.isRunning || timerStore.isPaused ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
||||
>
|
||||
<!-- Pulsing green dot -->
|
||||
<div class="w-2 h-2 rounded-full bg-status-running animate-pulse-dot" />
|
||||
<!-- Status dot -->
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="w-2 h-2 rounded-full"
|
||||
:class="timerStore.isRunning ? 'bg-status-running animate-pulse-dot' : 'bg-status-warning'"
|
||||
/>
|
||||
<span class="sr-only">{{ timerStore.isRunning ? 'Timer running' : timerStore.isPaused ? 'Timer paused' : 'Timer stopped' }}</span>
|
||||
|
||||
<!-- Project name -->
|
||||
<span class="text-[0.6875rem] text-text-secondary">
|
||||
@@ -70,18 +83,46 @@ async function handleDoubleClick() {
|
||||
</span>
|
||||
|
||||
<!-- Elapsed time -->
|
||||
<span class="text-[0.75rem] font-mono text-text-primary tracking-wider">
|
||||
<span class="text-[0.75rem] font-[family-name:var(--font-timer)] text-text-primary tracking-wider">
|
||||
{{ timerStore.formattedTime }}
|
||||
</span>
|
||||
|
||||
<!-- Pause / Resume button -->
|
||||
<button
|
||||
v-if="timerStore.isRunning"
|
||||
v-tooltip.bottom="'Pause timer'"
|
||||
@click="timerStore.pauseManual()"
|
||||
@mousedown.stop
|
||||
class="w-11 h-11 flex items-center justify-center text-text-tertiary hover:text-status-warning transition-colors duration-150"
|
||||
aria-label="Pause timer"
|
||||
>
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-3 h-3">
|
||||
<rect x="3" y="2" width="3.5" height="12" rx="1" />
|
||||
<rect x="9.5" y="2" width="3.5" height="12" rx="1" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
v-else-if="timerStore.timerState === 'PAUSED_MANUAL'"
|
||||
v-tooltip.bottom="'Resume timer'"
|
||||
@click="timerStore.resumeFromPause()"
|
||||
@mousedown.stop
|
||||
class="w-11 h-11 flex items-center justify-center text-text-tertiary hover:text-accent-text transition-colors duration-150"
|
||||
aria-label="Resume timer"
|
||||
>
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-3 h-3">
|
||||
<path d="M4 2.5a.5.5 0 01.77-.42l9 5.5a.5.5 0 010 .84l-9 5.5A.5.5 0 014 13.5V2.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Stop button -->
|
||||
<button
|
||||
v-tooltip.bottom="'Stop timer'"
|
||||
@click="timerStore.stop()"
|
||||
@mousedown.stop
|
||||
class="w-5 h-5 flex items-center justify-center text-text-tertiary hover:text-status-error transition-colors duration-150"
|
||||
title="Stop timer"
|
||||
class="w-11 h-11 flex items-center justify-center text-text-tertiary hover:text-status-error transition-colors duration-150"
|
||||
aria-label="Stop timer"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-3 h-3">
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-3 h-3">
|
||||
<rect x="3" y="3" width="10" height="10" rx="1" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -90,35 +131,38 @@ async function handleDoubleClick() {
|
||||
<!-- Right: Window controls -->
|
||||
<div class="flex items-center" @mousedown.stop>
|
||||
<button
|
||||
v-tooltip.bottom="'Minimize'"
|
||||
@click="minimize"
|
||||
class="w-10 h-10 flex items-center justify-center text-text-tertiary hover:text-text-secondary transition-colors duration-150"
|
||||
title="Minimize"
|
||||
class="w-11 h-11 flex items-center justify-center text-text-tertiary hover:text-text-secondary transition-colors duration-150"
|
||||
aria-label="Minimize"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" class="w-3.5 h-3.5">
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" class="w-3.5 h-3.5">
|
||||
<path d="M5 12h14" stroke="currentColor" stroke-width="2" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-tooltip.bottom="isMaximized ? 'Restore' : 'Maximize'"
|
||||
@click="toggleMaximize"
|
||||
class="w-10 h-10 flex items-center justify-center text-text-tertiary hover:text-text-secondary transition-colors duration-150"
|
||||
:title="isMaximized ? 'Restore' : 'Maximize'"
|
||||
class="w-11 h-11 flex items-center justify-center text-text-tertiary hover:text-text-secondary transition-colors duration-150"
|
||||
:aria-label="isMaximized ? 'Restore' : 'Maximize'"
|
||||
>
|
||||
<svg v-if="!isMaximized" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-3 h-3">
|
||||
<svg v-if="!isMaximized" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-3 h-3">
|
||||
<rect x="4" y="4" width="16" height="16" rx="1" />
|
||||
</svg>
|
||||
<svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-3 h-3">
|
||||
<svg v-else aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-3 h-3">
|
||||
<rect x="3" y="7" width="14" height="14" rx="1" />
|
||||
<path d="M7 7V5a1 1 0 011-1h12a1 1 0 011 1v12a1 1 0 01-1 1h-2" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-tooltip.bottom="'Close'"
|
||||
@click="close"
|
||||
class="w-10 h-10 flex items-center justify-center text-text-tertiary hover:text-status-error transition-colors duration-150"
|
||||
title="Close"
|
||||
class="w-11 h-11 flex items-center justify-center text-text-tertiary hover:text-status-error transition-colors duration-150"
|
||||
aria-label="Close"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-3.5 h-3.5">
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-3.5 h-3.5">
|
||||
<path fill-rule="evenodd" d="M5.47 5.47a.75.75 0 011.06 0L12 10.94l5.47-5.47a.75.75 0 111.06 1.06L13.06 12l5.47 5.47a.75.75 0 11-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 01-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 010-1.06z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user