pi-cc-extensions 0.3.0 → 0.5.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.
Files changed (30) hide show
  1. package/README.md +1 -18
  2. package/extensions/claude-code-style.ts +194 -41
  3. package/package.json +2 -3
  4. package/assets/readme/showcase.webp +0 -0
  5. package/assets/readme/welcome.webp +0 -0
  6. package/extensions/ccstyle-powerline/bash-mode/completion.ts +0 -556
  7. package/extensions/ccstyle-powerline/bash-mode/editor.ts +0 -442
  8. package/extensions/ccstyle-powerline/bash-mode/history.ts +0 -151
  9. package/extensions/ccstyle-powerline/bash-mode/shell-session.ts +0 -286
  10. package/extensions/ccstyle-powerline/bash-mode/transcript.ts +0 -108
  11. package/extensions/ccstyle-powerline/bash-mode/types.ts +0 -59
  12. package/extensions/ccstyle-powerline/colors.ts +0 -69
  13. package/extensions/ccstyle-powerline/context-usage.ts +0 -46
  14. package/extensions/ccstyle-powerline/fixed-editor/cluster.ts +0 -107
  15. package/extensions/ccstyle-powerline/fixed-editor/terminal-split.ts +0 -1389
  16. package/extensions/ccstyle-powerline/git-status.ts +0 -283
  17. package/extensions/ccstyle-powerline/icons.ts +0 -181
  18. package/extensions/ccstyle-powerline/powerline-config.ts +0 -234
  19. package/extensions/ccstyle-powerline/powerline.ts +0 -3218
  20. package/extensions/ccstyle-powerline/presets.ts +0 -109
  21. package/extensions/ccstyle-powerline/render-scheduler.ts +0 -24
  22. package/extensions/ccstyle-powerline/segments.ts +0 -486
  23. package/extensions/ccstyle-powerline/separators.ts +0 -41
  24. package/extensions/ccstyle-powerline/shortcuts.ts +0 -57
  25. package/extensions/ccstyle-powerline/theme.json +0 -26
  26. package/extensions/ccstyle-powerline/theme.ts +0 -241
  27. package/extensions/ccstyle-powerline/types.ts +0 -200
  28. package/extensions/ccstyle-powerline/welcome-dismiss.ts +0 -34
  29. package/extensions/ccstyle-powerline/welcome.ts +0 -611
  30. package/extensions/ccstyle-powerline/working-vibes.ts +0 -695
@@ -1,57 +0,0 @@
1
- import { matchesKey } from "@earendil-works/pi-tui";
2
-
3
- const CTRL_NAVIGATION_SHORTCUT_PATTERNS = new Map<string, RegExp>([
4
- // xterm/Windows Terminal, Kitty keyboard protocol, and modifyOtherKeys.
5
- ["ctrl+up", /^\x1b\[(?:1;5(?::[12])?A|57419;5(?::[12])?u|27;5;65~)$/],
6
- ["ctrl+down", /^\x1b\[(?:1;5(?::[12])?B|57420;5(?::[12])?u|27;5;66~)$/],
7
- ["ctrl+end", /^\x1b\[(?:1;5(?::[12])?F|4;5(?::[12])?~|57424;5(?::[12])?u|27;5;70~)$/],
8
- ]);
9
-
10
- const SUPER_SHORTCUT_PATTERNS = new Map<string, RegExp>([
11
- ["super+up", /^\x1b\[(?:1;9(?::[12])?[AH]|574(?:19|23);9(?::[12])?u|7;9(?::[12])?~|27;9;65~)$/],
12
- ["super+down", /^\x1b\[(?:1;9(?::[12])?[BF]|574(?:20|24);9(?::[12])?u|8;9(?::[12])?~|27;9;66~)$/],
13
- ["super+home", /^\x1b\[(?:1;9(?::[12])?H|57423;9(?::[12])?u|7;9(?::[12])?~)$/],
14
- ["super+end", /^\x1b\[(?:1;9(?::[12])?F|57424;9(?::[12])?u|8;9(?::[12])?~)$/],
15
- ["super+pageup", /^\x1b\[(?:5;9(?::[12])?~|57421;9(?::[12])?u)$/],
16
- ["super+pagedown", /^\x1b\[(?:6;9(?::[12])?~|57422;9(?::[12])?u)$/],
17
- ["super+shift+up", /^\x1b\[(?:1;10(?::[12])?[AH]|574(?:19|23);10(?::[12])?u|7;10(?::[12])?~|27;10;65~)$/],
18
- ["super+shift+down", /^\x1b\[(?:1;10(?::[12])?[BF]|574(?:20|24);10(?::[12])?u|8;10(?::[12])?~|27;10;66~)$/],
19
- ["super+shift+home", /^\x1b\[(?:1;10(?::[12])?H|57423;10(?::[12])?u|7;10(?::[12])?~)$/],
20
- ["super+shift+end", /^\x1b\[(?:1;10(?::[12])?F|57424;10(?::[12])?u|8;10(?::[12])?~)$/],
21
- ]);
22
-
23
- export function shortcutUsesSuper(shortcut: string): boolean {
24
- const parts = shortcut.toLowerCase().split("+");
25
- return parts.slice(0, -1).includes("super");
26
- }
27
-
28
- export function isSupportedSuperShortcut(shortcut: string): boolean {
29
- return SUPER_SHORTCUT_PATTERNS.has(shortcut.toLowerCase());
30
- }
31
-
32
- export function shortcutConflictKey(shortcut: string): string {
33
- switch (shortcut.toLowerCase()) {
34
- case "super+home":
35
- return "super+up";
36
- case "super+end":
37
- return "super+down";
38
- case "super+shift+home":
39
- return "super+shift+up";
40
- case "super+shift+end":
41
- return "super+shift+down";
42
- default:
43
- return shortcut;
44
- }
45
- }
46
-
47
- export function matchesConfiguredShortcut(data: string, shortcut: string): boolean {
48
- const normalizedShortcut = shortcut.toLowerCase();
49
- if (shortcutUsesSuper(normalizedShortcut)) {
50
- return SUPER_SHORTCUT_PATTERNS.get(normalizedShortcut)?.test(data) ?? false;
51
- }
52
-
53
- // Some Windows terminal/protocol combinations are not normalized by
54
- // pi-tui's matchesKey(), so recognize raw Ctrl+navigation encodings too.
55
- if (CTRL_NAVIGATION_SHORTCUT_PATTERNS.get(normalizedShortcut)?.test(data)) return true;
56
- return matchesKey(data, normalizedShortcut);
57
- }
@@ -1,26 +0,0 @@
1
- {
2
- "colors": {
3
- "model": "#3794FF",
4
- "shellMode": "#4FC1FF",
5
- "path": "#4EC9B0",
6
- "gitDirty": "#F48771",
7
- "gitClean": "#89D185",
8
- "thinking": "#808080",
9
- "thinkingMinimal": "#064675",
10
- "thinkingLow": "#125489",
11
- "thinkingMedium": "#1F5F99",
12
- "thinkingHigh": "#3F87C5",
13
- "thinkingXhigh": "#66A9DC",
14
- "thinkingMax": "#8FC5F0",
15
- "context": "#808080",
16
- "contextWarn": "#DCDCAA",
17
- "contextError": "#F14C4C",
18
- "cost": "#CE9178",
19
- "tokens": "#9CDCFE",
20
- "separator": "#6A6A6A",
21
- "border": "#3E3E42"
22
- },
23
- "icons": {
24
- "context": ""
25
- }
26
- }
@@ -1,241 +0,0 @@
1
- /**
2
- * Theme system for powerline-footer
3
- *
4
- * Colors are resolved in order:
5
- * 1. User overrides from theme.json (if exists)
6
- * 2. Preset colors
7
- * 3. Default colors
8
- */
9
-
10
- import type { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
11
- import { existsSync, readFileSync } from "node:fs";
12
- import { join, dirname } from "node:path";
13
- import { fileURLToPath } from "node:url";
14
- import type { ColorScheme, ColorValue, SemanticColor, ThemeLike } from "./types.ts";
15
-
16
- export interface PowerlineThemeConfig {
17
- colors?: unknown;
18
- icons?: unknown;
19
- }
20
-
21
- // VS Code Modern Dark-inspired fallback defaults. Native pi themes take
22
- // precedence for thinking-level colors when available.
23
- const DEFAULT_COLORS: Required<ColorScheme> = {
24
- model: "#3794ff",
25
- shellMode: "#4fc1ff",
26
- path: "#4ec9b0",
27
- gitDirty: "#f48771",
28
- gitClean: "#89d185",
29
- thinking: "#808080",
30
- thinkingOff: "#808080",
31
- thinkingMinimal: "#064675",
32
- thinkingLow: "#125489",
33
- thinkingMedium: "#1f5f99",
34
- thinkingHigh: "#3f87c5",
35
- thinkingXhigh: "#66a9dc",
36
- thinkingMax: "#8fc5f0",
37
- context: "#808080",
38
- contextWarn: "#dcdcaa",
39
- contextError: "#f14c4c",
40
- cost: "#ce9178",
41
- tokens: "#9cdcfe",
42
- separator: "#6a6a6a",
43
- border: "#3e3e42",
44
- };
45
-
46
- // Cache for user theme overrides
47
- let userThemeCache: ColorScheme | null = null;
48
- let userThemeCacheTime = 0;
49
- let themeConfigCache: PowerlineThemeConfig | null = null;
50
- let themeConfigCacheTime = 0;
51
- const CACHE_TTL = 5000; // 5 seconds
52
- const warnedInvalidThemeColors = new Set<string>();
53
-
54
- /** Force the next render to reload theme.json, including after Pi /reload. */
55
- export function invalidateThemeCache(): void {
56
- userThemeCache = null;
57
- userThemeCacheTime = 0;
58
- themeConfigCache = null;
59
- themeConfigCacheTime = 0;
60
- }
61
-
62
- function isRecord(value: unknown): value is Record<string, unknown> {
63
- return typeof value === "object" && value !== null && !Array.isArray(value);
64
- }
65
-
66
- function sanitizeUserThemeOverrides(value: unknown): ColorScheme {
67
- if (!isRecord(value)) {
68
- return {};
69
- }
70
-
71
- const sanitized: ColorScheme = {};
72
- for (const [key, rawColor] of Object.entries(value)) {
73
- if (!Object.prototype.hasOwnProperty.call(DEFAULT_COLORS, key)) {
74
- continue;
75
- }
76
- if (typeof rawColor !== "string") {
77
- continue;
78
- }
79
-
80
- const color = rawColor.trim();
81
- if (!color) {
82
- continue;
83
- }
84
-
85
- sanitized[key as SemanticColor] = color as ColorValue;
86
- }
87
-
88
- return sanitized;
89
- }
90
-
91
- /**
92
- * Get the path to the theme.json file
93
- */
94
- function getThemePath(): string {
95
- const extDir = dirname(fileURLToPath(import.meta.url));
96
- return join(extDir, "theme.json");
97
- }
98
-
99
- /**
100
- * Load user theme config from theme.json
101
- */
102
- export function loadThemeConfig(): PowerlineThemeConfig {
103
- const now = Date.now();
104
- if (themeConfigCache && now - themeConfigCacheTime < CACHE_TTL) {
105
- return themeConfigCache;
106
- }
107
-
108
- const themePath = getThemePath();
109
- try {
110
- if (existsSync(themePath)) {
111
- const content = readFileSync(themePath, "utf-8");
112
- const parsed = JSON.parse(content);
113
- themeConfigCache = isRecord(parsed) ? parsed : {};
114
- themeConfigCacheTime = now;
115
- return themeConfigCache;
116
- }
117
- } catch (error) {
118
- // Theme overrides are optional. If the file is unreadable or malformed,
119
- // keep rendering with built-in defaults instead of breaking the footer.
120
- console.debug(`[powerline-theme] Failed to load ${themePath}:`, error);
121
- }
122
-
123
- themeConfigCache = {};
124
- themeConfigCacheTime = now;
125
- return themeConfigCache;
126
- }
127
-
128
- function loadUserTheme(): ColorScheme {
129
- const now = Date.now();
130
- if (userThemeCache && now - userThemeCacheTime < CACHE_TTL) {
131
- return userThemeCache;
132
- }
133
-
134
- userThemeCache = sanitizeUserThemeOverrides(loadThemeConfig().colors);
135
- userThemeCacheTime = now;
136
- return userThemeCache;
137
- }
138
-
139
- /**
140
- * Resolve a semantic color to an actual color value
141
- */
142
- export function resolveColor(
143
- semantic: SemanticColor,
144
- presetColors?: ColorScheme
145
- ): ColorValue {
146
- const userTheme = loadUserTheme();
147
-
148
- // Priority: user overrides > preset colors > defaults
149
- return userTheme[semantic]
150
- ?? presetColors?.[semantic]
151
- ?? DEFAULT_COLORS[semantic];
152
- }
153
-
154
- /**
155
- * Check if a color value is a hex color
156
- */
157
- function isHexColor(color: ColorValue): color is `#${string}` {
158
- return typeof color === "string" && /^#[0-9a-fA-F]{6}$/.test(color);
159
- }
160
-
161
- /**
162
- * Convert hex color to ANSI escape code
163
- */
164
- function hexToAnsi(hex: string): string {
165
- const h = hex.replace("#", "");
166
- const r = parseInt(h.slice(0, 2), 16);
167
- const g = parseInt(h.slice(2, 4), 16);
168
- const b = parseInt(h.slice(4, 6), 16);
169
- return `\x1b[38;2;${r};${g};${b}m`;
170
- }
171
-
172
- /**
173
- * Apply a color to text using the pi theme or custom hex
174
- */
175
- export function applyColor(
176
- theme: ThemeLike,
177
- color: ColorValue,
178
- text: string
179
- ): string {
180
- if (isHexColor(color)) {
181
- return `${hexToAnsi(color)}${text}\x1b[0m`;
182
- }
183
-
184
- try {
185
- return theme.fg(color as ThemeColor, text);
186
- } catch (error) {
187
- const key = String(color);
188
- if (!warnedInvalidThemeColors.has(key)) {
189
- warnedInvalidThemeColors.add(key);
190
- if (warnedInvalidThemeColors.size > 200) {
191
- warnedInvalidThemeColors.clear();
192
- }
193
- console.debug(`[powerline-theme] Invalid theme color "${key}"; falling back to "text".`, error);
194
- }
195
- return theme.fg("text", text);
196
- }
197
- }
198
-
199
- /**
200
- * Apply a semantic color to text
201
- */
202
- export function fg(
203
- theme: ThemeLike,
204
- semantic: SemanticColor,
205
- text: string,
206
- presetColors?: ColorScheme
207
- ): string {
208
- const color = resolveColor(semantic, presetColors);
209
- return applyColor(theme, color, text);
210
- }
211
-
212
- const THINKING_SEMANTIC_COLORS: Record<string, SemanticColor> = {
213
- off: "thinkingOff",
214
- minimal: "thinkingMinimal",
215
- low: "thinkingLow",
216
- medium: "thinkingMedium",
217
- high: "thinkingHigh",
218
- xhigh: "thinkingXhigh",
219
- max: "thinkingMax",
220
- };
221
-
222
- /**
223
- * Apply the extension's monotonic blue scale so stronger thinking levels
224
- * become brighter and more prominent without switching to unrelated hues.
225
- */
226
- export function colorThinking(
227
- theme: ThemeLike,
228
- level: string,
229
- text: string,
230
- presetColors?: ColorScheme,
231
- ): string {
232
- const semantic = THINKING_SEMANTIC_COLORS[level] ?? "thinkingOff";
233
- return fg(theme, semantic, text, presetColors);
234
- }
235
-
236
- /**
237
- * Get the default color scheme
238
- */
239
- export function getDefaultColors(): Required<ColorScheme> {
240
- return { ...DEFAULT_COLORS };
241
- }
@@ -1,200 +0,0 @@
1
- import type { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
2
-
3
- // Theme color - either a pi theme color name or a custom hex color
4
- export type ColorValue = ThemeColor | `#${string}`;
5
- export type ThemeLike = Pick<Theme, "fg">;
6
-
7
- // Semantic color names for segments
8
- export type SemanticColor =
9
- | "model"
10
- | "shellMode"
11
- | "path"
12
- | "gitDirty"
13
- | "gitClean"
14
- | "thinking"
15
- | "thinkingOff"
16
- | "thinkingMinimal"
17
- | "thinkingLow"
18
- | "thinkingMedium"
19
- | "thinkingHigh"
20
- | "thinkingXhigh"
21
- | "thinkingMax"
22
- | "context"
23
- | "contextWarn"
24
- | "contextError"
25
- | "cost"
26
- | "tokens"
27
- | "separator"
28
- | "border";
29
-
30
- // Color scheme mapping semantic names to actual colors
31
- export type ColorScheme = Partial<Record<SemanticColor, ColorValue>>;
32
-
33
- // Built-in segment identifiers
34
- export type BuiltinStatusLineSegmentId =
35
- | "model"
36
- | "shell_mode"
37
- | "path"
38
- | "git"
39
- | "subagents"
40
- | "token_in"
41
- | "token_out"
42
- | "token_total"
43
- | "cost"
44
- | "context_pct"
45
- | "context_total"
46
- | "time_spent"
47
- | "time"
48
- | "session"
49
- | "hostname"
50
- | "cache_read"
51
- | "cache_write"
52
- | "thinking"
53
- | "extension_statuses";
54
-
55
- // Segment identifiers (built-in + dynamically registered custom items)
56
- export type StatusLineSegmentId = BuiltinStatusLineSegmentId | `custom:${string}`;
57
-
58
- // Separator styles
59
- export type StatusLineSeparatorStyle =
60
- | "powerline"
61
- | "powerline-thin"
62
- | "slash"
63
- | "pipe"
64
- | "block"
65
- | "none"
66
- | "ascii"
67
- | "dot"
68
- | "chevron"
69
- | "star";
70
-
71
- // Preset names
72
- export type StatusLinePreset =
73
- | "default"
74
- | "minimal"
75
- | "compact"
76
- | "full"
77
- | "nerd"
78
- | "ascii"
79
- | "custom";
80
-
81
- // Per-segment options
82
- export interface StatusLineSegmentOptions {
83
- model?: { showThinkingLevel?: boolean };
84
- path?: {
85
- mode?: "basename" | "abbreviated" | "full";
86
- maxLength?: number;
87
- };
88
- git?: {
89
- showBranch?: boolean;
90
- showStaged?: boolean;
91
- showUnstaged?: boolean;
92
- showUntracked?: boolean;
93
- showLines?: boolean;
94
- polling?: "full" | "branch" | "off";
95
- };
96
- time?: { format?: "12h" | "24h"; showSeconds?: boolean };
97
- }
98
-
99
- export type CustomItemPosition = "left" | "right" | "secondary";
100
-
101
- export interface CustomStatusItem {
102
- id: string;
103
- statusKey: string;
104
- position: CustomItemPosition;
105
- color?: ColorValue;
106
- prefix?: string;
107
- hideWhenMissing: boolean;
108
- excludeFromExtensionStatuses: boolean;
109
- }
110
-
111
- // Preset definition
112
- export interface PresetDef {
113
- leftSegments: BuiltinStatusLineSegmentId[];
114
- rightSegments: BuiltinStatusLineSegmentId[];
115
- /** Secondary row segments (shown in footer, above sub bar) */
116
- secondarySegments?: BuiltinStatusLineSegmentId[];
117
- separator: StatusLineSeparatorStyle;
118
- segmentOptions?: StatusLineSegmentOptions;
119
- /** Color scheme for this preset */
120
- colors?: ColorScheme;
121
- }
122
-
123
- // Separator definition
124
- export interface SeparatorDef {
125
- left: string;
126
- right: string;
127
- endCaps?: {
128
- left: string;
129
- right: string;
130
- useBgAsFg: boolean;
131
- };
132
- }
133
-
134
- // Git status data
135
- export interface GitStatus {
136
- branch: string | null;
137
- staged: number;
138
- unstaged: number;
139
- untracked: number;
140
- added: number;
141
- deleted: number;
142
- }
143
-
144
- // Usage statistics
145
- export interface UsageStats {
146
- input: number;
147
- output: number;
148
- cacheRead: number;
149
- cacheWrite: number;
150
- cost: number;
151
- }
152
-
153
- // Context passed to segment render functions
154
- export interface SegmentContext {
155
- // From pi-mono
156
- model: { id: string; name?: string; reasoning?: boolean; contextWindow?: number } | undefined;
157
- thinkingLevel: string;
158
- sessionId: string | undefined;
159
- cwd?: string;
160
-
161
- // Computed
162
- usageStats: UsageStats;
163
- contextPercent: number | null;
164
- contextWindow: number;
165
- autoCompactEnabled: boolean;
166
- customCompactionEnabled: boolean;
167
- usingSubscription: boolean;
168
- sessionStartTime: number;
169
- shellModeActive: boolean;
170
- shellRunning: boolean;
171
- shellName: string | null;
172
- shellCwd: string | null;
173
-
174
- // Git
175
- git: GitStatus;
176
-
177
- // Extension statuses
178
- extensionStatuses: ReadonlyMap<string, string>;
179
- hiddenExtensionStatusKeys: ReadonlySet<string>;
180
- customItemsById: ReadonlyMap<string, CustomStatusItem>;
181
-
182
- // Options
183
- options: StatusLineSegmentOptions;
184
-
185
- // Theming
186
- theme: ThemeLike;
187
- colors: ColorScheme;
188
- }
189
-
190
- // Rendered segment output
191
- export interface RenderedSegment {
192
- content: string;
193
- visible: boolean;
194
- }
195
-
196
- // Segment definition
197
- export interface StatusLineSegment {
198
- id: BuiltinStatusLineSegmentId;
199
- render(ctx: SegmentContext): RenderedSegment;
200
- }
@@ -1,34 +0,0 @@
1
- export interface WelcomeDismissScheduler<Context> {
2
- schedule(ctx: Context): void;
3
- cancel(): void;
4
- }
5
-
6
- interface WelcomeDismissSchedulerOptions<Context> {
7
- dismiss(ctx: Context): void;
8
- getGeneration(): number;
9
- isEnabled(): boolean;
10
- }
11
-
12
- export function createWelcomeDismissScheduler<Context>(
13
- options: WelcomeDismissSchedulerOptions<Context>,
14
- ): WelcomeDismissScheduler<Context> {
15
- let timer: ReturnType<typeof setTimeout> | null = null;
16
-
17
- return {
18
- schedule(ctx) {
19
- if (timer) return;
20
-
21
- const generation = options.getGeneration();
22
- timer = setTimeout(() => {
23
- timer = null;
24
- if (!options.isEnabled() || generation !== options.getGeneration()) return;
25
- options.dismiss(ctx);
26
- }, 0);
27
- },
28
- cancel() {
29
- if (!timer) return;
30
- clearTimeout(timer);
31
- timer = null;
32
- },
33
- };
34
- }