drexler 0.2.20 → 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 +119 -59
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,7 +18,7 @@ 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
24
|
const R_WINDOW_BOTTOM = 4;
|
|
@@ -26,7 +26,9 @@ const R_ACTIVITY = 5;
|
|
|
26
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
|
|
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;
|
|
30
32
|
|
|
31
33
|
export const PET_SCENE_WIDTH = 52;
|
|
32
34
|
|
|
@@ -37,6 +39,14 @@ function place(base: string, text: string, x: number): string {
|
|
|
37
39
|
return base.slice(0, x) + fit + base.slice(end);
|
|
38
40
|
}
|
|
39
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
|
+
|
|
40
50
|
function blankRow(width: number): string {
|
|
41
51
|
return " ".repeat(width);
|
|
42
52
|
}
|
|
@@ -47,6 +57,13 @@ function padDisplayText(input: string, width: number): string {
|
|
|
47
57
|
return `${fitted}${" ".repeat(Math.max(0, safeWidth - displayWidth(fitted)))}`;
|
|
48
58
|
}
|
|
49
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
|
+
|
|
50
67
|
function overlayFitted(row: string, text: string, x: number, width: number): string {
|
|
51
68
|
return place(row, padDisplayText(text, width), x);
|
|
52
69
|
}
|
|
@@ -98,9 +115,9 @@ function placeBoxLines(
|
|
|
98
115
|
}
|
|
99
116
|
|
|
100
117
|
function cupForEnergy(energy: number): string {
|
|
101
|
-
if (energy > 60) return "
|
|
102
|
-
if (energy > 30) return "
|
|
103
|
-
return "
|
|
118
|
+
if (energy > 60) return "c~";
|
|
119
|
+
if (energy > 30) return "c-";
|
|
120
|
+
return "c_";
|
|
104
121
|
}
|
|
105
122
|
|
|
106
123
|
function progressTicker(frame: number): string {
|
|
@@ -207,17 +224,25 @@ function drawOfficeBackground(rows: string[], width: number, frame: number, stat
|
|
|
207
224
|
Math.max(0, width - displayWidth(`pipe ${dealPct}`) - 1),
|
|
208
225
|
);
|
|
209
226
|
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
|
|
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;
|
|
213
237
|
const cloud = frame % 6 < 3 ? "(~~)" : " (~~)";
|
|
214
238
|
const sun = frame % 12 < 6 ? "\\o/" : "-o-";
|
|
239
|
+
const city = frame % 10 < 5 ? "▂▄▆ city" : "▃▅▇ city";
|
|
215
240
|
const tape = frame % 8 < 4 ? "▁▃▅▇" : "▂▄▆█";
|
|
216
241
|
const cursor = frame % 4 < 2 ? ">" : "*";
|
|
217
242
|
|
|
218
243
|
placeBoxLines(rows, R_WINDOW_TOP, 1, windowWidth, "Window", [
|
|
219
|
-
|
|
220
|
-
|
|
244
|
+
`╔╤╤╗ ${sun} ${cloud}`,
|
|
245
|
+
`║▥▥║ ${city}`,
|
|
221
246
|
]);
|
|
222
247
|
placeBoxLines(
|
|
223
248
|
rows,
|
|
@@ -226,11 +251,21 @@ function drawOfficeBackground(rows: string[], width: number, frame: number, stat
|
|
|
226
251
|
Math.min(boardWidth, width - boardX),
|
|
227
252
|
"Deal Board",
|
|
228
253
|
[
|
|
229
|
-
`DL
|
|
230
|
-
`PIPE ${tape} ${cursor}`,
|
|
254
|
+
`DL ${dealPct} FEE ${Math.round(stats.happiness).toString().padStart(3)}%`,
|
|
255
|
+
`PIPE ${tape} $ ${cursor}`,
|
|
231
256
|
],
|
|
232
257
|
);
|
|
233
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
|
+
|
|
234
269
|
rows[R_ACTIVITY] = centerText(
|
|
235
270
|
"─".repeat(width),
|
|
236
271
|
buildActivityLine("idle", frame),
|
|
@@ -238,25 +273,30 @@ function drawOfficeBackground(rows: string[], width: number, frame: number, stat
|
|
|
238
273
|
}
|
|
239
274
|
|
|
240
275
|
function drawOfficeFurniture(rows: string[], width: number, frame: number): void {
|
|
241
|
-
const lampX =
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
rows
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
rows
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
+
]);
|
|
260
300
|
}
|
|
261
301
|
|
|
262
302
|
function drawActivityAccents(
|
|
@@ -272,16 +312,16 @@ function drawActivityAccents(
|
|
|
272
312
|
);
|
|
273
313
|
|
|
274
314
|
const mascotRight = mascotX + MASCOT_WIDTH;
|
|
275
|
-
const leftAccentX = Math.max(1, mascotX -
|
|
276
|
-
const fileX = Math.max(1, width -
|
|
277
|
-
const rightAccentX = Math.min(fileX -
|
|
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);
|
|
278
318
|
|
|
279
319
|
switch (activity) {
|
|
280
320
|
case "eating":
|
|
281
321
|
rows[R_MASCOT_START + 5] = place(
|
|
282
322
|
rows[R_MASCOT_START + 5],
|
|
283
|
-
"
|
|
284
|
-
Math.max(1, Math.min(fileX -
|
|
323
|
+
"╭$╮",
|
|
324
|
+
Math.max(1, Math.min(fileX - 5, rightAccentX + 1)),
|
|
285
325
|
);
|
|
286
326
|
break;
|
|
287
327
|
case "playing":
|
|
@@ -330,33 +370,36 @@ function drawDesktopObjects(
|
|
|
330
370
|
): void {
|
|
331
371
|
const mascotX = Math.max(0, Math.floor((width - MASCOT_WIDTH) / 2));
|
|
332
372
|
const mascotRight = mascotX + MASCOT_WIDTH;
|
|
333
|
-
const
|
|
334
|
-
const laptopX = Math.max(8, mascotX -
|
|
335
|
-
const papersX = Math.min(
|
|
336
|
-
const
|
|
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);
|
|
337
377
|
const cursor = frame % 2 === 0 ? "_" : " ";
|
|
338
378
|
const screen =
|
|
339
379
|
activity === "working"
|
|
340
|
-
? `$>${cursor}
|
|
380
|
+
? `$>${cursor}DL`
|
|
341
381
|
: activity === "sleeping"
|
|
342
382
|
? "zzz..."
|
|
343
383
|
: "DREX";
|
|
344
384
|
const steam = stats.energy > 30
|
|
345
385
|
? frame % 4 < 2 ? " ((" : " ))"
|
|
346
386
|
: " ";
|
|
347
|
-
const paperFace = frame % 6 < 3 ? "
|
|
387
|
+
const paperFace = frame % 6 < 3 ? "▱▱▱" : "▰▱▱";
|
|
348
388
|
|
|
349
|
-
rows
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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);
|
|
360
403
|
}
|
|
361
404
|
}
|
|
362
405
|
|
|
@@ -364,9 +407,11 @@ function drawDesk(rows: string[], width: number, stats: PetStats): void {
|
|
|
364
407
|
const deskX = width > PET_SCENE_WIDTH ? 2 : 1;
|
|
365
408
|
const deskWidth = Math.max(4, width - deskX * 2);
|
|
366
409
|
const deskInner = Math.max(1, deskWidth - 2);
|
|
367
|
-
const surface =
|
|
368
|
-
const
|
|
369
|
-
const
|
|
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
|
+
: "╭────╮ ╭────╮ ╭────╮ ╭────╮";
|
|
370
415
|
|
|
371
416
|
rows[R_DESK_SURFACE] = place(
|
|
372
417
|
rows[R_DESK_SURFACE],
|
|
@@ -378,11 +423,20 @@ function drawDesk(rows: string[], width: number, stats: PetStats): void {
|
|
|
378
423
|
`│${padDisplayText(front, deskInner)}│`,
|
|
379
424
|
deskX,
|
|
380
425
|
);
|
|
381
|
-
rows[
|
|
382
|
-
rows[
|
|
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],
|
|
383
433
|
`╰${"─".repeat(Math.max(0, deskInner))}╯`,
|
|
384
434
|
deskX,
|
|
385
435
|
);
|
|
436
|
+
rows[R_FLOOR] = centerText(
|
|
437
|
+
rows[R_FLOOR],
|
|
438
|
+
fitDisplayText("░░░░░░░ deal-room carpet shadow ░░░░░░░", width),
|
|
439
|
+
);
|
|
386
440
|
}
|
|
387
441
|
|
|
388
442
|
function buildScene(
|
|
@@ -421,7 +475,13 @@ function rowColor(i: number, activity: PetActivity, frame: number, t: Theme): st
|
|
|
421
475
|
if (activity === "playing") return t.primaryLight;
|
|
422
476
|
return t.primaryLight;
|
|
423
477
|
}
|
|
424
|
-
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;
|
|
425
485
|
if (i === R_WALL) return t.dim;
|
|
426
486
|
if (i >= R_WINDOW_TOP && i <= R_WINDOW_BOTTOM) return t.primaryDim;
|
|
427
487
|
return t.primaryDim;
|