@cacinie/cace-timer 1.1.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +491 -192
- package/dist/commands/delete.d.ts +4 -0
- package/dist/commands/delete.js +48 -0
- package/dist/commands/export.d.ts +4 -0
- package/dist/commands/export.js +104 -0
- package/dist/commands/help.d.ts +1 -0
- package/dist/commands/help.js +73 -0
- package/dist/commands/list.d.ts +5 -0
- package/dist/commands/list.js +42 -0
- package/dist/commands/mark.d.ts +1 -0
- package/dist/commands/mark.js +27 -0
- package/dist/commands/pomodoro.d.ts +6 -0
- package/dist/commands/pomodoro.js +135 -0
- package/dist/commands/resume.d.ts +4 -0
- package/dist/commands/resume.js +54 -0
- package/dist/commands/search.d.ts +1 -0
- package/dist/commands/search.js +36 -0
- package/dist/commands/start.d.ts +4 -0
- package/dist/commands/start.js +47 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/stop.d.ts +3 -0
- package/dist/commands/stop.js +104 -0
- package/dist/commands/summary.d.ts +6 -0
- package/dist/commands/summary.js +128 -0
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +62 -0
- package/dist/data.d.ts +18 -0
- package/dist/data.js +161 -0
- package/dist/i18n.d.ts +7 -0
- package/dist/i18n.js +418 -0
- package/dist/index.js +142 -498
- package/dist/mascot.d.ts +9 -0
- package/dist/mascot.js +257 -0
- package/dist/parser.d.ts +6 -0
- package/dist/parser.js +45 -0
- package/dist/tui/countdown.d.ts +8 -0
- package/dist/tui/countdown.js +135 -0
- package/dist/tui/dashboard.d.ts +2 -0
- package/dist/tui/dashboard.js +135 -0
- package/dist/tui/index.d.ts +2 -0
- package/dist/tui/index.js +8 -0
- package/dist/tui/lifecycle.d.ts +28 -0
- package/dist/tui/lifecycle.js +82 -0
- package/dist/tui/reflection.d.ts +5 -0
- package/dist/tui/reflection.js +71 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +44 -0
- package/package.json +20 -4
package/dist/index.js
CHANGED
|
@@ -1,527 +1,171 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
-
if (k2 === undefined) k2 = k;
|
|
5
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
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
|
-
// Frame 4: 超开心
|
|
74
|
-
`
|
|
75
|
-
▄▄▄▄▄▄▄▄▄▄▄▄
|
|
76
|
-
█░░░░░░░░░░░░█
|
|
77
|
-
█░▄▄▄▄▄▄▄▄▄░█
|
|
78
|
-
█░│ ◉ ◉ │░█
|
|
79
|
-
█░│ ▽ │░█
|
|
80
|
-
█░│ ▽△▽ │░█
|
|
81
|
-
╰────────────╯
|
|
82
|
-
`,
|
|
83
|
-
];
|
|
84
|
-
const CACE_SMALL = `
|
|
85
|
-
▄▄▄▄▄▄▄▄▄▄
|
|
86
|
-
█░░░░░░░░░░█
|
|
87
|
-
█░▄▄▄▄▄▄▄░█
|
|
88
|
-
█│ ● ● │
|
|
89
|
-
█│ ▽ │
|
|
90
|
-
█│ ─── │
|
|
91
|
-
╰─────────╯`;
|
|
92
|
-
const CACE_HAPPY = `
|
|
93
|
-
▄▄▄▄▄▄▄▄▄▄
|
|
94
|
-
█░░░░░░░░░░█
|
|
95
|
-
█░▄▄▄▄▄▄▄░█
|
|
96
|
-
█│ ★ ★ │
|
|
97
|
-
█│ ▽ │
|
|
98
|
-
█│ ◡◡◡ │
|
|
99
|
-
╰─────────╯`;
|
|
100
|
-
const CACE_SLEEPY = `
|
|
101
|
-
▄▄▄▄▄▄▄▄▄▄
|
|
102
|
-
█░░░░░░░░░░█
|
|
103
|
-
█░▄▄▄▄▄▄▄░█
|
|
104
|
-
█│ ─ ─ │
|
|
105
|
-
█│ ▽ │
|
|
106
|
-
█│ ─── │
|
|
107
|
-
╰─────────╯`;
|
|
108
|
-
function getGreeting() {
|
|
109
|
-
const hour = new Date().getHours();
|
|
110
|
-
if (hour < 6)
|
|
111
|
-
return '夜深了';
|
|
112
|
-
if (hour < 12)
|
|
113
|
-
return '早上好';
|
|
114
|
-
if (hour < 18)
|
|
115
|
-
return '下午好';
|
|
116
|
-
return '晚上好';
|
|
117
|
-
}
|
|
118
|
-
async function showCaceAnimation(message = '') {
|
|
119
|
-
const cyan = '\x1b[36m';
|
|
120
|
-
const bold = '\x1b[1m';
|
|
121
|
-
const reset = '\x1b[0m';
|
|
122
|
-
// Animation loop
|
|
123
|
-
for (let i = 0; i < 2; i++) {
|
|
124
|
-
for (const frame of CACE_FRAMES) {
|
|
125
|
-
console.clear();
|
|
126
|
-
console.log(cyan + frame + reset);
|
|
127
|
-
console.log();
|
|
128
|
-
console.log(cyan + bold + ' ════════════════════════════════' + reset);
|
|
129
|
-
console.log(cyan + bold + ' C A C E T I M E R' + reset);
|
|
130
|
-
console.log(cyan + bold + ' ════════════════════════════════' + reset);
|
|
131
|
-
if (message) {
|
|
132
|
-
console.log();
|
|
133
|
-
console.log(' ' + message);
|
|
134
|
-
}
|
|
135
|
-
await sleep(180);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
function showCaceSmall(status = '', mood = 'normal') {
|
|
140
|
-
const cyan = '\x1b[36m';
|
|
141
|
-
const reset = '\x1b[0m';
|
|
142
|
-
let face = CACE_SMALL;
|
|
143
|
-
if (mood === 'happy')
|
|
144
|
-
face = CACE_HAPPY;
|
|
145
|
-
else if (mood === 'sleepy')
|
|
146
|
-
face = CACE_SLEEPY;
|
|
147
|
-
console.log(cyan + face + reset);
|
|
148
|
-
if (status) {
|
|
149
|
-
console.log(cyan + ' ' + status + reset);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
// ============ Utilities ============
|
|
153
|
-
function sleep(ms) {
|
|
154
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
155
|
-
}
|
|
156
|
-
function generateId() {
|
|
157
|
-
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
158
|
-
}
|
|
159
|
-
function formatDuration(ms) {
|
|
160
|
-
const seconds = Math.floor(ms / 1000);
|
|
161
|
-
const minutes = Math.floor(seconds / 60);
|
|
162
|
-
const hours = Math.floor(minutes / 60);
|
|
163
|
-
if (hours > 0) {
|
|
164
|
-
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
165
|
-
}
|
|
166
|
-
else if (minutes > 0) {
|
|
167
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
return `${seconds}s`;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
function formatTime(isoString) {
|
|
174
|
-
const date = new Date(isoString);
|
|
175
|
-
return date.toLocaleString('zh-CN', {
|
|
176
|
-
month: '2-digit',
|
|
177
|
-
day: '2-digit',
|
|
178
|
-
hour: '2-digit',
|
|
179
|
-
minute: '2-digit'
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
function formatDate(isoString) {
|
|
183
|
-
const date = new Date(isoString);
|
|
184
|
-
return date.toLocaleDateString('zh-CN');
|
|
185
|
-
}
|
|
186
|
-
// ============ Data Management ============
|
|
187
|
-
const DATA_FILE = path.join(os.homedir(), '.cace-timer.json');
|
|
188
|
-
function loadData() {
|
|
189
|
-
try {
|
|
190
|
-
if (fs.existsSync(DATA_FILE)) {
|
|
191
|
-
const content = fs.readFileSync(DATA_FILE, 'utf-8');
|
|
192
|
-
return JSON.parse(content);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
catch (e) {
|
|
196
|
-
// Ignore errors
|
|
197
|
-
}
|
|
198
|
-
return { current: null, history: [] };
|
|
199
|
-
}
|
|
200
|
-
function saveData(data) {
|
|
201
|
-
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
|
|
202
|
-
// Sync to external path if set
|
|
203
|
-
if (data.syncPath && fs.existsSync(path.dirname(data.syncPath))) {
|
|
204
|
-
fs.writeFileSync(data.syncPath, JSON.stringify(data, null, 2));
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
// ============ Commands ============
|
|
208
|
-
async function cmdStart(task, options) {
|
|
209
|
-
const data = loadData();
|
|
210
|
-
if (data.current) {
|
|
211
|
-
console.log('\x1b[33m⚠ 已有进行中的任务,请先使用 tk stop 结束\x1b[0m');
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const session = {
|
|
215
|
-
id: generateId(),
|
|
216
|
-
start: new Date().toISOString(),
|
|
217
|
-
task: task || '未命名任务',
|
|
218
|
-
tags: options.tag ? [options.tag] : [],
|
|
219
|
-
marks: [],
|
|
220
|
-
estimatedMinutes: options.estimate
|
|
221
|
-
};
|
|
222
|
-
data.current = session;
|
|
223
|
-
saveData(data);
|
|
224
|
-
await showCaceAnimation(`${getGreeting()}!开始记录: ${session.task}`);
|
|
225
|
-
console.log();
|
|
226
|
-
console.log(` 📌 任务: ${session.task}`);
|
|
227
|
-
if (session.tags.length > 0) {
|
|
228
|
-
console.log(` 🏷 标签: ${session.tags.join(', ')}`);
|
|
229
|
-
}
|
|
230
|
-
if (session.estimatedMinutes) {
|
|
231
|
-
console.log(` ⏱ 预计: ${session.estimatedMinutes} 分钟`);
|
|
232
|
-
}
|
|
233
|
-
console.log(` 🕐 开始: ${formatTime(session.start)}`);
|
|
234
|
-
console.log();
|
|
235
|
-
}
|
|
236
|
-
async function cmdMark(note) {
|
|
237
|
-
const data = loadData();
|
|
238
|
-
if (!data.current) {
|
|
239
|
-
console.log('\x1b[33m⚠ 没有进行中的任务,请先用 tk start 开始\x1b[0m');
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
const mark = {
|
|
243
|
-
time: new Date().toISOString(),
|
|
244
|
-
note: note || '标记点'
|
|
245
|
-
};
|
|
246
|
-
data.current.marks.push(mark);
|
|
247
|
-
saveData(data);
|
|
248
|
-
const elapsed = Date.now() - new Date(data.current.start).getTime();
|
|
249
|
-
console.log();
|
|
250
|
-
showCaceSmall('✓ 已标记', 'happy');
|
|
251
|
-
console.log();
|
|
252
|
-
console.log(` 📍 ${formatTime(mark.time)} - ${mark.note}`);
|
|
253
|
-
console.log(` ⏱ 已用时: ${formatDuration(elapsed)}`);
|
|
254
|
-
console.log();
|
|
255
|
-
}
|
|
256
|
-
async function cmdStop() {
|
|
257
|
-
const data = loadData();
|
|
258
|
-
if (!data.current) {
|
|
259
|
-
console.log('\x1b[33m⚠ 没有进行中的任务\x1b[0m');
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const session = data.current;
|
|
263
|
-
session.end = new Date().toISOString();
|
|
264
|
-
const duration = new Date(session.end).getTime() - new Date(session.start).getTime();
|
|
265
|
-
const durationMinutes = duration / 60000;
|
|
266
|
-
// Calculate efficiency score
|
|
267
|
-
const hasEstimate = session.estimatedMinutes && session.estimatedMinutes > 0;
|
|
268
|
-
let efficiency = -1;
|
|
269
|
-
if (hasEstimate) {
|
|
270
|
-
efficiency = Math.min(100, Math.round((session.estimatedMinutes / durationMinutes) * 100));
|
|
271
|
-
}
|
|
272
|
-
data.history.unshift(session);
|
|
273
|
-
data.current = null;
|
|
274
|
-
saveData(data);
|
|
275
|
-
console.log();
|
|
276
|
-
showCaceSmall('任务完成!', 'happy');
|
|
277
|
-
console.log();
|
|
278
|
-
console.log(' ┌─────────────────────────────────┐');
|
|
279
|
-
console.log(' │ 📊 任务总结 │');
|
|
280
|
-
console.log(' └─────────────────────────────────┘');
|
|
281
|
-
console.log();
|
|
282
|
-
console.log(` 📌 任务: ${session.task}`);
|
|
283
|
-
console.log(` 🕐 时长: ${formatDuration(duration)}`);
|
|
284
|
-
if (session.tags.length > 0) {
|
|
285
|
-
console.log(` 🏷 标签: ${session.tags.join(', ')}`);
|
|
286
|
-
}
|
|
287
|
-
if (session.marks.length > 0) {
|
|
288
|
-
console.log(` 📍 标记: ${session.marks.length} 个`);
|
|
289
|
-
}
|
|
290
|
-
// Efficiency display
|
|
291
|
-
if (hasEstimate) {
|
|
292
|
-
let effEmoji = '⭐';
|
|
293
|
-
let effColor = '\x1b[32m';
|
|
294
|
-
if (efficiency < 50) {
|
|
295
|
-
effEmoji = '💀';
|
|
296
|
-
effColor = '\x1b[31m';
|
|
297
|
-
}
|
|
298
|
-
else if (efficiency < 80) {
|
|
299
|
-
effEmoji = '💪';
|
|
300
|
-
effColor = '\x1b[33m';
|
|
301
|
-
}
|
|
302
|
-
else if (efficiency >= 100) {
|
|
303
|
-
effEmoji = '🏆';
|
|
304
|
-
}
|
|
305
|
-
console.log(` ${effEmoji} 效率分: ${effColor}${efficiency}%\x1b[0m`);
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
console.log(' ⏱ 未设置预估时长,无法计算效率分');
|
|
309
|
-
}
|
|
310
|
-
console.log();
|
|
311
|
-
}
|
|
312
|
-
function cmdStatus() {
|
|
313
|
-
const data = loadData();
|
|
314
|
-
console.log();
|
|
315
|
-
showCaceSmall('', data.current ? 'normal' : 'sleepy');
|
|
316
|
-
if (!data.current) {
|
|
317
|
-
console.log();
|
|
318
|
-
console.log(' 😴 当前没有进行中的任务');
|
|
319
|
-
console.log(' 使用 tk start "任务名" 开始新任务');
|
|
320
|
-
console.log();
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
const elapsed = Date.now() - new Date(data.current.start).getTime();
|
|
324
|
-
console.log();
|
|
325
|
-
console.log(' 🔥 进行中');
|
|
326
|
-
console.log();
|
|
327
|
-
console.log(` 📌 任务: ${data.current.task}`);
|
|
328
|
-
console.log(` 🕐 开始: ${formatTime(data.current.start)}`);
|
|
329
|
-
console.log(` ⏱ 已用: ${formatDuration(elapsed)}`);
|
|
330
|
-
if (data.current.tags.length > 0) {
|
|
331
|
-
console.log(` 🏷 标签: ${data.current.tags.join(', ')}`);
|
|
332
|
-
}
|
|
333
|
-
if (data.current.marks.length > 0) {
|
|
334
|
-
console.log(` 📍 标记: ${data.current.marks.length} 个`);
|
|
335
|
-
data.current.marks.forEach((m, i) => {
|
|
336
|
-
console.log(` ${i + 1}. ${formatTime(m.time)} - ${m.note}`);
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
console.log();
|
|
340
|
-
}
|
|
341
|
-
function cmdList(options) {
|
|
342
|
-
const data = loadData();
|
|
343
|
-
let sessions = data.history;
|
|
344
|
-
// Filter by today
|
|
345
|
-
if (options.today) {
|
|
346
|
-
const today = formatDate(new Date().toISOString());
|
|
347
|
-
sessions = sessions.filter(s => formatDate(s.start) === today);
|
|
348
|
-
}
|
|
349
|
-
// Filter by tag
|
|
350
|
-
if (options.tag) {
|
|
351
|
-
sessions = sessions.filter(s => s.tags.includes(options.tag));
|
|
352
|
-
}
|
|
353
|
-
// Limit
|
|
354
|
-
const limit = options.limit ?? 10;
|
|
355
|
-
sessions = sessions.slice(0, limit);
|
|
356
|
-
console.log();
|
|
357
|
-
showCaceSmall('历史记录');
|
|
358
|
-
console.log();
|
|
359
|
-
if (sessions.length === 0) {
|
|
360
|
-
console.log(' 📭 暂无记录');
|
|
361
|
-
console.log();
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
sessions.forEach((session, i) => {
|
|
365
|
-
const duration = session.end
|
|
366
|
-
? formatDuration(new Date(session.end).getTime() - new Date(session.start).getTime())
|
|
367
|
-
: '进行中';
|
|
368
|
-
console.log(` ${i + 1}. ${session.task}`);
|
|
369
|
-
console.log(` 📅 ${formatTime(session.start)} | ⏱ ${duration}`);
|
|
370
|
-
if (session.tags.length > 0) {
|
|
371
|
-
console.log(` 🏷 ${session.tags.map(t => '#' + t).join(' ')}`);
|
|
372
|
-
}
|
|
373
|
-
console.log();
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
function cmdSearch(keyword) {
|
|
377
|
-
const data = loadData();
|
|
378
|
-
const results = data.history.filter(s => s.task.toLowerCase().includes(keyword.toLowerCase()) ||
|
|
379
|
-
s.tags.some(t => t.toLowerCase().includes(keyword.toLowerCase())) ||
|
|
380
|
-
s.marks.some(m => m.note.toLowerCase().includes(keyword.toLowerCase())));
|
|
381
|
-
console.log();
|
|
382
|
-
showCaceSmall(`搜索: "${keyword}"`);
|
|
383
|
-
console.log();
|
|
384
|
-
if (results.length === 0) {
|
|
385
|
-
console.log(' 🔍 未找到匹配记录');
|
|
386
|
-
console.log();
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
console.log(` 找到 ${results.length} 条记录:\n`);
|
|
390
|
-
results.forEach((session, i) => {
|
|
391
|
-
const duration = session.end
|
|
392
|
-
? formatDuration(new Date(session.end).getTime() - new Date(session.start).getTime())
|
|
393
|
-
: '进行中';
|
|
394
|
-
console.log(` ${i + 1}. ${session.task}`);
|
|
395
|
-
console.log(` 📅 ${formatTime(session.start)} | ⏱ ${duration}`);
|
|
396
|
-
console.log();
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
function cmdSync(filePath) {
|
|
400
|
-
const data = loadData();
|
|
401
|
-
const absPath = path.resolve(filePath);
|
|
402
|
-
data.syncPath = absPath;
|
|
403
|
-
saveData(data);
|
|
404
|
-
console.log();
|
|
405
|
-
showCaceSmall('同步已配置');
|
|
406
|
-
console.log();
|
|
407
|
-
console.log(` 📂 同步路径: ${absPath}`);
|
|
408
|
-
console.log(' 💡 提示: 可将文件放在 Dropbox/iCloud/OneDrive 等同步目录');
|
|
409
|
-
console.log();
|
|
410
|
-
}
|
|
411
|
-
function cmdHelp() {
|
|
412
|
-
console.log();
|
|
413
|
-
showCaceSmall('');
|
|
414
|
-
console.log();
|
|
415
|
-
console.log(' ╔═══════════════════════════════════════════╗');
|
|
416
|
-
console.log(' ║ CACE TIMER - 命令帮助 ║');
|
|
417
|
-
console.log(' ╚═══════════════════════════════════════════╝');
|
|
418
|
-
console.log();
|
|
419
|
-
console.log(' tk start <任务名> [选项]');
|
|
420
|
-
console.log(' 开始新任务');
|
|
421
|
-
console.log(' --tag <标签> 添加标签');
|
|
422
|
-
console.log(' --estimate <分钟> 预估时长');
|
|
423
|
-
console.log();
|
|
424
|
-
console.log(' tk mark <备注>');
|
|
425
|
-
console.log(' 记录时间点');
|
|
426
|
-
console.log();
|
|
427
|
-
console.log(' tk stop');
|
|
428
|
-
console.log(' 结束当前任务(显示效率评分)');
|
|
429
|
-
console.log();
|
|
430
|
-
console.log(' tk status');
|
|
431
|
-
console.log(' 查看当前状态');
|
|
432
|
-
console.log();
|
|
433
|
-
console.log(' tk list [选项]');
|
|
434
|
-
console.log(' 查看历史记录');
|
|
435
|
-
console.log(' --today 仅今天');
|
|
436
|
-
console.log(' --tag <标签> 按标签筛选');
|
|
437
|
-
console.log(' --limit <数量> 限制数量');
|
|
438
|
-
console.log();
|
|
439
|
-
console.log(' tk search <关键词>');
|
|
440
|
-
console.log(' 搜索记录');
|
|
441
|
-
console.log();
|
|
442
|
-
console.log(' tk sync <文件路径>');
|
|
443
|
-
console.log(' 设置同步文件路径');
|
|
444
|
-
console.log();
|
|
445
|
-
console.log(' tk help');
|
|
446
|
-
console.log(' 显示帮助');
|
|
447
|
-
console.log();
|
|
448
|
-
}
|
|
449
|
-
function parseArgs(args) {
|
|
450
|
-
const result = {
|
|
451
|
-
command: '',
|
|
452
|
-
positional: [],
|
|
453
|
-
options: {}
|
|
454
|
-
};
|
|
455
|
-
let i = 0;
|
|
456
|
-
while (i < args.length) {
|
|
457
|
-
const arg = args[i];
|
|
458
|
-
if (arg.startsWith('--')) {
|
|
459
|
-
const key = arg.slice(2);
|
|
460
|
-
const nextArg = args[i + 1];
|
|
461
|
-
if (nextArg && !nextArg.startsWith('-')) {
|
|
462
|
-
result.options[key] = nextArg;
|
|
463
|
-
i += 2;
|
|
464
|
-
}
|
|
465
|
-
else {
|
|
466
|
-
result.options[key] = true;
|
|
467
|
-
i++;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
else if (!result.command) {
|
|
471
|
-
result.command = arg;
|
|
472
|
-
i++;
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
result.positional.push(arg);
|
|
476
|
-
i++;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return result;
|
|
480
|
-
}
|
|
481
|
-
// ============ Main ============
|
|
482
|
-
async function main() {
|
|
483
|
-
const args = process.argv.slice(2);
|
|
484
|
-
const parsed = parseArgs(args);
|
|
485
|
-
const { command, positional, options } = parsed;
|
|
4
|
+
const tui_1 = require("./tui");
|
|
5
|
+
const dashboard_1 = require("./tui/dashboard");
|
|
6
|
+
const parser_1 = require("./parser");
|
|
7
|
+
const data_1 = require("./data");
|
|
8
|
+
const i18n_1 = require("./i18n");
|
|
9
|
+
const start_1 = require("./commands/start");
|
|
10
|
+
const mark_1 = require("./commands/mark");
|
|
11
|
+
const stop_1 = require("./commands/stop");
|
|
12
|
+
const status_1 = require("./commands/status");
|
|
13
|
+
const list_1 = require("./commands/list");
|
|
14
|
+
const search_1 = require("./commands/search");
|
|
15
|
+
const sync_1 = require("./commands/sync");
|
|
16
|
+
const help_1 = require("./commands/help");
|
|
17
|
+
const summary_1 = require("./commands/summary");
|
|
18
|
+
const delete_1 = require("./commands/delete");
|
|
19
|
+
const resume_1 = require("./commands/resume");
|
|
20
|
+
const export_1 = require("./commands/export");
|
|
21
|
+
const pomodoro_1 = require("./commands/pomodoro");
|
|
22
|
+
// ============ Safe parseInt ============
|
|
23
|
+
function safeInt(val, fallback) {
|
|
24
|
+
if (typeof val !== 'string')
|
|
25
|
+
return fallback;
|
|
26
|
+
const n = parseInt(val);
|
|
27
|
+
return isNaN(n) || n <= 0 ? fallback : n;
|
|
28
|
+
}
|
|
29
|
+
function safeString(val) {
|
|
30
|
+
return typeof val === 'string' ? val : undefined;
|
|
31
|
+
}
|
|
32
|
+
// ============ Command Router ============
|
|
33
|
+
async function runCommand(command, positional, options) {
|
|
486
34
|
switch (command) {
|
|
487
35
|
case 'start':
|
|
488
|
-
await cmdStart(positional[0] || '', {
|
|
36
|
+
await (0, start_1.cmdStart)(positional[0] || '', {
|
|
489
37
|
tag: options.tag,
|
|
490
|
-
estimate:
|
|
38
|
+
estimate: safeInt(options.estimate, 0) || undefined,
|
|
491
39
|
});
|
|
492
40
|
break;
|
|
493
41
|
case 'mark':
|
|
494
|
-
await cmdMark(positional[0] || '
|
|
42
|
+
await (0, mark_1.cmdMark)(positional[0] || (0, i18n_1.t)('cmd.mark.markPoint'));
|
|
495
43
|
break;
|
|
496
44
|
case 'stop':
|
|
497
|
-
await cmdStop();
|
|
45
|
+
await (0, stop_1.cmdStop)({ reflection: safeString(options.reflection) });
|
|
498
46
|
break;
|
|
499
47
|
case 'status':
|
|
500
|
-
cmdStatus();
|
|
48
|
+
(0, status_1.cmdStatus)();
|
|
501
49
|
break;
|
|
502
50
|
case 'list':
|
|
503
51
|
case 'ls':
|
|
504
|
-
cmdList({
|
|
52
|
+
(0, list_1.cmdList)({
|
|
505
53
|
today: !!options.today,
|
|
506
|
-
tag: options.tag,
|
|
507
|
-
limit: options.limit ?
|
|
54
|
+
tag: safeString(options.tag),
|
|
55
|
+
limit: options.limit ? safeInt(options.limit, 10) : 10,
|
|
508
56
|
});
|
|
509
57
|
break;
|
|
510
58
|
case 'search':
|
|
511
|
-
cmdSearch(positional[0] || '');
|
|
59
|
+
(0, search_1.cmdSearch)(positional[0] || '');
|
|
512
60
|
break;
|
|
513
61
|
case 'sync':
|
|
514
|
-
cmdSync(positional[0] || '');
|
|
62
|
+
(0, sync_1.cmdSync)(positional[0] || '');
|
|
63
|
+
break;
|
|
64
|
+
case 'summary':
|
|
65
|
+
(0, summary_1.cmdSummary)({
|
|
66
|
+
today: !!options.today,
|
|
67
|
+
week: !!options.week,
|
|
68
|
+
month: !!options.month,
|
|
69
|
+
tag: safeString(options.tag),
|
|
70
|
+
});
|
|
71
|
+
break;
|
|
72
|
+
case 'delete':
|
|
73
|
+
(0, delete_1.cmdDelete)({
|
|
74
|
+
id: positional[0] && !positional[0].startsWith('--') ? positional[0] : undefined,
|
|
75
|
+
last: !!options.last,
|
|
76
|
+
});
|
|
77
|
+
break;
|
|
78
|
+
case 'resume':
|
|
79
|
+
await (0, resume_1.cmdResume)({
|
|
80
|
+
id: positional[0] && !positional[0].startsWith('--') ? positional[0] : undefined,
|
|
81
|
+
last: !!options.last,
|
|
82
|
+
});
|
|
83
|
+
break;
|
|
84
|
+
case 'export':
|
|
85
|
+
(0, export_1.cmdExport)({ format: safeString(options.format), output: safeString(options.output) });
|
|
86
|
+
break;
|
|
87
|
+
case 'pomodoro':
|
|
88
|
+
await (0, pomodoro_1.cmdPomodoro)(positional[0] || '', {
|
|
89
|
+
work: options.work ? safeInt(options.work, 25) : undefined,
|
|
90
|
+
break: options.break ? safeInt(options.break, 5) : undefined,
|
|
91
|
+
rounds: options.rounds ? safeInt(options.rounds, 4) : undefined,
|
|
92
|
+
tag: options.tag,
|
|
93
|
+
});
|
|
94
|
+
break;
|
|
95
|
+
case 'focus': {
|
|
96
|
+
const minutes = positional[0] ? safeInt(positional[0], 25) : 25;
|
|
97
|
+
const focusTask = positional.slice(1).join(' ') || (0, i18n_1.t)('cmd.focus.alias');
|
|
98
|
+
await (0, pomodoro_1.cmdPomodoro)(focusTask, {
|
|
99
|
+
work: minutes,
|
|
100
|
+
break: 5,
|
|
101
|
+
rounds: 1,
|
|
102
|
+
tag: options.tag,
|
|
103
|
+
});
|
|
515
104
|
break;
|
|
105
|
+
}
|
|
516
106
|
case 'help':
|
|
517
|
-
|
|
518
|
-
case '-h':
|
|
519
|
-
case '':
|
|
520
|
-
cmdHelp();
|
|
107
|
+
(0, help_1.cmdHelp)();
|
|
521
108
|
break;
|
|
522
109
|
default:
|
|
523
|
-
console.log(`\x1b[31m
|
|
524
|
-
console.log(
|
|
110
|
+
console.log(`\x1b[31m${(0, i18n_1.t)('error.unknownCommand', { command })}\x1b[0m`);
|
|
111
|
+
console.log((0, i18n_1.t)('error.useHelp'));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// ============ Dashboard action handler ============
|
|
115
|
+
async function runDashboardAction(action) {
|
|
116
|
+
switch (action) {
|
|
117
|
+
case 'start': {
|
|
118
|
+
// For dashboard start, use a generic task - user can specify more via CLI
|
|
119
|
+
await (0, start_1.cmdStart)('', { tag: undefined, estimate: undefined });
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'focus': {
|
|
123
|
+
await (0, pomodoro_1.cmdPomodoro)((0, i18n_1.t)('cmd.focus.alias'), { work: 25, break: 5, rounds: 1, tag: undefined });
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'mark':
|
|
127
|
+
await (0, mark_1.cmdMark)((0, i18n_1.t)('cmd.mark.markPoint'));
|
|
128
|
+
break;
|
|
129
|
+
case 'stop':
|
|
130
|
+
await (0, stop_1.cmdStop)();
|
|
131
|
+
break;
|
|
132
|
+
case 'summary':
|
|
133
|
+
(0, summary_1.cmdSummary)({ today: true });
|
|
134
|
+
break;
|
|
135
|
+
case 'list':
|
|
136
|
+
(0, list_1.cmdList)({ today: true });
|
|
137
|
+
break;
|
|
138
|
+
case 'help':
|
|
139
|
+
(0, help_1.cmdHelp)();
|
|
140
|
+
break;
|
|
141
|
+
case 'quit':
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// ============ Main ============
|
|
146
|
+
async function main() {
|
|
147
|
+
const args = process.argv.slice(2);
|
|
148
|
+
const parsed = (0, parser_1.parseArgs)(args);
|
|
149
|
+
const { command, positional, options } = parsed;
|
|
150
|
+
// Resolve locale before any command runs
|
|
151
|
+
const storedData = (0, data_1.loadData)();
|
|
152
|
+
const locale = (0, i18n_1.resolveLocale)(options.lang, storedData.lang);
|
|
153
|
+
(0, i18n_1.setLocale)(locale);
|
|
154
|
+
// Persist language preference if --lang was explicitly given
|
|
155
|
+
if (options.lang) {
|
|
156
|
+
storedData.lang = locale;
|
|
157
|
+
(0, data_1.saveData)(storedData);
|
|
158
|
+
}
|
|
159
|
+
// No command → dashboard (interactive) or help (non-interactive)
|
|
160
|
+
if (command === '' || command === '--help' || command === '-h') {
|
|
161
|
+
if (command === '' && (0, tui_1.isInteractiveTerminal)()) {
|
|
162
|
+
const action = await (0, dashboard_1.showDashboard)();
|
|
163
|
+
await runDashboardAction(action);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
(0, help_1.cmdHelp)();
|
|
167
|
+
return;
|
|
525
168
|
}
|
|
169
|
+
await runCommand(command, positional, parsed.options);
|
|
526
170
|
}
|
|
527
171
|
main().catch(console.error);
|
package/dist/mascot.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const CACE_SMALL = "\n \u256D\u2500\u256E \u256D\u2500\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F\n \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u256E\n \u2502 \u256D\u256E\u256D\u256E\u256D\u256E\u256D\u256E \u2502\n \u2502 \u25CF \u25CF \u2502\n \u2502 \u25BD \u2502\n \u2502 \u2500\u2500\u2500 \u2502\n \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n \u2502\u2502\n \u256D\u2500\u2500\u2500\u2500\u2500\u2534\u2534\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u256F \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2557 \u2570\u256E\n o\u2502 \u2551 CACE \u2551 \u2502o\n \u2502 \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u2502\n \u2570\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u256F\n \u2502 \u2502\n \u2502 \u2502\n \u2502 \u2502\n \u256D\u2534\u256E \u256D\u2534\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F";
|
|
2
|
+
export declare const CACE_HAPPY = "\n \u256D\u2500\u256E \u256D\u2500\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F\n \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u256E\n \u2502 \u256D\u256E\u256D\u256E\u256D\u256E\u256D\u256E \u2502\n \u2502 \u2605 \u2605 \u2502\n \u2502 \u25BD \u2502\n \u2502 \u25E1\u25E1\u25E1 \u2502\n \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n \u2502\u2502\n \u256D\u2500\u2500\u2500\u2500\u2500\u2534\u2534\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u256F \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2557 \u2570\u256E\n o\u2502 \u2551 CACE \u2551 \u2502o\n \u2502 \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u2502\n \u2570\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u256F\n \u256D\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u256E\n \u2502 \u2502\n \u2502 \u2502\n \u256D\u2534\u256E \u256D\u2534\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F";
|
|
3
|
+
export declare const CACE_SLEEPY = "\n \u256D\u2500\u256E \u256D\u2500\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F\n \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u256E\n \u2502 \u256D\u256E\u256D\u256E\u256D\u256E\u256D\u256E \u2502\n \u2502 \u2500 \u2500 \u2502\n \u2502 \u25BD \u2502\n \u2502 \uFF5E\uFF5E \u2502\n \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n \u2502\u2502\n \u256D\u2500\u2500\u2500\u2500\u2500\u2534\u2534\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u256F \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2557 \u2570\u256E\n o\u2502 \u2551 CACE \u2551 \u2502o\n \u2502 \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u2502\n \u2570\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u256F\n \u2502 \u2502\n \u2502 \u2502\n \u2502 \u2502\n \u256D\u2534\u256E \u256D\u2534\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F";
|
|
4
|
+
export declare const CACE_FOCUSED = "\n \u256D\u2500\u256E \u256D\u2500\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F\n \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u256E\n \u2502 \u256D\u256E\u256D\u256E\u256D\u256E\u256D\u256E \u2502\n \u2502 \u25C6 \u25C6 \u2502\n \u2502 \u25BD \u2502\n \u2502 \u25AC\u25AC\u25AC \u2502\n \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n \u2502\u2502\n \u256D\u2500\u2500\u2500\u2500\u2500\u2534\u2534\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u256F \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2557 \u2570\u256E\n o\u2502 \u2551 CACE \u2551 \u2502o\n \u2502 \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u2502\n \u2570\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u256F\n \u2502 \u2502\n \u2502 \u2502\n \u2502 \u2502\n \u256D\u2534\u256E \u256D\u2534\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F";
|
|
5
|
+
export declare const CACE_CELEBRATING = "\n \u266A \u256D\u2500\u256E \u256D\u2500\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F\n \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u256E\n \u2502 \u256D\u256E\u256D\u256E\u256D\u256E\u256D\u256E \u2502\n \u2502 \u25C9 \u25C9 \u2502\n \u2502 \u25BD \u2502\n \u2502 \u25BD\u25B3\u25BD \u2502\n \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n \u2502\u2502\n \u256D\u2500\u2500\u2500\u2500\u2500\u2534\u2534\u2500\u2500\u2500\u2500\u2500\u256E\n \u256D\u256F \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2557 \u2570\u256E\n o\u2502 \u2551 CACE \u2551 \u2502o\n \u2502 \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u2502\n \u2570\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u256F\n \u256D\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u256E\n \u2502 \u2502\n \u2502 \u2502\n \u256D\u2534\u256E \u256D\u2534\u256E\n \u2570\u2500\u256F \u2570\u2500\u256F";
|
|
6
|
+
export type CaceMood = 'normal' | 'happy' | 'sleepy' | 'focused' | 'celebrating';
|
|
7
|
+
export declare function getGreeting(): string;
|
|
8
|
+
export declare function showCaceAnimation(message?: string): Promise<void>;
|
|
9
|
+
export declare function showCaceSmall(status?: string, mood?: CaceMood): void;
|