lazy-skill 0.5.0 → 0.7.0
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/commands/connect.js +2 -10
- package/dist/commands/theme.js +2 -4
- package/dist/index.js +1 -1
- package/dist/ui/banner.js +24 -11
- package/dist/ui/onboarding.js +2 -4
- package/dist/ui/sprite-data.js +273 -0
- package/dist/ui/sprite.js +74 -0
- package/package.json +1 -1
package/dist/commands/connect.js
CHANGED
|
@@ -6,10 +6,10 @@ import { THEMES, style } from "../ui/theme.js";
|
|
|
6
6
|
import { boxWidth, visibleWidth } from "../ui/layout.js";
|
|
7
7
|
import { renderQr } from "../ui/qr.js";
|
|
8
8
|
import { blank, box, centre, fail, hint, line, muted, ok, status } from "../ui/layout.js";
|
|
9
|
-
import { banner
|
|
9
|
+
import { banner } from "../ui/banner.js";
|
|
10
10
|
import { messages } from "../ui/messages.js";
|
|
11
11
|
import { Spinner } from "../ui/spinner.js";
|
|
12
|
-
const VERSION = "0.
|
|
12
|
+
const VERSION = "0.7.0";
|
|
13
13
|
function friendlyPlatform() {
|
|
14
14
|
const p = platform();
|
|
15
15
|
return p === "darwin" || p === "win32" || p === "linux" ? p : "unknown";
|
|
@@ -71,14 +71,6 @@ export async function connectCommand(options) {
|
|
|
71
71
|
const plate = await renderQr(session.pairUrl);
|
|
72
72
|
const width = plate.length ? visibleWidth(plate[0]) + 4 : boxWidth();
|
|
73
73
|
box(theme, plate.map((row) => centre(row, width)), width);
|
|
74
|
-
// Bit sits under the code rather than over it: anything overlapping the
|
|
75
|
-
// module area would cost scan reliability for decoration.
|
|
76
|
-
blank();
|
|
77
|
-
const bit = mascot(theme, false);
|
|
78
|
-
for (let i = 0; i < bit.length; i++) {
|
|
79
|
-
const caption = i === 2 ? ` ${style.gray("waiting for your phone")}` : "";
|
|
80
|
-
console.log(` ${centre(bit[i], width - 18)}${caption}`);
|
|
81
|
-
}
|
|
82
74
|
blank();
|
|
83
75
|
muted(session.pairUrl);
|
|
84
76
|
blank();
|
package/dist/commands/theme.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readConfig, updateConfig } from "../lib/config.js";
|
|
2
2
|
import { THEMES, THEME_IDS, rgb, style } from "../ui/theme.js";
|
|
3
|
-
import { banner
|
|
3
|
+
import { banner } from "../ui/banner.js";
|
|
4
4
|
import { pointer, select } from "../ui/select.js";
|
|
5
5
|
import { blank, muted, ok } from "../ui/layout.js";
|
|
6
6
|
/**
|
|
@@ -30,7 +30,6 @@ export async function themeCommand(requested, version) {
|
|
|
30
30
|
initial: THEME_IDS.indexOf(current),
|
|
31
31
|
render: (index) => {
|
|
32
32
|
const theme = THEMES[THEME_IDS[index]];
|
|
33
|
-
const bit = mascot(theme, true);
|
|
34
33
|
const out = [""];
|
|
35
34
|
out.push(...banner(theme, version));
|
|
36
35
|
out.push("");
|
|
@@ -42,8 +41,7 @@ export async function themeCommand(requested, version) {
|
|
|
42
41
|
// padding a coloured string leaves the column ragged.
|
|
43
42
|
const padded = t.label.padEnd(14);
|
|
44
43
|
const label = active ? style.bold(padded) : style.gray(padded);
|
|
45
|
-
|
|
46
|
-
out.push(` ${pointer(active)} ${mark} ${rgb(t.accent, "████")} ${label}${beside}`);
|
|
44
|
+
out.push(` ${pointer(active)} ${mark} ${rgb(t.accent, "████")} ${label}`);
|
|
47
45
|
});
|
|
48
46
|
out.push("");
|
|
49
47
|
out.push(` ${style.gray("↑ ↓ to move · enter to choose")}`);
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { installCommand } from "./commands/install.js";
|
|
|
10
10
|
import { themeCommand } from "./commands/theme.js";
|
|
11
11
|
import { listenCommand } from "./commands/listen.js";
|
|
12
12
|
import { warnIfOutdated } from "./lib/version-check.js";
|
|
13
|
-
const VERSION = "0.
|
|
13
|
+
const VERSION = "0.7.0";
|
|
14
14
|
function help() {
|
|
15
15
|
const theme = THEMES[readConfig().theme];
|
|
16
16
|
const cmd = (name) => rgb(theme.accent, name.padEnd(26));
|
package/dist/ui/banner.js
CHANGED
|
@@ -41,21 +41,34 @@ export function banner(theme, version) {
|
|
|
41
41
|
return lines;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
* Bit,
|
|
44
|
+
* Bit, hand-drawn.
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* The generated 32x32 sprite is right for the phone, where there is
|
|
47
|
+
* resolution to spare. In a terminal it turned to mush: ellipses at that
|
|
48
|
+
* scale produce a cream blob with dark smears, and no amount of nudging the
|
|
49
|
+
* shapes fixed it. This is drawn character by character instead — a hood, a
|
|
50
|
+
* face, two closed eyes, a nose and a smile — which is far fewer pixels but
|
|
51
|
+
* reads instantly, which is the only thing that matters here.
|
|
52
|
+
*
|
|
53
|
+
* Every row is exactly 13 cells wide. Ragged rows shear the silhouette, and
|
|
54
|
+
* the colour codes make that impossible to spot by eye.
|
|
48
55
|
*/
|
|
56
|
+
const FACE = [227, 201, 164];
|
|
57
|
+
const FEATURE = [70, 48, 35];
|
|
49
58
|
export function mascot(theme, awake = false) {
|
|
50
59
|
const hood = (t) => rgb(theme.accent, t);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
60
|
+
const face = (t) => rgb(FACE, t);
|
|
61
|
+
const mark = (t) => rgb(FEATURE, t);
|
|
62
|
+
const zzz = (t) => rgb(theme.support, t);
|
|
63
|
+
// Open eyes are dots; closed eyes are the lower half of a block, which
|
|
64
|
+
// reads as a lid rather than a gap.
|
|
65
|
+
const eyes = awake ? mark("●●") : mark("▄▄");
|
|
54
66
|
return [
|
|
55
|
-
hood("
|
|
56
|
-
hood("
|
|
57
|
-
hood(" █ ") + eyes + hood("
|
|
58
|
-
hood("
|
|
59
|
-
hood("
|
|
67
|
+
hood(" ▄▄▄▄▄▄▄ "),
|
|
68
|
+
hood(" █") + face("███████") + hood("█ "),
|
|
69
|
+
hood(" █") + face("█") + eyes + face("█") + eyes + face("█") + hood("█ ") + (awake ? "" : zzz("z")),
|
|
70
|
+
hood(" █") + face("███") + mark("▾") + face("███") + hood("█ ") + (awake ? "" : zzz("z")),
|
|
71
|
+
hood(" █") + face("██") + mark("‿‿‿") + face("██") + hood("█ "),
|
|
72
|
+
hood(" ▀▀▀▀▀▀▀ "),
|
|
60
73
|
];
|
|
61
74
|
}
|
package/dist/ui/onboarding.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { THEMES, THEME_IDS, rgb, style } from "./theme.js";
|
|
2
|
-
import { banner
|
|
2
|
+
import { banner } from "./banner.js";
|
|
3
3
|
import { pointer, select } from "./select.js";
|
|
4
4
|
/**
|
|
5
5
|
* First-run theme choice, with live preview.
|
|
@@ -14,7 +14,6 @@ export async function chooseTheme(version) {
|
|
|
14
14
|
const rows = (index) => {
|
|
15
15
|
const id = THEME_IDS[index];
|
|
16
16
|
const theme = THEMES[id];
|
|
17
|
-
const bit = mascot(theme, true);
|
|
18
17
|
const out = [""];
|
|
19
18
|
out.push(...banner(theme, version));
|
|
20
19
|
out.push("");
|
|
@@ -29,8 +28,7 @@ export async function chooseTheme(version) {
|
|
|
29
28
|
const padded = t.label.padEnd(14);
|
|
30
29
|
const label = active ? style.bold(padded) : style.gray(padded);
|
|
31
30
|
// The mascot sits beside the list, aligned to its middle rows.
|
|
32
|
-
|
|
33
|
-
out.push(` ${pointer(active)} ${swatch} ${label}${beside}`);
|
|
31
|
+
out.push(` ${pointer(active)} ${swatch} ${label}`);
|
|
34
32
|
});
|
|
35
33
|
out.push("");
|
|
36
34
|
out.push(` ${style.gray("↑ ↓ to move · enter to choose")}`);
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// GENERATED by scripts/build-sprite.mjs — do not edit by hand.
|
|
2
|
+
// Run `pnpm sprite` after changing scripts/gen-sloth.mjs.
|
|
3
|
+
/**
|
|
4
|
+
* BIT — the Lazy Skill mascot. A sleepy sloth in a hoodie.
|
|
5
|
+
*
|
|
6
|
+
* Stored as a 32x32 character grid rather than SVG paths so one source
|
|
7
|
+
* scales crisply to any size, retints with the active theme, and swaps
|
|
8
|
+
* expression without a second asset.
|
|
9
|
+
*
|
|
10
|
+
* Legend: . transparent | o outline | H hood | h hood shadow | L hood light
|
|
11
|
+
* F fur | M muzzle | P eye patch | e eye | n nose | w eye white
|
|
12
|
+
*/
|
|
13
|
+
export const SPRITE_SIZE = 32;
|
|
14
|
+
/** Palette keys resolve against CSS custom properties, so themes apply free. */
|
|
15
|
+
/** Terminal colours are literal RGB — no CSS variables to resolve. The hood
|
|
16
|
+
* is substituted per theme at render time; everything else is fixed. */
|
|
17
|
+
export const PX = {
|
|
18
|
+
".": null,
|
|
19
|
+
o: "#120d1c",
|
|
20
|
+
H: "var(--ls-accent)",
|
|
21
|
+
h: "var(--ls-accent-lo)",
|
|
22
|
+
L: "var(--ls-accent-hi)",
|
|
23
|
+
F: "#e3c9a4",
|
|
24
|
+
M: "#f6e6cd",
|
|
25
|
+
P: "#5b4130",
|
|
26
|
+
e: "#2c1e16",
|
|
27
|
+
n: "#3d2b20",
|
|
28
|
+
w: "#fdf6ea",
|
|
29
|
+
};
|
|
30
|
+
export const EXPRESSIONS = ["idle", "curious", "waiting", "happy", "working", "excited", "annoyed"];
|
|
31
|
+
export const SPRITES = {
|
|
32
|
+
idle: [
|
|
33
|
+
"................................",
|
|
34
|
+
"................................",
|
|
35
|
+
"................o...............",
|
|
36
|
+
"............ooooHoooo...........",
|
|
37
|
+
"..........ooLLLLLLhhhoo.........",
|
|
38
|
+
"........ooLLLLLLLhhhhhhoo.......",
|
|
39
|
+
".......oLLLLLLLLhhhhhhhhho......",
|
|
40
|
+
"......oLLLLLLLLhhhhhhhhhhho.....",
|
|
41
|
+
"......oHLLLLLLFFFFFhhhhhhho.....",
|
|
42
|
+
".....oHHHHHFFFFFFFFFFFhhhhho....",
|
|
43
|
+
".....oHHHHFFFFFFFFFFFFFhhhho....",
|
|
44
|
+
".....oHHHFFFFFFFFFFFFFFFhhho....",
|
|
45
|
+
".....oHHFFFFFFFFFFFFFFFFFhho....",
|
|
46
|
+
"....oHHHFFPPPPFFFFFPPPPFFhhho...",
|
|
47
|
+
"....oHHHFPwwwwFFFFFwwwwPFhho....",
|
|
48
|
+
"....oHHFFPeeeeFFFFFeeeePFFho....",
|
|
49
|
+
"....oHHFFPPPPPFFFFFPPPPPFFho....",
|
|
50
|
+
"....oHFFPPPFFFFFFFFFFFPPPFFo....",
|
|
51
|
+
"....oHFFFPPFFFnnnnMFFFPPFFFo....",
|
|
52
|
+
"....oHHHFFFFMMnnnnMMMFFFFhho....",
|
|
53
|
+
"....oHFFFFFFMMMnnMMMMFFFFFFo....",
|
|
54
|
+
"....oHFFFFFFMnMMMMnMMFFFFFFo....",
|
|
55
|
+
"....oHHHHHFFMMnnnnMMMFFhhhho....",
|
|
56
|
+
"....oHHHHHHFFFMMMMMFFFhhhhho....",
|
|
57
|
+
"....oHHHHHHHHHFFFFFhhhhhhhho....",
|
|
58
|
+
"....ooHHHHHHHHHHHhhhhhhhhhoo....",
|
|
59
|
+
"......oHHHHHHHHHHHhhhhhhho......",
|
|
60
|
+
"....ooHHHHHHHHHHHHHHHHhHHHoo....",
|
|
61
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
62
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
63
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
64
|
+
"...oooooooooooooooooooooooooo...",
|
|
65
|
+
],
|
|
66
|
+
curious: [
|
|
67
|
+
"................................",
|
|
68
|
+
"................................",
|
|
69
|
+
"................o...............",
|
|
70
|
+
"............ooooHoooo...........",
|
|
71
|
+
"..........ooLLLLLLhhhoo.........",
|
|
72
|
+
"........ooLLLLLLLhhhhhhoo.......",
|
|
73
|
+
".......oLLLLLLLLhhhhhhhhho......",
|
|
74
|
+
"......oLLLLLLLLhhhhhhhhhhho.....",
|
|
75
|
+
"......oHLLLLLLFFFFFhhhhhhho.....",
|
|
76
|
+
".....oHHHHHFFFFFFFFFFFhhhhho....",
|
|
77
|
+
".....oHHHHFFFFFFFFFFFFFhhhho....",
|
|
78
|
+
".....oHHHFFFFFFFFFFFFFFFhhho....",
|
|
79
|
+
".....oHHFFFFFFFFFFFFFFFFFhho....",
|
|
80
|
+
"....oHHHFFPwwPFFFFFPwwPFFhhho...",
|
|
81
|
+
"....oHHHFPwwewFFFFFwwewPFhho....",
|
|
82
|
+
"....oHHFFPweewFFFFFweewPFFho....",
|
|
83
|
+
"....oHHFFPPwwPFFFFFPwwPPFFho....",
|
|
84
|
+
"....oHFFPPPFFFFFFFFFFFPPPFFo....",
|
|
85
|
+
"....oHFFFPPFFFnnnnMFFFPPFFFo....",
|
|
86
|
+
"....oHHHFFFFMMnnnnMMMFFFFhho....",
|
|
87
|
+
"....oHFFFFFFMMMnnMMMMFFFFFFo....",
|
|
88
|
+
"....oHFFFFFFMnMMMMnMMFFFFFFo....",
|
|
89
|
+
"....oHHHHHFFMMnnnnMMMFFhhhho....",
|
|
90
|
+
"....oHHHHHHFFFMMMMMFFFhhhhho....",
|
|
91
|
+
"....oHHHHHHHHHFFFFFhhhhhhhho....",
|
|
92
|
+
"....ooHHHHHHHHHHHhhhhhhhhhoo....",
|
|
93
|
+
"......oHHHHHHHHHHHhhhhhhho......",
|
|
94
|
+
"....ooHHHHHHHHHHHHHHHHhHHHoo....",
|
|
95
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
96
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
97
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
98
|
+
"...oooooooooooooooooooooooooo...",
|
|
99
|
+
],
|
|
100
|
+
waiting: [
|
|
101
|
+
"................................",
|
|
102
|
+
"................................",
|
|
103
|
+
"................o...............",
|
|
104
|
+
"............ooooHoooo...........",
|
|
105
|
+
"..........ooLLLLLLhhhoo.........",
|
|
106
|
+
"........ooLLLLLLLhhhhhhoo.......",
|
|
107
|
+
".......oLLLLLLLLhhhhhhhhho......",
|
|
108
|
+
"......oLLLLLLLLhhhhhhhhhhho.....",
|
|
109
|
+
"......oHLLLLLLFFFFFhhhhhhho.....",
|
|
110
|
+
".....oHHHHHFFFFFFFFFFFhhhhho....",
|
|
111
|
+
".....oHHHHFFFFFFFFFFFFFhhhho....",
|
|
112
|
+
".....oHHHFFFFFFFFFFFFFFFhhho....",
|
|
113
|
+
".....oHHFFFFFFFFFFFFFFFFFhho....",
|
|
114
|
+
"....oHHHFFPPPPFFFFFPPPPFFhhho...",
|
|
115
|
+
"....oHHHFPeeeeFFFFFwwwwPFhho....",
|
|
116
|
+
"....oHHFFPeeeeFFFFFeeeePFFho....",
|
|
117
|
+
"....oHHFFPPPPPFFFFFPPPPPFFho....",
|
|
118
|
+
"....oHFFPPPFFFFFFFFFFFPPPFFo....",
|
|
119
|
+
"....oHFFFPPFFFnnnnMFFFPPFFFo....",
|
|
120
|
+
"....oHHHFFFFMMnnnnMMMFFFFhho....",
|
|
121
|
+
"....oHFFFFFFMMMnnMMMMFFFFFFo....",
|
|
122
|
+
"....oHFFFFFFMnMMMMnMMFFFFFFo....",
|
|
123
|
+
"....oHHHHHFFMMnnnnMMMFFhhhho....",
|
|
124
|
+
"....oHHHHHHFFFMMMMMFFFhhhhho....",
|
|
125
|
+
"....oHHHHHHHHHFFFFFhhhhhhhho....",
|
|
126
|
+
"....ooHHHHHHHHHHHhhhhhhhhhoo....",
|
|
127
|
+
"......oHHHHHHHHHHHhhhhhhho......",
|
|
128
|
+
"....ooHHHHHHHHHHHHHHHHhHHHoo....",
|
|
129
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
130
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
131
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
132
|
+
"...oooooooooooooooooooooooooo...",
|
|
133
|
+
],
|
|
134
|
+
happy: [
|
|
135
|
+
"................................",
|
|
136
|
+
"................................",
|
|
137
|
+
"................o...............",
|
|
138
|
+
"............ooooHoooo...........",
|
|
139
|
+
"..........ooLLLLLLhhhoo.........",
|
|
140
|
+
"........ooLLLLLLLhhhhhhoo.......",
|
|
141
|
+
".......oLLLLLLLLhhhhhhhhho......",
|
|
142
|
+
"......oLLLLLLLLhhhhhhhhhhho.....",
|
|
143
|
+
"......oHLLLLLLFFFFFhhhhhhho.....",
|
|
144
|
+
".....oHHHHHFFFFFFFFFFFhhhhho....",
|
|
145
|
+
".....oHHHHFFFFFFFFFFFFFhhhho....",
|
|
146
|
+
".....oHHHFFFFFFFFFFFFFFFhhho....",
|
|
147
|
+
".....oHHFFFFFFFFFFFFFFFFFhho....",
|
|
148
|
+
"....oHHHFFPPPPFFFFFPPPPFFhhho...",
|
|
149
|
+
"....oHHHFPPwwPFFFFFPwePPFhho....",
|
|
150
|
+
"....oHHFFPweePFFFFFPeewPFFho....",
|
|
151
|
+
"....oHHFFPPwPPFFFFFPPwPPFFho....",
|
|
152
|
+
"....oHFFPPPFFFFFFFFFFFPPPFFo....",
|
|
153
|
+
"....oHFFFPPFFFnnnnMFFFPPFFFo....",
|
|
154
|
+
"....oHHHFFFFMMnnnnMMMFFFFhho....",
|
|
155
|
+
"....oHFFFFFFMMMnnMMMMFFFFFFo....",
|
|
156
|
+
"....oHFFFFFFMnMMMMnMMFFFFFFo....",
|
|
157
|
+
"....oHHHHHFFMMnnnnMMMFFhhhho....",
|
|
158
|
+
"....oHHHHHHFFFMMMMMFFFhhhhho....",
|
|
159
|
+
"....oHHHHHHHHHFFFFFhhhhhhhho....",
|
|
160
|
+
"....ooHHHHHHHHHHHhhhhhhhhhoo....",
|
|
161
|
+
"......oHHHHHHHHHHHhhhhhhho......",
|
|
162
|
+
"....ooHHHHHHHHHHHHHHHHhHHHoo....",
|
|
163
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
164
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
165
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
166
|
+
"...oooooooooooooooooooooooooo...",
|
|
167
|
+
],
|
|
168
|
+
working: [
|
|
169
|
+
"................................",
|
|
170
|
+
"................................",
|
|
171
|
+
"................o...............",
|
|
172
|
+
"............ooooHoooo...........",
|
|
173
|
+
"..........ooLLLLLLhhhoo.........",
|
|
174
|
+
"........ooLLLLLLLhhhhhhoo.......",
|
|
175
|
+
".......oLLLLLLLLhhhhhhhhho......",
|
|
176
|
+
"......oLLLLLLLLhhhhhhhhhhho.....",
|
|
177
|
+
"......oHLLLLLLFFFFFhhhhhhho.....",
|
|
178
|
+
".....oHHHHHFFFFFFFFFFFhhhhho....",
|
|
179
|
+
".....oHHHHFFFFFFFFFFFFFhhhho....",
|
|
180
|
+
".....oHHHFFFFFFFFFFFFFFFhhho....",
|
|
181
|
+
".....oHHFFFFFFFFFFFFFFFFFhho....",
|
|
182
|
+
"....oHHHFFPPPPFFFFFPPPPFFhhho...",
|
|
183
|
+
"....oHHHFPwwwwFFFFFwwwwPFhho....",
|
|
184
|
+
"....oHHFFPPPPPFFFFFPPPPPFFho....",
|
|
185
|
+
"....oHHFFPPPPPFFFFFPPPPPFFho....",
|
|
186
|
+
"....oHFFPPPFFFFFFFFFFFPPPFFo....",
|
|
187
|
+
"....oHFFFPPFFFnnnnMFFFPPFFFo....",
|
|
188
|
+
"....oHHHFFFFMMnnnnMMMFFFFhho....",
|
|
189
|
+
"....oHFFFFFFMMMnnMMMMFFFFFFo....",
|
|
190
|
+
"....oHFFFFFFMnMMMMnMMFFFFFFo....",
|
|
191
|
+
"....oHHHHHFFMMnnnnMMMFFhhhho....",
|
|
192
|
+
"....oHHHHHHFFFMMMMMFFFhhhhho....",
|
|
193
|
+
"....oHHHHHHHHHFFFFFhhhhhhhho....",
|
|
194
|
+
"....ooHHHHHHHHHHHhhhhhhhhhoo....",
|
|
195
|
+
"......oHHHHHHHHHHHhhhhhhho......",
|
|
196
|
+
"....ooHHHHHHHHHHHHHHHHhHHHoo....",
|
|
197
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
198
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
199
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
200
|
+
"...oooooooooooooooooooooooooo...",
|
|
201
|
+
],
|
|
202
|
+
excited: [
|
|
203
|
+
"................................",
|
|
204
|
+
"................................",
|
|
205
|
+
"................o...............",
|
|
206
|
+
"............ooooHoooo...........",
|
|
207
|
+
"..........ooLLLLLLhhhoo.........",
|
|
208
|
+
"........ooLLLLLLLhhhhhhoo.......",
|
|
209
|
+
".......oLLLLLLLLhhhhhhhhho......",
|
|
210
|
+
"......oLLLLLLLLhhhhhhhhhhho.....",
|
|
211
|
+
"......oHLLLLLLFFFFFhhhhhhho.....",
|
|
212
|
+
".....oHHHHHFFFFFFFFFFFhhhhho....",
|
|
213
|
+
".....oHHHHFFFFFFFFFFFFFhhhho....",
|
|
214
|
+
".....oHHHFFFFFFFFFFFFFFFhhho....",
|
|
215
|
+
".....oHHFFFFFFFFFFFFFFFFFhho....",
|
|
216
|
+
"....oHHHFFPPPPFFFFFPPPPFFhhho...",
|
|
217
|
+
"....oHHHFPPwwPFFFFFPwePPFhho....",
|
|
218
|
+
"....oHHFFPweePFFFFFPeewPFFho....",
|
|
219
|
+
"....oHHFFPPwPPFFFFFPPwPPFFho....",
|
|
220
|
+
"....oHFFPPPFFFFFFFFFFFPPPFFo....",
|
|
221
|
+
"....oHFFFPPFFFnnnnMFFFPPFFFo....",
|
|
222
|
+
"....oHHHFFFFMMnnnnMMMFFFFhho....",
|
|
223
|
+
"....oHFFFFFFMMMnnMMMMFFFFFFo....",
|
|
224
|
+
"....oHFFFFFFMnMMMMnMMFFFFFFo....",
|
|
225
|
+
"....oHHHHHFFMMnnnnMMMFFhhhho....",
|
|
226
|
+
"....oHHHHHHFFFMMMMMFFFhhhhho....",
|
|
227
|
+
"....oHHHHHHHHHFFFFFhhhhhhhho....",
|
|
228
|
+
"....ooHHHHHHHHHHHhhhhhhhhhoo....",
|
|
229
|
+
"......oHHHHHHHHHHHhhhhhhho......",
|
|
230
|
+
"....ooHHHHHHHHHHHHHHHHhHHHoo....",
|
|
231
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
232
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
233
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
234
|
+
"...oooooooooooooooooooooooooo...",
|
|
235
|
+
],
|
|
236
|
+
annoyed: [
|
|
237
|
+
"................................",
|
|
238
|
+
"................................",
|
|
239
|
+
"................o...............",
|
|
240
|
+
"............ooooHoooo...........",
|
|
241
|
+
"..........ooLLLLLLhhhoo.........",
|
|
242
|
+
"........ooLLLLLLLhhhhhhoo.......",
|
|
243
|
+
".......oLLLLLLLLhhhhhhhhho......",
|
|
244
|
+
"......oLLLLLLLLhhhhhhhhhhho.....",
|
|
245
|
+
"......oHLLLLLLFFFFFhhhhhhho.....",
|
|
246
|
+
".....oHHHHHFFFFFFFFFFFhhhhho....",
|
|
247
|
+
".....oHHHHFFFFFFFFFFFFFhhhho....",
|
|
248
|
+
".....oHHHFFFFFFFFFFFFFFFhhho....",
|
|
249
|
+
".....oHHFFFFFFFFFFFFFFFFFhho....",
|
|
250
|
+
"....oHHHFFPPPPFFFFFPPPPFFhhho...",
|
|
251
|
+
"....oHHHFPwwwwFFFFFwwwwPFhho....",
|
|
252
|
+
"....oHHFFPPPPPFFFFFPPPPPFFho....",
|
|
253
|
+
"....oHHFFPPPPPFFFFFPPPPPFFho....",
|
|
254
|
+
"....oHFFPPPFFFFFFFFFFFPPPFFo....",
|
|
255
|
+
"....oHFFFPPFFFnnnnMFFFPPFFFo....",
|
|
256
|
+
"....oHHHFFFFMMnnnnMMMFFFFhho....",
|
|
257
|
+
"....oHFFFFFFMMMnnMMMMFFFFFFo....",
|
|
258
|
+
"....oHFFFFFFMnMMMMnMMFFFFFFo....",
|
|
259
|
+
"....oHHHHHFFMMnnnnMMMFFhhhho....",
|
|
260
|
+
"....oHHHHHHFFFMMMMMFFFhhhhho....",
|
|
261
|
+
"....oHHHHHHHHHFFFFFhhhhhhhho....",
|
|
262
|
+
"....ooHHHHHHHHHHHhhhhhhhhhoo....",
|
|
263
|
+
"......oHHHHHHHHHHHhhhhhhho......",
|
|
264
|
+
"....ooHHHHHHHHHHHHHHHHhHHHoo....",
|
|
265
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
266
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
267
|
+
"...oHHHHHHHHHHHHHHHHHHHHHHHHo...",
|
|
268
|
+
"...oooooooooooooooooooooooooo...",
|
|
269
|
+
],
|
|
270
|
+
};
|
|
271
|
+
export function spriteRows(expression) {
|
|
272
|
+
return SPRITES[expression] ?? SPRITES.idle;
|
|
273
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { hasColor } from "./theme.js";
|
|
2
|
+
import { PX, SPRITE_SIZE, spriteRows } from "./sprite-data.js";
|
|
3
|
+
function parseHex(hex) {
|
|
4
|
+
const m = /^#([0-9a-f]{6})$/i.exec(hex.trim());
|
|
5
|
+
if (!m)
|
|
6
|
+
return null;
|
|
7
|
+
const n = Number.parseInt(m[1], 16);
|
|
8
|
+
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
9
|
+
}
|
|
10
|
+
/** Palette key -> literal colour, with the hood taking the active theme. */
|
|
11
|
+
function colourFor(key, theme) {
|
|
12
|
+
if (key === "." || key === undefined)
|
|
13
|
+
return null;
|
|
14
|
+
// The hood is the only themed part. Its highlight and shadow are derived
|
|
15
|
+
// from the accent rather than reusing the support hue — teal highlights on
|
|
16
|
+
// a purple hood read as a different material, not a lit edge.
|
|
17
|
+
if (key === "H")
|
|
18
|
+
return theme.accent;
|
|
19
|
+
if (key === "L") {
|
|
20
|
+
const [r, g, b] = theme.accent;
|
|
21
|
+
const lift = (c) => Math.round(c + (255 - c) * 0.42);
|
|
22
|
+
return [lift(r), lift(g), lift(b)];
|
|
23
|
+
}
|
|
24
|
+
if (key === "h") {
|
|
25
|
+
const [r, g, b] = theme.accent;
|
|
26
|
+
return [Math.round(r * 0.52), Math.round(g * 0.52), Math.round(b * 0.52)];
|
|
27
|
+
}
|
|
28
|
+
const value = PX[key];
|
|
29
|
+
return typeof value === "string" ? parseHex(value) : null;
|
|
30
|
+
}
|
|
31
|
+
const fg = ([r, g, b]) => `\x1b[38;2;${r};${g};${b}m`;
|
|
32
|
+
const bg = ([r, g, b]) => `\x1b[48;2;${r};${g};${b}m`;
|
|
33
|
+
const RESET = "\x1b[0m";
|
|
34
|
+
export function sprite(theme, expression = "idle", { small = false } = {}) {
|
|
35
|
+
const grid = spriteRows(expression);
|
|
36
|
+
const step = small ? 2 : 1;
|
|
37
|
+
const size = SPRITE_SIZE;
|
|
38
|
+
// Without colour there is nothing to draw with — two block characters would
|
|
39
|
+
// be an unreadable smear, so the sprite is simply omitted.
|
|
40
|
+
if (!hasColor)
|
|
41
|
+
return [];
|
|
42
|
+
const lines = [];
|
|
43
|
+
// Two pixel rows per output line.
|
|
44
|
+
for (let y = 0; y < size; y += step * 2) {
|
|
45
|
+
let out = "";
|
|
46
|
+
let openBg = false;
|
|
47
|
+
for (let x = 0; x < size; x += step) {
|
|
48
|
+
const top = colourFor(grid[y]?.[x], theme);
|
|
49
|
+
const bottom = colourFor(grid[y + step]?.[x], theme);
|
|
50
|
+
if (!top && !bottom) {
|
|
51
|
+
if (openBg) {
|
|
52
|
+
out += RESET;
|
|
53
|
+
openBg = false;
|
|
54
|
+
}
|
|
55
|
+
out += " ";
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (top && bottom) {
|
|
59
|
+
out += `${bg(bottom)}${fg(top)}▀${RESET}`;
|
|
60
|
+
openBg = false;
|
|
61
|
+
}
|
|
62
|
+
else if (top) {
|
|
63
|
+
out += `${fg(top)}▀${RESET}`;
|
|
64
|
+
}
|
|
65
|
+
else if (bottom) {
|
|
66
|
+
out += `${fg(bottom)}▄${RESET}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (openBg)
|
|
70
|
+
out += RESET;
|
|
71
|
+
lines.push(out);
|
|
72
|
+
}
|
|
73
|
+
return lines;
|
|
74
|
+
}
|