@zigai/pi-mode 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/package.json +1 -1
- package/src/constants.ts +2 -0
- package/src/editor.ts +2 -1
- package/src/index.ts +4 -1
- package/src/mode-state.ts +44 -2
- package/src/settings.ts +65 -20
- package/src/types.ts +2 -1
package/README.md
CHANGED
|
@@ -18,12 +18,14 @@ pi install npm:@zigai/pi-mode
|
|
|
18
18
|
- Adds `Ctrl+Shift+M` to select a mode.
|
|
19
19
|
- Adds `Ctrl+Space` to cycle modes.
|
|
20
20
|
- Can show the current mode in the prompt editor border when enabled.
|
|
21
|
-
- Colors the prompt editor border from the active mode
|
|
21
|
+
- Colors the prompt editor border from the active mode, with an opt-in setting for thinking-derived border colors.
|
|
22
22
|
|
|
23
23
|
Modes can store a provider, model, thinking level, and optional color. Project-local modes live in `.pi/modes.json` when present; otherwise global modes live in `~/.pi/agent/modes.json`.
|
|
24
24
|
|
|
25
25
|
By default, Pi Mode does not print the mode name in the editor border. To opt in, toggle `Show mode name` from `/mode` → `Configure modes…`, or set `"modeShowName": true` in global `~/.pi/agent/settings.json` or trusted project `.pi/settings.json`. The `/mode` toggle writes to global settings.
|
|
26
26
|
|
|
27
|
+
By default, Pi Mode uses the normal editor border color when the active mode does not define an explicit `color`. To opt in to thinking-derived border colors, toggle `Thinking border colors` from `/mode` → `Configure modes…`, or set `"modeUseThinkingBorderColors": true` in global `~/.pi/agent/settings.json` or trusted project `.pi/settings.json`. Explicit per-mode `color` values still apply.
|
|
28
|
+
|
|
27
29
|
## License
|
|
28
30
|
|
|
29
31
|
MIT
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -7,6 +7,8 @@ export const MODE_UI_CONFIGURE = "Configure modes…";
|
|
|
7
7
|
export const MODE_UI_ADD = "Add mode…";
|
|
8
8
|
export const MODE_UI_SHOW_NAME_ON = "Show mode name: on";
|
|
9
9
|
export const MODE_UI_SHOW_NAME_OFF = "Show mode name: off";
|
|
10
|
+
export const MODE_UI_THINKING_COLORS_ON = "Thinking border colors: on";
|
|
11
|
+
export const MODE_UI_THINKING_COLORS_OFF = "Thinking border colors: off";
|
|
10
12
|
export const MODE_UI_BACK = "Back";
|
|
11
13
|
|
|
12
14
|
export const ALL_THINKING_LEVELS: ThinkingLevel[] = [
|
package/src/editor.ts
CHANGED
|
@@ -63,6 +63,7 @@ function enhanceEditor(
|
|
|
63
63
|
requestRender: () => void,
|
|
64
64
|
): EditorLike {
|
|
65
65
|
const originalRender = editor.render.bind(editor);
|
|
66
|
+
const defaultBorderColor = editor.borderColor;
|
|
66
67
|
editor.render = (width: number) => modeLabelLine(originalRender(width), width, editor);
|
|
67
68
|
|
|
68
69
|
const borderColor = (text: string) => {
|
|
@@ -70,7 +71,7 @@ function enhanceEditor(
|
|
|
70
71
|
if (isBashMode) {
|
|
71
72
|
return ctx.ui.theme.getBashModeBorderColor()(text);
|
|
72
73
|
}
|
|
73
|
-
return getModeBorderColor(ctx, pi, getCurrentMode())(text);
|
|
74
|
+
return getModeBorderColor(ctx, pi, getCurrentMode(), defaultBorderColor)(text);
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
Object.defineProperty(editor, "borderColor", {
|
package/src/index.ts
CHANGED
|
@@ -33,8 +33,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
33
33
|
|
|
34
34
|
pi.on("session_start", async (_event, ctx) => {
|
|
35
35
|
setSettingsContext(ctx);
|
|
36
|
-
|
|
36
|
+
// Install the editor wrapper before loading mode files so startup renders use
|
|
37
|
+
// the configured border color immediately instead of briefly showing Pi's
|
|
38
|
+
// thinking-level border color.
|
|
37
39
|
applyModeEditor(pi, ctx);
|
|
40
|
+
await handleSessionActivated(pi, ctx);
|
|
38
41
|
});
|
|
39
42
|
|
|
40
43
|
pi.on("model_select", async (event, ctx) => {
|
package/src/mode-state.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
MODE_UI_CONFIGURE,
|
|
19
19
|
MODE_UI_SHOW_NAME_OFF,
|
|
20
20
|
MODE_UI_SHOW_NAME_ON,
|
|
21
|
+
MODE_UI_THINKING_COLORS_OFF,
|
|
22
|
+
MODE_UI_THINKING_COLORS_ON,
|
|
21
23
|
THINKING_UNSET_LABEL,
|
|
22
24
|
} from "./constants.ts";
|
|
23
25
|
import {
|
|
@@ -28,7 +30,12 @@ import {
|
|
|
28
30
|
getProjectModesPath,
|
|
29
31
|
withFileLock,
|
|
30
32
|
} from "./storage.ts";
|
|
31
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
setShowModeName,
|
|
35
|
+
setUseThinkingBorderColors,
|
|
36
|
+
shouldShowModeName,
|
|
37
|
+
shouldUseThinkingBorderColors,
|
|
38
|
+
} from "./settings.ts";
|
|
32
39
|
import type { ModeRuntime, ModesFile, ModesPatch, ModeSpec, ModeSpecPatch } from "./types.ts";
|
|
33
40
|
|
|
34
41
|
type ScopedModelItem = {
|
|
@@ -403,6 +410,7 @@ export function getModeBorderColor(
|
|
|
403
410
|
ctx: ExtensionContext,
|
|
404
411
|
pi: ExtensionAPI,
|
|
405
412
|
mode: string,
|
|
413
|
+
fallbackBorderColor?: (text: string) => string,
|
|
406
414
|
): (text: string) => string {
|
|
407
415
|
const theme = ctx.ui.theme;
|
|
408
416
|
const spec = runtime.data.modes[mode];
|
|
@@ -413,10 +421,15 @@ export function getModeBorderColor(
|
|
|
413
421
|
theme.getFgAnsi(color);
|
|
414
422
|
return (text: string) => theme.fg(color, text);
|
|
415
423
|
} catch {
|
|
416
|
-
// fall
|
|
424
|
+
// fall through to the configured fallback border color
|
|
417
425
|
}
|
|
418
426
|
}
|
|
419
427
|
|
|
428
|
+
if (!shouldUseThinkingBorderColors()) {
|
|
429
|
+
if (fallbackBorderColor !== undefined) return fallbackBorderColor;
|
|
430
|
+
return (text: string) => theme.fg("borderMuted", text);
|
|
431
|
+
}
|
|
432
|
+
|
|
420
433
|
return theme.getThinkingBorderColor(pi.getThinkingLevel());
|
|
421
434
|
}
|
|
422
435
|
|
|
@@ -854,10 +867,15 @@ async function configureModesUI(pi: ExtensionAPI, ctx: ExtensionContext): Promis
|
|
|
854
867
|
if (shouldShowModeName()) {
|
|
855
868
|
showModeNameChoice = MODE_UI_SHOW_NAME_ON;
|
|
856
869
|
}
|
|
870
|
+
let thinkingColorsChoice = MODE_UI_THINKING_COLORS_OFF;
|
|
871
|
+
if (shouldUseThinkingBorderColors()) {
|
|
872
|
+
thinkingColorsChoice = MODE_UI_THINKING_COLORS_ON;
|
|
873
|
+
}
|
|
857
874
|
const choice = await ctx.ui.select("Configure modes", [
|
|
858
875
|
...names,
|
|
859
876
|
MODE_UI_ADD,
|
|
860
877
|
showModeNameChoice,
|
|
878
|
+
thinkingColorsChoice,
|
|
861
879
|
MODE_UI_BACK,
|
|
862
880
|
]);
|
|
863
881
|
if (choice === undefined || choice.length === 0 || choice === MODE_UI_BACK) return;
|
|
@@ -887,6 +905,26 @@ async function configureModesUI(pi: ExtensionAPI, ctx: ExtensionContext): Promis
|
|
|
887
905
|
continue;
|
|
888
906
|
}
|
|
889
907
|
|
|
908
|
+
if (choice === MODE_UI_THINKING_COLORS_ON || choice === MODE_UI_THINKING_COLORS_OFF) {
|
|
909
|
+
const next = !shouldUseThinkingBorderColors();
|
|
910
|
+
try {
|
|
911
|
+
setUseThinkingBorderColors(next);
|
|
912
|
+
} catch (error: unknown) {
|
|
913
|
+
ctx.ui.notify(
|
|
914
|
+
`Thinking border colors were not saved: ${errorMessage(error)}`,
|
|
915
|
+
"error",
|
|
916
|
+
);
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
requestEditorRender?.();
|
|
920
|
+
let displayState = "disabled";
|
|
921
|
+
if (next) {
|
|
922
|
+
displayState = "enabled";
|
|
923
|
+
}
|
|
924
|
+
ctx.ui.notify(`Thinking border colors ${displayState}`, "info");
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
|
|
890
928
|
await editModeUI(pi, ctx, choice);
|
|
891
929
|
}
|
|
892
930
|
}
|
|
@@ -1019,6 +1057,10 @@ export async function handleSessionActivated(
|
|
|
1019
1057
|
runtime.currentMode = CUSTOM_MODE_NAME;
|
|
1020
1058
|
customOverlay = getCurrentSelectionSpec(pi);
|
|
1021
1059
|
}
|
|
1060
|
+
|
|
1061
|
+
if (ctx.hasUI) {
|
|
1062
|
+
requestEditorRender?.();
|
|
1063
|
+
}
|
|
1022
1064
|
}
|
|
1023
1065
|
|
|
1024
1066
|
export async function handleModelSelect(
|
package/src/settings.ts
CHANGED
|
@@ -19,11 +19,12 @@ import { Type, type TSchema } from "typebox";
|
|
|
19
19
|
import { Value } from "typebox/value";
|
|
20
20
|
|
|
21
21
|
export const SHOW_MODE_NAME_SETTINGS_KEY = "modeShowName";
|
|
22
|
+
export const USE_THINKING_BORDER_COLORS_SETTINGS_KEY = "modeUseThinkingBorderColors";
|
|
22
23
|
|
|
23
24
|
const SETTINGS_LOCK_TIMEOUT_MS = 5_000;
|
|
24
25
|
const STALE_SETTINGS_LOCK_MS = 30_000;
|
|
25
26
|
const SettingsObjectSchema = Type.Object({});
|
|
26
|
-
const
|
|
27
|
+
const BooleanSettingSchema = Type.Boolean();
|
|
27
28
|
|
|
28
29
|
type SettingsReadContext = {
|
|
29
30
|
cwd: string;
|
|
@@ -31,8 +32,13 @@ type SettingsReadContext = {
|
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
let settingsReadContext: SettingsReadContext | undefined;
|
|
34
|
-
let
|
|
35
|
-
|
|
35
|
+
let cachedSettings:
|
|
36
|
+
| {
|
|
37
|
+
mtimeKey: string;
|
|
38
|
+
showModeName: boolean;
|
|
39
|
+
useThinkingBorderColors: boolean;
|
|
40
|
+
}
|
|
41
|
+
| undefined;
|
|
36
42
|
|
|
37
43
|
type ProjectTrustContext = ExtensionContext & {
|
|
38
44
|
isProjectTrusted?: () => boolean;
|
|
@@ -52,8 +58,7 @@ export function setSettingsContext(ctx: ExtensionContext): void {
|
|
|
52
58
|
settingsReadContext.projectTrusted !== next.projectTrusted
|
|
53
59
|
) {
|
|
54
60
|
settingsReadContext = next;
|
|
55
|
-
|
|
56
|
-
cachedSettingsMtimeKey = undefined;
|
|
61
|
+
cachedSettings = undefined;
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
|
|
@@ -243,29 +248,62 @@ function updateSettingsObject(update: (settings: Record<string, unknown>) => voi
|
|
|
243
248
|
});
|
|
244
249
|
}
|
|
245
250
|
|
|
246
|
-
function
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
251
|
+
function applyBooleanSetting(
|
|
252
|
+
settings: Record<string, unknown>,
|
|
253
|
+
key: string,
|
|
254
|
+
fallback: boolean,
|
|
255
|
+
): boolean {
|
|
256
|
+
const parsed = parseOptionalBoolean(BooleanSettingSchema, settings[key]);
|
|
257
|
+
if (parsed === undefined) return fallback;
|
|
258
|
+
return parsed;
|
|
250
259
|
}
|
|
251
260
|
|
|
252
|
-
|
|
261
|
+
function readModeSettings(): {
|
|
262
|
+
showModeName: boolean;
|
|
263
|
+
useThinkingBorderColors: boolean;
|
|
264
|
+
} {
|
|
253
265
|
const mtimeKey = getSettingsMtimeKey();
|
|
254
|
-
if (
|
|
255
|
-
return
|
|
266
|
+
if (cachedSettings?.mtimeKey === mtimeKey) {
|
|
267
|
+
return cachedSettings;
|
|
256
268
|
}
|
|
257
269
|
|
|
258
270
|
const context = settingsReadContext ?? { cwd: process.cwd(), projectTrusted: false };
|
|
259
271
|
const manager = SettingsManager.create(context.cwd, getAgentDir(), {
|
|
260
272
|
projectTrusted: context.projectTrusted,
|
|
261
273
|
});
|
|
262
|
-
let
|
|
263
|
-
|
|
264
|
-
|
|
274
|
+
let showModeName = false;
|
|
275
|
+
let useThinkingBorderColors = false;
|
|
276
|
+
|
|
277
|
+
const globalSettings = manager.getGlobalSettings() as Record<string, unknown>;
|
|
278
|
+
showModeName = applyBooleanSetting(globalSettings, SHOW_MODE_NAME_SETTINGS_KEY, showModeName);
|
|
279
|
+
useThinkingBorderColors = applyBooleanSetting(
|
|
280
|
+
globalSettings,
|
|
281
|
+
USE_THINKING_BORDER_COLORS_SETTINGS_KEY,
|
|
282
|
+
useThinkingBorderColors,
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const projectSettings = manager.getProjectSettings() as Record<string, unknown>;
|
|
286
|
+
showModeName = applyBooleanSetting(projectSettings, SHOW_MODE_NAME_SETTINGS_KEY, showModeName);
|
|
287
|
+
useThinkingBorderColors = applyBooleanSetting(
|
|
288
|
+
projectSettings,
|
|
289
|
+
USE_THINKING_BORDER_COLORS_SETTINGS_KEY,
|
|
290
|
+
useThinkingBorderColors,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
cachedSettings = {
|
|
294
|
+
mtimeKey,
|
|
295
|
+
showModeName,
|
|
296
|
+
useThinkingBorderColors,
|
|
297
|
+
};
|
|
298
|
+
return cachedSettings;
|
|
299
|
+
}
|
|
265
300
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
301
|
+
export function shouldShowModeName(): boolean {
|
|
302
|
+
return readModeSettings().showModeName;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function shouldUseThinkingBorderColors(): boolean {
|
|
306
|
+
return readModeSettings().useThinkingBorderColors;
|
|
269
307
|
}
|
|
270
308
|
|
|
271
309
|
export function setShowModeName(show: boolean): void {
|
|
@@ -273,6 +311,13 @@ export function setShowModeName(show: boolean): void {
|
|
|
273
311
|
settings[SHOW_MODE_NAME_SETTINGS_KEY] = show;
|
|
274
312
|
});
|
|
275
313
|
|
|
276
|
-
|
|
277
|
-
|
|
314
|
+
cachedSettings = undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function setUseThinkingBorderColors(useThinkingBorderColors: boolean): void {
|
|
318
|
+
updateSettingsObject((settings) => {
|
|
319
|
+
settings[USE_THINKING_BORDER_COLORS_SETTINGS_KEY] = useThinkingBorderColors;
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
cachedSettings = undefined;
|
|
278
323
|
}
|
package/src/types.ts
CHANGED
|
@@ -9,7 +9,8 @@ export type ModeSpec = {
|
|
|
9
9
|
thinkingLevel?: ThinkingLevel;
|
|
10
10
|
/**
|
|
11
11
|
* Optional theme color token to use for the editor border.
|
|
12
|
-
* If unset, the border
|
|
12
|
+
* If unset, the default editor border is used unless thinking-derived
|
|
13
|
+
* border colors are enabled in settings.
|
|
13
14
|
*/
|
|
14
15
|
color?: ThemeColor;
|
|
15
16
|
};
|