Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 5x 27x 27x 27x 27x 5x 924x 924x 15x 15x 15x 7x 5x 7x 2x 5x 5x 5x 5x 3x 5x 27x 38x 38x 5x 13x 13x 13x 11x 9x 5x 2x 5x 8x 4x 4x 4x 2x 2x 2x 5x 4x 2x 2x 1x 1x 5x 1x 1x 1x 1x 1x 1x 1x 1x | import type { TaskBoardItem, TaskBoardSnapshot, ThemePreference, TimerSnapshot } from '@shared/types'
export type TaskViewTab = 'all' | 'active' | 'completed'
export interface TaskRowModel {
id: string
title: string
isCompleted: boolean
statusLabel: '进行中' | '已完成'
completedPomodoros: number
focusMinutes: number
completedAt: string | null
}
export interface TimerPomodoroDisplay {
ordinal: number
completed: number
}
export const formatTimerClock = (milliseconds: number): string => {
const totalSeconds = Math.max(0, Math.ceil(milliseconds / 1000))
const minutes = Math.floor(totalSeconds / 60)
const seconds = totalSeconds % 60
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
}
export const formatDurationLabel = (minutes: number): string => {
const safeMinutes = Number.isFinite(minutes) ? Math.max(0, Math.floor(minutes)) : 0
if (safeMinutes <= 60) return `${safeMinutes}m`
const hours = Math.floor(safeMinutes / 60)
const rest = safeMinutes % 60
return `${hours}h ${rest}m`
}
export const clampProgress = (value: number): number => Math.min(1, Math.max(0, value))
export const getSmoothedTimerProgress = (
snapshot: Pick<TimerSnapshot, 'status' | 'startedAt' | 'targetEndAt' | 'durationMs' | 'progress'>,
now: number
): number => {
if (snapshot.status !== 'running' || snapshot.targetEndAt === null || snapshot.durationMs <= 0) {
return clampProgress(snapshot.progress)
}
const remainingMs = Math.max(0, snapshot.targetEndAt - now)
const elapsedMs = Math.min(snapshot.durationMs, Math.max(0, snapshot.durationMs - remainingMs))
return clampProgress(elapsedMs / snapshot.durationMs)
}
export const resolveEffectiveTheme = (
preference: ThemePreference,
systemTheme: 'light' | 'dark'
): 'light' | 'dark' => (preference === 'system' ? systemTheme : preference)
export const buildTaskRows = (items: TaskBoardItem[]): TaskRowModel[] =>
items.map((item) => {
const isCompleted = Boolean(item.completedAt)
return {
id: item.id,
title: item.title,
isCompleted,
statusLabel: isCompleted ? '已完成' : '进行中',
completedPomodoros: item.completedPomodoros,
focusMinutes: item.focusMinutes,
completedAt: item.completedAt
}
})
export const getTaskRowsForTab = (board: TaskBoardSnapshot, tab: TaskViewTab): TaskRowModel[] => {
const activeRows = buildTaskRows(board.activeItems)
const completedRows = buildTaskRows(board.completedItems)
if (tab === 'active') return activeRows
if (tab === 'completed') return completedRows
return [...activeRows, ...completedRows]
}
export const buildTaskTitleById = (board: TaskBoardSnapshot): Record<string, string> =>
Object.fromEntries([...board.activeItems, ...board.completedItems].map((item) => [item.id, item.title]))
export const resolveTimerPomodoroDisplay = (
snapshot: Pick<TimerSnapshot, 'phase' | 'taskId' | 'lastFocusTaskId' | 'unboundFocusCount'>,
board: TaskBoardSnapshot
): TimerPomodoroDisplay => {
const taskById = new Map([...board.activeItems, ...board.completedItems].map((item) => [item.id, item]))
const displayTaskId = snapshot.phase === 'focus' ? snapshot.taskId : snapshot.lastFocusTaskId
const displayTask = displayTaskId ? taskById.get(displayTaskId) : undefined
if (displayTask) {
return {
completed: Math.max(0, displayTask.completedPomodoros),
ordinal: Math.max(1, displayTask.completedPomodoros + 1)
}
}
const completed = Math.max(0, snapshot.unboundFocusCount)
return {
completed,
ordinal: Math.max(1, completed + 1)
}
}
export const resolveCurrentTaskTitle = (
snapshot: Pick<TimerSnapshot, 'taskId' | 'phase' | 'status'>,
taskTitleById: Record<string, string>
): string => {
if (snapshot.taskId) {
return taskTitleById[snapshot.taskId] ?? '已删除任务'
}
if (snapshot.phase === 'focus' && snapshot.status === 'idle') {
return '当前尚未开始专注'
}
return '当前阶段未绑定任务'
}
export const reorderTaskIds = (ids: string[], sourceId: string, targetId: string): string[] => {
Iif (sourceId === targetId) return [...ids]
const sourceIndex = ids.indexOf(sourceId)
const targetIndex = ids.indexOf(targetId)
Iif (sourceIndex === -1 || targetIndex === -1) return [...ids]
const nextIds = [...ids]
const [movedId] = nextIds.splice(sourceIndex, 1)
nextIds.splice(targetIndex, 0, movedId)
return nextIds
}
|