drexler 0.2.16 → 0.2.18
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/README.md +3 -2
- package/package.json +1 -1
- package/src/commands.ts +3 -0
- package/src/index.ts +1 -0
- package/src/ui/App.tsx +80 -57
- package/src/ui/MascotIntro.tsx +385 -0
- package/src/ui/PetPanel.tsx +296 -388
package/README.md
CHANGED
|
@@ -89,9 +89,9 @@ rm -rf ~/.config/drexler # optional: wipe stored key + settings
|
|
|
89
89
|
|
|
90
90
|
### Interactive UI
|
|
91
91
|
|
|
92
|
-
Drexler runs as an Ink terminal UI when both stdin and stdout are TTYs. The normal launch shows one integrated startup panel with the mascot, tips, a **Mood** readout, and the **Drexler Deal Desk**. Short terminals automatically suppress oversized startup chrome so the chat stays usable.
|
|
92
|
+
Drexler runs as an Ink terminal UI when both stdin and stdout are TTYs. The normal launch shows one integrated startup panel with the mascot, tips, a **Mood** readout, and the **Drexler Deal Desk**. After startup, that dashboard remains the primary top chrome. Short terminals automatically suppress oversized startup chrome so the chat stays usable.
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
Type `/pet` to toggle pet mode for the current session. Pet mode keeps the top dashboard frame and swaps the normal mascot/tips/deal desk layout for a Drexler scene plus pet stats. `/pet on` and `/pet off` set it explicitly. Pet stats still decay and pet commands still work when the pet dashboard is hidden.
|
|
95
95
|
|
|
96
96
|
The startup panel is designed to stay stable while it boots: the mascot loading bar and Mood gauge animate without changing width, greeting copy is held in a fixed slot, and the Mood and Deal Desk boxes stay aligned when the greeting wraps. After boot, Mood resolves into a rotating Drexler-flavored posture with a short satirical subtext line.
|
|
97
97
|
|
|
@@ -136,6 +136,7 @@ Keyboard notes:
|
|
|
136
136
|
| `/clear` | shred conversation history (system prompt pinned) |
|
|
137
137
|
| `/exit` | meeting adjourned |
|
|
138
138
|
| `/synergy` | run a rotating animated morale event |
|
|
139
|
+
| `/pet [on\|off]` | toggle pet dashboard mode for this session |
|
|
139
140
|
| `/feed` | feed Drexler a deal memo |
|
|
140
141
|
| `/play` | corporate synergy game with Drexler |
|
|
141
142
|
| `/work` | Drexler grinds the deal pipeline |
|
package/package.json
CHANGED
package/src/commands.ts
CHANGED
|
@@ -47,6 +47,7 @@ const HELP_TEXT = `New memo to staff! Drexler permit following directives:
|
|
|
47
47
|
/clear - shred all documents (reset history)
|
|
48
48
|
/exit - meeting adjourned
|
|
49
49
|
/synergy - SYNERGY!
|
|
50
|
+
/pet [on|off] - toggle pet dashboard mode
|
|
50
51
|
/feed - feed Drexler a deal memo
|
|
51
52
|
/play - corporate synergy game with Drexler
|
|
52
53
|
/work - Drexler grinds the deal pipeline
|
|
@@ -86,6 +87,7 @@ export const COMMAND_PALETTE: ReadonlyArray<SlashCommand> = [
|
|
|
86
87
|
{ name: "/clear", description: "Reset conversation", group: "directives" },
|
|
87
88
|
{ name: "/exit", description: "Adjourn meeting", group: "directives" },
|
|
88
89
|
{ name: "/synergy", description: "SYNERGY!", group: "directives" },
|
|
90
|
+
{ name: "/pet", description: "Toggle pet dashboard mode", group: "directives" },
|
|
89
91
|
{ name: "/feed", description: "Feed Drexler a deal memo", group: "directives" },
|
|
90
92
|
{ name: "/play", description: "Play with Drexler", group: "directives" },
|
|
91
93
|
{ name: "/work", description: "Drexler grinds deals", group: "directives" },
|
|
@@ -369,6 +371,7 @@ export function dispatch(input: string, ctx: CommandContext): CommandAction {
|
|
|
369
371
|
);
|
|
370
372
|
return { type: "continue" };
|
|
371
373
|
|
|
374
|
+
case "pet":
|
|
372
375
|
case "feed":
|
|
373
376
|
case "play":
|
|
374
377
|
case "work":
|
package/src/index.ts
CHANGED
package/src/ui/App.tsx
CHANGED
|
@@ -31,8 +31,6 @@ import {
|
|
|
31
31
|
CompactPetPanel,
|
|
32
32
|
COMPACT_PET_PANEL_MIN_WIDTH,
|
|
33
33
|
COMPACT_PET_PANEL_ROWS,
|
|
34
|
-
PetPanel,
|
|
35
|
-
PET_PANEL_WIDTH,
|
|
36
34
|
TINY_PET_PANEL_ROWS,
|
|
37
35
|
type Environment,
|
|
38
36
|
} from "./PetPanel.tsx";
|
|
@@ -93,10 +91,6 @@ import {
|
|
|
93
91
|
import { getActiveTheme } from "./themes.ts";
|
|
94
92
|
|
|
95
93
|
const TRANSCRIPT_CHROME_ROWS = 12;
|
|
96
|
-
const PET_PANEL_MIN_MAIN_COLUMNS = 75;
|
|
97
|
-
const PET_PANEL_GAP_COLUMNS = 1;
|
|
98
|
-
const PET_PANEL_MIN_COLUMNS =
|
|
99
|
-
PET_PANEL_WIDTH + PET_PANEL_GAP_COLUMNS + PET_PANEL_MIN_MAIN_COLUMNS;
|
|
100
94
|
|
|
101
95
|
export function transcriptRowsForTerminalRows(rows: number): number {
|
|
102
96
|
return Math.max(1, Math.min(24, rows - TRANSCRIPT_CHROME_ROWS));
|
|
@@ -198,6 +192,7 @@ interface AppProps {
|
|
|
198
192
|
fetchFn?: FetchFn;
|
|
199
193
|
greeting?: string;
|
|
200
194
|
showIntroChrome?: boolean;
|
|
195
|
+
introInitiallyDone?: boolean;
|
|
201
196
|
}
|
|
202
197
|
|
|
203
198
|
export function App({
|
|
@@ -207,6 +202,7 @@ export function App({
|
|
|
207
202
|
fetchFn,
|
|
208
203
|
greeting,
|
|
209
204
|
showIntroChrome = false,
|
|
205
|
+
introInitiallyDone = false,
|
|
210
206
|
}: AppProps) {
|
|
211
207
|
const { exit } = useApp();
|
|
212
208
|
const { stdout } = useStdout();
|
|
@@ -228,36 +224,38 @@ export function App({
|
|
|
228
224
|
const mode = useMemo(() => pickLayout(cols), [cols]);
|
|
229
225
|
const chromeWidth = useMemo(() => Math.max(1, cols), [cols]);
|
|
230
226
|
const isCompact = mode === "very-narrow";
|
|
231
|
-
const [introDone, setIntroDone] = useState(
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
const compactPetRowBudget = showCompactPetPanel
|
|
227
|
+
const [introDone, setIntroDone] = useState(introInitiallyDone);
|
|
228
|
+
const dashboardAllowed = showIntroChrome && typeof greeting === "string";
|
|
229
|
+
const showFullDashboard = dashboardAllowed && rows >= 32;
|
|
230
|
+
const introActive = showFullDashboard && !introDone;
|
|
231
|
+
const [petMode, setPetMode] = useState(false);
|
|
232
|
+
const petModeRef = useRef(false);
|
|
233
|
+
const showFallbackPetPanel = petMode && !showFullDashboard;
|
|
234
|
+
const fallbackPetRowBudget = showFallbackPetPanel
|
|
240
235
|
? cols >= COMPACT_PET_PANEL_MIN_WIDTH
|
|
241
236
|
? COMPACT_PET_PANEL_ROWS
|
|
242
237
|
: TINY_PET_PANEL_ROWS
|
|
243
238
|
: 0;
|
|
244
|
-
const
|
|
245
|
-
? PET_PANEL_WIDTH + PET_PANEL_GAP_COLUMNS
|
|
246
|
-
: 0;
|
|
247
|
-
const contentWidth = showPetSidePanel
|
|
248
|
-
? Math.max(1, cols - petPanelReservedWidth)
|
|
249
|
-
: chromeWidth;
|
|
239
|
+
const contentWidth = chromeWidth;
|
|
250
240
|
const contentInputWidth = Math.max(1, contentWidth);
|
|
251
241
|
const contentStatusWidth = Math.max(1, contentInputWidth - 2);
|
|
252
|
-
const
|
|
253
|
-
|
|
242
|
+
const dashboardRowBudget =
|
|
243
|
+
showFullDashboard
|
|
244
|
+
? chromeWidth >= 112
|
|
245
|
+
? 14
|
|
246
|
+
: chromeWidth >= 72
|
|
247
|
+
? 26
|
|
248
|
+
: 6
|
|
249
|
+
: 0;
|
|
254
250
|
const maxTranscriptRows = useMemo(
|
|
255
251
|
() =>
|
|
256
252
|
Math.max(
|
|
257
253
|
1,
|
|
258
|
-
transcriptRowsForTerminalRows(rows) -
|
|
254
|
+
transcriptRowsForTerminalRows(rows) -
|
|
255
|
+
dashboardRowBudget -
|
|
256
|
+
fallbackPetRowBudget,
|
|
259
257
|
),
|
|
260
|
-
[
|
|
258
|
+
[dashboardRowBudget, fallbackPetRowBudget, rows],
|
|
261
259
|
);
|
|
262
260
|
|
|
263
261
|
const [items, setItems] = useState<ChatItem[]>([]);
|
|
@@ -315,7 +313,7 @@ export function App({
|
|
|
315
313
|
}, []);
|
|
316
314
|
const intro = useIntroAnimation(
|
|
317
315
|
chromeWidth,
|
|
318
|
-
|
|
316
|
+
introActive,
|
|
319
317
|
handleIntroComplete,
|
|
320
318
|
);
|
|
321
319
|
|
|
@@ -328,12 +326,7 @@ export function App({
|
|
|
328
326
|
const petActivityTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
329
327
|
const petDecayTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
330
328
|
|
|
331
|
-
const petEnv
|
|
332
|
-
const h = new Date().getHours();
|
|
333
|
-
if (h >= 9 && h < 18) return "office";
|
|
334
|
-
if (h >= 6 && h < 23) return "home";
|
|
335
|
-
return "outdoors";
|
|
336
|
-
}, []);
|
|
329
|
+
const petEnv: Environment = "office";
|
|
337
330
|
|
|
338
331
|
const PET_MESSAGES = useMemo(() => ({
|
|
339
332
|
feed: [
|
|
@@ -391,6 +384,10 @@ export function App({
|
|
|
391
384
|
},
|
|
392
385
|
[],
|
|
393
386
|
);
|
|
387
|
+
const setDashboardPetMode = useCallback((next: boolean) => {
|
|
388
|
+
petModeRef.current = next;
|
|
389
|
+
setPetMode(next);
|
|
390
|
+
}, []);
|
|
394
391
|
const updatePetStats = useCallback((updater: (stats: PetStats) => PetStats) => {
|
|
395
392
|
setPetStats((stats) => {
|
|
396
393
|
const next = updater(stats);
|
|
@@ -687,6 +684,38 @@ export function App({
|
|
|
687
684
|
runSynergyEvent();
|
|
688
685
|
return;
|
|
689
686
|
}
|
|
687
|
+
if (slashCommand === "/pet") {
|
|
688
|
+
const arg = lower.slice("/pet".length).trim();
|
|
689
|
+
if (arg === "") {
|
|
690
|
+
const next = !petModeRef.current;
|
|
691
|
+
setDashboardPetMode(next);
|
|
692
|
+
addItem(
|
|
693
|
+
"system",
|
|
694
|
+
next
|
|
695
|
+
? "Pet dashboard enabled. Deal desk converted to habitat."
|
|
696
|
+
: "Pet dashboard disabled. Deal desk restored.",
|
|
697
|
+
);
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (arg === "on" || arg === "off") {
|
|
701
|
+
const next = arg === "on";
|
|
702
|
+
const changed = petModeRef.current !== next;
|
|
703
|
+
setDashboardPetMode(next);
|
|
704
|
+
addItem(
|
|
705
|
+
"system",
|
|
706
|
+
changed
|
|
707
|
+
? next
|
|
708
|
+
? "Pet dashboard enabled. Deal desk converted to habitat."
|
|
709
|
+
: "Pet dashboard disabled. Deal desk restored."
|
|
710
|
+
: next
|
|
711
|
+
? "Pet dashboard already enabled."
|
|
712
|
+
: "Pet dashboard already disabled.",
|
|
713
|
+
);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
addItem("system", "Usage: /pet, /pet on, or /pet off.");
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
690
719
|
const isPetCommand =
|
|
691
720
|
slashCommand === "/feed" ||
|
|
692
721
|
slashCommand === "/play" ||
|
|
@@ -861,11 +890,11 @@ export function App({
|
|
|
861
890
|
config,
|
|
862
891
|
activeTheme,
|
|
863
892
|
model,
|
|
864
|
-
petStats,
|
|
865
893
|
PET_MESSAGES,
|
|
866
894
|
removeLastAssistantItem,
|
|
867
895
|
runLLM,
|
|
868
896
|
runSynergyEvent,
|
|
897
|
+
setDashboardPetMode,
|
|
869
898
|
triggerExit,
|
|
870
899
|
triggerPetActivity,
|
|
871
900
|
updatePetStats,
|
|
@@ -1123,15 +1152,15 @@ export function App({
|
|
|
1123
1152
|
const isBusy =
|
|
1124
1153
|
requestInFlight || streaming !== null || thinking !== null || synergyEvent !== null;
|
|
1125
1154
|
const headerStatus = isBusy ? "streaming" : deskStatus;
|
|
1126
|
-
const renderDealDeskHeader = (width: number) => (
|
|
1155
|
+
const renderDealDeskHeader = (width: number, marginBottom = 1) => (
|
|
1127
1156
|
<DealDeskHeader
|
|
1128
1157
|
mood={mood}
|
|
1129
1158
|
messageCount={msgCount}
|
|
1130
1159
|
status={headerStatus}
|
|
1131
1160
|
compact={isCompact}
|
|
1132
|
-
notice={!
|
|
1161
|
+
notice={!introActive ? deskNotice ?? undefined : undefined}
|
|
1133
1162
|
maxWidth={Math.max(1, width)}
|
|
1134
|
-
marginBottom={
|
|
1163
|
+
marginBottom={marginBottom}
|
|
1135
1164
|
/>
|
|
1136
1165
|
);
|
|
1137
1166
|
const dealDeskHeader = renderDealDeskHeader(chromeWidth);
|
|
@@ -1148,24 +1177,26 @@ export function App({
|
|
|
1148
1177
|
return (
|
|
1149
1178
|
<ThemeProvider value={activeTheme}>
|
|
1150
1179
|
<Box flexDirection="column">
|
|
1151
|
-
{
|
|
1180
|
+
{showFullDashboard && typeof greeting === "string" ? (
|
|
1152
1181
|
<Box marginBottom={1}>
|
|
1153
1182
|
<MascotDashboard
|
|
1154
1183
|
greeting={greeting}
|
|
1155
1184
|
width={chromeWidth}
|
|
1156
1185
|
mood={mood}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1186
|
+
mode={petMode && !introActive ? "pet" : "normal"}
|
|
1187
|
+
petStats={petStats}
|
|
1188
|
+
petActivity={petActivity}
|
|
1189
|
+
petEnv={petEnv}
|
|
1190
|
+
petPaused={isBusy}
|
|
1191
|
+
bootProgress={introActive ? intro.progress : 1}
|
|
1192
|
+
state={introActive ? intro.state : undefined}
|
|
1193
|
+
bar={introActive ? intro.bar : undefined}
|
|
1194
|
+
barColor={introActive ? introBarColor : undefined}
|
|
1195
|
+
mascotStatus={introActive ? intro.status : undefined}
|
|
1196
|
+
dealDesk={(width) => renderDealDeskHeader(width, 0)}
|
|
1163
1197
|
/>
|
|
1164
1198
|
</Box>
|
|
1165
|
-
) : (
|
|
1166
|
-
dealDeskHeader
|
|
1167
|
-
)}
|
|
1168
|
-
{showCompactPetPanel && (
|
|
1199
|
+
) : showFallbackPetPanel ? (
|
|
1169
1200
|
<Box marginBottom={1}>
|
|
1170
1201
|
<CompactPetPanel
|
|
1171
1202
|
stats={petStats}
|
|
@@ -1175,18 +1206,10 @@ export function App({
|
|
|
1175
1206
|
width={chromeWidth}
|
|
1176
1207
|
/>
|
|
1177
1208
|
</Box>
|
|
1209
|
+
) : (
|
|
1210
|
+
dealDeskHeader
|
|
1178
1211
|
)}
|
|
1179
1212
|
<Box flexDirection="row" alignItems="flex-start">
|
|
1180
|
-
{showPetSidePanel && (
|
|
1181
|
-
<Box marginRight={PET_PANEL_GAP_COLUMNS}>
|
|
1182
|
-
<PetPanel
|
|
1183
|
-
stats={petStats}
|
|
1184
|
-
activity={petActivity}
|
|
1185
|
-
env={petEnv}
|
|
1186
|
-
isPaused={isBusy}
|
|
1187
|
-
/>
|
|
1188
|
-
</Box>
|
|
1189
|
-
)}
|
|
1190
1213
|
<Box flexDirection="column" flexGrow={1}>
|
|
1191
1214
|
<TranscriptViewport
|
|
1192
1215
|
items={items}
|