drexler 0.2.14 → 0.2.15
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 +7 -1
- package/package.json +1 -1
- package/src/commands.ts +127 -20
- package/src/config.ts +141 -32
- package/src/conversation.ts +0 -4
- package/src/index.ts +68 -5
- package/src/pet/petState.ts +408 -0
- package/src/repl.ts +1 -1
- package/src/ui/App.tsx +543 -144
- package/src/ui/CommandPalette.tsx +2 -0
- package/src/ui/DealDeskHeader.tsx +0 -5
- package/src/ui/DeathScreen.tsx +110 -0
- package/src/ui/MarkdownBody.tsx +29 -5
- package/src/ui/MascotIntro.tsx +158 -57
- package/src/ui/Message.tsx +2 -105
- package/src/ui/PetPanel.tsx +537 -0
- package/src/ui/TranscriptViewport.tsx +206 -48
- package/src/ui/displayContent.ts +5 -2
package/src/ui/App.tsx
CHANGED
|
@@ -1,8 +1,42 @@
|
|
|
1
1
|
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
accrueLifetimeDeals,
|
|
5
|
+
actionCooldown,
|
|
6
|
+
applyFeed,
|
|
7
|
+
applyMinuteDecay,
|
|
8
|
+
applyName,
|
|
9
|
+
applyPlay,
|
|
10
|
+
applyPraise,
|
|
11
|
+
applyRest,
|
|
12
|
+
applyVibe,
|
|
13
|
+
applyWork,
|
|
14
|
+
formatCooldownRemaining,
|
|
15
|
+
formatTenure,
|
|
16
|
+
getPetMood,
|
|
17
|
+
getPetRank,
|
|
18
|
+
isPetDead,
|
|
19
|
+
loadPetState,
|
|
20
|
+
petTenureMs,
|
|
21
|
+
rankLabel,
|
|
22
|
+
sanitizePetName,
|
|
23
|
+
savePetState,
|
|
24
|
+
stampAction,
|
|
25
|
+
type PetActionKey,
|
|
26
|
+
type PetActivity,
|
|
27
|
+
type PetStats,
|
|
28
|
+
} from "../pet/petState.ts";
|
|
29
|
+
import { DeathScreen } from "./DeathScreen.tsx";
|
|
30
|
+
import {
|
|
31
|
+
PetPanel,
|
|
32
|
+
PET_PANEL_ROWS,
|
|
33
|
+
PET_PANEL_WIDTH,
|
|
34
|
+
type Environment,
|
|
35
|
+
} from "./PetPanel.tsx";
|
|
3
36
|
import {
|
|
4
37
|
dispatch,
|
|
5
38
|
filterPaletteByPrefix,
|
|
39
|
+
isArgumentParentCommand,
|
|
6
40
|
isSlash,
|
|
7
41
|
type CommandAction,
|
|
8
42
|
} from "../commands.ts";
|
|
@@ -23,7 +57,6 @@ import {
|
|
|
23
57
|
WITTICISMS,
|
|
24
58
|
} from "../sayings.ts";
|
|
25
59
|
import { type Config } from "../types.ts";
|
|
26
|
-
import { THEME_NAMES } from "../types.ts";
|
|
27
60
|
import { CommandPalette } from "./CommandPalette.tsx";
|
|
28
61
|
import { DealDeskHeader } from "./DealDeskHeader.tsx";
|
|
29
62
|
import {
|
|
@@ -50,10 +83,18 @@ import {
|
|
|
50
83
|
type SynergyEventDefinition,
|
|
51
84
|
} from "./SynergyEvent.tsx";
|
|
52
85
|
import { ThemeProvider } from "./ThemeContext.tsx";
|
|
53
|
-
import {
|
|
54
|
-
|
|
86
|
+
import {
|
|
87
|
+
estimateTranscriptRows,
|
|
88
|
+
TranscriptViewport,
|
|
89
|
+
} from "./TranscriptViewport.tsx";
|
|
90
|
+
import { getActiveTheme } from "./themes.ts";
|
|
55
91
|
|
|
56
92
|
const TRANSCRIPT_CHROME_ROWS = 12;
|
|
93
|
+
const PET_PANEL_MIN_MAIN_COLUMNS = 91;
|
|
94
|
+
const PET_PANEL_GAP_COLUMNS = 1;
|
|
95
|
+
const PET_PANEL_MIN_COLUMNS =
|
|
96
|
+
PET_PANEL_WIDTH + PET_PANEL_GAP_COLUMNS + PET_PANEL_MIN_MAIN_COLUMNS;
|
|
97
|
+
const PET_PANEL_MIN_ROWS = TRANSCRIPT_CHROME_ROWS + PET_PANEL_ROWS;
|
|
57
98
|
|
|
58
99
|
export function transcriptRowsForTerminalRows(rows: number): number {
|
|
59
100
|
return Math.max(1, Math.min(24, rows - TRANSCRIPT_CHROME_ROWS));
|
|
@@ -62,15 +103,22 @@ export function transcriptRowsForTerminalRows(rows: number): number {
|
|
|
62
103
|
export function nextTranscriptScrollOffset({
|
|
63
104
|
current,
|
|
64
105
|
itemCount,
|
|
106
|
+
totalRows,
|
|
107
|
+
visibleRows,
|
|
65
108
|
direction,
|
|
66
109
|
step = 3,
|
|
67
110
|
}: {
|
|
68
111
|
current: number;
|
|
69
|
-
itemCount
|
|
112
|
+
itemCount?: number;
|
|
113
|
+
totalRows?: number;
|
|
114
|
+
visibleRows?: number;
|
|
70
115
|
direction: "older" | "newer";
|
|
71
116
|
step?: number;
|
|
72
117
|
}): number {
|
|
73
|
-
const maxOffset =
|
|
118
|
+
const maxOffset =
|
|
119
|
+
totalRows !== undefined
|
|
120
|
+
? Math.max(0, totalRows - Math.max(1, visibleRows ?? 1))
|
|
121
|
+
: Math.max(0, (itemCount ?? 0) - 1);
|
|
74
122
|
if (direction === "older") {
|
|
75
123
|
return Math.min(maxOffset, current + step);
|
|
76
124
|
}
|
|
@@ -83,6 +131,46 @@ export function shouldRemoveVisibleAssistantForAction(
|
|
|
83
131
|
return action.type === "regenerate" && action.removedAssistant;
|
|
84
132
|
}
|
|
85
133
|
|
|
134
|
+
export interface HistoryNavState {
|
|
135
|
+
historyIdx: number | null;
|
|
136
|
+
draft: { value: string; cursor: number };
|
|
137
|
+
historyDraft: { value: string; cursor: number } | null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function historyNavStep(
|
|
141
|
+
state: HistoryNavState,
|
|
142
|
+
history: readonly string[],
|
|
143
|
+
direction: "up" | "down",
|
|
144
|
+
): HistoryNavState {
|
|
145
|
+
if (direction === "up") {
|
|
146
|
+
if (history.length === 0) return state;
|
|
147
|
+
const snapshot =
|
|
148
|
+
state.historyIdx === null ? { ...state.draft } : state.historyDraft;
|
|
149
|
+
const idx =
|
|
150
|
+
state.historyIdx === null
|
|
151
|
+
? history.length - 1
|
|
152
|
+
: Math.max(0, state.historyIdx - 1);
|
|
153
|
+
const entry = history[idx] ?? "";
|
|
154
|
+
return {
|
|
155
|
+
historyIdx: idx,
|
|
156
|
+
draft: { value: entry, cursor: graphemeLength(entry) },
|
|
157
|
+
historyDraft: snapshot,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (state.historyIdx === null) return state;
|
|
161
|
+
const next = state.historyIdx + 1;
|
|
162
|
+
if (next >= history.length) {
|
|
163
|
+
const restored = state.historyDraft ?? { value: "", cursor: 0 };
|
|
164
|
+
return { historyIdx: null, draft: restored, historyDraft: null };
|
|
165
|
+
}
|
|
166
|
+
const entry = history[next] ?? "";
|
|
167
|
+
return {
|
|
168
|
+
historyIdx: next,
|
|
169
|
+
draft: { value: entry, cursor: graphemeLength(entry) },
|
|
170
|
+
historyDraft: state.historyDraft,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
86
174
|
function pick<T>(arr: readonly T[]): T {
|
|
87
175
|
if (arr.length === 0) {
|
|
88
176
|
throw new Error("pick called on empty array");
|
|
@@ -136,12 +224,20 @@ export function App({
|
|
|
136
224
|
};
|
|
137
225
|
}, [stdout]);
|
|
138
226
|
const mode = useMemo(() => pickLayout(cols), [cols]);
|
|
139
|
-
const inputWidth = useMemo(() => Math.max(1, cols), [cols]);
|
|
140
227
|
const chromeWidth = useMemo(() => Math.max(1, cols), [cols]);
|
|
141
|
-
const statusBarWidth = inputWidth;
|
|
142
228
|
const isCompact = mode === "very-narrow";
|
|
143
229
|
const integratedIntro =
|
|
144
230
|
showIntroChrome && typeof greeting === "string" && rows >= 32;
|
|
231
|
+
const showPetPanel =
|
|
232
|
+
cols >= PET_PANEL_MIN_COLUMNS && rows >= PET_PANEL_MIN_ROWS && !integratedIntro;
|
|
233
|
+
const petPanelReservedWidth = showPetPanel
|
|
234
|
+
? PET_PANEL_WIDTH + PET_PANEL_GAP_COLUMNS
|
|
235
|
+
: 0;
|
|
236
|
+
const contentWidth = showPetPanel
|
|
237
|
+
? Math.max(1, cols - petPanelReservedWidth)
|
|
238
|
+
: chromeWidth;
|
|
239
|
+
const contentInputWidth = Math.max(1, contentWidth);
|
|
240
|
+
const contentStatusWidth = Math.max(1, contentInputWidth - 2);
|
|
145
241
|
const introRowBudget =
|
|
146
242
|
integratedIntro ? (chromeWidth >= 112 ? 14 : chromeWidth >= 72 ? 26 : 6) : 0;
|
|
147
243
|
const maxTranscriptRows = useMemo(
|
|
@@ -197,11 +293,6 @@ export function App({
|
|
|
197
293
|
const [witticism, setWitticism] = useState<string>(pick(WITTICISMS));
|
|
198
294
|
const [model, setModel] = useState<string>(config.model);
|
|
199
295
|
const [msgCount, setMsgCount] = useState<number>(0);
|
|
200
|
-
const [tokenCount, setTokenCount] = useState<number>(
|
|
201
|
-
conversation.approximateTokens(),
|
|
202
|
-
);
|
|
203
|
-
const [lastLatencyMs, setLastLatencyMs] = useState<number | null>(null);
|
|
204
|
-
const [fallbackModel, setFallbackModel] = useState<string | null>(null);
|
|
205
296
|
const [deskStatus, setDeskStatus] = useState<"idle" | "error">("idle");
|
|
206
297
|
const [deskNotice, setDeskNotice] = useState<string | null>(null);
|
|
207
298
|
const [history, setHistory] = useState<string[]>([]);
|
|
@@ -210,6 +301,146 @@ export function App({
|
|
|
210
301
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
211
302
|
const intro = useIntroAnimation(chromeWidth, integratedIntro);
|
|
212
303
|
|
|
304
|
+
const [petStats, setPetStats] = useState<PetStats>(() => loadPetState());
|
|
305
|
+
const [petActivity, setPetActivity] = useState<PetActivity>("idle");
|
|
306
|
+
const [isDead, setIsDead] = useState(false);
|
|
307
|
+
const [deathReason, setDeathReason] = useState("energy");
|
|
308
|
+
const [deathVariant, setDeathVariant] = useState(0);
|
|
309
|
+
const petStatsRef = useRef<PetStats>(petStats);
|
|
310
|
+
const petActivityTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
311
|
+
const petDecayTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
312
|
+
|
|
313
|
+
const petEnv = useMemo((): Environment => {
|
|
314
|
+
const h = new Date().getHours();
|
|
315
|
+
if (h >= 9 && h < 18) return "office";
|
|
316
|
+
if (h >= 6 && h < 23) return "home";
|
|
317
|
+
return "outdoors";
|
|
318
|
+
}, []);
|
|
319
|
+
|
|
320
|
+
const PET_MESSAGES = useMemo(() => ({
|
|
321
|
+
feed: [
|
|
322
|
+
"Drexler receives deal memo. Hunger: satisfied. Pipeline: expanding.",
|
|
323
|
+
"Drexler consumes quarterly report. Fortifying.",
|
|
324
|
+
"Deal deck delivered. Drexler is replenished.",
|
|
325
|
+
"Nutrition acquired via term sheet. Excellent.",
|
|
326
|
+
"Drexler ingests synergy bundle. Caloric intake: maximized.",
|
|
327
|
+
"Pipeline refueled. Drexler gives brief nod of approval.",
|
|
328
|
+
],
|
|
329
|
+
play: [
|
|
330
|
+
"Drexler engages in corporate synergy games. Morale: elevated.",
|
|
331
|
+
"Drexler attempts leisure. Unfamiliar but effective.",
|
|
332
|
+
"Golf simulation initiated. Handicap: nonexistent.",
|
|
333
|
+
"Corporate retreat protocols engaged. Team building: successful.",
|
|
334
|
+
"Drexler plays. Competitors watch nervously.",
|
|
335
|
+
"Recreational time allocated. ROI: unclear but positive.",
|
|
336
|
+
],
|
|
337
|
+
work: [
|
|
338
|
+
"Drexler retreats to deal desk. Pipeline throughput: increasing.",
|
|
339
|
+
"Grind mode initiated. Coffee consumed preemptively.",
|
|
340
|
+
"Drexler is doing the work. Others take note.",
|
|
341
|
+
"Deal origination in progress. Board is watching.",
|
|
342
|
+
"Drexler enters flow state. Productivity: exceptional.",
|
|
343
|
+
"All-nighter commenced. Regrets: minimal.",
|
|
344
|
+
],
|
|
345
|
+
praise: [
|
|
346
|
+
"Drexler acknowledges commendation. Briefly.",
|
|
347
|
+
"Praise received. Filed under: expected.",
|
|
348
|
+
"Drexler nods. One singular nod.",
|
|
349
|
+
"Affirmation noted. Drexler remains unmoved. Mostly.",
|
|
350
|
+
"Kind words processed. Ego: appropriately inflated.",
|
|
351
|
+
"Drexler accepts compliment with characteristic restraint.",
|
|
352
|
+
],
|
|
353
|
+
rest: [
|
|
354
|
+
"Drexler retires briefly. Strategic recharge in progress.",
|
|
355
|
+
"Under-desk nap initiated. Do not disturb.",
|
|
356
|
+
"Drexler powers down. Temporarily.",
|
|
357
|
+
"Rest mode engaged. Energy recovery: imminent.",
|
|
358
|
+
"Strategic downtime commenced. Drexler will return stronger.",
|
|
359
|
+
"Drexler sleeps. Dreams of closed deals.",
|
|
360
|
+
],
|
|
361
|
+
}), []);
|
|
362
|
+
|
|
363
|
+
const triggerPetActivity = useCallback(
|
|
364
|
+
(activity: PetActivity, durationMs: number) => {
|
|
365
|
+
if (petActivityTimerRef.current !== null) {
|
|
366
|
+
clearTimeout(petActivityTimerRef.current);
|
|
367
|
+
}
|
|
368
|
+
setPetActivity(activity);
|
|
369
|
+
petActivityTimerRef.current = setTimeout(() => {
|
|
370
|
+
setPetActivity("idle");
|
|
371
|
+
petActivityTimerRef.current = null;
|
|
372
|
+
}, durationMs);
|
|
373
|
+
},
|
|
374
|
+
[],
|
|
375
|
+
);
|
|
376
|
+
const updatePetStats = useCallback((updater: (stats: PetStats) => PetStats) => {
|
|
377
|
+
setPetStats((stats) => {
|
|
378
|
+
const next = updater(stats);
|
|
379
|
+
petStatsRef.current = next;
|
|
380
|
+
savePetState(next);
|
|
381
|
+
return next;
|
|
382
|
+
});
|
|
383
|
+
}, []);
|
|
384
|
+
const applyPetAction = useCallback(
|
|
385
|
+
(action: PetActionKey, mutator: (stats: PetStats) => PetStats) => {
|
|
386
|
+
const before = getPetRank(petStatsRef.current);
|
|
387
|
+
updatePetStats((s) => {
|
|
388
|
+
const next = stampAction(mutator(s), action);
|
|
389
|
+
return accrueLifetimeDeals(next, action);
|
|
390
|
+
});
|
|
391
|
+
const after = getPetRank(petStatsRef.current);
|
|
392
|
+
if (after !== before) {
|
|
393
|
+
addItem(
|
|
394
|
+
"system",
|
|
395
|
+
`PROMOTION MEMO: Drexler ranked up to ${rankLabel(after)}. Reward: more meetings.`,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
[updatePetStats, addItem],
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
petStatsRef.current = petStats;
|
|
404
|
+
}, [petStats]);
|
|
405
|
+
|
|
406
|
+
// Real-time stat decay matches the offline per-hour decay rate.
|
|
407
|
+
useEffect(() => {
|
|
408
|
+
if (!showPetPanel) {
|
|
409
|
+
return () => {
|
|
410
|
+
savePetState(petStatsRef.current);
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
petDecayTimerRef.current = setInterval(() => {
|
|
414
|
+
updatePetStats(applyMinuteDecay);
|
|
415
|
+
}, 60_000);
|
|
416
|
+
return () => {
|
|
417
|
+
if (petDecayTimerRef.current !== null) {
|
|
418
|
+
clearInterval(petDecayTimerRef.current);
|
|
419
|
+
petDecayTimerRef.current = null;
|
|
420
|
+
}
|
|
421
|
+
if (petActivityTimerRef.current !== null) {
|
|
422
|
+
clearTimeout(petActivityTimerRef.current);
|
|
423
|
+
petActivityTimerRef.current = null;
|
|
424
|
+
}
|
|
425
|
+
savePetState(petStatsRef.current);
|
|
426
|
+
};
|
|
427
|
+
}, [showPetPanel, updatePetStats]);
|
|
428
|
+
|
|
429
|
+
// Death detection
|
|
430
|
+
useEffect(() => {
|
|
431
|
+
if (!showPetPanel || isDead || !isPetDead(petStats)) return;
|
|
432
|
+
const reason =
|
|
433
|
+
petStats.hunger <= 0 ? "hunger" :
|
|
434
|
+
petStats.happiness <= 0 ? "happiness" : "energy";
|
|
435
|
+
setDeathReason(reason);
|
|
436
|
+
setDeathVariant(Math.floor(Math.random() * 5));
|
|
437
|
+
setIsDead(true);
|
|
438
|
+
const deadStats = { ...petStats, dead: true };
|
|
439
|
+
petStatsRef.current = deadStats;
|
|
440
|
+
savePetState(deadStats);
|
|
441
|
+
exitTimerRef.current = setTimeout(() => exit(), 5000);
|
|
442
|
+
}, [petStats, showPetPanel, isDead, exit]);
|
|
443
|
+
|
|
213
444
|
const paletteItems = useMemo(() => filterPaletteByPrefix(input), [input]);
|
|
214
445
|
const paletteOpen = paletteItems.length > 0;
|
|
215
446
|
useEffect(() => {
|
|
@@ -220,23 +451,17 @@ export function App({
|
|
|
220
451
|
setScrollOffset(0);
|
|
221
452
|
}, [items.length]);
|
|
222
453
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
THEME_NAMES.find((name) => THEMES[name] === active) ??
|
|
231
|
-
config.theme ??
|
|
232
|
-
"apollo"
|
|
233
|
-
);
|
|
234
|
-
}, [activeTheme, config.theme]);
|
|
235
|
-
|
|
454
|
+
const visibleTranscriptRows = synergyEvent
|
|
455
|
+
? Math.max(1, maxTranscriptRows - synergyEventRows(contentWidth, isCompact))
|
|
456
|
+
: maxTranscriptRows;
|
|
457
|
+
const estimatedTranscriptRows = useMemo(
|
|
458
|
+
() => estimateTranscriptRows(items, isCompact, contentWidth),
|
|
459
|
+
[items, isCompact, contentWidth],
|
|
460
|
+
);
|
|
236
461
|
const scrollHint = useMemo(() => {
|
|
237
|
-
if (
|
|
462
|
+
if (estimatedTranscriptRows <= visibleTranscriptRows) return undefined;
|
|
238
463
|
return scrollOffset > 0 ? "PageDown newer" : "PageUp scrollback";
|
|
239
|
-
}, [
|
|
464
|
+
}, [estimatedTranscriptRows, visibleTranscriptRows, scrollOffset]);
|
|
240
465
|
|
|
241
466
|
// throttle streaming updates so React doesn't re-render every token
|
|
242
467
|
const streamBufRef = useRef("");
|
|
@@ -249,6 +474,7 @@ export function App({
|
|
|
249
474
|
const exitingRef = useRef(false);
|
|
250
475
|
const exitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
251
476
|
const synergyTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
477
|
+
const historyDraftRef = useRef<{ value: string; cursor: number } | null>(null);
|
|
252
478
|
const flushStream = useCallback(() => {
|
|
253
479
|
if (!mountedRef.current) return;
|
|
254
480
|
setStreaming(streamBufRef.current);
|
|
@@ -260,6 +486,7 @@ export function App({
|
|
|
260
486
|
if (exitingRef.current) return;
|
|
261
487
|
exitingRef.current = true;
|
|
262
488
|
abortRef.current?.abort();
|
|
489
|
+
savePetState(petStatsRef.current);
|
|
263
490
|
if (streamTimerRef.current !== null) {
|
|
264
491
|
clearTimeout(streamTimerRef.current);
|
|
265
492
|
streamTimerRef.current = null;
|
|
@@ -333,12 +560,10 @@ export function App({
|
|
|
333
560
|
if (requestInFlightRef.current) return;
|
|
334
561
|
requestInFlightRef.current = true;
|
|
335
562
|
setRequestInFlight(true);
|
|
336
|
-
const startedAt = Date.now();
|
|
337
563
|
try {
|
|
338
564
|
setThinking(pick(THINKING_LINES));
|
|
339
565
|
setDeskStatus("idle");
|
|
340
566
|
setDeskNotice(null);
|
|
341
|
-
setFallbackModel(null);
|
|
342
567
|
streamBufRef.current = "";
|
|
343
568
|
setStreaming(null);
|
|
344
569
|
let firstToken = true;
|
|
@@ -389,7 +614,6 @@ export function App({
|
|
|
389
614
|
}
|
|
390
615
|
setThinking(null);
|
|
391
616
|
setStreaming(null);
|
|
392
|
-
setLastLatencyMs(Date.now() - startedAt);
|
|
393
617
|
if (cancelledRef.current) {
|
|
394
618
|
cancelledRef.current = false;
|
|
395
619
|
if (result?.content) {
|
|
@@ -405,7 +629,6 @@ export function App({
|
|
|
405
629
|
if (result.fellBack) {
|
|
406
630
|
addItem("system", `(fell back to ${result.modelUsed})`);
|
|
407
631
|
notices.push(`fallback ${result.modelUsed}`);
|
|
408
|
-
setFallbackModel(result.modelUsed);
|
|
409
632
|
}
|
|
410
633
|
if (detectPersonaDrift(result.content)) {
|
|
411
634
|
addItem("system", `(persona drift detected — model used 'I')`);
|
|
@@ -425,7 +648,6 @@ export function App({
|
|
|
425
648
|
setDeskNotice(result?.error ?? "stream error");
|
|
426
649
|
}
|
|
427
650
|
setMsgCount(conversation.length);
|
|
428
|
-
setTokenCount(conversation.approximateTokens());
|
|
429
651
|
setWitticism(pick(WITTICISMS));
|
|
430
652
|
} finally {
|
|
431
653
|
requestInFlightRef.current = false;
|
|
@@ -444,6 +666,134 @@ export function App({
|
|
|
444
666
|
|
|
445
667
|
const handleSlashWithMutation = useCallback(
|
|
446
668
|
async (line: string): Promise<void> => {
|
|
669
|
+
const lower = line.toLowerCase().trim();
|
|
670
|
+
const [slashCommand = lower] = lower.split(/\s+/, 1);
|
|
671
|
+
|
|
672
|
+
// Pet commands — handled before dispatch so they don't hit the unknown-command path
|
|
673
|
+
if (lower === "/synergy") {
|
|
674
|
+
runSynergyEvent();
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
const isPetCommand =
|
|
678
|
+
slashCommand === "/feed" ||
|
|
679
|
+
slashCommand === "/play" ||
|
|
680
|
+
slashCommand === "/work" ||
|
|
681
|
+
slashCommand === "/praise" ||
|
|
682
|
+
slashCommand === "/rest" ||
|
|
683
|
+
slashCommand === "/vibe";
|
|
684
|
+
if (isPetCommand && isDead) {
|
|
685
|
+
addItem("system", "Drexler is in HR. Restructuring paperwork pending — try again after revival.");
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const cooldownAction: PetActionKey | null =
|
|
689
|
+
slashCommand === "/feed" ? "feed"
|
|
690
|
+
: slashCommand === "/play" ? "play"
|
|
691
|
+
: slashCommand === "/work" ? "work"
|
|
692
|
+
: slashCommand === "/praise" ? "praise"
|
|
693
|
+
: slashCommand === "/rest" ? "rest"
|
|
694
|
+
: slashCommand === "/vibe" ? "vibe"
|
|
695
|
+
: null;
|
|
696
|
+
if (cooldownAction !== null) {
|
|
697
|
+
const cd = actionCooldown(petStatsRef.current, cooldownAction);
|
|
698
|
+
if (!cd.ok) {
|
|
699
|
+
addItem(
|
|
700
|
+
"system",
|
|
701
|
+
`Drexler ${cooldownAction === "feed" ? "just ate" : cooldownAction === "play" ? "just played" : cooldownAction === "work" ? "just worked" : cooldownAction === "praise" ? "just got praised" : cooldownAction === "rest" ? "just rested" : "just vibed"}. Wait ${formatCooldownRemaining(cd.remainingMs)} before the next attempt. Drexler resents micromanagement.`,
|
|
702
|
+
);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (slashCommand === "/feed") {
|
|
707
|
+
applyPetAction("feed", applyFeed);
|
|
708
|
+
triggerPetActivity("eating", 3500);
|
|
709
|
+
addItem("system", pick(PET_MESSAGES.feed));
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (slashCommand === "/play") {
|
|
713
|
+
applyPetAction("play", applyPlay);
|
|
714
|
+
triggerPetActivity("playing", 4000);
|
|
715
|
+
addItem("system", pick(PET_MESSAGES.play));
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (slashCommand === "/work") {
|
|
719
|
+
applyPetAction("work", applyWork);
|
|
720
|
+
triggerPetActivity("working", 5000);
|
|
721
|
+
addItem("system", pick(PET_MESSAGES.work));
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (slashCommand === "/praise") {
|
|
725
|
+
applyPetAction("praise", applyPraise);
|
|
726
|
+
triggerPetActivity("praised", 3000);
|
|
727
|
+
addItem("system", pick(PET_MESSAGES.praise));
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (slashCommand === "/rest") {
|
|
731
|
+
applyPetAction("rest", applyRest);
|
|
732
|
+
triggerPetActivity("sleeping", 5000);
|
|
733
|
+
addItem("system", pick(PET_MESSAGES.rest));
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
if (slashCommand === "/vibe") {
|
|
737
|
+
const result = applyVibe(petStatsRef.current);
|
|
738
|
+
applyPetAction("vibe", () => result.stats);
|
|
739
|
+
triggerPetActivity("vibing", 3500);
|
|
740
|
+
addItem("system", result.message);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (slashCommand === "/name") {
|
|
744
|
+
const arg = line.slice("/name".length).trim();
|
|
745
|
+
if (arg.length === 0) {
|
|
746
|
+
const current = petStatsRef.current.name;
|
|
747
|
+
addItem(
|
|
748
|
+
"system",
|
|
749
|
+
current
|
|
750
|
+
? `Drexler's pet name on file: "${current}". /name <new> to reassign.`
|
|
751
|
+
: "No pet name on file. /name <name> to issue corporate identity.",
|
|
752
|
+
);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const cleaned = sanitizePetName(arg);
|
|
756
|
+
if (cleaned.length === 0) {
|
|
757
|
+
addItem(
|
|
758
|
+
"system",
|
|
759
|
+
"Drexler refuses unprintable identity. Pick letters, numbers, spaces, dots, or apostrophes (≤16 chars).",
|
|
760
|
+
);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
updatePetStats((s) => applyName(s, cleaned));
|
|
764
|
+
addItem("system", `Pet renamed: "${cleaned}". Memo distributed to all departments.`);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (slashCommand === "/profile") {
|
|
768
|
+
const s = petStatsRef.current;
|
|
769
|
+
const tenure = formatTenure(petTenureMs(s));
|
|
770
|
+
const mood = getPetMood(s);
|
|
771
|
+
const stats = [
|
|
772
|
+
["hunger", s.hunger],
|
|
773
|
+
["happiness", s.happiness],
|
|
774
|
+
["energy", s.energy],
|
|
775
|
+
["deals", s.deals],
|
|
776
|
+
] as const;
|
|
777
|
+
const dominant = stats.reduce(
|
|
778
|
+
(best, cur) => (cur[1] > best[1] ? cur : best),
|
|
779
|
+
);
|
|
780
|
+
const rank = getPetRank(s);
|
|
781
|
+
const lines = [
|
|
782
|
+
"Drexler personnel file:",
|
|
783
|
+
` name : ${s.name ?? "(unnamed associate)"}`,
|
|
784
|
+
` rank : ${rankLabel(rank)}`,
|
|
785
|
+
` tenure : ${tenure}`,
|
|
786
|
+
` mood : ${mood}`,
|
|
787
|
+
` hunger : ${Math.round(s.hunger)}%`,
|
|
788
|
+
` happiness : ${Math.round(s.happiness)}%`,
|
|
789
|
+
` energy : ${Math.round(s.energy)}%`,
|
|
790
|
+
` deals : ${Math.round(s.deals)}%`,
|
|
791
|
+
` standout : ${dominant[0]} (${Math.round(dominant[1])}%)`,
|
|
792
|
+
];
|
|
793
|
+
addItem("system", lines.join("\n"));
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
|
|
447
797
|
let captured = "";
|
|
448
798
|
const mutableConfig: Config = { ...config, model };
|
|
449
799
|
const action = dispatch(line, {
|
|
@@ -453,15 +803,8 @@ export function App({
|
|
|
453
803
|
captured += (captured ? "\n" : "") + s;
|
|
454
804
|
},
|
|
455
805
|
});
|
|
456
|
-
const lower = line.toLowerCase().trim();
|
|
457
|
-
if (lower === "/synergy") {
|
|
458
|
-
runSynergyEvent();
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
806
|
if (lower === "/clear" || lower.startsWith("/clear ")) {
|
|
462
807
|
setItems([]);
|
|
463
|
-
setLastLatencyMs(null);
|
|
464
|
-
setFallbackModel(null);
|
|
465
808
|
}
|
|
466
809
|
if (mutableConfig.model !== model) {
|
|
467
810
|
setModel(mutableConfig.model);
|
|
@@ -498,7 +841,6 @@ export function App({
|
|
|
498
841
|
await runLLM(action.instruction);
|
|
499
842
|
}
|
|
500
843
|
setMsgCount(conversation.length);
|
|
501
|
-
setTokenCount(conversation.approximateTokens());
|
|
502
844
|
},
|
|
503
845
|
[
|
|
504
846
|
addItem,
|
|
@@ -506,10 +848,14 @@ export function App({
|
|
|
506
848
|
config,
|
|
507
849
|
activeTheme,
|
|
508
850
|
model,
|
|
851
|
+
petStats,
|
|
852
|
+
PET_MESSAGES,
|
|
509
853
|
removeLastAssistantItem,
|
|
510
854
|
runLLM,
|
|
511
855
|
runSynergyEvent,
|
|
512
856
|
triggerExit,
|
|
857
|
+
triggerPetActivity,
|
|
858
|
+
updatePetStats,
|
|
513
859
|
],
|
|
514
860
|
);
|
|
515
861
|
|
|
@@ -528,13 +874,46 @@ export function App({
|
|
|
528
874
|
addItem("user", line);
|
|
529
875
|
conversation.push("user", line);
|
|
530
876
|
setMsgCount(conversation.length);
|
|
531
|
-
setTokenCount(conversation.approximateTokens());
|
|
532
877
|
await runLLM();
|
|
533
878
|
},
|
|
534
879
|
[addItem, conversation, handleSlashWithMutation, runLLM],
|
|
535
880
|
);
|
|
536
881
|
|
|
882
|
+
const reportSubmitError = useCallback(
|
|
883
|
+
(err: unknown) => {
|
|
884
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
885
|
+
addItem("system", `${STREAM_ERROR} [${msg}]`);
|
|
886
|
+
setDeskStatus("error");
|
|
887
|
+
setDeskNotice("submit failed");
|
|
888
|
+
},
|
|
889
|
+
[addItem],
|
|
890
|
+
);
|
|
891
|
+
|
|
537
892
|
useInput((char, key) => {
|
|
893
|
+
// Scroll keys are always live — they only mutate scrollOffset and never
|
|
894
|
+
// commit input, so we let the user review history during streaming.
|
|
895
|
+
if (key.pageUp) {
|
|
896
|
+
setScrollOffset((offset) =>
|
|
897
|
+
nextTranscriptScrollOffset({
|
|
898
|
+
current: offset,
|
|
899
|
+
totalRows: estimatedTranscriptRows,
|
|
900
|
+
visibleRows: visibleTranscriptRows,
|
|
901
|
+
direction: "older",
|
|
902
|
+
}),
|
|
903
|
+
);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
if (key.pageDown) {
|
|
907
|
+
setScrollOffset((offset) =>
|
|
908
|
+
nextTranscriptScrollOffset({
|
|
909
|
+
current: offset,
|
|
910
|
+
totalRows: estimatedTranscriptRows,
|
|
911
|
+
visibleRows: visibleTranscriptRows,
|
|
912
|
+
direction: "newer",
|
|
913
|
+
}),
|
|
914
|
+
);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
538
917
|
const busy =
|
|
539
918
|
requestInFlightRef.current ||
|
|
540
919
|
synergyActiveRef.current ||
|
|
@@ -565,32 +944,25 @@ export function App({
|
|
|
565
944
|
}
|
|
566
945
|
return;
|
|
567
946
|
}
|
|
568
|
-
if (key.pageUp) {
|
|
569
|
-
setScrollOffset((offset) =>
|
|
570
|
-
nextTranscriptScrollOffset({
|
|
571
|
-
current: offset,
|
|
572
|
-
itemCount: items.length,
|
|
573
|
-
direction: "older",
|
|
574
|
-
}),
|
|
575
|
-
);
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (key.pageDown) {
|
|
579
|
-
setScrollOffset((offset) =>
|
|
580
|
-
nextTranscriptScrollOffset({
|
|
581
|
-
current: offset,
|
|
582
|
-
itemCount: items.length,
|
|
583
|
-
direction: "newer",
|
|
584
|
-
}),
|
|
585
|
-
);
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
947
|
if (paletteOpen && key.return) {
|
|
589
948
|
const sel = paletteItems[paletteIdx];
|
|
590
949
|
if (sel) {
|
|
950
|
+
// Bare /theme, /model, etc. — open the chooser, do not execute.
|
|
951
|
+
if (isArgumentParentCommand(sel.name)) {
|
|
952
|
+
const filled = sel.name + " ";
|
|
953
|
+
updateDraft({
|
|
954
|
+
value: filled,
|
|
955
|
+
cursor: graphemeLength(filled),
|
|
956
|
+
});
|
|
957
|
+
setPaletteIdx(0);
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
591
960
|
updateDraft({ value: "", cursor: 0 });
|
|
592
961
|
setHistoryIdx(null);
|
|
593
|
-
|
|
962
|
+
historyDraftRef.current = null;
|
|
963
|
+
onSubmit(sel.name).catch((err) => {
|
|
964
|
+
reportSubmitError(err);
|
|
965
|
+
});
|
|
594
966
|
}
|
|
595
967
|
return;
|
|
596
968
|
}
|
|
@@ -598,6 +970,7 @@ export function App({
|
|
|
598
970
|
const submitted = draftRef.current.value;
|
|
599
971
|
updateDraft({ value: "", cursor: 0 });
|
|
600
972
|
setHistoryIdx(null);
|
|
973
|
+
historyDraftRef.current = null;
|
|
601
974
|
const trimmedSubmit = submitted.trim();
|
|
602
975
|
if (trimmedSubmit.length > 0) {
|
|
603
976
|
setHistory((prev) => {
|
|
@@ -605,7 +978,9 @@ export function App({
|
|
|
605
978
|
return next.length > 50 ? next.slice(-50) : next;
|
|
606
979
|
});
|
|
607
980
|
}
|
|
608
|
-
|
|
981
|
+
onSubmit(submitted).catch((err) => {
|
|
982
|
+
reportSubmitError(err);
|
|
983
|
+
});
|
|
609
984
|
return;
|
|
610
985
|
}
|
|
611
986
|
if (key.ctrl && char === "c") {
|
|
@@ -649,10 +1024,18 @@ export function App({
|
|
|
649
1024
|
return;
|
|
650
1025
|
}
|
|
651
1026
|
if (history.length === 0) return;
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
1027
|
+
const next = historyNavStep(
|
|
1028
|
+
{
|
|
1029
|
+
historyIdx,
|
|
1030
|
+
draft: draftRef.current,
|
|
1031
|
+
historyDraft: historyDraftRef.current,
|
|
1032
|
+
},
|
|
1033
|
+
history,
|
|
1034
|
+
"up",
|
|
1035
|
+
);
|
|
1036
|
+
historyDraftRef.current = next.historyDraft;
|
|
1037
|
+
setHistoryIdx(next.historyIdx);
|
|
1038
|
+
updateDraft(next.draft);
|
|
656
1039
|
return;
|
|
657
1040
|
}
|
|
658
1041
|
if (key.downArrow) {
|
|
@@ -661,15 +1044,18 @@ export function App({
|
|
|
661
1044
|
return;
|
|
662
1045
|
}
|
|
663
1046
|
if (historyIdx === null) return;
|
|
664
|
-
const next =
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
1047
|
+
const next = historyNavStep(
|
|
1048
|
+
{
|
|
1049
|
+
historyIdx,
|
|
1050
|
+
draft: draftRef.current,
|
|
1051
|
+
historyDraft: historyDraftRef.current,
|
|
1052
|
+
},
|
|
1053
|
+
history,
|
|
1054
|
+
"down",
|
|
1055
|
+
);
|
|
1056
|
+
historyDraftRef.current = next.historyDraft;
|
|
1057
|
+
setHistoryIdx(next.historyIdx);
|
|
1058
|
+
updateDraft(next.draft);
|
|
673
1059
|
return;
|
|
674
1060
|
}
|
|
675
1061
|
if (key.ctrl && char === "a") {
|
|
@@ -726,13 +1112,8 @@ export function App({
|
|
|
726
1112
|
const headerStatus = isBusy ? "streaming" : deskStatus;
|
|
727
1113
|
const renderDealDeskHeader = (width: number) => (
|
|
728
1114
|
<DealDeskHeader
|
|
729
|
-
model={model}
|
|
730
1115
|
mood={mood}
|
|
731
1116
|
messageCount={msgCount}
|
|
732
|
-
themeName={themeName}
|
|
733
|
-
approximateTokens={tokenCount}
|
|
734
|
-
latencyMs={lastLatencyMs}
|
|
735
|
-
fallbackModel={fallbackModel}
|
|
736
1117
|
status={headerStatus}
|
|
737
1118
|
compact={isCompact}
|
|
738
1119
|
notice={!integratedIntro ? deskNotice ?? undefined : undefined}
|
|
@@ -741,11 +1122,16 @@ export function App({
|
|
|
741
1122
|
/>
|
|
742
1123
|
);
|
|
743
1124
|
const dealDeskHeader = renderDealDeskHeader(chromeWidth);
|
|
744
|
-
const visibleTranscriptRows = synergyEvent
|
|
745
|
-
? Math.max(1, maxTranscriptRows - synergyEventRows(chromeWidth, isCompact))
|
|
746
|
-
: maxTranscriptRows;
|
|
747
1125
|
const introBarColor = introPhaseColor(intro.colorPhase, t);
|
|
748
1126
|
|
|
1127
|
+
if (isDead) {
|
|
1128
|
+
return (
|
|
1129
|
+
<ThemeProvider value={activeTheme}>
|
|
1130
|
+
<DeathScreen reason={deathReason} variant={deathVariant} />
|
|
1131
|
+
</ThemeProvider>
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
749
1135
|
return (
|
|
750
1136
|
<ThemeProvider value={activeTheme}>
|
|
751
1137
|
<Box flexDirection="column">
|
|
@@ -766,73 +1152,86 @@ export function App({
|
|
|
766
1152
|
) : (
|
|
767
1153
|
dealDeskHeader
|
|
768
1154
|
)}
|
|
769
|
-
<
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
{streaming !== null && (
|
|
779
|
-
<Box marginBottom={1}>
|
|
780
|
-
<StreamingMessage content={streaming} width={chromeWidth} />
|
|
781
|
-
</Box>
|
|
782
|
-
)}
|
|
783
|
-
{thinking !== null && streaming === null && (
|
|
784
|
-
<Box marginBottom={1}>
|
|
785
|
-
<Spinner label={thinking} width={chromeWidth} />
|
|
1155
|
+
<Box flexDirection="row" alignItems="flex-start">
|
|
1156
|
+
{showPetPanel && (
|
|
1157
|
+
<Box marginRight={PET_PANEL_GAP_COLUMNS}>
|
|
1158
|
+
<PetPanel
|
|
1159
|
+
stats={petStats}
|
|
1160
|
+
activity={petActivity}
|
|
1161
|
+
env={petEnv}
|
|
1162
|
+
isPaused={isBusy}
|
|
1163
|
+
/>
|
|
786
1164
|
</Box>
|
|
787
1165
|
)}
|
|
788
|
-
|
|
789
|
-
<
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
1166
|
+
<Box flexDirection="column" flexGrow={1}>
|
|
1167
|
+
<TranscriptViewport
|
|
1168
|
+
items={items}
|
|
1169
|
+
maxRows={visibleTranscriptRows}
|
|
1170
|
+
cols={contentWidth}
|
|
793
1171
|
compact={isCompact}
|
|
1172
|
+
scrollOffset={scrollOffset}
|
|
794
1173
|
/>
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
</Text>
|
|
801
|
-
</Box>
|
|
802
|
-
) : (
|
|
803
|
-
<>
|
|
804
|
-
{paletteOpen && (
|
|
805
|
-
<CommandPalette
|
|
806
|
-
items={paletteItems}
|
|
807
|
-
selectedIdx={paletteIdx}
|
|
808
|
-
width={chromeWidth}
|
|
809
|
-
/>
|
|
1174
|
+
<Box flexDirection="column">
|
|
1175
|
+
{streaming !== null && (
|
|
1176
|
+
<Box marginBottom={1}>
|
|
1177
|
+
<StreamingMessage content={streaming} width={contentWidth} />
|
|
1178
|
+
</Box>
|
|
810
1179
|
)}
|
|
811
|
-
|
|
812
|
-
<
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
}
|
|
821
|
-
width={inputWidth}
|
|
822
|
-
/>
|
|
823
|
-
</Box>
|
|
824
|
-
<Box paddingLeft={2}>
|
|
825
|
-
<StatusBar
|
|
826
|
-
messageCount={msgCount}
|
|
827
|
-
witticism={witticism}
|
|
828
|
-
maxWidth={Math.max(1, statusBarWidth - 2)}
|
|
829
|
-
status={isBusy ? "streaming" : deskStatus}
|
|
1180
|
+
{thinking !== null && streaming === null && (
|
|
1181
|
+
<Box paddingX={1} marginBottom={1}>
|
|
1182
|
+
<Spinner label={thinking} width={contentWidth} />
|
|
1183
|
+
</Box>
|
|
1184
|
+
)}
|
|
1185
|
+
{synergyEvent !== null && (
|
|
1186
|
+
<SynergyEvent
|
|
1187
|
+
event={synergyEvent.event}
|
|
1188
|
+
frame={synergyEvent.frame}
|
|
1189
|
+
width={contentWidth}
|
|
830
1190
|
compact={isCompact}
|
|
831
|
-
scrollHint={scrollHint}
|
|
832
1191
|
/>
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1192
|
+
)}
|
|
1193
|
+
{exitMsg !== null ? (
|
|
1194
|
+
<Box paddingX={1} marginBottom={1}>
|
|
1195
|
+
<Text color={t.primaryLight} bold>
|
|
1196
|
+
{exitMsg}
|
|
1197
|
+
</Text>
|
|
1198
|
+
</Box>
|
|
1199
|
+
) : (
|
|
1200
|
+
<>
|
|
1201
|
+
{paletteOpen && (
|
|
1202
|
+
<CommandPalette
|
|
1203
|
+
items={paletteItems}
|
|
1204
|
+
selectedIdx={paletteIdx}
|
|
1205
|
+
width={contentWidth}
|
|
1206
|
+
/>
|
|
1207
|
+
)}
|
|
1208
|
+
<Box flexDirection="column">
|
|
1209
|
+
<InputBox
|
|
1210
|
+
value={input}
|
|
1211
|
+
cursor={cursor}
|
|
1212
|
+
disabled={isBusy}
|
|
1213
|
+
disabledLabel={
|
|
1214
|
+
synergyEvent !== null
|
|
1215
|
+
? "(Synergy event running... boardroom locked)"
|
|
1216
|
+
: undefined
|
|
1217
|
+
}
|
|
1218
|
+
width={contentInputWidth}
|
|
1219
|
+
/>
|
|
1220
|
+
</Box>
|
|
1221
|
+
<Box paddingLeft={2}>
|
|
1222
|
+
<StatusBar
|
|
1223
|
+
messageCount={msgCount}
|
|
1224
|
+
witticism={witticism}
|
|
1225
|
+
maxWidth={contentStatusWidth}
|
|
1226
|
+
status={isBusy ? "streaming" : deskStatus}
|
|
1227
|
+
compact={isCompact}
|
|
1228
|
+
scrollHint={scrollHint}
|
|
1229
|
+
/>
|
|
1230
|
+
</Box>
|
|
1231
|
+
</>
|
|
1232
|
+
)}
|
|
1233
|
+
</Box>
|
|
1234
|
+
</Box>
|
|
836
1235
|
</Box>
|
|
837
1236
|
</Box>
|
|
838
1237
|
</ThemeProvider>
|