@groeponline/pi-wishcraft 1.1.0 → 1.2.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/CHANGELOG.md +13 -0
- package/README.md +16 -7
- package/ROADMAP.md +41 -260
- package/docs/commands.md +6 -2
- package/docs/configuration.md +21 -0
- package/docs/design/accessibility.md +71 -0
- package/docs/design/deck-layout.md +69 -0
- package/docs/design/motion-gallery.md +80 -0
- package/docs/design/motion-system.md +119 -0
- package/docs/design/presets.md +196 -0
- package/docs/design/regression-testing.md +54 -0
- package/docs/design/responsive.md +53 -0
- package/docs/design/signal.md +68 -0
- package/docs/design/theme-contract.md +132 -0
- package/docs/design/vnext-overview.md +55 -0
- package/docs/design/vnext-release-plan.md +212 -0
- package/docs/index.md +19 -1
- package/docs/segments.md +1 -1
- package/package.json +2 -1
- package/src/config/appearance.ts +230 -0
- package/src/config/parse.ts +41 -0
- package/src/config/presets.ts +184 -0
- package/src/config/structural-presets.ts +555 -0
- package/src/config/tokens.ts +154 -0
- package/src/config/types.ts +210 -2
- package/src/extension/commands/commands.ts +36 -12
- package/src/extension/commands/powerline-completions.ts +4 -0
- package/src/extension/commands/queue-commands.ts +6 -0
- package/src/extension/core/segment-context.ts +7 -1
- package/src/extension/core/state.ts +14 -0
- package/src/extension/core/types.ts +7 -0
- package/src/extension/session/session-lifecycle.ts +20 -0
- package/src/extension/settings/appearance-write.ts +135 -0
- package/src/extension/settings/wishcraft-config-items.ts +119 -0
- package/src/extension/settings/wishcraft-config.ts +28 -114
- package/src/extension/skills/skill-manager.ts +5 -0
- package/src/extension/ui/deck/component.ts +358 -0
- package/src/extension/ui/deck/index.ts +34 -0
- package/src/extension/ui/deck/render.ts +260 -0
- package/src/extension/ui/deck/route-bodies.ts +209 -0
- package/src/extension/ui/deck/routes.ts +37 -0
- package/src/extension/ui/deck/session-snapshot.ts +104 -0
- package/src/extension/ui/deck/types.ts +92 -0
- package/src/extension/ui/powerline-menu-view.ts +19 -15
- package/src/extension/ui/signal-layout.ts +77 -0
- package/src/extension/ui/status-line-renderers.ts +20 -2
- package/src/motion/accessibility.ts +59 -0
- package/src/motion/catalog-extra.ts +429 -0
- package/src/motion/catalog.ts +336 -0
- package/src/motion/composer.ts +147 -0
- package/src/motion/frames.ts +84 -0
- package/src/motion/gallery.ts +77 -0
- package/src/motion/index.ts +78 -0
- package/src/motion/policy.ts +128 -0
- package/src/motion/scheduler.ts +159 -0
- package/src/motion/types.ts +132 -0
- package/src/render/timer.ts +1 -0
- package/src/signal/controller.ts +123 -0
- package/src/signal/integration.ts +42 -0
- package/src/signal/render.ts +178 -0
- package/src/theme/detect.ts +48 -0
- package/src/theme/tokens/index.ts +9 -0
- package/src/theme/tokens/mapping.ts +62 -0
- package/src/theme/tokens/types.ts +39 -0
package/src/config/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { MotionEvent } from "../motion/types.ts";
|
|
2
3
|
import type { CostCurrencyCode } from "../usage/rates.ts";
|
|
3
4
|
|
|
4
5
|
// Theme color - either a pi theme color name or a custom hex color
|
|
@@ -28,6 +29,176 @@ export type SemanticColor =
|
|
|
28
29
|
// Color scheme mapping semantic names to actual colors
|
|
29
30
|
export type ColorScheme = Partial<Record<SemanticColor, ColorValue>>;
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Semantic design tokens (vNext). Presets fill abstract roles; the segment
|
|
34
|
+
* colors above are derived from them. See src/config/tokens.ts for the mapping
|
|
35
|
+
* and the default palette.
|
|
36
|
+
*/
|
|
37
|
+
export interface WishcraftTokens {
|
|
38
|
+
/** Deck background. */
|
|
39
|
+
surface: ColorValue;
|
|
40
|
+
/** Raised areas: headers, footers, selected rows. */
|
|
41
|
+
surfaceRaised: ColorValue;
|
|
42
|
+
text: ColorValue;
|
|
43
|
+
textMuted: ColorValue;
|
|
44
|
+
primary: ColorValue;
|
|
45
|
+
secondary: ColorValue;
|
|
46
|
+
accent: ColorValue;
|
|
47
|
+
success: ColorValue;
|
|
48
|
+
warning: ColorValue;
|
|
49
|
+
error: ColorValue;
|
|
50
|
+
/** Focused control or active route. */
|
|
51
|
+
focus: ColorValue;
|
|
52
|
+
/** Selected row background. */
|
|
53
|
+
selection: ColorValue;
|
|
54
|
+
/** Resting motion, e.g. a still rail. */
|
|
55
|
+
motionDim: ColorValue;
|
|
56
|
+
/** The travelling head of a sweep. */
|
|
57
|
+
motionHot: ColorValue;
|
|
58
|
+
/** Cells just behind the head. */
|
|
59
|
+
motionTrail: ColorValue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type TokenRole = keyof WishcraftTokens;
|
|
63
|
+
|
|
64
|
+
/** Motion catalog id, e.g. `ember-relay`. */
|
|
65
|
+
export type MotionRef = string;
|
|
66
|
+
|
|
67
|
+
export type StructuralPresetName =
|
|
68
|
+
| "lanternwake"
|
|
69
|
+
| "threadbound"
|
|
70
|
+
| "scryglass"
|
|
71
|
+
| "runebloom"
|
|
72
|
+
| "moonwell"
|
|
73
|
+
| "hexforge"
|
|
74
|
+
| "vellum"
|
|
75
|
+
| "wisp"
|
|
76
|
+
| "starweave"
|
|
77
|
+
| "crucible";
|
|
78
|
+
|
|
79
|
+
export const STRUCTURAL_PRESET_NAMES = [
|
|
80
|
+
"lanternwake",
|
|
81
|
+
"threadbound",
|
|
82
|
+
"scryglass",
|
|
83
|
+
"runebloom",
|
|
84
|
+
"moonwell",
|
|
85
|
+
"hexforge",
|
|
86
|
+
"vellum",
|
|
87
|
+
"wisp",
|
|
88
|
+
"starweave",
|
|
89
|
+
"crucible",
|
|
90
|
+
] as const satisfies readonly StructuralPresetName[];
|
|
91
|
+
|
|
92
|
+
export type ChromeFrame = "rounded" | "square" | "double" | "minimal" | "borderless";
|
|
93
|
+
export type ChromeDensity = "compact" | "medium" | "spacious";
|
|
94
|
+
|
|
95
|
+
export interface ChromeSpec {
|
|
96
|
+
frame: ChromeFrame;
|
|
97
|
+
corners: { tl: string; tr: string; bl: string; br: string };
|
|
98
|
+
dividers: { horizontal: string; vertical: string; cross: string };
|
|
99
|
+
density: ChromeDensity;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type SignalLayout =
|
|
103
|
+
| "standard"
|
|
104
|
+
| "capsule"
|
|
105
|
+
| "woven"
|
|
106
|
+
| "sparse"
|
|
107
|
+
| "block"
|
|
108
|
+
| "arc"
|
|
109
|
+
| "editorial"
|
|
110
|
+
| "fluid"
|
|
111
|
+
| "constellation"
|
|
112
|
+
| "cell";
|
|
113
|
+
|
|
114
|
+
export interface SignalSpec {
|
|
115
|
+
layout: SignalLayout;
|
|
116
|
+
separators: {
|
|
117
|
+
left: string;
|
|
118
|
+
right: string;
|
|
119
|
+
subLeft?: string;
|
|
120
|
+
subRight?: string;
|
|
121
|
+
};
|
|
122
|
+
caps: {
|
|
123
|
+
leftOpen?: string;
|
|
124
|
+
leftClose?: string;
|
|
125
|
+
rightOpen?: string;
|
|
126
|
+
rightClose?: string;
|
|
127
|
+
};
|
|
128
|
+
/** Signature lane animation from the motion catalog. */
|
|
129
|
+
animation: MotionRef;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type DeckNavigation = "tabs" | "rail" | "minimal";
|
|
133
|
+
export type DeckPanelStyle = "framed" | "borderless" | "inset";
|
|
134
|
+
export type DeckActivityStyle = "pulse" | "bar" | "none";
|
|
135
|
+
|
|
136
|
+
export interface DeckSpec {
|
|
137
|
+
navigation: DeckNavigation;
|
|
138
|
+
panelStyle: DeckPanelStyle;
|
|
139
|
+
activityStyle: DeckActivityStyle;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface WelcomeSpec {
|
|
143
|
+
lantern: boolean;
|
|
144
|
+
ambient: boolean;
|
|
145
|
+
motionId?: MotionRef;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export type GlyphMode = "nerd" | "ascii" | "auto";
|
|
149
|
+
|
|
150
|
+
export interface GlyphSet {
|
|
151
|
+
mode: GlyphMode;
|
|
152
|
+
/** Leading ornament for the model segment (Nerd). */
|
|
153
|
+
model?: string;
|
|
154
|
+
/** ASCII fallback for the model ornament. */
|
|
155
|
+
modelAscii?: string;
|
|
156
|
+
/** Default segment marker (Nerd). */
|
|
157
|
+
segment?: string;
|
|
158
|
+
/** ASCII fallback for segment markers. */
|
|
159
|
+
segmentAscii?: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** vNext structural personality: tokens, chrome, signal grammar, motion, deck, welcome, glyphs. */
|
|
163
|
+
export interface StructuralPresetDef {
|
|
164
|
+
name: StructuralPresetName;
|
|
165
|
+
displayName: string;
|
|
166
|
+
description: string;
|
|
167
|
+
tokens: Partial<WishcraftTokens>;
|
|
168
|
+
chrome: ChromeSpec;
|
|
169
|
+
signal: SignalSpec;
|
|
170
|
+
motion: Partial<Record<MotionEvent, MotionRef>>;
|
|
171
|
+
deck: DeckSpec;
|
|
172
|
+
welcome: WelcomeSpec;
|
|
173
|
+
glyphs: GlyphSet;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Decoupled appearance layers. Each field can point at a different structural
|
|
178
|
+
* preset so palette, signal layout, chrome, and motion stay independent.
|
|
179
|
+
*/
|
|
180
|
+
export interface AppearanceMixConfig {
|
|
181
|
+
base?: StructuralPresetName;
|
|
182
|
+
palette?: StructuralPresetName;
|
|
183
|
+
signalLayout?: StructuralPresetName;
|
|
184
|
+
chrome?: StructuralPresetName;
|
|
185
|
+
glyphs?: StructuralPresetName;
|
|
186
|
+
deck?: StructuralPresetName;
|
|
187
|
+
welcome?: StructuralPresetName;
|
|
188
|
+
motion?: StructuralPresetName | Partial<Record<MotionEvent, MotionRef>>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface ResolvedAppearance {
|
|
192
|
+
base: StructuralPresetName;
|
|
193
|
+
tokens: WishcraftTokens;
|
|
194
|
+
chrome: ChromeSpec;
|
|
195
|
+
signal: SignalSpec;
|
|
196
|
+
motion: Partial<Record<MotionEvent, MotionRef>>;
|
|
197
|
+
deck: DeckSpec;
|
|
198
|
+
welcome: WelcomeSpec;
|
|
199
|
+
glyphs: GlyphSet;
|
|
200
|
+
}
|
|
201
|
+
|
|
31
202
|
// Built-in segment identifiers
|
|
32
203
|
export const BUILTIN_STATUS_LINE_SEGMENT_IDS = [
|
|
33
204
|
"model",
|
|
@@ -78,7 +249,24 @@ export type StatusLineSeparatorStyle =
|
|
|
78
249
|
export type PowerlinePlacement = "above" | "below";
|
|
79
250
|
|
|
80
251
|
export type StatusLinePreset =
|
|
81
|
-
|
|
252
|
+
| "default"
|
|
253
|
+
| "minimal"
|
|
254
|
+
| "compact"
|
|
255
|
+
| "full"
|
|
256
|
+
| "nerd"
|
|
257
|
+
| "ascii"
|
|
258
|
+
| "chef"
|
|
259
|
+
// vNext Structural Signature Presets
|
|
260
|
+
| "lanternwake"
|
|
261
|
+
| "threadbound"
|
|
262
|
+
| "scryglass"
|
|
263
|
+
| "runebloom"
|
|
264
|
+
| "moonwell"
|
|
265
|
+
| "hexforge"
|
|
266
|
+
| "vellum"
|
|
267
|
+
| "wisp"
|
|
268
|
+
| "starweave"
|
|
269
|
+
| "crucible";
|
|
82
270
|
|
|
83
271
|
// Optional `{value}` template override shared by all segment option groups.
|
|
84
272
|
// Replaces `{value}` in the rendered value text (e.g. "{value} tok/s").
|
|
@@ -153,7 +341,7 @@ export interface CustomStatusItem {
|
|
|
153
341
|
excludeFromExtensionStatuses: boolean;
|
|
154
342
|
}
|
|
155
343
|
|
|
156
|
-
|
|
344
|
+
|
|
157
345
|
export interface PresetDef {
|
|
158
346
|
leftSegments: StatusLineSegmentId[];
|
|
159
347
|
rightSegments: StatusLineSegmentId[];
|
|
@@ -163,6 +351,24 @@ export interface PresetDef {
|
|
|
163
351
|
segmentOptions?: StatusLineSegmentOptions;
|
|
164
352
|
/** Color scheme for this preset */
|
|
165
353
|
colors?: ColorScheme;
|
|
354
|
+
/**
|
|
355
|
+
* Semantic design tokens (vNext). When present, `colors` is derived from these
|
|
356
|
+
* and any explicit `colors` entry still wins. Optional, so the presets that
|
|
357
|
+
* predate tokens keep rendering exactly as before.
|
|
358
|
+
*/
|
|
359
|
+
tokens?: Partial<WishcraftTokens>;
|
|
360
|
+
/** Deck and overlay frame geometry (vNext). */
|
|
361
|
+
chrome?: ChromeSpec;
|
|
362
|
+
/** Signal lane grammar and separators (vNext). */
|
|
363
|
+
signal?: SignalSpec;
|
|
364
|
+
/** Per-event motion overrides (vNext). */
|
|
365
|
+
motion?: Partial<Record<MotionEvent, MotionRef>>;
|
|
366
|
+
/** Deck navigation and panel chrome (vNext). */
|
|
367
|
+
deck?: DeckSpec;
|
|
368
|
+
/** Welcome screen lantern and ambient motion (vNext). */
|
|
369
|
+
welcome?: WelcomeSpec;
|
|
370
|
+
/** Nerd / ASCII glyph grammar (vNext). */
|
|
371
|
+
glyphs?: GlyphSet;
|
|
166
372
|
}
|
|
167
373
|
|
|
168
374
|
// Separator definition
|
|
@@ -311,4 +517,6 @@ export interface CustomPresetConfig {
|
|
|
311
517
|
separator?: StatusLineSeparatorStyle;
|
|
312
518
|
colors?: ColorScheme;
|
|
313
519
|
segmentOptions?: StatusLineSegmentOptions;
|
|
520
|
+
/** Semantic design tokens; `colors` entries still win over derived values. */
|
|
521
|
+
tokens?: Partial<WishcraftTokens>;
|
|
314
522
|
}
|
|
@@ -18,7 +18,9 @@ import {
|
|
|
18
18
|
} from "../settings/settings-io.ts";
|
|
19
19
|
import { showOpenPortsList, showSelectOverlay } from "../ui/menu-views.ts";
|
|
20
20
|
import { showTpsOverlay, showUsageOverlay } from "../ui/token-overlays.ts";
|
|
21
|
-
import {
|
|
21
|
+
import { openWishcraftDeck } from "../ui/deck/index.ts";
|
|
22
|
+
import { showPowerlineClassicMenu } from "../ui/powerline-menu-view.ts";
|
|
23
|
+
import { syncAppearanceForLayoutPreset } from "../settings/appearance-write.ts";
|
|
22
24
|
import { openStashHistory } from "../shortcuts/shortcuts-router.ts";
|
|
23
25
|
import { ensureShellSession, setBashModeActive } from "./bash-mode-actions.ts";
|
|
24
26
|
import { setupCustomEditor } from "../ui/custom-editor.ts";
|
|
@@ -31,6 +33,7 @@ import { publishPowerlineStatuses } from "../core/status-export.ts";
|
|
|
31
33
|
import { config, normalizePreset } from "../core/state.ts";
|
|
32
34
|
import type { RuntimeState } from "../core/types.ts";
|
|
33
35
|
import { getPowerlineArgumentCompletions } from "./powerline-completions.ts";
|
|
36
|
+
import { settleSignal } from "../../signal/integration.ts";
|
|
34
37
|
|
|
35
38
|
export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
36
39
|
registerCdCommand(pi, () => rt.currentCtx?.cwd ?? process.cwd());
|
|
@@ -39,9 +42,9 @@ export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
|
39
42
|
registerSkillManagerCommand(pi, rt);
|
|
40
43
|
registerWishcraftConfigCommand(pi, rt);
|
|
41
44
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
description: "Configure
|
|
45
|
+
// `/signal` is the vNext command; `/powerline` remains a transparent alias.
|
|
46
|
+
const signalCommand: Parameters<ExtensionAPI["registerCommand"]>[1] = {
|
|
47
|
+
description: "Configure Signal status (toggle, preset, placement)",
|
|
45
48
|
getArgumentCompletions(argumentPrefix) {
|
|
46
49
|
return getPowerlineArgumentCompletions(argumentPrefix);
|
|
47
50
|
},
|
|
@@ -54,8 +57,9 @@ export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
|
54
57
|
rt.enabled = !rt.enabled;
|
|
55
58
|
if (rt.enabled) {
|
|
56
59
|
setupCustomEditor(pi, rt, ctx);
|
|
57
|
-
ctx.ui.notify("
|
|
60
|
+
ctx.ui.notify("Signal enabled", "info");
|
|
58
61
|
} else {
|
|
62
|
+
settleSignal(rt);
|
|
59
63
|
rt.shellSession?.dispose();
|
|
60
64
|
rt.shellSession = null;
|
|
61
65
|
rt.bashTranscript.clear();
|
|
@@ -87,12 +91,20 @@ export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
|
87
91
|
rt.currentEditor = null;
|
|
88
92
|
rt.statusRenderScheduler.cancel();
|
|
89
93
|
resetLayoutCache(rt);
|
|
90
|
-
ctx.ui.notify("
|
|
94
|
+
ctx.ui.notify("Signal disabled", "info");
|
|
91
95
|
}
|
|
92
96
|
return;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
const normalizedArgs = args.trim().toLowerCase();
|
|
100
|
+
if (normalizedArgs === "menu") {
|
|
101
|
+
await showPowerlineClassicMenu(rt, ctx);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (normalizedArgs === "deck") {
|
|
105
|
+
await openWishcraftDeck(rt, ctx, "signal");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
96
108
|
if (normalizedArgs === "doctor") {
|
|
97
109
|
await runPowerlineDoctor(rt, ctx);
|
|
98
110
|
return;
|
|
@@ -123,12 +135,12 @@ export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
|
123
135
|
)
|
|
124
136
|
) {
|
|
125
137
|
ctx.ui.notify(
|
|
126
|
-
`
|
|
138
|
+
`Signal placement set to: ${config.placement}`,
|
|
127
139
|
"info",
|
|
128
140
|
);
|
|
129
141
|
} else {
|
|
130
142
|
ctx.ui.notify(
|
|
131
|
-
`
|
|
143
|
+
`Signal placement set to: ${config.placement} (not persisted; check settings.json)`,
|
|
132
144
|
"warning",
|
|
133
145
|
);
|
|
134
146
|
}
|
|
@@ -144,7 +156,9 @@ export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
|
144
156
|
setupCustomEditor(pi, rt, ctx);
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
|
|
159
|
+
const persisted = writePowerlinePresetSetting(preset, ctx.cwd);
|
|
160
|
+
syncAppearanceForLayoutPreset(rt, ctx.cwd, preset);
|
|
161
|
+
if (persisted) {
|
|
148
162
|
ctx.ui.notify(`Preset set to: ${preset}`, "info");
|
|
149
163
|
} else {
|
|
150
164
|
ctx.ui.notify(
|
|
@@ -159,6 +173,11 @@ export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
|
159
173
|
const presetList = Object.keys(PRESETS).join(", ");
|
|
160
174
|
ctx.ui.notify(`Available presets: ${presetList}`, "info");
|
|
161
175
|
},
|
|
176
|
+
};
|
|
177
|
+
pi.registerCommand("signal", signalCommand);
|
|
178
|
+
pi.registerCommand("powerline", {
|
|
179
|
+
...signalCommand,
|
|
180
|
+
description: "Compatibility alias for /signal",
|
|
162
181
|
});
|
|
163
182
|
|
|
164
183
|
pi.registerCommand("stash-history", {
|
|
@@ -250,8 +269,12 @@ export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
|
250
269
|
|
|
251
270
|
pi.registerCommand("usage", {
|
|
252
271
|
description: "Show session / today / week token usage overlay",
|
|
253
|
-
handler: async (
|
|
272
|
+
handler: async (args, ctx) => {
|
|
254
273
|
rt.currentCtx = ctx;
|
|
274
|
+
if (args?.trim().toLowerCase() === "deck") {
|
|
275
|
+
await openWishcraftDeck(rt, ctx, "usage");
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
255
278
|
await showUsageOverlay(rt, ctx);
|
|
256
279
|
},
|
|
257
280
|
});
|
|
@@ -295,9 +318,10 @@ export function registerCommands(pi: ExtensionAPI, rt: RuntimeState): void {
|
|
|
295
318
|
// Configurable powerline shortcuts (re-bound on /reload via settings.powerlineShortcuts).
|
|
296
319
|
if (rt.resolvedShortcuts.menu) {
|
|
297
320
|
pi.registerShortcut(rt.resolvedShortcuts.menu as KeyId, {
|
|
298
|
-
description: "
|
|
321
|
+
description: "Wishcraft Deck (operator control surface)",
|
|
299
322
|
handler: async (ctx) => {
|
|
300
|
-
|
|
323
|
+
rt.currentCtx = ctx;
|
|
324
|
+
await openWishcraftDeck(rt, ctx, "home");
|
|
301
325
|
},
|
|
302
326
|
});
|
|
303
327
|
}
|
|
@@ -19,6 +19,10 @@ export const POWERLINE_SUBCOMMANDS: ReadonlyArray<{
|
|
|
19
19
|
value: "placement",
|
|
20
20
|
description: "Move the primary row above or below the editor",
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
value: "menu",
|
|
24
|
+
description: "Open Navigate / Configure / Status",
|
|
25
|
+
},
|
|
22
26
|
];
|
|
23
27
|
|
|
24
28
|
const PLACEMENT_DESCRIPTIONS: Record<(typeof POWERLINE_PLACEMENT_VALUES)[number], string> = {
|
|
@@ -15,6 +15,7 @@ import { getCurrentEditorText } from "../shortcuts/shortcuts-router.ts";
|
|
|
15
15
|
import { getQueueContext } from "../queue/queue-context.ts";
|
|
16
16
|
import { config } from "../core/state.ts";
|
|
17
17
|
import type { RuntimeState } from "../core/types.ts";
|
|
18
|
+
import { openWishcraftDeck } from "../ui/deck/index.ts";
|
|
18
19
|
|
|
19
20
|
export function registerQueueCommands(
|
|
20
21
|
pi: ExtensionAPI,
|
|
@@ -70,6 +71,11 @@ export function registerQueueCommands(
|
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
if (action === "deck") {
|
|
75
|
+
await openWishcraftDeck(rt, ctx, "ideas");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
if (action === "next") {
|
|
74
80
|
const item = findNextIdea(rt, ctx);
|
|
75
81
|
if (!item) {
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
|
|
6
6
|
import type { SegmentContext, ColorScheme } from "../../config/types.ts";
|
|
7
7
|
import { getPreset } from "../../config/presets.ts";
|
|
8
|
+
import { liveColorScheme } from "../../config/appearance.ts";
|
|
8
9
|
import {
|
|
9
10
|
collectHiddenExtensionStatusKeys,
|
|
10
11
|
deriveAutoCustomItems,
|
|
@@ -125,7 +126,12 @@ export function buildSegmentContext(
|
|
|
125
126
|
theme: Theme,
|
|
126
127
|
): SegmentContext {
|
|
127
128
|
const presetDef = getPreset(config.preset);
|
|
128
|
-
const colors: ColorScheme =
|
|
129
|
+
const colors: ColorScheme = liveColorScheme(
|
|
130
|
+
presetDef,
|
|
131
|
+
config.appearance,
|
|
132
|
+
config.preset,
|
|
133
|
+
getDefaultColors,
|
|
134
|
+
);
|
|
129
135
|
|
|
130
136
|
// Build usage stats and get thinking level from session (cached; the full
|
|
131
137
|
// event list is only re-scanned when events are appended or the trailing
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
import { CoreContextUsageCache } from "../../usage/context.ts";
|
|
13
13
|
import { createWelcomeDismissScheduler } from "../../welcome/auto-dismiss.ts";
|
|
14
14
|
import { createRenderScheduler } from "../../render/timer.ts";
|
|
15
|
+
import { MotionScheduler, policyFromEnvironment } from "../../motion/index.ts";
|
|
16
|
+
import { createSignalRuntime } from "../../signal/controller.ts";
|
|
15
17
|
import {
|
|
16
18
|
resolveShortcutConfig,
|
|
17
19
|
parseBashModeSettings,
|
|
@@ -47,6 +49,8 @@ export let config: PowerlineConfig = {
|
|
|
47
49
|
segments: {},
|
|
48
50
|
presets: {},
|
|
49
51
|
segmentLabels: {},
|
|
52
|
+
appearance: {},
|
|
53
|
+
motionLevel: "full",
|
|
50
54
|
};
|
|
51
55
|
|
|
52
56
|
export function setConfig(next: PowerlineConfig): void {
|
|
@@ -135,6 +139,8 @@ export function createRuntimeState(
|
|
|
135
139
|
|
|
136
140
|
resolvedShortcuts,
|
|
137
141
|
bashModeSettings,
|
|
142
|
+
motionPolicy: policyFromEnvironment(process.env, config.motionLevel),
|
|
143
|
+
signal: createSignalRuntime(),
|
|
138
144
|
} as RuntimeState;
|
|
139
145
|
|
|
140
146
|
rt.welcomeDismissScheduler = createWelcomeDismissScheduler({
|
|
@@ -159,5 +165,13 @@ export function createRuntimeState(
|
|
|
159
165
|
rt.tuiRef?.requestRender();
|
|
160
166
|
}, STATUS_RENDER_DEBOUNCE_MS);
|
|
161
167
|
|
|
168
|
+
rt.motionScheduler = new MotionScheduler({
|
|
169
|
+
requestRender: () => {
|
|
170
|
+
// Motion only changes the Signal rail; segment data remains cached.
|
|
171
|
+
rt.lastLayoutResult = null;
|
|
172
|
+
rt.tuiRef?.requestRender();
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
162
176
|
return rt;
|
|
163
177
|
}
|
|
@@ -13,6 +13,8 @@ import type {
|
|
|
13
13
|
import type { CoreContextUsageCache } from "../../usage/context.ts";
|
|
14
14
|
import type { WelcomeDismissScheduler } from "../../welcome/auto-dismiss.ts";
|
|
15
15
|
import type { RenderScheduler } from "../../render/timer.ts";
|
|
16
|
+
import type { MotionPolicy, MotionScheduler } from "../../motion/index.ts";
|
|
17
|
+
import type { SignalRuntime } from "../../signal/controller.ts";
|
|
16
18
|
|
|
17
19
|
export type ShortcutBinding = string | null;
|
|
18
20
|
|
|
@@ -98,4 +100,9 @@ export interface RuntimeState {
|
|
|
98
100
|
|
|
99
101
|
welcomeDismissScheduler: WelcomeDismissScheduler<any>;
|
|
100
102
|
statusRenderScheduler: RenderScheduler;
|
|
103
|
+
/** The single shared clock for every Wishcraft animation surface. */
|
|
104
|
+
motionScheduler: MotionScheduler;
|
|
105
|
+
motionPolicy: MotionPolicy;
|
|
106
|
+
/** Live Signal event, motion frame, and activity label. */
|
|
107
|
+
signal: SignalRuntime;
|
|
101
108
|
}
|
|
@@ -3,6 +3,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { BashTranscriptStore } from "../../../bash-mode/transcript.ts";
|
|
4
4
|
import { BashCompletionEngine } from "../../../bash-mode/completion.ts";
|
|
5
5
|
import { parsePowerlineConfig } from "../../config/powerline-config.ts";
|
|
6
|
+
import { policyFromEnvironment } from "../../motion/accessibility.ts";
|
|
6
7
|
import { registerCustomSegments } from "../../segments/index.ts";
|
|
7
8
|
import { registerCustomPresets } from "../../config/presets.ts";
|
|
8
9
|
import { invalidateGitStatus } from "../../git/status.ts";
|
|
@@ -76,6 +77,10 @@ import {
|
|
|
76
77
|
clearSkillsCountPublisher,
|
|
77
78
|
} from "../skills/skill-status.ts";
|
|
78
79
|
import { maybeAppendReadHint } from "./read-hints.ts";
|
|
80
|
+
import {
|
|
81
|
+
dispatchSignalEvent,
|
|
82
|
+
settleSignal,
|
|
83
|
+
} from "../../signal/integration.ts";
|
|
79
84
|
|
|
80
85
|
/**
|
|
81
86
|
* Fire the configured `powerline.costAlert` warning at most once per session.
|
|
@@ -162,6 +167,7 @@ export function registerSessionLifecycle(
|
|
|
162
167
|
): void {
|
|
163
168
|
// Track session start
|
|
164
169
|
pi.on("session_start", async (event, ctx) => {
|
|
170
|
+
settleSignal(rt);
|
|
165
171
|
clearSkillsCountPublisher();
|
|
166
172
|
rt.shellSession?.dispose();
|
|
167
173
|
rt.shellSession = null;
|
|
@@ -183,6 +189,7 @@ export function registerSessionLifecycle(
|
|
|
183
189
|
rt.bashModeSettings = parseBashModeSettings(settings, rt.resolvedShortcuts);
|
|
184
190
|
rt.showLastPrompt = settings.showLastPrompt !== false;
|
|
185
191
|
setConfig(parsePowerlineConfig(settings.powerline, PRESET_NAMES));
|
|
192
|
+
rt.motionPolicy = policyFromEnvironment(process.env, config.motionLevel);
|
|
186
193
|
rt.queueStore.setSentRetentionMs(
|
|
187
194
|
config.queue.retentionHours * 60 * 60 * 1000,
|
|
188
195
|
);
|
|
@@ -238,6 +245,7 @@ export function registerSessionLifecycle(
|
|
|
238
245
|
rt.welcomeOverlayShouldDismiss = false;
|
|
239
246
|
rt.welcomeDismissScheduler.cancel();
|
|
240
247
|
rt.statusRenderScheduler.cancel();
|
|
248
|
+
rt.motionScheduler.dispose();
|
|
241
249
|
rt.restoreFooterStatusRepaintHook?.();
|
|
242
250
|
rt.restoreFooterStatusRepaintHook = null;
|
|
243
251
|
rt.stashShortcutInputUnsubscribe?.();
|
|
@@ -320,6 +328,7 @@ export function registerSessionLifecycle(
|
|
|
320
328
|
onVibeAgentStart();
|
|
321
329
|
dismissWelcome(rt, ctx);
|
|
322
330
|
rt.currentCtx = ctx;
|
|
331
|
+
dispatchSignalEvent(rt, config.appearance, "thinking");
|
|
323
332
|
});
|
|
324
333
|
|
|
325
334
|
pi.on("message_update", async (event, ctx) => {
|
|
@@ -333,6 +342,9 @@ export function registerSessionLifecycle(
|
|
|
333
342
|
rt.currentCtx = ctx;
|
|
334
343
|
rt.layoutDirty = true;
|
|
335
344
|
rt.statusRenderScheduler.schedule(CONTEXT_STATUS_RENDER_MS);
|
|
345
|
+
if (rt.signal.event !== "streaming") {
|
|
346
|
+
dispatchSignalEvent(rt, config.appearance, "streaming");
|
|
347
|
+
}
|
|
336
348
|
}
|
|
337
349
|
});
|
|
338
350
|
|
|
@@ -374,6 +386,7 @@ export function registerSessionLifecycle(
|
|
|
374
386
|
rt.powerlineCompacting = true;
|
|
375
387
|
rt.currentCtx = ctx;
|
|
376
388
|
requestQueueRender(rt);
|
|
389
|
+
dispatchSignalEvent(rt, config.appearance, "compact");
|
|
377
390
|
});
|
|
378
391
|
|
|
379
392
|
pi.on("session_compact", async (event, ctx) => {
|
|
@@ -406,6 +419,12 @@ export function registerSessionLifecycle(
|
|
|
406
419
|
// Also dismiss on tool calls (agent is working) + refresh vibe if rate limit allows
|
|
407
420
|
pi.on("tool_call", async (event, ctx) => {
|
|
408
421
|
dismissWelcome(rt, ctx);
|
|
422
|
+
dispatchSignalEvent(
|
|
423
|
+
rt,
|
|
424
|
+
config.appearance,
|
|
425
|
+
"tool.start",
|
|
426
|
+
`tool ${event.toolName}`,
|
|
427
|
+
);
|
|
409
428
|
if (ctx.hasUI) {
|
|
410
429
|
// Extract recent agent context from session for richer vibe generation
|
|
411
430
|
const agentContext = getRecentAgentContext(ctx);
|
|
@@ -422,6 +441,7 @@ export function registerSessionLifecycle(
|
|
|
422
441
|
rt.isStreaming = false;
|
|
423
442
|
rt.liveAssistantUsage = null;
|
|
424
443
|
rt.coreContextUsageCache.reset();
|
|
444
|
+
dispatchSignalEvent(rt, config.appearance, "success");
|
|
425
445
|
|
|
426
446
|
let hasUI = false;
|
|
427
447
|
try {
|