@underactive/pi-topping 0.6.4 → 0.7.0
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 +84 -81
- package/package.json +1 -1
- package/src/activity-meter.ts +3 -3
- package/src/format.ts +30 -10
- package/src/menu.ts +21 -34
- package/src/preview.ts +18 -17
- package/src/prompt-decorator.ts +27 -16
- package/src/session.ts +69 -47
- package/src/settings.ts +75 -55
- package/src/util.ts +9 -0
- package/src/word-packs.ts +5 -4
- package/src/words.ts +2 -10
package/src/prompt-decorator.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { MessageRenderer
|
|
1
|
+
import type { MessageRenderer } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
3
|
-
import {
|
|
3
|
+
import { getThinkingLevelColorizer, isThinkingLevel, type ThinkingLevel } from "./format.ts";
|
|
4
|
+
import { DEFAULT_SETTINGS, isBorderStyle, isPromptBorderColor, type BorderStyle, type DoneMarkerBorderColor, type DoneMarkerBorderStyle, type DoneMarkerStyle, type PromptBorderColor } from "./settings.ts";
|
|
4
5
|
import { stripControlChars } from "./util.ts";
|
|
5
6
|
|
|
6
7
|
export const PROMPT_BOX_TYPE = "pi-topping-prompt";
|
|
@@ -25,11 +26,15 @@ export interface PromptBoxDetails {
|
|
|
25
26
|
icon?: string;
|
|
26
27
|
provider?: string;
|
|
27
28
|
model?: string;
|
|
28
|
-
borderColor?:
|
|
29
|
+
borderColor?: PromptBorderColor;
|
|
29
30
|
borderStyle?: BorderStyle;
|
|
31
|
+
thinkingLevel?: ThinkingLevel;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
type PromptTheme = {
|
|
34
|
+
type PromptTheme = {
|
|
35
|
+
fg(color: string, text: string): string;
|
|
36
|
+
getThinkingBorderColor(level: ThinkingLevel): (text: string) => string;
|
|
37
|
+
};
|
|
33
38
|
|
|
34
39
|
const COMPLETION_MARKER_TRAIL_RUNS = [6, 4, 2, 1] as const;
|
|
35
40
|
|
|
@@ -51,15 +56,16 @@ export function buildCompletionMarkerLine(
|
|
|
51
56
|
width: number,
|
|
52
57
|
theme: PromptTheme,
|
|
53
58
|
borderStyle: DoneMarkerBorderStyle,
|
|
54
|
-
borderColor:
|
|
59
|
+
borderColor: DoneMarkerBorderColor,
|
|
55
60
|
markerStyle: DoneMarkerStyle = DEFAULT_SETTINGS.decorations.doneMarkerStyle,
|
|
61
|
+
thinkingLevel?: ThinkingLevel,
|
|
56
62
|
): string {
|
|
57
63
|
const safeWidth = Math.max(0, Math.floor(width));
|
|
58
64
|
if (safeWidth === 0) return "";
|
|
59
65
|
if (borderStyle === "none") return truncateToWidth(content, safeWidth);
|
|
60
66
|
|
|
61
67
|
const g = BORDER_GLYPHS[borderStyle];
|
|
62
|
-
const border = (
|
|
68
|
+
const border = getThinkingLevelColorizer(theme, borderColor, thinkingLevel);
|
|
63
69
|
const prefix = `${g.bl}${g.h.repeat(2)} `;
|
|
64
70
|
|
|
65
71
|
const suffix = markerStyle === "bookend"
|
|
@@ -81,8 +87,10 @@ export function buildPromptBoxLines(
|
|
|
81
87
|
|
|
82
88
|
const borderStyle = isBorderStyle(options.borderStyle) ? options.borderStyle : "double";
|
|
83
89
|
const g = BORDER_GLYPHS[borderStyle];
|
|
84
|
-
const borderColor =
|
|
85
|
-
const
|
|
90
|
+
const borderColor = isPromptBorderColor(options.borderColor) ? options.borderColor : DEFAULT_SETTINGS.decorations.borderColor;
|
|
91
|
+
const thinkingLevel = isThinkingLevel(options.thinkingLevel) ? options.thinkingLevel : undefined;
|
|
92
|
+
const border = getThinkingLevelColorizer(theme, borderColor, thinkingLevel);
|
|
93
|
+
const iconColor = (text: string) => theme.fg("text", text);
|
|
86
94
|
const label = (text: string) => theme.fg("customMessageLabel", text);
|
|
87
95
|
const muted = (text: string) => theme.fg("dim", text);
|
|
88
96
|
const bodyText = (text: string) => theme.fg("customMessageText", text);
|
|
@@ -113,7 +121,7 @@ export function buildPromptBoxLines(
|
|
|
113
121
|
const titleLength = icon ? visibleWidth(`${g.h}${g.h} ${icon} `) : 0;
|
|
114
122
|
const timeLength = time ? time.length + 3 : 0; // time + right-side " ═"
|
|
115
123
|
const topFill = Math.max(0, innerWidth - titleLength - timeLength);
|
|
116
|
-
const iconSeg = icon ? `${border(`${g.h}${g.h} `)}${
|
|
124
|
+
const iconSeg = icon ? `${border(`${g.h}${g.h} `)}${iconColor(icon)}${border(" ")}` : "";
|
|
117
125
|
const timeSeg = time ? `${border(" ")}${muted(time)}${border(` ${g.h}`)}` : "";
|
|
118
126
|
const topLine = border(g.tl) + iconSeg + border(g.h.repeat(topFill)) + timeSeg + border(g.tr);
|
|
119
127
|
const lines = [visibleWidth(topLine) > width ? truncateToWidth(topLine, width) : topLine];
|
|
@@ -138,16 +146,19 @@ export function buildPromptBoxLines(
|
|
|
138
146
|
export const promptBoxRenderer: MessageRenderer<PromptBoxDetails> = (message, _options, theme) => {
|
|
139
147
|
const content = typeof message.content === "string" ? message.content.replace(/(?![\n\t])[\p{Cc}\p{Cf}]/gu, "") : "";
|
|
140
148
|
const details = (message.details ?? {}) as PromptBoxDetails;
|
|
141
|
-
|
|
149
|
+
let cachedWidth: number | undefined;
|
|
150
|
+
let cachedLines: string[] | undefined;
|
|
142
151
|
return {
|
|
143
152
|
render(width: number): string[] {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
linesByWidth.set(width, lines);
|
|
153
|
+
if (cachedWidth !== width) {
|
|
154
|
+
cachedWidth = width;
|
|
155
|
+
cachedLines = buildPromptBoxLines(content, details.submittedAt, width, theme, details);
|
|
148
156
|
}
|
|
149
|
-
return
|
|
157
|
+
return cachedLines!;
|
|
158
|
+
},
|
|
159
|
+
invalidate() {
|
|
160
|
+
cachedWidth = undefined;
|
|
161
|
+
cachedLines = undefined;
|
|
150
162
|
},
|
|
151
|
-
invalidate() { linesByWidth.clear(); },
|
|
152
163
|
};
|
|
153
164
|
};
|
package/src/session.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
buildWorkingMessage,
|
|
19
19
|
DEFAULT_WORKING_WORD,
|
|
20
20
|
dimAttribute,
|
|
21
|
+
getThinkingLevelColorizer,
|
|
22
|
+
isThinkingLevel,
|
|
21
23
|
ELAPSED_INTERVAL_MS,
|
|
22
24
|
fadeThemeColorString,
|
|
23
25
|
formatElapsed,
|
|
@@ -34,15 +36,16 @@ import {
|
|
|
34
36
|
TOKEN_RATE_FADE_SHADE_COUNT,
|
|
35
37
|
TOKEN_RATE_PLACEHOLDER,
|
|
36
38
|
StreamingWordCounter,
|
|
39
|
+
type ThinkingLevel,
|
|
37
40
|
} from "./format.ts";
|
|
38
41
|
import { isSiblingSetupEnabled } from "./flags.ts";
|
|
39
42
|
import { showMenu } from "./menu.ts";
|
|
40
43
|
import { registerSetupCommand } from "./setup-command.ts";
|
|
41
|
-
import { applyMenuResult, buildMenuSections, loadSettings, saveSettings, type
|
|
44
|
+
import { applyMenuResult, buildMenuSections, loadSettings, saveSettings, type ThinkingLevelColor } from "./settings.ts";
|
|
42
45
|
import { notifyMissingToppingsOnce } from "./toppings.ts";
|
|
43
46
|
import { PreviewRenderer } from "./preview.ts";
|
|
44
47
|
import { buildCompletionMarkerContent, buildCompletionMarkerLine, PROMPT_BOX_TYPE, promptBoxRenderer, type PromptBoxDetails } from "./prompt-decorator.ts";
|
|
45
|
-
import { stripControlChars } from "./util.ts";
|
|
48
|
+
import { modelsResemble, stripControlChars } from "./util.ts";
|
|
46
49
|
import { loadBundledWordPacks, loadUserWordPacks, pickWorkingTextSelection, type WorkingTextSelection, type WordPack } from "./word-packs.ts";
|
|
47
50
|
|
|
48
51
|
type MessageStartEvent = Extract<ExtensionEvent, { type: "message_start" }>;
|
|
@@ -73,6 +76,7 @@ interface DoneEntryData {
|
|
|
73
76
|
elapsedMs: number;
|
|
74
77
|
tokens?: number;
|
|
75
78
|
midTurnInputs?: number;
|
|
79
|
+
thinkingLevel?: ThinkingLevel;
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
interface SessionState {
|
|
@@ -86,6 +90,8 @@ interface SessionState {
|
|
|
86
90
|
lastTokenRateSampledAt: number;
|
|
87
91
|
lastTokenRateTotal: number;
|
|
88
92
|
responseModel: string;
|
|
93
|
+
/** Last raw responseModel value seen, or the NOT_SENT sentinel before the first call. */
|
|
94
|
+
lastResponseModelRaw: unknown;
|
|
89
95
|
responseModelHoldTimer: ReturnType<typeof setTimeout> | null;
|
|
90
96
|
responseModelFadeTimer: ReturnType<typeof setInterval> | null;
|
|
91
97
|
responseModelFadeGeneration: number;
|
|
@@ -123,6 +129,7 @@ function makeFreshState(): SessionState {
|
|
|
123
129
|
lastTokenRateSampledAt: 0,
|
|
124
130
|
lastTokenRateTotal: 0,
|
|
125
131
|
responseModel: "",
|
|
132
|
+
lastResponseModelRaw: NOT_SENT,
|
|
126
133
|
responseModelHoldTimer: null,
|
|
127
134
|
responseModelFadeTimer: null,
|
|
128
135
|
responseModelFadeGeneration: 0,
|
|
@@ -148,6 +155,7 @@ export class SessionManager {
|
|
|
148
155
|
#settings = loadSettings();
|
|
149
156
|
#userPacks: WordPack[] = [];
|
|
150
157
|
#bundledPacks = loadBundledWordPacks();
|
|
158
|
+
#allPacks: WordPack[] = [...this.#bundledPacks];
|
|
151
159
|
#currentCtx: ExtensionContext | null = null;
|
|
152
160
|
readonly #pi: ExtensionAPI;
|
|
153
161
|
|
|
@@ -163,6 +171,7 @@ export class SessionManager {
|
|
|
163
171
|
this.#state = makeFreshState();
|
|
164
172
|
this.#settings = loadSettings();
|
|
165
173
|
this.#userPacks = loadUserWordPacks();
|
|
174
|
+
this.#allPacks = [...this.#bundledPacks, ...this.#userPacks];
|
|
166
175
|
this.#state.activityMeter.setDirection(this.#settings.decorations.meterDirection);
|
|
167
176
|
if (this.usable(ctx)) {
|
|
168
177
|
this.applyIndicator(ctx);
|
|
@@ -194,6 +203,7 @@ export class SessionManager {
|
|
|
194
203
|
model: ctx.model?.id,
|
|
195
204
|
borderColor: this.#settings.decorations.borderColor,
|
|
196
205
|
borderStyle: this.#settings.decorations.borderStyle,
|
|
206
|
+
thinkingLevel: ctx.thinkingLevel,
|
|
197
207
|
},
|
|
198
208
|
},
|
|
199
209
|
{ triggerTurn: true },
|
|
@@ -212,6 +222,7 @@ export class SessionManager {
|
|
|
212
222
|
if (!this.usable(ctx)) return;
|
|
213
223
|
if (!this.#state.startTime) this.resetTurn(Date.now());
|
|
214
224
|
this.#state.busy = true;
|
|
225
|
+
if (!this.#state.waiting) this.applyIndicator(ctx);
|
|
215
226
|
this.startTimer();
|
|
216
227
|
this.tick();
|
|
217
228
|
};
|
|
@@ -227,19 +238,17 @@ export class SessionManager {
|
|
|
227
238
|
|
|
228
239
|
#onMessageUpdate = async (event: MessageUpdateEvent, ctx: ExtensionContext): Promise<void> => {
|
|
229
240
|
this.#currentCtx = ctx;
|
|
241
|
+
if (!this.usable(ctx)) return;
|
|
230
242
|
const assistantEvent = event.assistantMessageEvent;
|
|
231
243
|
if (
|
|
232
|
-
this.usable(ctx) &&
|
|
233
244
|
assistantEvent &&
|
|
234
245
|
(assistantEvent.type === "text_delta" || assistantEvent.type === "thinking_delta")
|
|
235
246
|
) {
|
|
236
247
|
this.#state.liveTokens += this.#counter.count(assistantEvent.delta, assistantEvent.type);
|
|
237
248
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
this.updateResponseModel(rawResponseModel);
|
|
242
|
-
}
|
|
249
|
+
const rawResponseModel = (assistantEvent as { partial?: { responseModel?: unknown } } | undefined)?.partial?.responseModel
|
|
250
|
+
?? (event.message as { responseModel?: unknown } | undefined)?.responseModel;
|
|
251
|
+
this.updateResponseModel(rawResponseModel);
|
|
243
252
|
};
|
|
244
253
|
|
|
245
254
|
#onMessageEnd = async (event: MessageEndEvent, ctx: ExtensionContext): Promise<void> => {
|
|
@@ -314,6 +323,7 @@ export class SessionManager {
|
|
|
314
323
|
elapsedMs,
|
|
315
324
|
tokens: this.#state.confirmTokens,
|
|
316
325
|
midTurnInputs: this.#state.midTurnInputs,
|
|
326
|
+
thinkingLevel: ctx.thinkingLevel,
|
|
317
327
|
});
|
|
318
328
|
}
|
|
319
329
|
this.#currentCtx = null;
|
|
@@ -331,8 +341,8 @@ export class SessionManager {
|
|
|
331
341
|
const elapsedMs = typeof entry.data.elapsedMs === "number" && Number.isFinite(entry.data.elapsedMs) ? entry.data.elapsedMs : 0;
|
|
332
342
|
const features = this.#settings.features;
|
|
333
343
|
const details: string[] = [];
|
|
334
|
-
if (features.doneMarkerTokens && typeof entry.data.tokens === "number") details.push(`↓ ${formatTokens(entry.data.tokens)} tokens`);
|
|
335
|
-
if (features.doneMarkerInputs && typeof entry.data.midTurnInputs === "number" && entry.data.midTurnInputs) {
|
|
344
|
+
if (features.doneMarkerTokens && typeof entry.data.tokens === "number" && Number.isFinite(entry.data.tokens) && entry.data.tokens >= 0) details.push(`↓ ${formatTokens(entry.data.tokens)} tokens`);
|
|
345
|
+
if (features.doneMarkerInputs && typeof entry.data.midTurnInputs === "number" && Number.isInteger(entry.data.midTurnInputs) && entry.data.midTurnInputs > 0) {
|
|
336
346
|
details.push(`${entry.data.midTurnInputs} mid-turn input${entry.data.midTurnInputs === 1 ? "" : "s"}`);
|
|
337
347
|
}
|
|
338
348
|
const icon = features.doneMarkerIcon ? theme.fg("text", this.#settings.decorations.useNerdFont ? "" : "π") : "";
|
|
@@ -340,6 +350,7 @@ export class SessionManager {
|
|
|
340
350
|
const borderStyle = this.#settings.decorations.doneMarkerBorderStyle;
|
|
341
351
|
const borderColor = this.#settings.decorations.doneMarkerBorderColor;
|
|
342
352
|
const markerStyle = this.#settings.decorations.doneMarkerStyle;
|
|
353
|
+
const thinkingLevel = isThinkingLevel(entry.data.thinkingLevel) ? entry.data.thinkingLevel : undefined;
|
|
343
354
|
if (borderStyle === "none") return new Text(markerContent);
|
|
344
355
|
|
|
345
356
|
let cachedWidth: number | undefined;
|
|
@@ -348,7 +359,7 @@ export class SessionManager {
|
|
|
348
359
|
render(width: number): string[] {
|
|
349
360
|
if (cachedWidth !== width) {
|
|
350
361
|
cachedWidth = width;
|
|
351
|
-
cachedLines = ["", buildCompletionMarkerLine(markerContent, width, theme, borderStyle, borderColor, markerStyle), ""];
|
|
362
|
+
cachedLines = ["", buildCompletionMarkerLine(markerContent, width, theme, borderStyle, borderColor, markerStyle, thinkingLevel), ""];
|
|
352
363
|
}
|
|
353
364
|
return cachedLines!;
|
|
354
365
|
},
|
|
@@ -407,11 +418,6 @@ export class SessionManager {
|
|
|
407
418
|
return this.#settings.decorations.animatedSpinner && this.#settings.loaderOrder[0] !== "spinner";
|
|
408
419
|
}
|
|
409
420
|
|
|
410
|
-
private spinnerColor(): SettingColor {
|
|
411
|
-
const decorations = this.#settings.decorations;
|
|
412
|
-
return decorations.spinnerColorEnabled ? decorations.spinnerColor : "accent";
|
|
413
|
-
}
|
|
414
|
-
|
|
415
421
|
private indicatorFingerprint(): string {
|
|
416
422
|
const decorations = this.#settings.decorations;
|
|
417
423
|
return `${decorations.animatedSpinner}:${decorations.spinnerColor}:${decorations.spinnerColorEnabled}:${this.#settings.loaderOrder[0]}`;
|
|
@@ -430,12 +436,10 @@ export class SessionManager {
|
|
|
430
436
|
return;
|
|
431
437
|
}
|
|
432
438
|
|
|
433
|
-
const color = this.spinnerColor
|
|
434
|
-
|
|
435
|
-
ctx.ui.
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
ctx.ui.setWorkingIndicator({ frames: SPINNER_FRAMES.map((frame) => ctx.ui.theme.fg(color, frame)) });
|
|
439
|
+
const color = this.#settings.decorations.spinnerColor;
|
|
440
|
+
ctx.ui.setWorkingIndicator({
|
|
441
|
+
frames: SPINNER_FRAMES.map((frame) => getThinkingLevelColorizer(ctx.ui.theme, color, ctx.thinkingLevel)(frame)),
|
|
442
|
+
});
|
|
439
443
|
}
|
|
440
444
|
|
|
441
445
|
private stopTimer(): void {
|
|
@@ -446,7 +450,17 @@ export class SessionManager {
|
|
|
446
450
|
}
|
|
447
451
|
|
|
448
452
|
private updateResponseModel(raw: unknown): void {
|
|
453
|
+
if (raw === this.#state.lastResponseModelRaw) return;
|
|
454
|
+
this.#state.lastResponseModelRaw = raw;
|
|
449
455
|
const responseModel = typeof raw === "string" ? stripControlChars(raw).trim() : "";
|
|
456
|
+
const selectedModel = this.#currentCtx?.model?.id;
|
|
457
|
+
if (responseModel && modelsResemble(selectedModel, responseModel)) {
|
|
458
|
+
if (this.#state.responseModel) {
|
|
459
|
+
this.#state.responseModel = "";
|
|
460
|
+
this.tick();
|
|
461
|
+
}
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
450
464
|
if (responseModel && responseModel !== this.#state.responseModel) {
|
|
451
465
|
this.#state.responseModel = responseModel;
|
|
452
466
|
this.tick();
|
|
@@ -470,13 +484,19 @@ export class SessionManager {
|
|
|
470
484
|
}
|
|
471
485
|
}
|
|
472
486
|
|
|
473
|
-
private startResponseModelFade(ctx: ExtensionContext, model: string, color:
|
|
487
|
+
private startResponseModelFade(ctx: ExtensionContext, model: string, color: ThinkingLevelColor, dimmed: boolean): void {
|
|
474
488
|
this.cancelResponseModelFade(ctx);
|
|
475
489
|
const generation = this.#state.responseModelFadeGeneration;
|
|
490
|
+
const colorizer = getThinkingLevelColorizer(ctx.ui.theme, color, ctx.thinkingLevel);
|
|
476
491
|
const render = (shade?: number): void => {
|
|
477
492
|
const colored = shade === undefined
|
|
478
|
-
?
|
|
479
|
-
: fadeThemeColorString(
|
|
493
|
+
? colorizer(model)
|
|
494
|
+
: fadeThemeColorString(
|
|
495
|
+
model,
|
|
496
|
+
shade,
|
|
497
|
+
ctx.ui.theme,
|
|
498
|
+
colorizer,
|
|
499
|
+
);
|
|
480
500
|
const responseModel = dimmed ? dimAttribute(colored) : colored;
|
|
481
501
|
ctx.ui.setStatus(RESPONSE_MODEL_STATUS_KEY, responseModel);
|
|
482
502
|
};
|
|
@@ -511,7 +531,7 @@ export class SessionManager {
|
|
|
511
531
|
}
|
|
512
532
|
|
|
513
533
|
private pickWorkingWord(): WorkingTextSelection {
|
|
514
|
-
return pickWorkingTextSelection(this.#settings.wordPacks,
|
|
534
|
+
return pickWorkingTextSelection(this.#settings.wordPacks, this.#allPacks);
|
|
515
535
|
}
|
|
516
536
|
|
|
517
537
|
private resetTurn(now: number): void {
|
|
@@ -523,6 +543,7 @@ export class SessionManager {
|
|
|
523
543
|
state.confirmTokens = 0;
|
|
524
544
|
state.liveTokens = 0;
|
|
525
545
|
state.responseModel = "";
|
|
546
|
+
state.lastResponseModelRaw = NOT_SENT;
|
|
526
547
|
state.activityMeter.reset();
|
|
527
548
|
resetTokenRateState(state);
|
|
528
549
|
state.midTurnInputs = 0;
|
|
@@ -557,10 +578,10 @@ export class SessionManager {
|
|
|
557
578
|
state.lastTokenRateSampledAt = now;
|
|
558
579
|
}
|
|
559
580
|
const spinner = this.spinnerInMessage()
|
|
560
|
-
? ctx.ui.theme.
|
|
581
|
+
? getThinkingLevelColorizer(ctx.ui.theme, decorations.spinnerColor, ctx.thinkingLevel)(SPINNER_FRAMES[Math.floor(now / SPINNER_FRAME_MS) % SPINNER_FRAMES.length]!)
|
|
561
582
|
: "";
|
|
562
583
|
const responseModelColored = features.responseModel && state.responseModel
|
|
563
|
-
? ctx.ui.theme
|
|
584
|
+
? getThinkingLevelColorizer(ctx.ui.theme, decorations.responseModelColor, ctx.thinkingLevel)(state.responseModel)
|
|
564
585
|
: "";
|
|
565
586
|
const responseModel = responseModelColored && decorations.responseModelDimmed ? dimAttribute(responseModelColored) : responseModelColored;
|
|
566
587
|
if (isFullyDefaultAppearance(features, decorations)) {
|
|
@@ -578,17 +599,13 @@ export class SessionManager {
|
|
|
578
599
|
const styled = decorations.shimmer
|
|
579
600
|
? shimmerString(word, now - state.shimmerOrigin, ctx.ui.theme, decorations.shimmerDirection, decorations.shimmerSpeed, decorations.shimmerInverted)
|
|
580
601
|
: ctx.ui.theme.fg("text", word);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
decorations.meterDimmed,
|
|
589
|
-
),
|
|
590
|
-
)
|
|
591
|
-
: "";
|
|
602
|
+
let meter = "";
|
|
603
|
+
if (decorations.tokenActivityMonitor) {
|
|
604
|
+
const meterColorizer = getThinkingLevelColorizer(ctx.ui.theme, decorations.meterColor, ctx.thinkingLevel);
|
|
605
|
+
meter = state.activityMeter.render((level, char) =>
|
|
606
|
+
ActivityMeter.colorizeCell(level, char, ctx.ui.theme, meterColorizer, decorations.meterDimmed),
|
|
607
|
+
);
|
|
608
|
+
}
|
|
592
609
|
let tokenRateText = "";
|
|
593
610
|
if (features.tokenRate) {
|
|
594
611
|
const latestTokenRate = formatTokenRate(state.rateTracker.tokenRate);
|
|
@@ -600,18 +617,22 @@ export class SessionManager {
|
|
|
600
617
|
}
|
|
601
618
|
tokenRateText = state.tokenRateText;
|
|
602
619
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
620
|
+
let tokenRateSegment = "";
|
|
621
|
+
if (features.tokenRate) {
|
|
622
|
+
if (!tokenRateText) {
|
|
623
|
+
tokenRateSegment = ctx.ui.theme.fg("dim", TOKEN_RATE_PLACEHOLDER);
|
|
624
|
+
} else {
|
|
625
|
+
const tokenRateColorizer = getThinkingLevelColorizer(ctx.ui.theme, decorations.tokenRateColor, ctx.thinkingLevel);
|
|
626
|
+
tokenRateSegment = now < state.tokenRateFadeStartsAt
|
|
627
|
+
? tokenRateColorizer(tokenRateText)
|
|
609
628
|
: fadeThemeColorString(
|
|
610
629
|
tokenRateText,
|
|
611
630
|
Math.floor((now - state.tokenRateFadeStartsAt) / (TOKEN_RATE_FADE_MS / TOKEN_RATE_FADE_SHADE_COUNT)),
|
|
612
631
|
ctx.ui.theme,
|
|
613
|
-
|
|
632
|
+
tokenRateColorizer,
|
|
614
633
|
);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
615
636
|
const tokenRateStyled = tokenRateSegment && decorations.tokenRateDimmed ? dimAttribute(tokenRateSegment) : tokenRateSegment;
|
|
616
637
|
const msg = buildWorkingMessage(
|
|
617
638
|
ctx.ui.theme,
|
|
@@ -639,8 +660,9 @@ export class SessionManager {
|
|
|
639
660
|
}
|
|
640
661
|
|
|
641
662
|
this.#userPacks = loadUserWordPacks();
|
|
663
|
+
this.#allPacks = [...this.#bundledPacks, ...this.#userPacks];
|
|
642
664
|
const before = this.indicatorFingerprint();
|
|
643
|
-
const preview = new PreviewRenderer(ctx,
|
|
665
|
+
const preview = new PreviewRenderer(ctx, this.#allPacks);
|
|
644
666
|
const result = await showMenu<Record<string, boolean | string>>(ctx, {
|
|
645
667
|
title: "Pi Topping: Settings",
|
|
646
668
|
maxHeight: "75%",
|