drexler 0.2.19 → 0.2.21
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/package.json +1 -1
- package/src/ui/App.tsx +23 -1
- package/src/ui/MascotIntro.tsx +1 -1
- package/src/ui/PetPanel.tsx +185 -33
package/package.json
CHANGED
package/src/ui/App.tsx
CHANGED
|
@@ -910,6 +910,21 @@ export function App({
|
|
|
910
910
|
return;
|
|
911
911
|
}
|
|
912
912
|
if (isSlash(line)) {
|
|
913
|
+
// Bare /theme, /model, /startup, /retry, /export — repopulate the
|
|
914
|
+
// input with "<cmd> " so the palette catches the argument chooser
|
|
915
|
+
// and the user picks via ↑↓ + Enter. Avoids the "print current
|
|
916
|
+
// value and dead-end" feeling of dispatching the base command.
|
|
917
|
+
const lower = line.toLowerCase();
|
|
918
|
+
if (isArgumentParentCommand(lower)) {
|
|
919
|
+
const filled = `${lower} `;
|
|
920
|
+
updateDraft({ value: filled, cursor: graphemeLength(filled) });
|
|
921
|
+
setPaletteIdx(0);
|
|
922
|
+
addItem(
|
|
923
|
+
"system",
|
|
924
|
+
`Pick a ${lower.slice(1)} option below — ↑↓ to choose, Enter to apply, Esc to cancel.`,
|
|
925
|
+
);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
913
928
|
await handleSlashWithMutation(line);
|
|
914
929
|
return;
|
|
915
930
|
}
|
|
@@ -918,7 +933,14 @@ export function App({
|
|
|
918
933
|
setMsgCount(conversation.length);
|
|
919
934
|
await runLLM();
|
|
920
935
|
},
|
|
921
|
-
[
|
|
936
|
+
[
|
|
937
|
+
addItem,
|
|
938
|
+
conversation,
|
|
939
|
+
handleSlashWithMutation,
|
|
940
|
+
runLLM,
|
|
941
|
+
setPaletteIdx,
|
|
942
|
+
updateDraft,
|
|
943
|
+
],
|
|
922
944
|
);
|
|
923
945
|
|
|
924
946
|
const reportSubmitError = useCallback(
|
package/src/ui/MascotIntro.tsx
CHANGED
|
@@ -171,7 +171,7 @@ const RIGHT_COLUMN_PAD_RIGHT = 1;
|
|
|
171
171
|
const LEFT_PANEL_MIN_COPY = 24;
|
|
172
172
|
const PET_STATS_MIN_WIDTH = 24;
|
|
173
173
|
const PET_STATS_MAX_WIDTH = 58;
|
|
174
|
-
const PET_SPLIT_DIVIDER_HEIGHT =
|
|
174
|
+
const PET_SPLIT_DIVIDER_HEIGHT = 19;
|
|
175
175
|
const PET_SPLIT_DIVIDER_ROWS: number[] = Array.from(
|
|
176
176
|
{ length: PET_SPLIT_DIVIDER_HEIGHT },
|
|
177
177
|
(_, i) => i,
|
package/src/ui/PetPanel.tsx
CHANGED
|
@@ -18,14 +18,17 @@ export type Environment = "office" | "home" | "outdoors";
|
|
|
18
18
|
|
|
19
19
|
const PANEL_BORDER_COLUMNS = 2;
|
|
20
20
|
const PANEL_PADDING_COLUMNS = 2;
|
|
21
|
-
const SCENE_ROWS =
|
|
21
|
+
const SCENE_ROWS = 18;
|
|
22
22
|
const R_WALL = 0;
|
|
23
23
|
const R_WINDOW_TOP = 1;
|
|
24
|
-
const R_WINDOW_BOTTOM =
|
|
25
|
-
const R_ACTIVITY =
|
|
26
|
-
const R_MASCOT_START =
|
|
24
|
+
const R_WINDOW_BOTTOM = 4;
|
|
25
|
+
const R_ACTIVITY = 5;
|
|
26
|
+
const R_MASCOT_START = 6;
|
|
27
27
|
const R_DESK_SURFACE = R_MASCOT_START + BRIEFCASE_FINAL.length;
|
|
28
28
|
const R_DESK_FRONT = R_DESK_SURFACE + 1;
|
|
29
|
+
const R_DESK_DRAWERS = R_DESK_FRONT + 1;
|
|
30
|
+
const R_DESK_BOTTOM = R_DESK_DRAWERS + 1;
|
|
31
|
+
const R_FLOOR = R_DESK_BOTTOM + 1;
|
|
29
32
|
|
|
30
33
|
export const PET_SCENE_WIDTH = 52;
|
|
31
34
|
|
|
@@ -36,6 +39,14 @@ function place(base: string, text: string, x: number): string {
|
|
|
36
39
|
return base.slice(0, x) + fit + base.slice(end);
|
|
37
40
|
}
|
|
38
41
|
|
|
42
|
+
function placeSprite(rows: string[], row: number, x: number, sprite: readonly string[]): void {
|
|
43
|
+
for (let i = 0; i < sprite.length; i++) {
|
|
44
|
+
const targetRow = row + i;
|
|
45
|
+
if (targetRow < 0 || targetRow >= rows.length) continue;
|
|
46
|
+
rows[targetRow] = place(rows[targetRow] ?? "", sprite[i] ?? "", x);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
function blankRow(width: number): string {
|
|
40
51
|
return " ".repeat(width);
|
|
41
52
|
}
|
|
@@ -46,6 +57,13 @@ function padDisplayText(input: string, width: number): string {
|
|
|
46
57
|
return `${fitted}${" ".repeat(Math.max(0, safeWidth - displayWidth(fitted)))}`;
|
|
47
58
|
}
|
|
48
59
|
|
|
60
|
+
function centerPadDisplayText(input: string, width: number): string {
|
|
61
|
+
const safeWidth = Math.max(1, width);
|
|
62
|
+
const fitted = fitDisplayText(input, safeWidth);
|
|
63
|
+
const left = Math.max(0, Math.floor((safeWidth - displayWidth(fitted)) / 2));
|
|
64
|
+
return `${" ".repeat(left)}${fitted}${" ".repeat(Math.max(0, safeWidth - left - displayWidth(fitted)))}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
49
67
|
function overlayFitted(row: string, text: string, x: number, width: number): string {
|
|
50
68
|
return place(row, padDisplayText(text, width), x);
|
|
51
69
|
}
|
|
@@ -73,23 +91,33 @@ function sceneBoxBottom(width: number): string {
|
|
|
73
91
|
return `╰${"─".repeat(safeWidth - 2)}╯`;
|
|
74
92
|
}
|
|
75
93
|
|
|
76
|
-
function
|
|
94
|
+
function placeBoxLines(
|
|
77
95
|
rows: string[],
|
|
78
96
|
row: number,
|
|
79
97
|
x: number,
|
|
80
98
|
width: number,
|
|
81
99
|
title: string,
|
|
82
|
-
body: string,
|
|
100
|
+
body: readonly string[],
|
|
83
101
|
): void {
|
|
84
102
|
rows[row] = place(rows[row] ?? "", sceneBoxTop(title, width), x);
|
|
85
|
-
|
|
86
|
-
|
|
103
|
+
for (let i = 0; i < body.length; i++) {
|
|
104
|
+
rows[row + i + 1] = place(
|
|
105
|
+
rows[row + i + 1] ?? "",
|
|
106
|
+
sceneBoxBody(body[i] ?? "", width),
|
|
107
|
+
x,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
rows[row + body.length + 1] = place(
|
|
111
|
+
rows[row + body.length + 1] ?? "",
|
|
112
|
+
sceneBoxBottom(width),
|
|
113
|
+
x,
|
|
114
|
+
);
|
|
87
115
|
}
|
|
88
116
|
|
|
89
117
|
function cupForEnergy(energy: number): string {
|
|
90
|
-
if (energy > 60) return "
|
|
91
|
-
if (energy > 30) return "
|
|
92
|
-
return "
|
|
118
|
+
if (energy > 60) return "c~";
|
|
119
|
+
if (energy > 30) return "c-";
|
|
120
|
+
return "c_";
|
|
93
121
|
}
|
|
94
122
|
|
|
95
123
|
function progressTicker(frame: number): string {
|
|
@@ -196,27 +224,81 @@ function drawOfficeBackground(rows: string[], width: number, frame: number, stat
|
|
|
196
224
|
Math.max(0, width - displayWidth(`pipe ${dealPct}`) - 1),
|
|
197
225
|
);
|
|
198
226
|
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
227
|
+
const compact = width < 62;
|
|
228
|
+
const windowWidth = compact
|
|
229
|
+
? 18
|
|
230
|
+
: Math.min(30, Math.max(20, Math.floor(width * 0.32)));
|
|
231
|
+
const boardWidth = compact
|
|
232
|
+
? Math.min(26, Math.max(20, width - windowWidth - 5))
|
|
233
|
+
: Math.min(36, Math.max(26, Math.floor(width * 0.36)));
|
|
234
|
+
const boardX = Math.max(windowWidth + 3, width - boardWidth - 2);
|
|
235
|
+
const windowRight = 1 + windowWidth;
|
|
236
|
+
const gapWidth = boardX - windowRight;
|
|
237
|
+
const cloud = frame % 6 < 3 ? "(~~)" : " (~~)";
|
|
238
|
+
const sun = frame % 12 < 6 ? "\\o/" : "-o-";
|
|
239
|
+
const city = frame % 10 < 5 ? "▂▄▆ city" : "▃▅▇ city";
|
|
240
|
+
const tape = frame % 8 < 4 ? "▁▃▅▇" : "▂▄▆█";
|
|
241
|
+
const cursor = frame % 4 < 2 ? ">" : "*";
|
|
242
|
+
|
|
243
|
+
placeBoxLines(rows, R_WINDOW_TOP, 1, windowWidth, "Window", [
|
|
244
|
+
`╔╤╤╗ ${sun} ${cloud}`,
|
|
245
|
+
`║▥▥║ ${city}`,
|
|
246
|
+
]);
|
|
247
|
+
placeBoxLines(
|
|
206
248
|
rows,
|
|
207
249
|
R_WINDOW_TOP,
|
|
208
250
|
boardX,
|
|
209
251
|
Math.min(boardWidth, width - boardX),
|
|
210
252
|
"Deal Board",
|
|
211
|
-
|
|
253
|
+
[
|
|
254
|
+
`DL ${dealPct} FEE ${Math.round(stats.happiness).toString().padStart(3)}%`,
|
|
255
|
+
`PIPE ${tape} $ ${cursor}`,
|
|
256
|
+
],
|
|
212
257
|
);
|
|
213
258
|
|
|
259
|
+
if (gapWidth >= 7) {
|
|
260
|
+
const clockX = windowRight + Math.floor((gapWidth - 5) / 2);
|
|
261
|
+
const hour = frame % 8 < 4 ? "09" : "10";
|
|
262
|
+
placeSprite(rows, R_WINDOW_TOP, clockX, [
|
|
263
|
+
"╭──╮",
|
|
264
|
+
`│${hour}│`,
|
|
265
|
+
"╰──╯",
|
|
266
|
+
]);
|
|
267
|
+
}
|
|
268
|
+
|
|
214
269
|
rows[R_ACTIVITY] = centerText(
|
|
215
270
|
"─".repeat(width),
|
|
216
271
|
buildActivityLine("idle", frame),
|
|
217
272
|
);
|
|
218
273
|
}
|
|
219
274
|
|
|
275
|
+
function drawOfficeFurniture(rows: string[], width: number, frame: number): void {
|
|
276
|
+
const lampX = 1;
|
|
277
|
+
const cabinetX = Math.max(1, width - 8);
|
|
278
|
+
const shade = frame % 8 < 4 ? "╭░░░░╮" : "╭▒▒▒▒╮";
|
|
279
|
+
const plantTop = frame % 6 < 3 ? " ╲│╱ " : " ╱│╲ ";
|
|
280
|
+
|
|
281
|
+
placeSprite(rows, R_MASCOT_START, lampX, [
|
|
282
|
+
` ${shade} `,
|
|
283
|
+
" ╱▒▒▒▒╲",
|
|
284
|
+
" ╰─┬──╯",
|
|
285
|
+
" │ ",
|
|
286
|
+
" ╭─┴─╮ ",
|
|
287
|
+
" │IN │ ",
|
|
288
|
+
" ╰───╯ ",
|
|
289
|
+
]);
|
|
290
|
+
|
|
291
|
+
placeSprite(rows, R_MASCOT_START, cabinetX, [
|
|
292
|
+
plantTop,
|
|
293
|
+
" ╲│╱ ",
|
|
294
|
+
" ╰┬╯ ",
|
|
295
|
+
"╭FILE╮",
|
|
296
|
+
"│▤▤▤│",
|
|
297
|
+
"├────┤",
|
|
298
|
+
"│▤▤▤│",
|
|
299
|
+
]);
|
|
300
|
+
}
|
|
301
|
+
|
|
220
302
|
function drawActivityAccents(
|
|
221
303
|
rows: string[],
|
|
222
304
|
width: number,
|
|
@@ -230,35 +312,38 @@ function drawActivityAccents(
|
|
|
230
312
|
);
|
|
231
313
|
|
|
232
314
|
const mascotRight = mascotX + MASCOT_WIDTH;
|
|
233
|
-
const leftAccentX = Math.max(1, mascotX -
|
|
234
|
-
const
|
|
315
|
+
const leftAccentX = Math.max(1, mascotX - 3);
|
|
316
|
+
const fileX = Math.max(1, width - 8);
|
|
317
|
+
const rightAccentX = Math.min(fileX - 6, mascotRight + 2);
|
|
235
318
|
|
|
236
319
|
switch (activity) {
|
|
237
320
|
case "eating":
|
|
238
|
-
rows[
|
|
321
|
+
rows[R_MASCOT_START + 5] = place(
|
|
322
|
+
rows[R_MASCOT_START + 5],
|
|
323
|
+
"╭$╮",
|
|
324
|
+
Math.max(1, Math.min(fileX - 5, rightAccentX + 1)),
|
|
325
|
+
);
|
|
239
326
|
break;
|
|
240
327
|
case "playing":
|
|
241
328
|
rows[R_MASCOT_START + 2] = place(rows[R_MASCOT_START + 2], "*", leftAccentX);
|
|
242
|
-
rows[R_MASCOT_START + 2] = place(rows[R_MASCOT_START + 2], "*", rightAccentX + 6);
|
|
329
|
+
rows[R_MASCOT_START + 2] = place(rows[R_MASCOT_START + 2], "*", Math.min(fileX - 2, rightAccentX + 6));
|
|
243
330
|
break;
|
|
244
331
|
case "working":
|
|
245
332
|
rows[R_MASCOT_START + 1] = place(rows[R_MASCOT_START + 1], "$", leftAccentX);
|
|
246
|
-
rows[R_MASCOT_START + 3] = place(rows[R_MASCOT_START + 3], "$", rightAccentX + 6);
|
|
333
|
+
rows[R_MASCOT_START + 3] = place(rows[R_MASCOT_START + 3], "$", Math.min(fileX - 2, rightAccentX + 6));
|
|
247
334
|
break;
|
|
248
335
|
case "sleeping":
|
|
249
336
|
rows[R_MASCOT_START] = place(rows[R_MASCOT_START], "z z Z", rightAccentX);
|
|
250
337
|
break;
|
|
251
338
|
case "praised":
|
|
252
339
|
rows[R_MASCOT_START + 1] = place(rows[R_MASCOT_START + 1], "* *", leftAccentX);
|
|
253
|
-
rows[R_MASCOT_START + 1] = place(rows[R_MASCOT_START + 1], "* *", rightAccentX + 4);
|
|
340
|
+
rows[R_MASCOT_START + 1] = place(rows[R_MASCOT_START + 1], "* *", Math.min(fileX - 4, rightAccentX + 4));
|
|
254
341
|
break;
|
|
255
342
|
case "vibing":
|
|
256
343
|
rows[R_MASCOT_START + 3] = place(rows[R_MASCOT_START + 3], "~ ~", leftAccentX);
|
|
257
|
-
rows[R_MASCOT_START + 3] = place(rows[R_MASCOT_START + 3], "~ ~", rightAccentX + 4);
|
|
344
|
+
rows[R_MASCOT_START + 3] = place(rows[R_MASCOT_START + 3], "~ ~", Math.min(fileX - 4, rightAccentX + 4));
|
|
258
345
|
break;
|
|
259
346
|
default:
|
|
260
|
-
rows[R_MASCOT_START + 2] = place(rows[R_MASCOT_START + 2], "[IN]", 2);
|
|
261
|
-
rows[R_MASCOT_START + 2] = place(rows[R_MASCOT_START + 2], "[OUT]", Math.max(2, width - 8));
|
|
262
347
|
break;
|
|
263
348
|
}
|
|
264
349
|
}
|
|
@@ -276,12 +361,57 @@ function drawMascot(rows: string[], width: number, activity: PetActivity, frame:
|
|
|
276
361
|
return mascotX;
|
|
277
362
|
}
|
|
278
363
|
|
|
364
|
+
function drawDesktopObjects(
|
|
365
|
+
rows: string[],
|
|
366
|
+
width: number,
|
|
367
|
+
activity: PetActivity,
|
|
368
|
+
frame: number,
|
|
369
|
+
stats: PetStats,
|
|
370
|
+
): void {
|
|
371
|
+
const mascotX = Math.max(0, Math.floor((width - MASCOT_WIDTH) / 2));
|
|
372
|
+
const mascotRight = mascotX + MASCOT_WIDTH;
|
|
373
|
+
const cabinetX = Math.max(1, width - 8);
|
|
374
|
+
const laptopX = Math.max(8, mascotX - 9);
|
|
375
|
+
const papersX = Math.min(cabinetX - 10, mascotRight + 2);
|
|
376
|
+
const mugX = Math.min(cabinetX - 5, mascotRight + 6);
|
|
377
|
+
const cursor = frame % 2 === 0 ? "_" : " ";
|
|
378
|
+
const screen =
|
|
379
|
+
activity === "working"
|
|
380
|
+
? `$>${cursor}DL`
|
|
381
|
+
: activity === "sleeping"
|
|
382
|
+
? "zzz..."
|
|
383
|
+
: "DREX";
|
|
384
|
+
const steam = stats.energy > 30
|
|
385
|
+
? frame % 4 < 2 ? " ((" : " ))"
|
|
386
|
+
: " ";
|
|
387
|
+
const paperFace = frame % 6 < 3 ? "▱▱▱" : "▰▱▱";
|
|
388
|
+
|
|
389
|
+
placeSprite(rows, R_MASCOT_START + 4, laptopX, [
|
|
390
|
+
"╭──────╮",
|
|
391
|
+
`│${padDisplayText(screen, 6)}│`,
|
|
392
|
+
"╰─┬──┬─╯",
|
|
393
|
+
]);
|
|
394
|
+
|
|
395
|
+
if (papersX > mascotRight) {
|
|
396
|
+
rows[R_MASCOT_START + 6] = place(rows[R_MASCOT_START + 6], paperFace, papersX);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (mugX > mascotRight + 1) {
|
|
400
|
+
rows[R_MASCOT_START + 4] = place(rows[R_MASCOT_START + 4], steam, mugX + 1);
|
|
401
|
+
rows[R_MASCOT_START + 5] = place(rows[R_MASCOT_START + 5], "╭─╮", mugX);
|
|
402
|
+
rows[R_MASCOT_START + 6] = place(rows[R_MASCOT_START + 6], `╰${cupForEnergy(stats.energy)}╯`, mugX);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
279
406
|
function drawDesk(rows: string[], width: number, stats: PetStats): void {
|
|
280
407
|
const deskX = width > PET_SCENE_WIDTH ? 2 : 1;
|
|
281
408
|
const deskWidth = Math.max(4, width - deskX * 2);
|
|
282
409
|
const deskInner = Math.max(1, deskWidth - 2);
|
|
283
|
-
const surface =
|
|
284
|
-
const front = `DESK
|
|
410
|
+
const surface = `▱▱▱ [${cupForEnergy(stats.energy)}] ▬▬▬▬▬ COV OK`;
|
|
411
|
+
const front = `[IN] ║ DREXLER DEAL DESK ║ PIPE ${Math.round(stats.deals)}% ║ [OUT]`;
|
|
412
|
+
const drawers = width < 68
|
|
413
|
+
? "╭────╮ ╭────╮ ╭────╮"
|
|
414
|
+
: "╭────╮ ╭────╮ ╭────╮ ╭────╮";
|
|
285
415
|
|
|
286
416
|
rows[R_DESK_SURFACE] = place(
|
|
287
417
|
rows[R_DESK_SURFACE],
|
|
@@ -290,9 +420,23 @@ function drawDesk(rows: string[], width: number, stats: PetStats): void {
|
|
|
290
420
|
);
|
|
291
421
|
rows[R_DESK_FRONT] = place(
|
|
292
422
|
rows[R_DESK_FRONT],
|
|
293
|
-
|
|
423
|
+
`│${padDisplayText(front, deskInner)}│`,
|
|
424
|
+
deskX,
|
|
425
|
+
);
|
|
426
|
+
rows[R_DESK_DRAWERS] = place(
|
|
427
|
+
rows[R_DESK_DRAWERS],
|
|
428
|
+
`│${centerPadDisplayText(drawers, deskInner)}│`,
|
|
429
|
+
deskX,
|
|
430
|
+
);
|
|
431
|
+
rows[R_DESK_BOTTOM] = place(
|
|
432
|
+
rows[R_DESK_BOTTOM],
|
|
433
|
+
`╰${"─".repeat(Math.max(0, deskInner))}╯`,
|
|
294
434
|
deskX,
|
|
295
435
|
);
|
|
436
|
+
rows[R_FLOOR] = centerText(
|
|
437
|
+
rows[R_FLOOR],
|
|
438
|
+
fitDisplayText("░░░░░░░ deal-room carpet shadow ░░░░░░░", width),
|
|
439
|
+
);
|
|
296
440
|
}
|
|
297
441
|
|
|
298
442
|
function buildScene(
|
|
@@ -305,8 +449,10 @@ function buildScene(
|
|
|
305
449
|
const rows: string[] = Array.from({ length: SCENE_ROWS }, () => blankRow(sceneWidth));
|
|
306
450
|
|
|
307
451
|
drawOfficeBackground(rows, sceneWidth, frame, stats);
|
|
308
|
-
|
|
452
|
+
drawOfficeFurniture(rows, sceneWidth, frame);
|
|
309
453
|
const mascotX = drawMascot(rows, sceneWidth, activity, frame);
|
|
454
|
+
drawDesktopObjects(rows, sceneWidth, activity, frame, stats);
|
|
455
|
+
drawDesk(rows, sceneWidth, stats);
|
|
310
456
|
drawActivityAccents(rows, sceneWidth, activity, frame, mascotX);
|
|
311
457
|
return rows.map((row) => overlayFitted(blankRow(sceneWidth), row, 0, sceneWidth));
|
|
312
458
|
}
|
|
@@ -329,7 +475,13 @@ function rowColor(i: number, activity: PetActivity, frame: number, t: Theme): st
|
|
|
329
475
|
if (activity === "playing") return t.primaryLight;
|
|
330
476
|
return t.primaryLight;
|
|
331
477
|
}
|
|
332
|
-
if (
|
|
478
|
+
if (
|
|
479
|
+
i === R_DESK_SURFACE ||
|
|
480
|
+
i === R_DESK_FRONT ||
|
|
481
|
+
i === R_DESK_DRAWERS ||
|
|
482
|
+
i === R_DESK_BOTTOM ||
|
|
483
|
+
i === R_FLOOR
|
|
484
|
+
) return t.primaryDim;
|
|
333
485
|
if (i === R_WALL) return t.dim;
|
|
334
486
|
if (i >= R_WINDOW_TOP && i <= R_WINDOW_BOTTOM) return t.primaryDim;
|
|
335
487
|
return t.primaryDim;
|