feat: 階段 6 工具四待辦事項(全域待辦、類型自訂、到點提醒、確認後執行並回報)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// 工具四(待辦事項)的純邏輯:解析、渲染、到點判斷。讀寫與指令在 register.tsx。
|
||||
|
||||
export const TODO_COMMAND = 'anima-todo'
|
||||
export const TODO_KEY = 'todos'
|
||||
export const TYPES_KEY = 'todoTypes'
|
||||
export const DEFAULT_TYPES = ['提醒', '開發', '研究', '維護']
|
||||
|
||||
export type Status = 'pending' | 'reminded' | 'done'
|
||||
export type Todo = { id: number; type: string; at: number; task: string; project: string; status: Status; result?: string }
|
||||
|
||||
export const USAGE =
|
||||
'用法:\n' +
|
||||
'/jsc-anima:anima-todo add <類型> <YYYY-MM-DD> <HH:MM> <任務> 建立\n' +
|
||||
'/jsc-anima:anima-todo update <編號> <類型> <YYYY-MM-DD> <HH:MM> <任務> 更新\n' +
|
||||
'/jsc-anima:anima-todo list 列出全部\n' +
|
||||
'/jsc-anima:anima-todo type <名稱> 新增自訂類型\n' +
|
||||
'/jsc-anima:anima-todo run <編號> 確認後執行\n' +
|
||||
'/jsc-anima:anima-todo done <編號> 標記完成(不執行)'
|
||||
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
|
||||
export const fmtTime = (ms: number): string => {
|
||||
const d = new Date(ms)
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||
}
|
||||
|
||||
// 排程時間以本機時區解讀。
|
||||
export const parseTime = (date: string | undefined, time: string | undefined): number | undefined => {
|
||||
const d = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date ?? '')
|
||||
const t = /^(\d{1,2}):(\d{2})$/.exec(time ?? '')
|
||||
if (!d || !t) return undefined
|
||||
const [y, mo, da, h, mi] = [d[1], d[2], d[3], t[1], t[2]].map(Number) as [number, number, number, number, number]
|
||||
const out = new Date(y, mo - 1, da, h, mi)
|
||||
if (out.getMonth() !== mo - 1 || out.getDate() !== da || h > 23 || mi > 59) return undefined
|
||||
return out.getTime()
|
||||
}
|
||||
|
||||
export type Parsed =
|
||||
| { op: 'add'; type: string; at: number; task: string }
|
||||
| { op: 'update'; id: number; type: string; at: number; task: string }
|
||||
| { op: 'list' }
|
||||
| { op: 'type'; name: string }
|
||||
| { op: 'run' | 'done'; id: number }
|
||||
|
||||
const spec = (w: string[]): { type: string; at: number; task: string } | undefined => {
|
||||
const [type, date, time, ...rest] = w
|
||||
const at = parseTime(date, time)
|
||||
const task = rest.join(' ').trim()
|
||||
return type && at !== undefined && task ? { type, at, task } : undefined
|
||||
}
|
||||
|
||||
export const parseArgs = (args: string): Parsed | undefined => {
|
||||
const w = args.trim().split(/\s+/).filter(Boolean)
|
||||
const [op, ...rest] = w
|
||||
const id = Number(rest[0])
|
||||
if (op === 'list' && rest.length === 0) return { op }
|
||||
if (op === 'type' && rest.length === 1) return { op, name: rest[0]! }
|
||||
if ((op === 'run' || op === 'done') && rest.length === 1 && Number.isInteger(id) && id > 0) return { op, id }
|
||||
if (op === 'add') {
|
||||
const s = spec(rest)
|
||||
return s ? { op, ...s } : undefined
|
||||
}
|
||||
if (op === 'update' && Number.isInteger(id) && id > 0) {
|
||||
const s = spec(rest.slice(1))
|
||||
return s ? { op, id, ...s } : undefined
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const dueNow = (todos: Todo[], now: number): Todo[] => todos.filter(t => t.status === 'pending' && t.at <= now)
|
||||
|
||||
export const nextId = (todos: Todo[]): number => todos.reduce((m, t) => Math.max(m, t.id), 0) + 1
|
||||
|
||||
const STATUS_TEXT: Record<Status, string> = { pending: '待辦', reminded: '已提醒', done: '完成' }
|
||||
|
||||
export const renderList = (todos: Todo[]): string =>
|
||||
todos.length === 0
|
||||
? '目前沒有待辦。'
|
||||
: todos
|
||||
.slice()
|
||||
.sort((a, b) => a.at - b.at)
|
||||
.map(t => `#${t.id} [${t.type}] ${fmtTime(t.at)} ${t.task}(${STATUS_TEXT[t.status]},專案:${t.project || '無'})`)
|
||||
.join('\n')
|
||||
|
||||
export const reminderText = (t: Todo): string =>
|
||||
`到點了:[${t.type}] ${t.task}(#${t.id},專案:${t.project || '無'})。要我執行的話請輸入 /jsc-anima:${TODO_COMMAND} run ${t.id},確認前我不會動手。`
|
||||
|
||||
export const RUN_SYSTEM = '你要執行使用者確認過的待辦,簡短回報你做了什麼與結果;沒做到的事如實說明。'
|
||||
Reference in New Issue
Block a user