@cacinie/cace-timer 1.3.2 → 1.4.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/dist/index.js CHANGED
@@ -19,6 +19,8 @@ const delete_1 = require("./commands/delete");
19
19
  const resume_1 = require("./commands/resume");
20
20
  const export_1 = require("./commands/export");
21
21
  const pomodoro_1 = require("./commands/pomodoro");
22
+ const mascot_1 = require("./commands/mascot");
23
+ const terminal_1 = require("./terminal");
22
24
  // ============ Safe parseInt ============
23
25
  function safeInt(val, fallback) {
24
26
  if (typeof val !== 'string')
@@ -32,6 +34,15 @@ function safeString(val) {
32
34
  // ============ Command Router ============
33
35
  async function runCommand(command, positional, options) {
34
36
  switch (command) {
37
+ case 'mascot':
38
+ if (positional[0] && positional[0] !== 'preview')
39
+ throw new Error('Usage: tk mascot preview [--all]');
40
+ await (0, mascot_1.cmdMascot)({
41
+ all: !!options.all,
42
+ expression: safeString(options.expression),
43
+ size: safeString(options.size),
44
+ });
45
+ break;
35
46
  case 'start':
36
47
  await (0, start_1.cmdStart)(positional[0] || '', {
37
48
  tag: options.tag,
@@ -107,8 +118,7 @@ async function runCommand(command, positional, options) {
107
118
  (0, help_1.cmdHelp)();
108
119
  break;
109
120
  default:
110
- console.log(`\x1b[31m${(0, i18n_1.t)('error.unknownCommand', { command })}\x1b[0m`);
111
- console.log((0, i18n_1.t)('error.useHelp'));
121
+ throw new Error((0, i18n_1.t)('error.unknownCommand', { command }) + '\n' + (0, i18n_1.t)('error.useHelp'));
112
122
  }
113
123
  }
114
124
  // ============ Dashboard action handler ============
@@ -147,6 +157,23 @@ async function main() {
147
157
  const args = process.argv.slice(2);
148
158
  const parsed = (0, parser_1.parseArgs)(args);
149
159
  const { command, positional, options } = parsed;
160
+ const color = safeString(options.color) ?? 'auto';
161
+ const theme = safeString(options.theme) ?? 'dark';
162
+ if (!['auto', 'always', '256', 'never'].includes(color) ||
163
+ (options.color !== undefined && typeof options.color !== 'string')) {
164
+ throw new Error('--color: auto | always | 256 | never');
165
+ }
166
+ if (!['dark', 'light'].includes(theme) ||
167
+ (options.theme !== undefined && typeof options.theme !== 'string')) {
168
+ throw new Error('--theme: dark | light');
169
+ }
170
+ (0, terminal_1.configureDisplay)({
171
+ animation: !options['no-animation'],
172
+ tui: !options['no-tui'],
173
+ ascii: !!options.ascii,
174
+ color: color,
175
+ theme: theme,
176
+ });
150
177
  // Resolve locale before any command runs
151
178
  const storedData = (0, data_1.loadData)();
152
179
  const locale = (0, i18n_1.resolveLocale)(options.lang, storedData.lang);
@@ -158,7 +185,7 @@ async function main() {
158
185
  }
159
186
  // No command → dashboard (interactive) or help (non-interactive)
160
187
  if (command === '' || command === '--help' || command === '-h') {
161
- if (command === '' && (0, tui_1.isInteractiveTerminal)()) {
188
+ if (command === '' && !options.help && (0, tui_1.isInteractiveTerminal)()) {
162
189
  const action = await (0, dashboard_1.showDashboard)();
163
190
  await runDashboardAction(action);
164
191
  return;
@@ -168,4 +195,7 @@ async function main() {
168
195
  }
169
196
  await runCommand(command, positional, parsed.options);
170
197
  }
171
- main().catch(console.error);
198
+ main().catch((error) => {
199
+ console.error((0, terminal_1.paint)(error instanceof Error ? error.message : String(error), 'red'));
200
+ process.exitCode = 1;
201
+ });
@@ -0,0 +1,171 @@
1
+ export declare const DARK_PALETTE: {
2
+ readonly h: "72cbbb";
3
+ readonly m: "439da5";
4
+ readonly e: "b2f4d5";
5
+ readonly p: "dba4b1";
6
+ readonly s: "e6d5c4";
7
+ readonly c: "99958b";
8
+ readonly g: "82989e";
9
+ readonly w: "d5e7e3";
10
+ readonly d: "6f8b8c";
11
+ };
12
+ export declare const LIGHT_PALETTE: {
13
+ readonly h: "24776e";
14
+ readonly m: "245d68";
15
+ readonly e: "276849";
16
+ readonly p: "a04b6b";
17
+ readonly s: "866049";
18
+ readonly c: "ab9b8b";
19
+ readonly g: "5b6572";
20
+ readonly w: "526764";
21
+ readonly d: "687776";
22
+ };
23
+ export type ColorRole = keyof typeof DARK_PALETTE;
24
+ export declare const FULL: readonly ["{h} _,", "{h} /ノ", "{h} .---'/`-----.", "{h} / / ノ \\ {h}\\", "{h} / .ノノ /\\ ヽ {h}\\", "{h} / ノ ノ ヽハ {p}* {h}\\", "{h} /ノ / ノヽノ ヽハ {h}\\", "{h} |:|{g} .---. .---. {h}|:|", "{g} ({h}|:|{g}( {e}ィ◕゚{g} )-( {e}゚◕ミ{g} ){h}|:|{g})", "{h} |:|{g} '---' '---' {h}|:|", "{h} |:| {s}({p}// //{s}) {h}|:|", "{h} \\:\\ {s}\\ ᴗ / {h}/:/", "{h} \\_\\ {c}\\ / {h}/_/", "{m} '--'{c}`.___.'{m}'--'", "{g} .-{s}\\_/{g}-.", "{g} / {p}>o<{g} \\", "{g} '-------'"];
25
+ export declare const COMPACT: readonly ["{h} _,", "{h} /ノ", "{h} .-'/`--.", "{h} / /ノ\\ {h}\\", "{h} /ノノ ヽハ{p}* {h}\\", "{g} ({h}|:{g}({e}◕゚{g})-({e}◕゚{g}){h}:|{g})", "{h} |:{s}({p}// //{s}){h}:|", "{h} \\:{c} \\ {s}ᴗ {c}/ {h}:/", "{h} '- {c}`._.' {h}-'", "{g} / {p}>o<{g} \\", "{g} '-----'"];
26
+ export declare const TINY: readonly ["{h} .---.", "{g} ({h}|{e}o-o{h}|{g})", "{h} \\{s} v {h}/", "{c} '-'", "{g} /{p}><{g}\\"];
27
+ export declare const EXPRESSIONS: readonly [{
28
+ readonly key: "little_smile";
29
+ readonly zh: "轻笑";
30
+ readonly en: "Little smile";
31
+ readonly left: "ィ◕゚";
32
+ readonly right: "゚◕ミ";
33
+ readonly mouth: "ᴗ";
34
+ readonly mark: "✧";
35
+ }, {
36
+ readonly key: "happy";
37
+ readonly zh: "开心";
38
+ readonly en: "Happy";
39
+ readonly left: "/^\\";
40
+ readonly right: "/^\\";
41
+ readonly mouth: "ᴗ";
42
+ readonly mark: "*";
43
+ }, {
44
+ readonly key: "wink";
45
+ readonly zh: "眨眼";
46
+ readonly en: "Wink";
47
+ readonly left: "ィ◕゚";
48
+ readonly right: " > ";
49
+ readonly mouth: "ᴗ";
50
+ readonly mark: "☆";
51
+ }, {
52
+ readonly key: "shy";
53
+ readonly zh: "害羞";
54
+ readonly en: "Shy";
55
+ readonly left: "。◕ ";
56
+ readonly right: " ◕。";
57
+ readonly mouth: "ᴗ";
58
+ readonly mark: "";
59
+ }, {
60
+ readonly key: "surprised";
61
+ readonly zh: "惊讶";
62
+ readonly en: "Surprised";
63
+ readonly left: " O ";
64
+ readonly right: " O ";
65
+ readonly mouth: "o";
66
+ readonly mark: "!";
67
+ }, {
68
+ readonly key: "head_tilt";
69
+ readonly zh: "歪头";
70
+ readonly en: "Head tilt";
71
+ readonly left: "ィ◕゚";
72
+ readonly right: " o ";
73
+ readonly mouth: "ー";
74
+ readonly mark: "?";
75
+ }, {
76
+ readonly key: "peek";
77
+ readonly zh: "探头";
78
+ readonly en: "Peek";
79
+ readonly left: "◕゚ ";
80
+ readonly right: "◕゚ ";
81
+ readonly mouth: "v";
82
+ readonly mark: "";
83
+ }, {
84
+ readonly key: "smug";
85
+ readonly zh: "得意";
86
+ readonly en: "Smug";
87
+ readonly left: " - ";
88
+ readonly right: " ◕ ";
89
+ readonly mouth: "ᴗ";
90
+ readonly mark: "✧";
91
+ }, {
92
+ readonly key: "pouting";
93
+ readonly zh: "气鼓鼓";
94
+ readonly en: "Pouting";
95
+ readonly left: " > ";
96
+ readonly right: " < ";
97
+ readonly mouth: "3";
98
+ readonly mark: "!";
99
+ }, {
100
+ readonly key: "sad";
101
+ readonly zh: "委屈";
102
+ readonly en: "Sad";
103
+ readonly left: "。◕ ";
104
+ readonly right: " ◕。";
105
+ readonly mouth: "~";
106
+ readonly mark: "";
107
+ }, {
108
+ readonly key: "crying";
109
+ readonly zh: "哭泣";
110
+ readonly en: "Crying";
111
+ readonly left: " T ";
112
+ readonly right: " T ";
113
+ readonly mouth: "~";
114
+ readonly mark: "";
115
+ }, {
116
+ readonly key: "sleepy";
117
+ readonly zh: "困倦";
118
+ readonly en: "Sleepy";
119
+ readonly left: " - ";
120
+ readonly right: " - ";
121
+ readonly mouth: "o";
122
+ readonly mark: "z";
123
+ }, {
124
+ readonly key: "asleep";
125
+ readonly zh: "熟睡";
126
+ readonly en: "Asleep";
127
+ readonly left: " u ";
128
+ readonly right: " u ";
129
+ readonly mouth: "ー";
130
+ readonly mark: "z Z z";
131
+ }, {
132
+ readonly key: "excited";
133
+ readonly zh: "期待";
134
+ readonly en: "Excited";
135
+ readonly left: " ✧ ";
136
+ readonly right: " ✧ ";
137
+ readonly mouth: "ᴗ";
138
+ readonly mark: "*";
139
+ }, {
140
+ readonly key: "dizzy";
141
+ readonly zh: "晕乎";
142
+ readonly en: "Dizzy";
143
+ readonly left: " @ ";
144
+ readonly right: " @ ";
145
+ readonly mouth: "~";
146
+ readonly mark: "~";
147
+ }, {
148
+ readonly key: "side_eye";
149
+ readonly zh: "小嫌弃";
150
+ readonly en: "Side eye";
151
+ readonly left: " ¬ ";
152
+ readonly right: " ¬ ";
153
+ readonly mouth: "ー";
154
+ readonly mark: "...";
155
+ }];
156
+ export type Expression = (typeof EXPRESSIONS)[number]['key'];
157
+ export type MascotSize = 'full' | 'compact' | 'tiny';
158
+ export declare const SIZES: {
159
+ readonly full: {
160
+ readonly width: 38;
161
+ readonly height: 17;
162
+ };
163
+ readonly compact: {
164
+ readonly width: 22;
165
+ readonly height: 11;
166
+ };
167
+ readonly tiny: {
168
+ readonly width: 8;
169
+ readonly height: 5;
170
+ };
171
+ };
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SIZES = exports.EXPRESSIONS = exports.TINY = exports.COMPACT = exports.FULL = exports.LIGHT_PALETTE = exports.DARK_PALETTE = void 0;
4
+ // Adapted from CacinieP's MINT reference, 2026-09-09.
5
+ // Source and contour decisions: docs/design/mint.md.
6
+ // Art is data: no timers, terminal access or Python dependency.
7
+ exports.DARK_PALETTE = {
8
+ h: '72cbbb',
9
+ m: '439da5',
10
+ e: 'b2f4d5',
11
+ p: 'dba4b1',
12
+ s: 'e6d5c4',
13
+ c: '99958b', // Soft lower contour; recedes behind eyes and cheeks.
14
+ g: '82989e',
15
+ w: 'd5e7e3',
16
+ d: '6f8b8c',
17
+ };
18
+ exports.LIGHT_PALETTE = {
19
+ h: '24776e',
20
+ m: '245d68',
21
+ e: '276849',
22
+ p: 'a04b6b',
23
+ s: '866049',
24
+ c: 'ab9b8b',
25
+ g: '5b6572',
26
+ w: '526764',
27
+ d: '687776',
28
+ };
29
+ exports.FULL = [
30
+ '{h} _,',
31
+ '{h} /ノ',
32
+ "{h} .---'/`-----.",
33
+ '{h} / / ノ \\ {h}\\',
34
+ '{h} / .ノノ /\\ ヽ {h}\\',
35
+ '{h} / ノ ノ ヽハ {p}* {h}\\',
36
+ '{h} /ノ / ノヽノ ヽハ {h}\\',
37
+ '{h} |:|{g} .---. .---. {h}|:|',
38
+ '{g} ({h}|:|{g}( {e}ィ◕゚{g} )-( {e}゚◕ミ{g} ){h}|:|{g})',
39
+ "{h} |:|{g} '---' '---' {h}|:|",
40
+ '{h} |:| {s}({p}// //{s}) {h}|:|',
41
+ '{h} \\:\\ {s}\\ ᴗ / {h}/:/',
42
+ '{h} \\_\\ {c}\\ / {h}/_/',
43
+ "{m} '--'{c}`.___.'{m}'--'",
44
+ '{g} .-{s}\\_/{g}-.',
45
+ '{g} / {p}>o<{g} \\',
46
+ "{g} '-------'",
47
+ ];
48
+ exports.COMPACT = [
49
+ '{h} _,',
50
+ '{h} /ノ',
51
+ "{h} .-'/`--.",
52
+ '{h} / /ノ\\ {h}\\',
53
+ '{h} /ノノ ヽハ{p}* {h}\\',
54
+ '{g} ({h}|:{g}({e}◕゚{g})-({e}◕゚{g}){h}:|{g})',
55
+ '{h} |:{s}({p}// //{s}){h}:|',
56
+ '{h} \\:{c} \\ {s}ᴗ {c}/ {h}:/',
57
+ "{h} '- {c}`._.' {h}-'",
58
+ '{g} / {p}>o<{g} \\',
59
+ "{g} '-----'",
60
+ ];
61
+ exports.TINY = [
62
+ '{h} .---.',
63
+ '{g} ({h}|{e}o-o{h}|{g})',
64
+ '{h} \\{s} v {h}/',
65
+ "{c} '-'",
66
+ '{g} /{p}><{g}\\',
67
+ ];
68
+ exports.EXPRESSIONS = [
69
+ {
70
+ key: 'little_smile',
71
+ zh: '轻笑',
72
+ en: 'Little smile',
73
+ left: 'ィ◕゚',
74
+ right: '゚◕ミ',
75
+ mouth: 'ᴗ',
76
+ mark: '✧',
77
+ },
78
+ {
79
+ key: 'happy',
80
+ zh: '开心',
81
+ en: 'Happy',
82
+ left: '/^\\',
83
+ right: '/^\\',
84
+ mouth: 'ᴗ',
85
+ mark: '*',
86
+ },
87
+ {
88
+ key: 'wink',
89
+ zh: '眨眼',
90
+ en: 'Wink',
91
+ left: 'ィ◕゚',
92
+ right: ' > ',
93
+ mouth: 'ᴗ',
94
+ mark: '☆',
95
+ },
96
+ {
97
+ key: 'shy',
98
+ zh: '害羞',
99
+ en: 'Shy',
100
+ left: '。◕ ',
101
+ right: ' ◕。',
102
+ mouth: 'ᴗ',
103
+ mark: '',
104
+ },
105
+ {
106
+ key: 'surprised',
107
+ zh: '惊讶',
108
+ en: 'Surprised',
109
+ left: ' O ',
110
+ right: ' O ',
111
+ mouth: 'o',
112
+ mark: '!',
113
+ },
114
+ {
115
+ key: 'head_tilt',
116
+ zh: '歪头',
117
+ en: 'Head tilt',
118
+ left: 'ィ◕゚',
119
+ right: ' o ',
120
+ mouth: 'ー',
121
+ mark: '?',
122
+ },
123
+ {
124
+ key: 'peek',
125
+ zh: '探头',
126
+ en: 'Peek',
127
+ left: '◕゚ ',
128
+ right: '◕゚ ',
129
+ mouth: 'v',
130
+ mark: '',
131
+ },
132
+ {
133
+ key: 'smug',
134
+ zh: '得意',
135
+ en: 'Smug',
136
+ left: ' - ',
137
+ right: ' ◕ ',
138
+ mouth: 'ᴗ',
139
+ mark: '✧',
140
+ },
141
+ {
142
+ key: 'pouting',
143
+ zh: '气鼓鼓',
144
+ en: 'Pouting',
145
+ left: ' > ',
146
+ right: ' < ',
147
+ mouth: '3',
148
+ mark: '!',
149
+ },
150
+ {
151
+ key: 'sad',
152
+ zh: '委屈',
153
+ en: 'Sad',
154
+ left: '。◕ ',
155
+ right: ' ◕。',
156
+ mouth: '~',
157
+ mark: '',
158
+ },
159
+ {
160
+ key: 'crying',
161
+ zh: '哭泣',
162
+ en: 'Crying',
163
+ left: ' T ',
164
+ right: ' T ',
165
+ mouth: '~',
166
+ mark: '',
167
+ },
168
+ {
169
+ key: 'sleepy',
170
+ zh: '困倦',
171
+ en: 'Sleepy',
172
+ left: ' - ',
173
+ right: ' - ',
174
+ mouth: 'o',
175
+ mark: 'z',
176
+ },
177
+ {
178
+ key: 'asleep',
179
+ zh: '熟睡',
180
+ en: 'Asleep',
181
+ left: ' u ',
182
+ right: ' u ',
183
+ mouth: 'ー',
184
+ mark: 'z Z z',
185
+ },
186
+ {
187
+ key: 'excited',
188
+ zh: '期待',
189
+ en: 'Excited',
190
+ left: ' ✧ ',
191
+ right: ' ✧ ',
192
+ mouth: 'ᴗ',
193
+ mark: '*',
194
+ },
195
+ {
196
+ key: 'dizzy',
197
+ zh: '晕乎',
198
+ en: 'Dizzy',
199
+ left: ' @ ',
200
+ right: ' @ ',
201
+ mouth: '~',
202
+ mark: '~',
203
+ },
204
+ {
205
+ key: 'side_eye',
206
+ zh: '小嫌弃',
207
+ en: 'Side eye',
208
+ left: ' ¬ ',
209
+ right: ' ¬ ',
210
+ mouth: 'ー',
211
+ mark: '...',
212
+ },
213
+ ];
214
+ exports.SIZES = {
215
+ full: { width: 38, height: 17 },
216
+ compact: { width: 22, height: 11 },
217
+ tiny: { width: 8, height: 5 },
218
+ };
@@ -0,0 +1,25 @@
1
+ import { ColorMode } from '../terminal';
2
+ import { ColorRole, Expression, MascotSize } from './assets/mint';
3
+ export interface Span {
4
+ role: ColorRole;
5
+ text: string;
6
+ }
7
+ export interface MascotFrame {
8
+ rows: Span[][];
9
+ width: number;
10
+ height: number;
11
+ }
12
+ export interface FrameOptions {
13
+ expression?: Expression;
14
+ size?: MascotSize;
15
+ elapsedMs?: number;
16
+ motion?: boolean;
17
+ ascii?: boolean;
18
+ }
19
+ export declare function plainFrame(frame: MascotFrame): string;
20
+ export declare function getFrame(options?: FrameOptions): MascotFrame;
21
+ export declare function renderFrame(frame: MascotFrame, options?: {
22
+ color?: ColorMode;
23
+ theme?: 'dark' | 'light';
24
+ format?: 'ansi' | 'blessed';
25
+ }): string;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.plainFrame = plainFrame;
4
+ exports.getFrame = getFrame;
5
+ exports.renderFrame = renderFrame;
6
+ const terminal_1 = require("../terminal");
7
+ const mint_1 = require("./assets/mint");
8
+ const ASCII = {
9
+ ノ: '/',
10
+ ノ: '/',
11
+ ヽ: '\\',
12
+ ハ: 'h',
13
+ ィ: ',',
14
+ '◕': 'O',
15
+ ゚: "'",
16
+ '╰': '\\',
17
+ '─': '_',
18
+ '╯': '/',
19
+ ミ: '=',
20
+ ᴗ: 'v',
21
+ ー: '-',
22
+ '。': '.',
23
+ '✧': '*',
24
+ '☆': '*',
25
+ '¬': '-',
26
+ };
27
+ function asciiText(value) {
28
+ return Array.from(value, (char) => {
29
+ if (/^[\x20-\x7e]$/.test(char))
30
+ return char;
31
+ return (ASCII[char] ?? '?').padEnd((0, terminal_1.cellWidth)(char), ' ');
32
+ }).join('');
33
+ }
34
+ function spans(row, ascii) {
35
+ let role = 'w';
36
+ const result = [];
37
+ for (const part of row.split(/(\{[hmespgwdc]\})/)) {
38
+ if (/^\{[hmespgwdc]\}$/.test(part))
39
+ role = part[1];
40
+ else if (part)
41
+ result.push({ role, text: ascii ? asciiText(part) : part });
42
+ }
43
+ return result;
44
+ }
45
+ function plainFrame(frame) {
46
+ return frame.rows.map((row) => row.map((span) => span.text).join('')).join('\n');
47
+ }
48
+ function getFrame(options = {}) {
49
+ const { expression = 'little_smile', size = 'full', elapsedMs = 0, motion = false, ascii = false, } = options;
50
+ const selected = mint_1.EXPRESSIONS.find((item) => item.key === expression);
51
+ const blink = motion && expression === 'little_smile' && elapsedMs % 4000 >= 3800;
52
+ const source = size === 'full' ? mint_1.FULL : size === 'compact' ? mint_1.COMPACT : mint_1.TINY;
53
+ const resting = expression === 'asleep' || expression === 'sleepy';
54
+ const success = expression === 'happy' || expression === 'excited' || expression === 'smug';
55
+ const attention = expression === 'surprised' || expression === 'head_tilt';
56
+ const smallEyes = blink || resting ? '- ' : success ? '^ ' : attention ? 'O ' : '◕゚';
57
+ const rows = source.map((original, index) => {
58
+ let row = original;
59
+ if (size === 'full') {
60
+ row = row
61
+ .replace('ィ◕゚', blink ? ' ー ' : selected.left)
62
+ .replace('゚◕ミ', blink ? ' ー ' : selected.right)
63
+ .replace('ᴗ', selected.mouth);
64
+ // Shift the complete head together, preserving the jaw across expressions.
65
+ if (['wink', 'head_tilt', 'peek'].includes(expression) && index < 14)
66
+ row = ' ' + row;
67
+ if (index === 3 && selected.mark) {
68
+ const used = spans(row, false).reduce((sum, span) => sum + (0, terminal_1.cellWidth)(span.text), 0);
69
+ row += ' '.repeat(Math.max(0, 33 - used)) + '{p}' + selected.mark;
70
+ }
71
+ if (expression === 'crying')
72
+ row = row.replace(/\/\//g, '||');
73
+ }
74
+ else if (size === 'compact') {
75
+ row = row.replace(/◕゚/g, smallEyes).replace('ᴗ', resting ? '-' : attention ? 'o' : 'ᴗ');
76
+ }
77
+ else {
78
+ row = row.replace('o-o', blink || resting ? '---' : success ? '^-^' : attention ? 'O-O' : 'o-o');
79
+ }
80
+ return spans(row, ascii);
81
+ });
82
+ // Every expression shares one canvas, including an ambiguous-width policy.
83
+ const ambiguityExtra = Math.max(0, (0, terminal_1.cellWidth)('◕') - 1);
84
+ const width = mint_1.SIZES[size].width + (size === 'full' ? 3 : 2) * ambiguityExtra;
85
+ for (const row of rows) {
86
+ const used = row.reduce((sum, span) => sum + (0, terminal_1.cellWidth)(span.text), 0);
87
+ if (used > width)
88
+ throw new Error(`MINT ${size} row exceeds its ${width}-column canvas (${used})`);
89
+ row.push({ role: 'w', text: ' '.repeat(width - used) });
90
+ }
91
+ return { rows, width, height: mint_1.SIZES[size].height };
92
+ }
93
+ function renderFrame(frame, options = {}) {
94
+ const color = (0, terminal_1.resolveColor)(options.color);
95
+ if (color === 'never')
96
+ return plainFrame(frame);
97
+ const palette = options.theme === 'light' ? mint_1.LIGHT_PALETTE : mint_1.DARK_PALETTE;
98
+ return frame.rows
99
+ .map((row) => row
100
+ .map((span) => {
101
+ if (!span.text.trim())
102
+ return span.text;
103
+ const hex = palette[span.role];
104
+ if (options.format === 'blessed')
105
+ return `{#${hex}-fg}${span.text}{/}`;
106
+ const [r, g, b] = [0, 2, 4].map((index) => parseInt(hex.slice(index, index + 2), 16));
107
+ const code = color === '256'
108
+ ? `38;5;${16 + 36 * Math.round((r / 255) * 5) + 6 * Math.round((g / 255) * 5) + Math.round((b / 255) * 5)}`
109
+ : `38;2;${r};${g};${b}`;
110
+ return `\x1b[${code}m${span.text}\x1b[0m`;
111
+ })
112
+ .join(''))
113
+ .join('\n');
114
+ }
@@ -0,0 +1,3 @@
1
+ import { Expression } from './assets/mint';
2
+ export type MascotState = 'idle' | 'focused' | 'waiting' | 'needs_input' | 'unclassified' | 'job_succeeded' | 'task_completed' | 'failed' | 'resting';
3
+ export declare function expressionForState(state: MascotState, elapsedMs?: number): Expression;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expressionForState = expressionForState;
4
+ // Only verified application events select these states. A successful job is
5
+ // deliberately separate from completion of the user's entire task.
6
+ function expressionForState(state, elapsedMs = 0) {
7
+ switch (state) {
8
+ case 'waiting':
9
+ return elapsedMs < 700 ? 'peek' : 'head_tilt';
10
+ case 'needs_input':
11
+ return 'surprised';
12
+ case 'unclassified':
13
+ return 'head_tilt';
14
+ case 'job_succeeded':
15
+ return 'happy';
16
+ case 'task_completed':
17
+ return elapsedMs < 350 ? 'excited' : elapsedMs < 1000 ? 'happy' : 'little_smile';
18
+ case 'failed':
19
+ return elapsedMs < 500 ? 'surprised' : 'head_tilt';
20
+ case 'resting':
21
+ return 'asleep';
22
+ default:
23
+ return 'little_smile';
24
+ }
25
+ }
package/dist/mascot.d.ts CHANGED
@@ -1,9 +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
1
  export type CaceMood = 'normal' | 'happy' | 'sleepy' | 'focused' | 'celebrating';
2
+ export declare const CACE_SMALL: string;
3
+ export declare const CACE_HAPPY: string;
4
+ export declare const CACE_SLEEPY: string;
5
+ export declare const CACE_FOCUSED: string;
6
+ export declare const CACE_CELEBRATING: string;
7
7
  export declare function getGreeting(): string;
8
8
  export declare function showCaceAnimation(message?: string): Promise<void>;
9
9
  export declare function showCaceSmall(status?: string, mood?: CaceMood): void;