@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/settings.ts
CHANGED
|
@@ -7,7 +7,20 @@ import { isWordPackEnabled, isWordPackId, type WordPack } from "./word-packs.ts"
|
|
|
7
7
|
import { isPlainObject } from "./util.ts";
|
|
8
8
|
|
|
9
9
|
export const SETTING_COLOR_VALUES = ["accent", "border", "borderAccent", "success", "error", "warning"] as const;
|
|
10
|
-
export
|
|
10
|
+
export const LOADER_COLOR_VALUES = [...SETTING_COLOR_VALUES, "text", "muted"] as const;
|
|
11
|
+
export const THINKING_LEVEL_COLOR_VALUES = ["thinking-level", ...LOADER_COLOR_VALUES] as const;
|
|
12
|
+
export type ThinkingLevelColor = (typeof THINKING_LEVEL_COLOR_VALUES)[number];
|
|
13
|
+
export const THINKING_LEVEL_SETTING_COLOR_VALUES = ["thinking-level", ...SETTING_COLOR_VALUES] as const;
|
|
14
|
+
export type ThinkingLevelSettingColor = (typeof THINKING_LEVEL_SETTING_COLOR_VALUES)[number];
|
|
15
|
+
export const SPINNER_COLOR_VALUES = THINKING_LEVEL_SETTING_COLOR_VALUES;
|
|
16
|
+
export type SpinnerColor = ThinkingLevelSettingColor;
|
|
17
|
+
export const PROMPT_BORDER_COLOR_VALUES = THINKING_LEVEL_SETTING_COLOR_VALUES;
|
|
18
|
+
export type PromptBorderColor = ThinkingLevelSettingColor;
|
|
19
|
+
export const DONE_MARKER_BORDER_COLOR_VALUES = THINKING_LEVEL_SETTING_COLOR_VALUES;
|
|
20
|
+
export type DoneMarkerBorderColor = ThinkingLevelSettingColor;
|
|
21
|
+
const THINKING_LEVEL_COLOR_MIGRATION_VERSION = 2;
|
|
22
|
+
const DONE_MARKER_BORDER_COLOR_MIGRATION_VERSION = 3;
|
|
23
|
+
export const SETTINGS_SCHEMA_VERSION = 3;
|
|
11
24
|
|
|
12
25
|
export const BORDER_STYLE_VALUES = ["double", "single", "rounded", "heavy"] as const;
|
|
13
26
|
export type BorderStyle = (typeof BORDER_STYLE_VALUES)[number];
|
|
@@ -28,10 +41,18 @@ export function isDoneMarkerStyle(value: unknown): value is DoneMarkerStyle {
|
|
|
28
41
|
return typeof value === "string" && DONE_MARKER_STYLE_VALUES.some(style => style === value);
|
|
29
42
|
}
|
|
30
43
|
|
|
31
|
-
export function
|
|
32
|
-
return typeof value === "string" &&
|
|
44
|
+
export function isThinkingLevelColor(value: unknown): value is ThinkingLevelColor {
|
|
45
|
+
return typeof value === "string" && THINKING_LEVEL_COLOR_VALUES.some(color => color === value);
|
|
33
46
|
}
|
|
34
47
|
|
|
48
|
+
export function isThinkingLevelSettingColor(value: unknown): value is ThinkingLevelSettingColor {
|
|
49
|
+
return typeof value === "string" && THINKING_LEVEL_SETTING_COLOR_VALUES.some(color => color === value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const isSpinnerColor = isThinkingLevelSettingColor;
|
|
53
|
+
export const isPromptBorderColor = isThinkingLevelSettingColor;
|
|
54
|
+
export const isDoneMarkerBorderColor = isThinkingLevelSettingColor;
|
|
55
|
+
|
|
35
56
|
export interface DecoratorSettings {
|
|
36
57
|
decorations: {
|
|
37
58
|
animatedSpinner: boolean;
|
|
@@ -45,21 +66,21 @@ export interface DecoratorSettings {
|
|
|
45
66
|
meterDirection: "ltr" | "rtl";
|
|
46
67
|
meterDirectionEnabled: boolean;
|
|
47
68
|
decorateUserPrompt: boolean;
|
|
48
|
-
borderColor:
|
|
69
|
+
borderColor: PromptBorderColor;
|
|
49
70
|
borderColorEnabled: boolean;
|
|
50
71
|
borderStyle: BorderStyle;
|
|
51
72
|
borderStyleEnabled: boolean;
|
|
52
73
|
doneMarkerBorderStyle: DoneMarkerBorderStyle;
|
|
53
|
-
doneMarkerBorderColor:
|
|
74
|
+
doneMarkerBorderColor: DoneMarkerBorderColor;
|
|
54
75
|
doneMarkerStyle: DoneMarkerStyle;
|
|
55
|
-
spinnerColor:
|
|
76
|
+
spinnerColor: SpinnerColor;
|
|
56
77
|
spinnerColorEnabled: boolean;
|
|
57
|
-
meterColor:
|
|
78
|
+
meterColor: ThinkingLevelColor;
|
|
58
79
|
meterColorEnabled: boolean;
|
|
59
80
|
meterDimmed: boolean;
|
|
60
|
-
tokenRateColor:
|
|
81
|
+
tokenRateColor: ThinkingLevelColor;
|
|
61
82
|
tokenRateDimmed: boolean;
|
|
62
|
-
responseModelColor:
|
|
83
|
+
responseModelColor: ThinkingLevelColor;
|
|
63
84
|
responseModelDimmed: boolean;
|
|
64
85
|
promptIcon: boolean;
|
|
65
86
|
promptTimestamp: boolean;
|
|
@@ -84,7 +105,7 @@ export interface DecoratorSettings {
|
|
|
84
105
|
}
|
|
85
106
|
|
|
86
107
|
export const DEFAULT_SETTINGS: DecoratorSettings = {
|
|
87
|
-
decorations: { animatedSpinner: true, shimmer: true, shimmerInverted: false, shimmerDirection: "ltr", shimmerDirectionEnabled: true, shimmerSpeed: "normal", shimmerSpeedEnabled: true, tokenActivityMonitor: true, meterDirection: "rtl", meterDirectionEnabled: true, decorateUserPrompt: true, borderColor: "
|
|
108
|
+
decorations: { animatedSpinner: true, shimmer: true, shimmerInverted: false, shimmerDirection: "ltr", shimmerDirectionEnabled: true, shimmerSpeed: "normal", shimmerSpeedEnabled: true, tokenActivityMonitor: true, meterDirection: "rtl", meterDirectionEnabled: true, decorateUserPrompt: true, borderColor: "thinking-level", borderColorEnabled: true, borderStyle: "double", borderStyleEnabled: true, doneMarkerBorderStyle: "none", doneMarkerBorderColor: "thinking-level", doneMarkerStyle: "elite", spinnerColor: "thinking-level", spinnerColorEnabled: true, meterColor: "accent", meterColorEnabled: true, meterDimmed: false, tokenRateColor: "warning", tokenRateDimmed: false, responseModelColor: "accent", responseModelDimmed: false, promptIcon: true, promptTimestamp: true, promptProvider: true, promptModel: true, useNerdFont: true },
|
|
88
109
|
features: { substituteDefaultMessage: true, elapsedTime: true, outputTokens: true, tokenRate: true, responseModel: true, doneMarker: true, doneMarkerIcon: true, randomizeDoneMarker: true, doneMarkerTokens: true, doneMarkerInputs: true },
|
|
89
110
|
loaderOrder: [...DEFAULT_LOADER_ORDER],
|
|
90
111
|
wordPacks: {},
|
|
@@ -128,7 +149,12 @@ function mergeGroup<T extends Record<string, boolean | string>>(defaults: T, par
|
|
|
128
149
|
if (typeof merged[key] === "boolean" && typeof value === "boolean") valid = value;
|
|
129
150
|
else if ((key === "meterDirection" || key === "shimmerDirection") && (value === "ltr" || value === "rtl")) valid = value;
|
|
130
151
|
else if (key === "shimmerSpeed" && (value === "slow" || value === "normal" || value === "fast")) valid = value;
|
|
131
|
-
else if (key
|
|
152
|
+
else if (key === "spinnerColor" && value === "default") valid = "thinking-level";
|
|
153
|
+
else if (key === "spinnerColor" && isSpinnerColor(value)) valid = value;
|
|
154
|
+
else if (key === "borderColor" && isPromptBorderColor(value)) valid = value;
|
|
155
|
+
else if (key === "doneMarkerBorderColor" && value === "default") valid = "thinking-level";
|
|
156
|
+
else if (key === "doneMarkerBorderColor" && isDoneMarkerBorderColor(value)) valid = value;
|
|
157
|
+
else if ((key === "meterColor" || key === "tokenRateColor" || key === "responseModelColor") && isThinkingLevelColor(value)) valid = value;
|
|
132
158
|
else if (key === "borderStyle" && isBorderStyle(value)) valid = value;
|
|
133
159
|
else if (key === "doneMarkerBorderStyle" && isDoneMarkerBorderStyle(value)) valid = value;
|
|
134
160
|
else if (key === "doneMarkerStyle" && isDoneMarkerStyle(value)) valid = value;
|
|
@@ -137,36 +163,14 @@ function mergeGroup<T extends Record<string, boolean | string>>(defaults: T, par
|
|
|
137
163
|
return merged;
|
|
138
164
|
}
|
|
139
165
|
|
|
140
|
-
/** Map a persisted setting value to its menu cycle label (directions and speeds only; colors pass through). */
|
|
141
|
-
function toCycleValue(stored: unknown): string {
|
|
142
|
-
if (stored === "ltr") return "Left to Right";
|
|
143
|
-
if (stored === "rtl") return "Right to Left";
|
|
144
|
-
if (stored === "slow") return "Slow";
|
|
145
|
-
if (stored === "normal") return "Normal";
|
|
146
|
-
if (stored === "fast") return "Fast";
|
|
147
|
-
return String(stored);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/** Map a menu cycle label back to its persisted value. */
|
|
151
|
-
function fromCycleValue(value: string): string {
|
|
152
|
-
if (value === "Left to Right") return "ltr";
|
|
153
|
-
if (value === "Right to Left") return "rtl";
|
|
154
|
-
if (value === "Slow") return "slow";
|
|
155
|
-
if (value === "Normal") return "normal";
|
|
156
|
-
if (value === "Fast") return "fast";
|
|
157
|
-
return value;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
166
|
/** Convert a persisted or menu direction to a safe preview direction. */
|
|
161
167
|
export function fromCycleDirection(value: unknown): "ltr" | "rtl" {
|
|
162
|
-
|
|
163
|
-
return stored === "rtl" ? "rtl" : "ltr";
|
|
168
|
+
return value === "rtl" ? "rtl" : "ltr";
|
|
164
169
|
}
|
|
165
170
|
|
|
166
171
|
/** Convert a persisted or menu speed to a safe preview speed. */
|
|
167
172
|
export function fromCycleSpeed(value: unknown): "slow" | "normal" | "fast" {
|
|
168
|
-
|
|
169
|
-
return stored === "slow" || stored === "fast" ? stored : "normal";
|
|
173
|
+
return value === "slow" || value === "fast" ? value : "normal";
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
type MenuSectionName = "User Prompt" | "“Working” Loader" | "Completion Marker" | "Options";
|
|
@@ -174,42 +178,45 @@ type DecorationSettings = DecoratorSettings["decorations"];
|
|
|
174
178
|
type FeatureSettings = DecoratorSettings["features"];
|
|
175
179
|
type DecorationBooleanKey = { [Key in keyof DecorationSettings]: DecorationSettings[Key] extends boolean ? Key : never }[keyof DecorationSettings];
|
|
176
180
|
type MenuEntryBase = { id: string; label: string; section: MenuSectionName };
|
|
177
|
-
type DecorationMenuEntry = MenuEntryBase & { group: "decorations"; key: keyof DecorationSettings; cycleValues?: readonly string[]; cycleEnabledBy?: DecorationBooleanKey; cycleDisabledValue?: string };
|
|
178
|
-
type FeatureMenuEntry = MenuEntryBase & { group: "features"; key: keyof FeatureSettings; cycleValues?: never; cycleEnabledBy?: never; cycleDisabledValue?: never };
|
|
181
|
+
type DecorationMenuEntry = MenuEntryBase & { group: "decorations"; key: keyof DecorationSettings; cycleValues?: readonly string[]; cycleValueLabels?: Readonly<Record<string, string>>; cycleEnabledBy?: DecorationBooleanKey; cycleDisabledValue?: string };
|
|
182
|
+
type FeatureMenuEntry = MenuEntryBase & { group: "features"; key: keyof FeatureSettings; cycleValues?: never; cycleValueLabels?: never; cycleEnabledBy?: never; cycleDisabledValue?: never };
|
|
179
183
|
type MenuEntry = DecorationMenuEntry | FeatureMenuEntry;
|
|
184
|
+
const THINKING_LEVEL_CYCLE_LABELS = { "thinking-level": "thinkingLevel" } as const;
|
|
185
|
+
const DIRECTION_CYCLE_LABELS = { ltr: "Left to Right", rtl: "Right to Left" } as const;
|
|
186
|
+
const SPEED_CYCLE_LABELS = { slow: "Slow", normal: "Normal", fast: "Fast" } as const;
|
|
180
187
|
export const MENU_ENTRIES: readonly MenuEntry[] = [
|
|
181
188
|
{ id: "decorateUserPrompt", label: "High-vis prompt", section: "User Prompt", group: "decorations", key: "decorateUserPrompt" },
|
|
182
189
|
{ id: "borderStyle", label: "Border style", section: "User Prompt", group: "decorations", key: "borderStyle", cycleValues: BORDER_STYLE_VALUES, cycleEnabledBy: "borderStyleEnabled", cycleDisabledValue: "double" },
|
|
183
|
-
{ id: "borderColor", label: "Border color", section: "User Prompt", group: "decorations", key: "borderColor", cycleValues:
|
|
190
|
+
{ id: "borderColor", label: "Border color", section: "User Prompt", group: "decorations", key: "borderColor", cycleValues: PROMPT_BORDER_COLOR_VALUES, cycleValueLabels: THINKING_LEVEL_CYCLE_LABELS, cycleEnabledBy: "borderColorEnabled", cycleDisabledValue: "thinking-level" },
|
|
184
191
|
{ id: "promptIcon", label: "Pi icon", section: "User Prompt", group: "decorations", key: "promptIcon" },
|
|
185
192
|
{ id: "promptTimestamp", label: "Timestamp", section: "User Prompt", group: "decorations", key: "promptTimestamp" },
|
|
186
193
|
{ id: "promptProvider", label: "Provider", section: "User Prompt", group: "decorations", key: "promptProvider" },
|
|
187
194
|
{ id: "promptModel", label: "Model", section: "User Prompt", group: "decorations", key: "promptModel" },
|
|
188
195
|
{ id: "animatedSpinner", label: "Animated spinner", section: "“Working” Loader", group: "decorations", key: "animatedSpinner" },
|
|
189
|
-
{ id: "spinnerColor", label: "Animated spinner color", section: "“Working” Loader", group: "decorations", key: "spinnerColor", cycleValues:
|
|
196
|
+
{ id: "spinnerColor", label: "Animated spinner color", section: "“Working” Loader", group: "decorations", key: "spinnerColor", cycleValues: SPINNER_COLOR_VALUES, cycleValueLabels: THINKING_LEVEL_CYCLE_LABELS, cycleEnabledBy: "spinnerColorEnabled", cycleDisabledValue: "thinking-level" },
|
|
190
197
|
{ id: "substituteDefaultMessage", label: "Randomize “Working” text", section: "“Working” Loader", group: "features", key: "substituteDefaultMessage" },
|
|
191
198
|
{ id: "shimmer", label: "Text shimmer", section: "“Working” Loader", group: "decorations", key: "shimmer" },
|
|
192
199
|
{ id: "shimmerInverted", label: "Invert shimmer", section: "“Working” Loader", group: "decorations", key: "shimmerInverted" },
|
|
193
|
-
{ id: "shimmerDirection", label: "Text shimmer direction", section: "“Working” Loader", group: "decorations", key: "shimmerDirection", cycleValues: ["
|
|
194
|
-
{ id: "shimmerSpeed", label: "Text shimmer speed", section: "“Working” Loader", group: "decorations", key: "shimmerSpeed", cycleValues: ["
|
|
200
|
+
{ id: "shimmerDirection", label: "Text shimmer direction", section: "“Working” Loader", group: "decorations", key: "shimmerDirection", cycleValues: ["ltr", "rtl"], cycleValueLabels: DIRECTION_CYCLE_LABELS, cycleEnabledBy: "shimmerDirectionEnabled", cycleDisabledValue: "ltr" },
|
|
201
|
+
{ id: "shimmerSpeed", label: "Text shimmer speed", section: "“Working” Loader", group: "decorations", key: "shimmerSpeed", cycleValues: ["slow", "normal", "fast"], cycleValueLabels: SPEED_CYCLE_LABELS, cycleEnabledBy: "shimmerSpeedEnabled", cycleDisabledValue: "normal" },
|
|
195
202
|
{ id: "tokenActivityMonitor", label: "Token activity monitor", section: "“Working” Loader", group: "decorations", key: "tokenActivityMonitor" },
|
|
196
|
-
{ id: "meterColor", label: "Token activity monitor color", section: "“Working” Loader", group: "decorations", key: "meterColor", cycleValues:
|
|
197
|
-
{ id: "meterDirection", label: "Token activity monitor direction", section: "“Working” Loader", group: "decorations", key: "meterDirection", cycleValues: ["
|
|
203
|
+
{ id: "meterColor", label: "Token activity monitor color", section: "“Working” Loader", group: "decorations", key: "meterColor", cycleValues: THINKING_LEVEL_COLOR_VALUES, cycleValueLabels: THINKING_LEVEL_CYCLE_LABELS, cycleEnabledBy: "meterColorEnabled", cycleDisabledValue: "accent" },
|
|
204
|
+
{ id: "meterDirection", label: "Token activity monitor direction", section: "“Working” Loader", group: "decorations", key: "meterDirection", cycleValues: ["ltr", "rtl"], cycleValueLabels: DIRECTION_CYCLE_LABELS, cycleEnabledBy: "meterDirectionEnabled", cycleDisabledValue: "rtl" },
|
|
198
205
|
{ id: "meterDimmed", label: "Token activity monitor dimmed", section: "“Working” Loader", group: "decorations", key: "meterDimmed" },
|
|
199
206
|
{ id: "elapsedTime", label: "Elapsed time since prompt", section: "“Working” Loader", group: "features", key: "elapsedTime" },
|
|
200
207
|
{ id: "outputTokens", label: "Show output tokens", section: "“Working” Loader", group: "features", key: "outputTokens" },
|
|
201
208
|
// id differs from key: the Elements Order row already owns "tokenRate" in the menu's shared value namespace.
|
|
202
209
|
{ id: "showTokenRate", label: "Token rate", section: "“Working” Loader", group: "features", key: "tokenRate" },
|
|
203
|
-
{ id: "tokenRateColor", label: "Token rate color", section: "“Working” Loader", group: "decorations", key: "tokenRateColor", cycleValues:
|
|
210
|
+
{ id: "tokenRateColor", label: "Token rate color", section: "“Working” Loader", group: "decorations", key: "tokenRateColor", cycleValues: THINKING_LEVEL_COLOR_VALUES, cycleValueLabels: THINKING_LEVEL_CYCLE_LABELS },
|
|
204
211
|
{ id: "tokenRateDimmed", label: "Token rate dimmed", section: "“Working” Loader", group: "decorations", key: "tokenRateDimmed" },
|
|
205
212
|
// id differs from key: the Elements Order row already owns "responseModel" in the menu's shared value namespace.
|
|
206
213
|
{ id: "showResponseModel", label: "Response model", section: "“Working” Loader", group: "features", key: "responseModel" },
|
|
207
|
-
{ id: "responseModelColor", label: "Response model color", section: "“Working” Loader", group: "decorations", key: "responseModelColor", cycleValues:
|
|
214
|
+
{ id: "responseModelColor", label: "Response model color", section: "“Working” Loader", group: "decorations", key: "responseModelColor", cycleValues: THINKING_LEVEL_COLOR_VALUES, cycleValueLabels: THINKING_LEVEL_CYCLE_LABELS },
|
|
208
215
|
{ id: "responseModelDimmed", label: "Response model dimmed", section: "“Working” Loader", group: "decorations", key: "responseModelDimmed" },
|
|
209
216
|
{ id: "doneMarker", label: "Show completion marker", section: "Completion Marker", group: "features", key: "doneMarker" },
|
|
210
217
|
{ id: "doneMarkerStyle", label: "Marker style", section: "Completion Marker", group: "decorations", key: "doneMarkerStyle", cycleValues: DONE_MARKER_STYLE_VALUES },
|
|
211
218
|
{ id: "doneMarkerBorderStyle", label: "Border style", section: "Completion Marker", group: "decorations", key: "doneMarkerBorderStyle", cycleValues: DONE_MARKER_BORDER_STYLE_VALUES },
|
|
212
|
-
{ id: "doneMarkerBorderColor", label: "Border color", section: "Completion Marker", group: "decorations", key: "doneMarkerBorderColor", cycleValues:
|
|
219
|
+
{ id: "doneMarkerBorderColor", label: "Border color", section: "Completion Marker", group: "decorations", key: "doneMarkerBorderColor", cycleValues: DONE_MARKER_BORDER_COLOR_VALUES, cycleValueLabels: THINKING_LEVEL_CYCLE_LABELS },
|
|
213
220
|
{ id: "doneMarkerIcon", label: "Pi icon", section: "Completion Marker", group: "features", key: "doneMarkerIcon" },
|
|
214
221
|
{ id: "randomizeDoneMarker", label: "Randomize “Worked” text", section: "Completion Marker", group: "features", key: "randomizeDoneMarker" },
|
|
215
222
|
{ id: "doneMarkerTokens", label: "Tokens spent", section: "Completion Marker", group: "features", key: "doneMarkerTokens" },
|
|
@@ -222,7 +229,7 @@ function menuItem(entry: MenuEntry, settings: DecoratorSettings): MenuSection["i
|
|
|
222
229
|
const cycleEnabled = entry.group === "decorations" && entry.cycleEnabledBy
|
|
223
230
|
? settings.decorations[entry.cycleEnabledBy]
|
|
224
231
|
: undefined;
|
|
225
|
-
return { id: entry.id, label: entry.label, cycleValues: entry.cycleValues, cycleEnabledBy: entry.cycleEnabledBy, cycleDisabledValue: entry.cycleDisabledValue, cycleEnabled, value
|
|
232
|
+
return { id: entry.id, label: entry.label, cycleValues: entry.cycleValues, cycleValueLabels: entry.cycleValueLabels, cycleEnabledBy: entry.cycleEnabledBy, cycleDisabledValue: entry.cycleDisabledValue, cycleEnabled, value };
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
function isDecorationBooleanKey(key: keyof DecorationSettings): key is DecorationBooleanKey {
|
|
@@ -230,31 +237,36 @@ function isDecorationBooleanKey(key: keyof DecorationSettings): key is Decoratio
|
|
|
230
237
|
}
|
|
231
238
|
|
|
232
239
|
function setDecorationCycleValue(decorations: DecorationSettings, key: keyof DecorationSettings, value: string): void {
|
|
233
|
-
const stored = fromCycleValue(value);
|
|
234
240
|
switch (key) {
|
|
235
241
|
case "borderColor":
|
|
242
|
+
if (isPromptBorderColor(value)) decorations[key] = value;
|
|
243
|
+
return;
|
|
236
244
|
case "spinnerColor":
|
|
245
|
+
if (isSpinnerColor(value)) decorations[key] = value;
|
|
246
|
+
return;
|
|
237
247
|
case "meterColor":
|
|
238
248
|
case "tokenRateColor":
|
|
239
249
|
case "responseModelColor":
|
|
250
|
+
if (isThinkingLevelColor(value)) decorations[key] = value;
|
|
251
|
+
return;
|
|
240
252
|
case "doneMarkerBorderColor":
|
|
241
|
-
if (
|
|
253
|
+
if (isDoneMarkerBorderColor(value)) decorations[key] = value;
|
|
242
254
|
return;
|
|
243
255
|
case "borderStyle":
|
|
244
|
-
if (isBorderStyle(
|
|
256
|
+
if (isBorderStyle(value)) decorations[key] = value;
|
|
245
257
|
return;
|
|
246
258
|
case "doneMarkerBorderStyle":
|
|
247
|
-
if (isDoneMarkerBorderStyle(
|
|
259
|
+
if (isDoneMarkerBorderStyle(value)) decorations[key] = value;
|
|
248
260
|
return;
|
|
249
261
|
case "doneMarkerStyle":
|
|
250
|
-
if (isDoneMarkerStyle(
|
|
262
|
+
if (isDoneMarkerStyle(value)) decorations[key] = value;
|
|
251
263
|
return;
|
|
252
264
|
case "shimmerDirection":
|
|
253
265
|
case "meterDirection":
|
|
254
|
-
if (
|
|
266
|
+
if (value === "ltr" || value === "rtl") decorations[key] = value;
|
|
255
267
|
return;
|
|
256
268
|
case "shimmerSpeed":
|
|
257
|
-
if (
|
|
269
|
+
if (value === "slow" || value === "normal" || value === "fast") decorations[key] = value;
|
|
258
270
|
return;
|
|
259
271
|
default:
|
|
260
272
|
// Fail loudly if a MENU_ENTRIES cycle entry is added without a handler here.
|
|
@@ -321,6 +333,14 @@ export function loadSettings(): DecoratorSettings {
|
|
|
321
333
|
wordPacks.simcity = true;
|
|
322
334
|
}
|
|
323
335
|
const settings = { decorations: mergeGroup(DEFAULT_SETTINGS.decorations, parsed.decorations), features: mergeGroup(DEFAULT_SETTINGS.features, parsed.features), loaderOrder: parseLoaderOrder(parsed.loaderOrder), wordPacks };
|
|
336
|
+
const schemaVersion = typeof parsed.schemaVersion === "number" ? parsed.schemaVersion : 1;
|
|
337
|
+
if (schemaVersion < THINKING_LEVEL_COLOR_MIGRATION_VERSION) {
|
|
338
|
+
settings.decorations.spinnerColor = "thinking-level";
|
|
339
|
+
if (settings.decorations.borderColor === "borderAccent") settings.decorations.borderColor = "thinking-level";
|
|
340
|
+
}
|
|
341
|
+
if (schemaVersion < DONE_MARKER_BORDER_COLOR_MIGRATION_VERSION) {
|
|
342
|
+
settings.decorations.doneMarkerBorderColor = "thinking-level";
|
|
343
|
+
}
|
|
324
344
|
for (const entry of MENU_ENTRIES) {
|
|
325
345
|
if (entry.group === "decorations" && entry.cycleEnabledBy && entry.cycleDisabledValue !== undefined && !settings.decorations[entry.cycleEnabledBy]) {
|
|
326
346
|
setDecorationCycleValue(settings.decorations, entry.key, entry.cycleDisabledValue);
|
|
@@ -343,5 +363,5 @@ export function atomicWriteFile(path: string, contents: string): void {
|
|
|
343
363
|
}
|
|
344
364
|
|
|
345
365
|
export function saveSettings(settings: DecoratorSettings): void {
|
|
346
|
-
atomicWriteFile(settingsPath(), `${JSON.stringify(settings, null, 2)}\n`);
|
|
366
|
+
atomicWriteFile(settingsPath(), `${JSON.stringify({ schemaVersion: SETTINGS_SCHEMA_VERSION, ...settings }, null, 2)}\n`);
|
|
347
367
|
}
|
package/src/util.ts
CHANGED
|
@@ -7,3 +7,12 @@ export function isPlainObject(value: unknown): value is Record<string, unknown>
|
|
|
7
7
|
export function stripControlChars(text: string): string {
|
|
8
8
|
return text.replace(/[\p{Cc}\p{Cf}]/gu, "");
|
|
9
9
|
}
|
|
10
|
+
|
|
11
|
+
/** True when a reported response model appears to be a decorated form of the selected model. */
|
|
12
|
+
export function modelsResemble(selectedModel?: string, responseModel?: string): boolean {
|
|
13
|
+
if (!selectedModel || !responseModel) return false;
|
|
14
|
+
const selectedName = selectedModel.split(/[\\/]/).at(-1)?.toLowerCase() ?? "";
|
|
15
|
+
const selectedTokens = selectedName.match(/[a-z0-9]{2,}/g) ?? [];
|
|
16
|
+
const normalizedResponse = responseModel.toLowerCase();
|
|
17
|
+
return selectedTokens.length > 0 && selectedTokens.every((token) => normalizedResponse.includes(token));
|
|
18
|
+
}
|
package/src/word-packs.ts
CHANGED
|
@@ -84,7 +84,6 @@ export function loadBundledWordPacks(): WordPack[] {
|
|
|
84
84
|
packs.push(pack);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
if (!packs.some((pack) => pack.id === "simcity")) throw new Error("Missing bundled SimCity word pack");
|
|
88
87
|
const bundledIds = new Set(packs.map((pack) => pack.id));
|
|
89
88
|
for (const id of RESERVED_BUNDLED_PACK_IDS) {
|
|
90
89
|
if (!bundledIds.has(id)) throw new Error(`Missing bundled word pack: ${id}`);
|
|
@@ -109,9 +108,11 @@ export function isWordPackEnabled(id: string, enabled: Record<string, boolean>):
|
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
export function selectWorkingTextSelection(enabled: Record<string, boolean>, packs: readonly WordPack[], fraction: number): WorkingTextSelection {
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
111
|
+
const enabledWords = packs.filter((pack) => isWordPackEnabled(pack.id, enabled)).flatMap((pack) => pack.words);
|
|
112
|
+
const total = WORDS.length + enabledWords.length;
|
|
113
|
+
const idx = Math.min(total - 1, Math.floor(fraction * total));
|
|
114
|
+
const entry = idx < WORDS.length ? WORDS[idx] : enabledWords[idx - WORDS.length];
|
|
115
|
+
return { text: entry.present_tense, pastTense: entry.past_tense };
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
export function pickWorkingTextSelection(enabled: Record<string, boolean>, packs: readonly WordPack[]): WorkingTextSelection {
|
package/src/words.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Activity word list for the randomized "Working
|
|
2
|
+
* Activity word list for the randomized "Working" indicator.
|
|
3
3
|
*
|
|
4
4
|
* A random word is picked on prompt and on tool execution start
|
|
5
|
-
* (see
|
|
5
|
+
* (see SessionManager in src/session.ts).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
export interface WordEntry {
|
|
@@ -10,14 +10,6 @@ export interface WordEntry {
|
|
|
10
10
|
past_tense: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function pickRawWord(): WordEntry {
|
|
14
|
-
return WORDS[Math.floor(Math.random() * WORDS.length)]!;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function pickRandomWord(): string {
|
|
18
|
-
return `${pickRawWord().present_tense}…`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
13
|
export const WORDS: WordEntry[] = [
|
|
22
14
|
{ present_tense: "Accomplishing", past_tense: "Accomplished" },
|
|
23
15
|
{ present_tense: "Actioning", past_tense: "Actioned" },
|