rinco-pi-hud 1.0.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/LICENSE +21 -0
- package/NOTICE +18 -0
- package/README.md +204 -0
- package/docs/CONTRIBUTING.md +110 -0
- package/docs/README.zh-CN.md +204 -0
- package/docs/footer.md +139 -0
- package/extensions/hud/commands/settings.ts +765 -0
- package/extensions/hud/config/config-counts.ts +87 -0
- package/extensions/hud/config/config.ts +65 -0
- package/extensions/hud/config/file-io.ts +128 -0
- package/extensions/hud/config/normalize.ts +374 -0
- package/extensions/hud/config/persist.ts +277 -0
- package/extensions/hud/config/schema.ts +303 -0
- package/extensions/hud/footer/format.ts +279 -0
- package/extensions/hud/footer/index.ts +543 -0
- package/extensions/hud/footer/layout.ts +183 -0
- package/extensions/hud/footer/render-labels.ts +305 -0
- package/extensions/hud/index.ts +466 -0
- package/extensions/hud/segments/extension-status.ts +88 -0
- package/extensions/hud/segments/git.ts +384 -0
- package/extensions/hud/segments/mcp-status.ts +25 -0
- package/extensions/hud/segments/package-version.ts +651 -0
- package/extensions/hud/segments/project-state.ts +67 -0
- package/extensions/hud/segments/runtime-definitions.ts +695 -0
- package/extensions/hud/segments/runtime.ts +167 -0
- package/extensions/hud/segments/skill-activity.ts +152 -0
- package/extensions/hud/session/context.ts +19 -0
- package/extensions/hud/session/lifecycle.ts +60 -0
- package/extensions/hud/session/live-context.ts +75 -0
- package/extensions/hud/state/index.ts +67 -0
- package/extensions/hud/state/project-refresh.ts +104 -0
- package/extensions/hud/state/telemetry.ts +261 -0
- package/extensions/hud/telemetry/codex-usage/LICENSE +21 -0
- package/extensions/hud/telemetry/codex-usage/app-server-client.ts +231 -0
- package/extensions/hud/telemetry/codex-usage/format.ts +278 -0
- package/extensions/hud/telemetry/codex-usage/index.ts +462 -0
- package/extensions/hud/telemetry/codex-usage/normalize.ts +264 -0
- package/extensions/hud/telemetry/codex-usage/query.ts +207 -0
- package/extensions/hud/telemetry/codex-usage/safety.ts +27 -0
- package/extensions/hud/telemetry/codex-usage/types.ts +137 -0
- package/extensions/hud/telemetry/format.ts +434 -0
- package/extensions/hud/telemetry/token-switch.ts +114 -0
- package/extensions/hud/ui/icons.ts +239 -0
- package/extensions/hud/ui/style.ts +276 -0
- package/package.json +56 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon mode defaults and resolvers.
|
|
3
|
+
*
|
|
4
|
+
* Nerd defaults must stay byte-identical to historical `defaultConfig.icons`,
|
|
5
|
+
* except where intentionally changed to match the Starship Nerd Font preset.
|
|
6
|
+
* User string overrides always win over mode defaults.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type IconMode = "auto" | "nerd" | "ascii";
|
|
10
|
+
|
|
11
|
+
export type IconGlyphs = {
|
|
12
|
+
cwd: string;
|
|
13
|
+
git: string;
|
|
14
|
+
ahead: string;
|
|
15
|
+
behind: string;
|
|
16
|
+
diverged: string;
|
|
17
|
+
conflicted: string;
|
|
18
|
+
untracked: string;
|
|
19
|
+
stashed: string;
|
|
20
|
+
modified: string;
|
|
21
|
+
staged: string;
|
|
22
|
+
renamed: string;
|
|
23
|
+
deleted: string;
|
|
24
|
+
typechanged: string;
|
|
25
|
+
cacheHit: string;
|
|
26
|
+
username: string;
|
|
27
|
+
time: string;
|
|
28
|
+
os: string;
|
|
29
|
+
package: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type ResolvedIcons = IconGlyphs & { mode: IconMode };
|
|
33
|
+
|
|
34
|
+
export const ICON_GLYPH_KEYS = [
|
|
35
|
+
"cwd",
|
|
36
|
+
"git",
|
|
37
|
+
"ahead",
|
|
38
|
+
"behind",
|
|
39
|
+
"diverged",
|
|
40
|
+
"conflicted",
|
|
41
|
+
"untracked",
|
|
42
|
+
"stashed",
|
|
43
|
+
"modified",
|
|
44
|
+
"staged",
|
|
45
|
+
"renamed",
|
|
46
|
+
"deleted",
|
|
47
|
+
"typechanged",
|
|
48
|
+
"cacheHit",
|
|
49
|
+
"username",
|
|
50
|
+
"time",
|
|
51
|
+
"os",
|
|
52
|
+
"package",
|
|
53
|
+
] as const satisfies readonly (keyof IconGlyphs)[];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Nerd Font defaults.
|
|
57
|
+
*
|
|
58
|
+
* The `cwd` icon is intentionally empty — Starship's `directory` module
|
|
59
|
+
* has no default symbol. Other defaults match historical values; new
|
|
60
|
+
* additions (e.g. `package`) come from the Starship Nerd Font preset
|
|
61
|
+
* (https://starship.rs/presets/nerd-font).
|
|
62
|
+
*/
|
|
63
|
+
export const NERD_DEFAULT_ICONS: IconGlyphs = {
|
|
64
|
+
cwd: "",
|
|
65
|
+
git: "",
|
|
66
|
+
ahead: "↑",
|
|
67
|
+
behind: "↓",
|
|
68
|
+
diverged: "⇕",
|
|
69
|
+
conflicted: "=",
|
|
70
|
+
untracked: "?",
|
|
71
|
+
stashed: "$",
|
|
72
|
+
modified: "!",
|
|
73
|
+
staged: "+",
|
|
74
|
+
renamed: "»",
|
|
75
|
+
deleted: "✘",
|
|
76
|
+
typechanged: "T",
|
|
77
|
+
cacheHit: "",
|
|
78
|
+
username: "",
|
|
79
|
+
time: "",
|
|
80
|
+
os: "",
|
|
81
|
+
// Starship Nerd Font preset — `package` module glyph.
|
|
82
|
+
package: "",
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const ASCII_DEFAULT_ICONS: IconGlyphs = {
|
|
86
|
+
cwd: "",
|
|
87
|
+
git: "*",
|
|
88
|
+
ahead: "^",
|
|
89
|
+
behind: "v",
|
|
90
|
+
diverged: "^v",
|
|
91
|
+
conflicted: "=",
|
|
92
|
+
untracked: "?",
|
|
93
|
+
stashed: "$",
|
|
94
|
+
modified: "!",
|
|
95
|
+
staged: "+",
|
|
96
|
+
renamed: ">",
|
|
97
|
+
deleted: "x",
|
|
98
|
+
typechanged: "T",
|
|
99
|
+
cacheHit: "c",
|
|
100
|
+
username: "@",
|
|
101
|
+
time: "t",
|
|
102
|
+
os: "o",
|
|
103
|
+
package: "pkg",
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const OS_PLATFORM_ICONS_NERD: Record<string, string> = {
|
|
107
|
+
darwin: "\uf179",
|
|
108
|
+
linux: "\uf17c",
|
|
109
|
+
win32: "\uf17a",
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const OS_PLATFORM_ICONS_ASCII: Record<string, string> = {
|
|
113
|
+
darwin: "mac",
|
|
114
|
+
linux: "linux",
|
|
115
|
+
win32: "win",
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/** Short ASCII labels keyed by runtime `name`. */
|
|
119
|
+
export const RUNTIME_ASCII_SYMBOLS: Record<string, string> = {
|
|
120
|
+
xmake: "xm",
|
|
121
|
+
maven: "mvn",
|
|
122
|
+
gradle: "grd",
|
|
123
|
+
bun: "bun",
|
|
124
|
+
deno: "deno",
|
|
125
|
+
lua: "lua",
|
|
126
|
+
nodejs: "node",
|
|
127
|
+
python: "py",
|
|
128
|
+
golang: "go",
|
|
129
|
+
rust: "rs",
|
|
130
|
+
java: "java",
|
|
131
|
+
ruby: "rb",
|
|
132
|
+
php: "php",
|
|
133
|
+
buf: "buf",
|
|
134
|
+
cmake: "cmake",
|
|
135
|
+
cpp: "c++",
|
|
136
|
+
c: "c",
|
|
137
|
+
cobol: "cob",
|
|
138
|
+
conda: "conda",
|
|
139
|
+
crystal: "cr",
|
|
140
|
+
dart: "dart",
|
|
141
|
+
dotnet: ".net",
|
|
142
|
+
elixir: "ex",
|
|
143
|
+
elm: "elm",
|
|
144
|
+
erlang: "erl",
|
|
145
|
+
fennel: "fnl",
|
|
146
|
+
fortran: "f90",
|
|
147
|
+
gleam: "glm",
|
|
148
|
+
guix_shell: "guix",
|
|
149
|
+
haskell: "hs",
|
|
150
|
+
haxe: "hx",
|
|
151
|
+
helm: "helm",
|
|
152
|
+
julia: "jl",
|
|
153
|
+
kotlin: "kt",
|
|
154
|
+
meson: "meson",
|
|
155
|
+
mojo: "mojo",
|
|
156
|
+
nim: "nim",
|
|
157
|
+
nix_shell: "nix",
|
|
158
|
+
ocaml: "ml",
|
|
159
|
+
odin: "odin",
|
|
160
|
+
opa: "opa",
|
|
161
|
+
perl: "pl",
|
|
162
|
+
pixi: "pixi",
|
|
163
|
+
pulumi: "pul",
|
|
164
|
+
purescript: "purs",
|
|
165
|
+
raku: "raku",
|
|
166
|
+
red: "red",
|
|
167
|
+
rlang: "R",
|
|
168
|
+
scala: "scala",
|
|
169
|
+
solidity: "sol",
|
|
170
|
+
spack: "spack",
|
|
171
|
+
swift: "swift",
|
|
172
|
+
terraform: "tf",
|
|
173
|
+
typst: "typ",
|
|
174
|
+
vagrant: "vag",
|
|
175
|
+
vlang: "v",
|
|
176
|
+
zig: "zig",
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export function isIconMode(value: unknown): value is IconMode {
|
|
180
|
+
return value === "auto" || value === "nerd" || value === "ascii";
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function normalizeIconMode(value: unknown): IconMode {
|
|
184
|
+
return isIconMode(value) ? value : "auto";
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function modeDefaultIcons(mode: IconMode): IconGlyphs {
|
|
188
|
+
return mode === "ascii" ? { ...ASCII_DEFAULT_ICONS } : { ...NERD_DEFAULT_ICONS };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function resolveConfiguredIcons(
|
|
192
|
+
mode: IconMode,
|
|
193
|
+
overrides: Partial<IconGlyphs> = {},
|
|
194
|
+
): ResolvedIcons {
|
|
195
|
+
const base = modeDefaultIcons(mode);
|
|
196
|
+
return {
|
|
197
|
+
mode,
|
|
198
|
+
...base,
|
|
199
|
+
...overrides,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Honor a custom `icons.os` when it differs from the mode default.
|
|
205
|
+
* Otherwise map by platform for the active mode.
|
|
206
|
+
*/
|
|
207
|
+
export function resolveOsIcon(
|
|
208
|
+
configuredOsIcon: string,
|
|
209
|
+
mode: IconMode = "auto",
|
|
210
|
+
platform: string = process.platform,
|
|
211
|
+
): string {
|
|
212
|
+
const modeDefault = modeDefaultIcons(mode).os;
|
|
213
|
+
if (configuredOsIcon !== modeDefault) return configuredOsIcon;
|
|
214
|
+
const platformMap = mode === "ascii" ? OS_PLATFORM_ICONS_ASCII : OS_PLATFORM_ICONS_NERD;
|
|
215
|
+
return platformMap[platform] ?? configuredOsIcon;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function resolveRuntimeSymbol(
|
|
219
|
+
name: string,
|
|
220
|
+
nerdSymbol: string,
|
|
221
|
+
mode: IconMode = "auto",
|
|
222
|
+
): string {
|
|
223
|
+
if (mode !== "ascii") return nerdSymbol;
|
|
224
|
+
return RUNTIME_ASCII_SYMBOLS[name] ?? (name.slice(0, 3) || "*");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Resolve the package-version segment icon for the active mode.
|
|
229
|
+
*
|
|
230
|
+
* Honors a configured `icons.package` override; otherwise falls back to the
|
|
231
|
+
* mode default (Nerd Font preset / ASCII label).
|
|
232
|
+
*/
|
|
233
|
+
export function resolvePackageIcon(configuredPackageIcon: string, mode: IconMode = "auto"): string {
|
|
234
|
+
const modeDefault = modeDefaultIcons(mode).package;
|
|
235
|
+
if (typeof configuredPackageIcon === "string" && configuredPackageIcon.length > 0) {
|
|
236
|
+
return configuredPackageIcon;
|
|
237
|
+
}
|
|
238
|
+
return modeDefault;
|
|
239
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import type { ThemeColor } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { ColorSource, ColorSpec } from "../config/config.js";
|
|
3
|
+
|
|
4
|
+
type ThemeLike = {
|
|
5
|
+
fg(color: string, text: string): string;
|
|
6
|
+
bold?: (text: string) => string;
|
|
7
|
+
italic?: (text: string) => string;
|
|
8
|
+
underline?: (text: string) => string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type { ThemeLike };
|
|
12
|
+
|
|
13
|
+
function isHexColor(value: string): boolean {
|
|
14
|
+
return /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function expandHexColor(hex: string): string {
|
|
18
|
+
const body = hex.slice(1);
|
|
19
|
+
if (body.length === 3) {
|
|
20
|
+
return body
|
|
21
|
+
.split("")
|
|
22
|
+
.map((ch) => ch + ch)
|
|
23
|
+
.join("");
|
|
24
|
+
}
|
|
25
|
+
return body;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function hexToAnsi(hex: string, isBackground = false): string {
|
|
29
|
+
const normalized = expandHexColor(hex);
|
|
30
|
+
const r = Number.parseInt(normalized.slice(0, 2), 16);
|
|
31
|
+
const g = Number.parseInt(normalized.slice(2, 4), 16);
|
|
32
|
+
const b = Number.parseInt(normalized.slice(4, 6), 16);
|
|
33
|
+
return `\x1b[${isBackground ? 48 : 38};2;${r};${g};${b}m`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const terminalColorCodes = new Map([
|
|
37
|
+
["black", 30],
|
|
38
|
+
["red", 31],
|
|
39
|
+
["green", 32],
|
|
40
|
+
["yellow", 33],
|
|
41
|
+
["blue", 34],
|
|
42
|
+
["purple", 35],
|
|
43
|
+
["cyan", 36],
|
|
44
|
+
["white", 37],
|
|
45
|
+
["bright-black", 90],
|
|
46
|
+
["bright-red", 91],
|
|
47
|
+
["bright-green", 92],
|
|
48
|
+
["bright-yellow", 93],
|
|
49
|
+
["bright-blue", 94],
|
|
50
|
+
["bright-purple", 95],
|
|
51
|
+
["bright-cyan", 96],
|
|
52
|
+
["bright-white", 97],
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
const terminalStyleModifiers = new Map([
|
|
56
|
+
["bold", 1],
|
|
57
|
+
["dim", 2],
|
|
58
|
+
["dimmed", 2],
|
|
59
|
+
["italic", 3],
|
|
60
|
+
["underline", 4],
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const themeColorNameMap = new Map([
|
|
64
|
+
["red", "error"],
|
|
65
|
+
["bright-red", "error"],
|
|
66
|
+
["green", "success"],
|
|
67
|
+
["bright-green", "success"],
|
|
68
|
+
["yellow", "warning"],
|
|
69
|
+
["bright-yellow", "warning"],
|
|
70
|
+
["blue", "syntaxFunction"],
|
|
71
|
+
["bright-blue", "syntaxFunction"],
|
|
72
|
+
["cyan", "syntaxFunction"],
|
|
73
|
+
["bright-cyan", "syntaxFunction"],
|
|
74
|
+
["purple", "syntaxKeyword"],
|
|
75
|
+
["bright-purple", "syntaxKeyword"],
|
|
76
|
+
["black", "muted"],
|
|
77
|
+
["bright-black", "muted"],
|
|
78
|
+
["white", "text"],
|
|
79
|
+
["bright-white", "text"],
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
const themeStyleModifiers = new Set(["bold", "italic", "underline"]);
|
|
83
|
+
|
|
84
|
+
const themeColorTokens = new Set<ThemeColor>([
|
|
85
|
+
"accent",
|
|
86
|
+
"border",
|
|
87
|
+
"borderAccent",
|
|
88
|
+
"borderMuted",
|
|
89
|
+
"success",
|
|
90
|
+
"error",
|
|
91
|
+
"warning",
|
|
92
|
+
"muted",
|
|
93
|
+
"dim",
|
|
94
|
+
"text",
|
|
95
|
+
"thinkingText",
|
|
96
|
+
"userMessageText",
|
|
97
|
+
"customMessageText",
|
|
98
|
+
"customMessageLabel",
|
|
99
|
+
"toolTitle",
|
|
100
|
+
"toolOutput",
|
|
101
|
+
"mdHeading",
|
|
102
|
+
"mdLink",
|
|
103
|
+
"mdLinkUrl",
|
|
104
|
+
"mdCode",
|
|
105
|
+
"mdCodeBlock",
|
|
106
|
+
"mdCodeBlockBorder",
|
|
107
|
+
"mdQuote",
|
|
108
|
+
"mdQuoteBorder",
|
|
109
|
+
"mdHr",
|
|
110
|
+
"mdListBullet",
|
|
111
|
+
"toolDiffAdded",
|
|
112
|
+
"toolDiffRemoved",
|
|
113
|
+
"toolDiffContext",
|
|
114
|
+
"syntaxComment",
|
|
115
|
+
"syntaxKeyword",
|
|
116
|
+
"syntaxFunction",
|
|
117
|
+
"syntaxVariable",
|
|
118
|
+
"syntaxString",
|
|
119
|
+
"syntaxNumber",
|
|
120
|
+
"syntaxType",
|
|
121
|
+
"syntaxOperator",
|
|
122
|
+
"syntaxPunctuation",
|
|
123
|
+
"thinkingOff",
|
|
124
|
+
"thinkingMinimal",
|
|
125
|
+
"thinkingLow",
|
|
126
|
+
"thinkingMedium",
|
|
127
|
+
"thinkingHigh",
|
|
128
|
+
"thinkingXhigh",
|
|
129
|
+
"bashMode",
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
function terminalColorToAnsi(color: string, isBackground = false): string | undefined {
|
|
133
|
+
const normalized = color.toLowerCase();
|
|
134
|
+
const colorCode = terminalColorCodes.get(normalized);
|
|
135
|
+
if (colorCode !== undefined) return `${isBackground ? colorCode + 10 : colorCode}`;
|
|
136
|
+
|
|
137
|
+
if (/^(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(normalized)) {
|
|
138
|
+
return `${isBackground ? 48 : 38};5;${normalized}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (isHexColor(normalized)) return hexToAnsi(normalized, isBackground).slice(2, -1);
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function isExplicitTerminalColorToken(token: string): boolean {
|
|
146
|
+
const normalized = token.toLowerCase();
|
|
147
|
+
if (normalized.startsWith("fg:") || normalized.startsWith("bg:")) return true;
|
|
148
|
+
if (isHexColor(normalized)) return true;
|
|
149
|
+
return /^(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(normalized);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function isSupportedStyleToken(token: string): boolean {
|
|
153
|
+
const normalized = token.toLowerCase();
|
|
154
|
+
if (terminalStyleModifiers.has(normalized)) return true;
|
|
155
|
+
if (terminalColorToAnsi(normalized) !== undefined) return true;
|
|
156
|
+
|
|
157
|
+
const isForeground = normalized.startsWith("fg:");
|
|
158
|
+
const isBackground = normalized.startsWith("bg:");
|
|
159
|
+
if (isForeground || isBackground) {
|
|
160
|
+
return terminalColorToAnsi(normalized.slice(3), isBackground) !== undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return themeColorTokens.has(token as ThemeColor);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function isSupportedColorSpec(style: ColorSpec): boolean {
|
|
167
|
+
const trimmed = style.trim();
|
|
168
|
+
if (trimmed === "") return true;
|
|
169
|
+
return trimmed.split(/\s+/).every(isSupportedStyleToken);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function applyThemeModifiers(theme: ThemeLike, styleTokens: string[], text: string): string {
|
|
173
|
+
let rendered = text;
|
|
174
|
+
for (const token of styleTokens) {
|
|
175
|
+
const normalized = token.toLowerCase();
|
|
176
|
+
if (normalized === "bold") rendered = theme.bold?.(rendered) ?? rendered;
|
|
177
|
+
if (normalized === "italic") rendered = theme.italic?.(rendered) ?? rendered;
|
|
178
|
+
if (normalized === "underline") rendered = theme.underline?.(rendered) ?? rendered;
|
|
179
|
+
}
|
|
180
|
+
return rendered;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function safeThemeFg(theme: ThemeLike, color: string, text: string): string {
|
|
184
|
+
try {
|
|
185
|
+
return theme.fg(color, text);
|
|
186
|
+
} catch {
|
|
187
|
+
return text;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function mapThemeColor(styleTokens: string[]): string | undefined {
|
|
192
|
+
let fallback: string | undefined;
|
|
193
|
+
for (const token of styleTokens) {
|
|
194
|
+
const normalized = token.toLowerCase();
|
|
195
|
+
if (themeStyleModifiers.has(normalized)) continue;
|
|
196
|
+
if (normalized === "dim" || normalized === "dimmed") {
|
|
197
|
+
fallback = "muted";
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const mapped = themeColorNameMap.get(normalized);
|
|
202
|
+
if (mapped) return mapped;
|
|
203
|
+
return token;
|
|
204
|
+
}
|
|
205
|
+
return fallback;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Colorize text using a theme color token or hex color.
|
|
210
|
+
* Non-hex values are passed directly to `theme.fg()`; invalid tokens fall back
|
|
211
|
+
* to unstyled text so a config typo does not break rendering.
|
|
212
|
+
*/
|
|
213
|
+
export function colorize(theme: ThemeLike, color: ColorSpec, text: string): string {
|
|
214
|
+
if (isHexColor(color)) {
|
|
215
|
+
return `${hexToAnsi(color)}${text}\x1b[39m`;
|
|
216
|
+
}
|
|
217
|
+
return safeThemeFg(theme, color, text);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Render text with Starship-style terminal styling strings (e.g. "bold red", "fg:202",
|
|
222
|
+
* "bg:blue", "underline bg:#bf5700").
|
|
223
|
+
*/
|
|
224
|
+
export function renderTerminalStyle(style: string, text: string): string {
|
|
225
|
+
const codes: string[] = [];
|
|
226
|
+
for (const token of style.trim().split(/\s+/)) {
|
|
227
|
+
if (!token) continue;
|
|
228
|
+
|
|
229
|
+
const normalized = token.toLowerCase();
|
|
230
|
+
const modifier = terminalStyleModifiers.get(normalized);
|
|
231
|
+
if (modifier !== undefined) {
|
|
232
|
+
codes.push(`${modifier}`);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const isForeground = normalized.startsWith("fg:");
|
|
237
|
+
const isBackground = normalized.startsWith("bg:");
|
|
238
|
+
const colorName = isForeground || isBackground ? normalized.slice(3) : normalized;
|
|
239
|
+
const color = terminalColorToAnsi(colorName, isBackground);
|
|
240
|
+
if (color) codes.push(color);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return codes.length ? `\x1b[${codes.join(";")}m${text}\x1b[0m` : text;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Apply Starship-style terminal styling first, falling back to Pi theme tokens for
|
|
248
|
+
* legacy config values such as "accent" or "syntaxKeyword".
|
|
249
|
+
*/
|
|
250
|
+
export function renderStyle(theme: ThemeLike, style: ColorSpec, text: string): string {
|
|
251
|
+
if (style.trim() === "") return text;
|
|
252
|
+
const styled = renderTerminalStyle(style, text);
|
|
253
|
+
return styled === text ? colorize(theme, style, text) : styled;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function renderThemeStyle(theme: ThemeLike, style: ColorSpec, text: string): string {
|
|
257
|
+
const trimmed = style.trim();
|
|
258
|
+
if (trimmed === "") return text;
|
|
259
|
+
|
|
260
|
+
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
261
|
+
if (tokens.some(isExplicitTerminalColorToken)) return renderTerminalStyle(style, text);
|
|
262
|
+
|
|
263
|
+
const color = mapThemeColor(tokens) ?? "text";
|
|
264
|
+
return safeThemeFg(theme, color, applyThemeModifiers(theme, tokens, text));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function renderStyleForSource(
|
|
268
|
+
theme: ThemeLike,
|
|
269
|
+
source: ColorSource,
|
|
270
|
+
style: ColorSpec,
|
|
271
|
+
text: string,
|
|
272
|
+
): string {
|
|
273
|
+
return source === "terminal"
|
|
274
|
+
? renderStyle(theme, style, text)
|
|
275
|
+
: renderThemeStyle(theme, style, text);
|
|
276
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rinco-pi-hud",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A dynamic HUD footer for Pi, ported from Sakura Zentui Footer.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Rinisnotarobot/rinco-pi-hud.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Rinisnotarobot/rinco-pi-hud#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Rinisnotarobot/rinco-pi-hud/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"pi-package",
|
|
17
|
+
"pi-extension",
|
|
18
|
+
"tui",
|
|
19
|
+
"hud",
|
|
20
|
+
"sakura"
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
"extensions",
|
|
24
|
+
"docs",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"NOTICE"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"lint": "biome lint .",
|
|
34
|
+
"lint:fix": "biome lint --write .",
|
|
35
|
+
"format:check": "biome format .",
|
|
36
|
+
"format": "biome format --write .",
|
|
37
|
+
"verify": "npm run typecheck && npm run lint && npm run test",
|
|
38
|
+
"pack:check": "npm pack --dry-run"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
42
|
+
"@earendil-works/pi-tui": "*"
|
|
43
|
+
},
|
|
44
|
+
"pi": {
|
|
45
|
+
"extensions": [
|
|
46
|
+
"./extensions/hud/index.ts"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@biomejs/biome": "^2.5.7",
|
|
51
|
+
"@earendil-works/pi-ai": "^0.83.0",
|
|
52
|
+
"@types/node": "^24.12.4",
|
|
53
|
+
"typescript": "^5.9.3",
|
|
54
|
+
"vitest": "^4.1.9"
|
|
55
|
+
}
|
|
56
|
+
}
|