drexler 0.2.10 → 0.2.12
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/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/src/index.ts +7 -14
- package/src/ui/App.tsx +49 -16
- package/src/ui/DealDeskHeader.tsx +4 -2
- package/src/ui/MascotIntro.tsx +115 -75
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.12
|
|
4
|
+
|
|
5
|
+
- Removed the duplicate startup card render so normal launches show one startup panel.
|
|
6
|
+
- Tightened the embedded Deal Desk width so its box stays clean inside the startup panel.
|
|
7
|
+
|
|
8
|
+
## 0.2.11
|
|
9
|
+
|
|
10
|
+
- Moved the live Deal Desk chrome into the startup panel on normal launches.
|
|
11
|
+
- Kept a standalone Deal Desk header for fast and no-intro launches.
|
|
12
|
+
- Added coverage for embedded startup-panel Deal Desk rendering.
|
|
13
|
+
|
|
3
14
|
## 0.2.10
|
|
4
15
|
|
|
5
16
|
- Closed transcript turn blocks with right-side borders and corners so user and Drexler cards align with the input frame.
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
} from "./renderer.ts";
|
|
20
20
|
import { startRepl } from "./repl.ts";
|
|
21
21
|
import { App } from "./ui/App.tsx";
|
|
22
|
-
import { MascotIntro } from "./ui/MascotIntro.tsx";
|
|
23
22
|
import { promptForApiKeyWithInk } from "./ui/SetupPrompt.tsx";
|
|
24
23
|
import { ThemeProvider } from "./ui/ThemeContext.tsx";
|
|
25
24
|
import { getActiveTheme, selectTheme, setActiveTheme } from "./ui/themes.ts";
|
|
@@ -127,22 +126,10 @@ async function main(): Promise<void> {
|
|
|
127
126
|
|
|
128
127
|
if (isInteractive) {
|
|
129
128
|
if (!skipIntro) {
|
|
130
|
-
// Print intro to stdout before Ink mounts. Ink's <Static> can't host
|
|
131
|
-
// animated state, and we want the banner visible from boot.
|
|
132
129
|
console.log("");
|
|
133
130
|
await typewriterBanner();
|
|
134
131
|
console.log(tagline());
|
|
135
132
|
console.log("");
|
|
136
|
-
// Animated welcome card via transient Ink instance.
|
|
137
|
-
const intro = render(
|
|
138
|
-
React.createElement(ThemeProvider, {
|
|
139
|
-
value: getActiveTheme(),
|
|
140
|
-
children: React.createElement(MascotIntro, { greeting }),
|
|
141
|
-
}),
|
|
142
|
-
{ exitOnCtrlC: false },
|
|
143
|
-
);
|
|
144
|
-
await intro.waitUntilExit();
|
|
145
|
-
intro.unmount();
|
|
146
133
|
}
|
|
147
134
|
|
|
148
135
|
console.log("");
|
|
@@ -152,7 +139,13 @@ async function main(): Promise<void> {
|
|
|
152
139
|
const { waitUntilExit } = render(
|
|
153
140
|
React.createElement(ThemeProvider, {
|
|
154
141
|
value: getActiveTheme(),
|
|
155
|
-
children: React.createElement(App, {
|
|
142
|
+
children: React.createElement(App, {
|
|
143
|
+
conversation,
|
|
144
|
+
config,
|
|
145
|
+
mood,
|
|
146
|
+
greeting,
|
|
147
|
+
showIntroChrome: !skipIntro,
|
|
148
|
+
}),
|
|
156
149
|
}),
|
|
157
150
|
{ exitOnCtrlC: false },
|
|
158
151
|
);
|
package/src/ui/App.tsx
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
insertAtCursor,
|
|
35
35
|
} from "./graphemes.ts";
|
|
36
36
|
import { InputBox } from "./InputBox.tsx";
|
|
37
|
+
import { MascotDashboard } from "./MascotIntro.tsx";
|
|
37
38
|
import { StreamingMessage } from "./Message.tsx";
|
|
38
39
|
import { Spinner } from "./Spinner.tsx";
|
|
39
40
|
import { StatusBar } from "./StatusBar.tsx";
|
|
@@ -101,9 +102,18 @@ interface AppProps {
|
|
|
101
102
|
config: Config;
|
|
102
103
|
mood?: string;
|
|
103
104
|
fetchFn?: FetchFn;
|
|
105
|
+
greeting?: string;
|
|
106
|
+
showIntroChrome?: boolean;
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
export function App({
|
|
109
|
+
export function App({
|
|
110
|
+
conversation,
|
|
111
|
+
config,
|
|
112
|
+
mood = "neutral",
|
|
113
|
+
fetchFn,
|
|
114
|
+
greeting,
|
|
115
|
+
showIntroChrome = false,
|
|
116
|
+
}: AppProps) {
|
|
107
117
|
const { exit } = useApp();
|
|
108
118
|
const { stdout } = useStdout();
|
|
109
119
|
const [activeTheme, setActiveThemeSnapshot] = useState(() => getActiveTheme());
|
|
@@ -126,9 +136,14 @@ export function App({ conversation, config, mood = "neutral", fetchFn }: AppProp
|
|
|
126
136
|
const chromeWidth = useMemo(() => Math.max(1, cols), [cols]);
|
|
127
137
|
const statusBarWidth = inputWidth;
|
|
128
138
|
const isCompact = mode === "very-narrow";
|
|
139
|
+
const integratedIntro = showIntroChrome && typeof greeting === "string";
|
|
129
140
|
const maxTranscriptRows = useMemo(
|
|
130
|
-
() =>
|
|
131
|
-
|
|
141
|
+
() =>
|
|
142
|
+
Math.max(
|
|
143
|
+
1,
|
|
144
|
+
transcriptRowsForTerminalRows(rows) - (integratedIntro ? 10 : 0),
|
|
145
|
+
),
|
|
146
|
+
[integratedIntro, rows],
|
|
132
147
|
);
|
|
133
148
|
|
|
134
149
|
const [items, setItems] = useState<ChatItem[]>([]);
|
|
@@ -701,6 +716,26 @@ export function App({ conversation, config, mood = "neutral", fetchFn }: AppProp
|
|
|
701
716
|
const isBusy =
|
|
702
717
|
requestInFlight || streaming !== null || thinking !== null || synergyEvent !== null;
|
|
703
718
|
const headerStatus = isBusy ? "streaming" : deskStatus;
|
|
719
|
+
const embeddedDealDeskWidth =
|
|
720
|
+
chromeWidth >= 112
|
|
721
|
+
? Math.min(72, Math.max(42, Math.floor(chromeWidth * 0.34)))
|
|
722
|
+
: Math.max(32, chromeWidth - 8);
|
|
723
|
+
const dealDeskHeader = (
|
|
724
|
+
<DealDeskHeader
|
|
725
|
+
model={model}
|
|
726
|
+
mood={mood}
|
|
727
|
+
messageCount={msgCount}
|
|
728
|
+
themeName={themeName}
|
|
729
|
+
approximateTokens={tokenCount}
|
|
730
|
+
latencyMs={lastLatencyMs}
|
|
731
|
+
fallbackModel={fallbackModel}
|
|
732
|
+
status={headerStatus}
|
|
733
|
+
compact={isCompact}
|
|
734
|
+
notice={!integratedIntro ? deskNotice ?? undefined : undefined}
|
|
735
|
+
maxWidth={integratedIntro ? embeddedDealDeskWidth : chromeWidth}
|
|
736
|
+
marginBottom={integratedIntro ? 0 : 1}
|
|
737
|
+
/>
|
|
738
|
+
);
|
|
704
739
|
const visibleTranscriptRows = synergyEvent
|
|
705
740
|
? Math.max(1, maxTranscriptRows - synergyEventRows(chromeWidth, isCompact))
|
|
706
741
|
: maxTranscriptRows;
|
|
@@ -708,19 +743,17 @@ export function App({ conversation, config, mood = "neutral", fetchFn }: AppProp
|
|
|
708
743
|
return (
|
|
709
744
|
<ThemeProvider value={activeTheme}>
|
|
710
745
|
<Box flexDirection="column">
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
maxWidth={chromeWidth}
|
|
723
|
-
/>
|
|
746
|
+
{integratedIntro ? (
|
|
747
|
+
<Box marginBottom={1}>
|
|
748
|
+
<MascotDashboard
|
|
749
|
+
greeting={greeting}
|
|
750
|
+
width={chromeWidth}
|
|
751
|
+
dealDesk={dealDeskHeader}
|
|
752
|
+
/>
|
|
753
|
+
</Box>
|
|
754
|
+
) : (
|
|
755
|
+
dealDeskHeader
|
|
756
|
+
)}
|
|
724
757
|
<TranscriptViewport
|
|
725
758
|
items={items}
|
|
726
759
|
maxRows={visibleTranscriptRows}
|
|
@@ -16,6 +16,7 @@ export interface DealDeskHeaderProps {
|
|
|
16
16
|
compact?: boolean;
|
|
17
17
|
notice?: string;
|
|
18
18
|
maxWidth?: number;
|
|
19
|
+
marginBottom?: number;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const DEFAULT_WIDTH = 80;
|
|
@@ -151,6 +152,7 @@ function DealDeskHeaderInner({
|
|
|
151
152
|
compact = false,
|
|
152
153
|
notice,
|
|
153
154
|
maxWidth = DEFAULT_WIDTH,
|
|
155
|
+
marginBottom = 1,
|
|
154
156
|
}: DealDeskHeaderProps) {
|
|
155
157
|
const t = useTheme();
|
|
156
158
|
const width = Math.max(MIN_WIDTH, Math.floor(maxWidth));
|
|
@@ -194,7 +196,7 @@ function DealDeskHeaderInner({
|
|
|
194
196
|
|
|
195
197
|
if (width < FRAMED_MIN_WIDTH) {
|
|
196
198
|
return (
|
|
197
|
-
<Box width={width} marginBottom={
|
|
199
|
+
<Box width={width} marginBottom={marginBottom}>
|
|
198
200
|
<Text color={statusColor[status]} wrap="truncate">
|
|
199
201
|
{tinyLine({ model, messageCount, status, width })}
|
|
200
202
|
</Text>
|
|
@@ -203,7 +205,7 @@ function DealDeskHeaderInner({
|
|
|
203
205
|
}
|
|
204
206
|
|
|
205
207
|
return (
|
|
206
|
-
<Box flexDirection="column" width={width} marginBottom={
|
|
208
|
+
<Box flexDirection="column" width={width} marginBottom={marginBottom}>
|
|
207
209
|
<Text color={t.primaryDim}>{lines[0]}</Text>
|
|
208
210
|
<Text color={statusColor[status]}>{lines[1]}</Text>
|
|
209
211
|
{lines.slice(2, -1).map((line, index) => (
|
package/src/ui/MascotIntro.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
3
3
|
import { STARTUP_TIPS } from "../startupTips.ts";
|
|
4
4
|
import {
|
|
5
5
|
MascotFrame,
|
|
@@ -139,6 +139,16 @@ interface IntroProps {
|
|
|
139
139
|
greeting: string;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
interface MascotDashboardProps {
|
|
143
|
+
greeting: string;
|
|
144
|
+
width: number;
|
|
145
|
+
state?: MascotState;
|
|
146
|
+
bar?: string;
|
|
147
|
+
barColor?: string;
|
|
148
|
+
mascotStatus?: string;
|
|
149
|
+
dealDesk?: ReactNode;
|
|
150
|
+
}
|
|
151
|
+
|
|
142
152
|
function bootBar(frameIdx: number, total: number): string {
|
|
143
153
|
const active = Math.max(
|
|
144
154
|
1,
|
|
@@ -167,59 +177,21 @@ function TipsPanel({ width }: { width: number }) {
|
|
|
167
177
|
);
|
|
168
178
|
}
|
|
169
179
|
|
|
170
|
-
export function
|
|
180
|
+
export function MascotDashboard({
|
|
181
|
+
greeting,
|
|
182
|
+
width,
|
|
183
|
+
state = FRAMES[FRAMES.length - 1]!,
|
|
184
|
+
bar = bootBar(FRAMES.length - 1, FRAMES.length),
|
|
185
|
+
barColor,
|
|
186
|
+
mascotStatus = `${INTRO_STATUS_PREFIX}${INTRO_BOOT_NOTES[INTRO_BOOT_NOTES.length - 1]}`,
|
|
187
|
+
dealDesk,
|
|
188
|
+
}: MascotDashboardProps) {
|
|
171
189
|
const t = useTheme();
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
useEffect(() => {
|
|
178
|
-
if (!stdout) return;
|
|
179
|
-
const handler = () => setCols(stdout.columns ?? 80);
|
|
180
|
-
stdout.on("resize", handler);
|
|
181
|
-
return () => {
|
|
182
|
-
stdout.off("resize", handler);
|
|
183
|
-
};
|
|
184
|
-
}, [stdout]);
|
|
185
|
-
|
|
186
|
-
useInput((_input, key) => {
|
|
187
|
-
if (key.escape || key.return || (key.ctrl && _input === "c")) exit();
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
const mountedRef = useRef(true);
|
|
191
|
-
useEffect(() => {
|
|
192
|
-
return () => {
|
|
193
|
-
mountedRef.current = false;
|
|
194
|
-
};
|
|
195
|
-
}, []);
|
|
196
|
-
|
|
197
|
-
useEffect(() => {
|
|
198
|
-
const compact = cols < 72;
|
|
199
|
-
const total = compact ? COMPACT_NOTES.length : FRAMES.length;
|
|
200
|
-
if (frameIdx >= total - 1) {
|
|
201
|
-
const handle = setTimeout(() => {
|
|
202
|
-
if (mountedRef.current) exit();
|
|
203
|
-
}, SETTLE_HOLD_MS);
|
|
204
|
-
return () => clearTimeout(handle);
|
|
205
|
-
}
|
|
206
|
-
const delay = compact
|
|
207
|
-
? COMPACT_DELAY_MS
|
|
208
|
-
: (FRAMES[frameIdx] ?? FRAMES[FRAMES.length - 1]!).delayMs;
|
|
209
|
-
const handle = setTimeout(() => {
|
|
210
|
-
if (mountedRef.current) setFrameIdx((i) => i + 1);
|
|
211
|
-
}, delay);
|
|
212
|
-
return () => clearTimeout(handle);
|
|
213
|
-
}, [cols, frameIdx, exit]);
|
|
214
|
-
|
|
215
|
-
const state = FRAMES[frameIdx] ?? FRAMES[FRAMES.length - 1]!;
|
|
216
|
-
// Below 21 cols, mascot (17) + gutter (4) overflow — render text-only.
|
|
217
|
-
const tinyTerminal = cols < 21;
|
|
218
|
-
const compact = cols < 72;
|
|
219
|
-
const sideBySide = cols >= 112;
|
|
220
|
-
const available = compact
|
|
221
|
-
? Math.max(1, cols - 1)
|
|
222
|
-
: Math.max(28, cols);
|
|
190
|
+
const resolvedBarColor = barColor ?? t.primaryLight;
|
|
191
|
+
const tinyTerminal = width < 21;
|
|
192
|
+
const compact = width < 72;
|
|
193
|
+
const sideBySide = width >= 112;
|
|
194
|
+
const available = compact ? Math.max(1, width - 1) : Math.max(28, width);
|
|
223
195
|
const innerWidth = compact
|
|
224
196
|
? available
|
|
225
197
|
: Math.max(24, available - FRAME_CHROME_WIDTH);
|
|
@@ -239,29 +211,16 @@ export function MascotIntro({ greeting }: IntroProps) {
|
|
|
239
211
|
: sideBySide
|
|
240
212
|
? Math.max(18, leftPanelWidth - MASCOT_WIDTH - GUTTER_WIDTH)
|
|
241
213
|
: innerWidth;
|
|
242
|
-
const bar = bootBar(
|
|
243
|
-
Math.min(frameIdx, compact ? COMPACT_NOTES.length - 1 : FRAMES.length - 1),
|
|
244
|
-
compact ? COMPACT_NOTES.length : FRAMES.length,
|
|
245
|
-
);
|
|
246
|
-
const barColor =
|
|
247
|
-
frameIdx < (compact ? COMPACT_NOTES.length : FRAMES.length) / 3
|
|
248
|
-
? t.error
|
|
249
|
-
: frameIdx < ((compact ? COMPACT_NOTES.length : FRAMES.length) * 2) / 3
|
|
250
|
-
? t.warning
|
|
251
|
-
: t.primaryLight;
|
|
252
|
-
const note = compact
|
|
253
|
-
? COMPACT_NOTES[Math.min(frameIdx, COMPACT_NOTES.length - 1)]!
|
|
254
|
-
: state.note;
|
|
255
|
-
const mascotStatus = `${INTRO_STATUS_PREFIX}${note}`;
|
|
256
214
|
|
|
257
215
|
if (tinyTerminal) {
|
|
258
216
|
return (
|
|
259
217
|
<Box width={available} flexDirection="column">
|
|
260
|
-
<Text color={
|
|
218
|
+
<Text color={resolvedBarColor}>{mascotStatus}</Text>
|
|
261
219
|
<Text bold color={t.primaryLight}>
|
|
262
220
|
Drexler™
|
|
263
221
|
</Text>
|
|
264
222
|
<Text color={t.primaryLight}>{greeting}</Text>
|
|
223
|
+
{dealDesk ? <Box marginTop={1}>{dealDesk}</Box> : null}
|
|
265
224
|
</Box>
|
|
266
225
|
);
|
|
267
226
|
}
|
|
@@ -269,12 +228,13 @@ export function MascotIntro({ greeting }: IntroProps) {
|
|
|
269
228
|
if (compact) {
|
|
270
229
|
return (
|
|
271
230
|
<Box marginLeft={1} width={available} flexDirection="column">
|
|
272
|
-
<Text color={
|
|
273
|
-
<Text color={
|
|
231
|
+
<Text color={resolvedBarColor}>{bar}</Text>
|
|
232
|
+
<Text color={resolvedBarColor}>{mascotStatus}</Text>
|
|
274
233
|
<Text bold color={t.primaryLight}>
|
|
275
234
|
Drexler International™
|
|
276
235
|
</Text>
|
|
277
236
|
<Text color={t.primaryLight}>{greeting}</Text>
|
|
237
|
+
{dealDesk ? <Box marginTop={1}>{dealDesk}</Box> : null}
|
|
278
238
|
</Box>
|
|
279
239
|
);
|
|
280
240
|
}
|
|
@@ -297,8 +257,8 @@ export function MascotIntro({ greeting }: IntroProps) {
|
|
|
297
257
|
marginRight={sideBySide ? GUTTER_WIDTH : 0}
|
|
298
258
|
>
|
|
299
259
|
<MascotFrame {...state} />
|
|
300
|
-
<Text color={
|
|
301
|
-
<Text color={
|
|
260
|
+
<Text color={resolvedBarColor}>{bar}</Text>
|
|
261
|
+
<Text color={resolvedBarColor}>{mascotStatus}</Text>
|
|
302
262
|
</Box>
|
|
303
263
|
<Box
|
|
304
264
|
flexDirection="column"
|
|
@@ -323,16 +283,96 @@ export function MascotIntro({ greeting }: IntroProps) {
|
|
|
323
283
|
</Text>
|
|
324
284
|
))}
|
|
325
285
|
</Box>
|
|
326
|
-
<Box
|
|
327
|
-
|
|
286
|
+
<Box
|
|
287
|
+
flexDirection="column"
|
|
288
|
+
width={tipsWidth}
|
|
289
|
+
paddingRight={1}
|
|
290
|
+
>
|
|
291
|
+
<TipsPanel width={Math.max(1, tipsWidth - 1)} />
|
|
292
|
+
{dealDesk ? <Box marginTop={1}>{dealDesk}</Box> : null}
|
|
328
293
|
</Box>
|
|
329
294
|
</>
|
|
330
295
|
) : (
|
|
331
|
-
<Box marginTop={1} width={tipsWidth}>
|
|
296
|
+
<Box marginTop={1} width={tipsWidth} flexDirection="column">
|
|
332
297
|
<TipsPanel width={tipsWidth} />
|
|
298
|
+
{dealDesk ? <Box marginTop={1}>{dealDesk}</Box> : null}
|
|
333
299
|
</Box>
|
|
334
300
|
)}
|
|
335
301
|
</Box>
|
|
336
302
|
</Box>
|
|
337
303
|
);
|
|
338
304
|
}
|
|
305
|
+
|
|
306
|
+
export function MascotIntro({ greeting }: IntroProps) {
|
|
307
|
+
const t = useTheme();
|
|
308
|
+
const { exit } = useApp();
|
|
309
|
+
const { stdout } = useStdout();
|
|
310
|
+
const [cols, setCols] = useState(stdout?.columns ?? 80);
|
|
311
|
+
const [frameIdx, setFrameIdx] = useState(0);
|
|
312
|
+
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
if (!stdout) return;
|
|
315
|
+
const handler = () => setCols(stdout.columns ?? 80);
|
|
316
|
+
stdout.on("resize", handler);
|
|
317
|
+
return () => {
|
|
318
|
+
stdout.off("resize", handler);
|
|
319
|
+
};
|
|
320
|
+
}, [stdout]);
|
|
321
|
+
|
|
322
|
+
useInput((_input, key) => {
|
|
323
|
+
if (key.escape || key.return || (key.ctrl && _input === "c")) exit();
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const mountedRef = useRef(true);
|
|
327
|
+
useEffect(() => {
|
|
328
|
+
return () => {
|
|
329
|
+
mountedRef.current = false;
|
|
330
|
+
};
|
|
331
|
+
}, []);
|
|
332
|
+
|
|
333
|
+
useEffect(() => {
|
|
334
|
+
const compact = cols < 72;
|
|
335
|
+
const total = compact ? COMPACT_NOTES.length : FRAMES.length;
|
|
336
|
+
if (frameIdx >= total - 1) {
|
|
337
|
+
const handle = setTimeout(() => {
|
|
338
|
+
if (mountedRef.current) exit();
|
|
339
|
+
}, SETTLE_HOLD_MS);
|
|
340
|
+
return () => clearTimeout(handle);
|
|
341
|
+
}
|
|
342
|
+
const delay = compact
|
|
343
|
+
? COMPACT_DELAY_MS
|
|
344
|
+
: (FRAMES[frameIdx] ?? FRAMES[FRAMES.length - 1]!).delayMs;
|
|
345
|
+
const handle = setTimeout(() => {
|
|
346
|
+
if (mountedRef.current) setFrameIdx((i) => i + 1);
|
|
347
|
+
}, delay);
|
|
348
|
+
return () => clearTimeout(handle);
|
|
349
|
+
}, [cols, frameIdx, exit]);
|
|
350
|
+
|
|
351
|
+
const state = FRAMES[frameIdx] ?? FRAMES[FRAMES.length - 1]!;
|
|
352
|
+
const compact = cols < 72;
|
|
353
|
+
const bar = bootBar(
|
|
354
|
+
Math.min(frameIdx, compact ? COMPACT_NOTES.length - 1 : FRAMES.length - 1),
|
|
355
|
+
compact ? COMPACT_NOTES.length : FRAMES.length,
|
|
356
|
+
);
|
|
357
|
+
const barColor =
|
|
358
|
+
frameIdx < (compact ? COMPACT_NOTES.length : FRAMES.length) / 3
|
|
359
|
+
? t.error
|
|
360
|
+
: frameIdx < ((compact ? COMPACT_NOTES.length : FRAMES.length) * 2) / 3
|
|
361
|
+
? t.warning
|
|
362
|
+
: t.primaryLight;
|
|
363
|
+
const note = compact
|
|
364
|
+
? COMPACT_NOTES[Math.min(frameIdx, COMPACT_NOTES.length - 1)]!
|
|
365
|
+
: state.note;
|
|
366
|
+
const mascotStatus = `${INTRO_STATUS_PREFIX}${note}`;
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<MascotDashboard
|
|
370
|
+
greeting={greeting}
|
|
371
|
+
width={cols}
|
|
372
|
+
state={state}
|
|
373
|
+
bar={bar}
|
|
374
|
+
barColor={barColor}
|
|
375
|
+
mascotStatus={mascotStatus}
|
|
376
|
+
/>
|
|
377
|
+
);
|
|
378
|
+
}
|