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,543 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import type { PolishedTuiConfig, SeparatorStyle } from "../config/config.js";
|
|
4
|
+
import { FOOTER_FORMAT_ALIASES } from "../config/config.js";
|
|
5
|
+
import {
|
|
6
|
+
collectExtensionStatusSegments,
|
|
7
|
+
type ExtensionStatusSegment,
|
|
8
|
+
} from "../segments/extension-status.js";
|
|
9
|
+
import { composeCategorizedFooterRows, composeFooterContent } from "./layout.js";
|
|
10
|
+
import { parseFooterFormat, renderFormatSplit, stripOrphanSeparators } from "./format.js";
|
|
11
|
+
import {
|
|
12
|
+
buildGitStatusLabels,
|
|
13
|
+
buildTelemetryLabels,
|
|
14
|
+
safeStatusText,
|
|
15
|
+
sanitizeFooterInputs,
|
|
16
|
+
} from "./render-labels.js";
|
|
17
|
+
import {
|
|
18
|
+
buildContextDisplayLabel,
|
|
19
|
+
buildSessionDurationLabel,
|
|
20
|
+
contextColorTier,
|
|
21
|
+
formatCwdLabel,
|
|
22
|
+
formatGitBranchText,
|
|
23
|
+
formatGitCommitSegment,
|
|
24
|
+
formatGitMetricsSegment,
|
|
25
|
+
formatOsLabel,
|
|
26
|
+
formatPackageVersionSegment,
|
|
27
|
+
formatRuntimeSegment,
|
|
28
|
+
formatTimeLabel,
|
|
29
|
+
formatUsernameHostLabel,
|
|
30
|
+
} from "../telemetry/format.js";
|
|
31
|
+
import { resolveRuntimeSymbol } from "../ui/icons.js";
|
|
32
|
+
import type { LiveContextOverride } from "../session/live-context.js";
|
|
33
|
+
import { parseMcpStatus } from "../segments/mcp-status.js";
|
|
34
|
+
import type { FooterState } from "../state/index.js";
|
|
35
|
+
import { renderStyleForSource } from "../ui/style.js";
|
|
36
|
+
|
|
37
|
+
const separatorText: Record<SeparatorStyle, string> = {
|
|
38
|
+
pipe: " | ",
|
|
39
|
+
dot: " · ",
|
|
40
|
+
chevron: " › ",
|
|
41
|
+
none: " ",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export function installFooter(
|
|
45
|
+
ctx: ExtensionContext,
|
|
46
|
+
state: FooterState,
|
|
47
|
+
getConfig: () => PolishedTuiConfig,
|
|
48
|
+
hooks: {
|
|
49
|
+
setRequestRender: (fn: (() => void) | undefined) => void;
|
|
50
|
+
scheduleProjectRefresh: (ctx: ExtensionContext) => void;
|
|
51
|
+
setExtensionStatusesGetter?: (fn: (() => ReadonlyMap<string, string>) | undefined) => void;
|
|
52
|
+
getLiveContext?: () => LiveContextOverride | undefined;
|
|
53
|
+
},
|
|
54
|
+
): void {
|
|
55
|
+
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
56
|
+
hooks.setRequestRender(() => tui.requestRender());
|
|
57
|
+
hooks.setExtensionStatusesGetter?.(() => footerData.getExtensionStatuses());
|
|
58
|
+
const unsubscribeBranch = footerData.onBranchChange(() => {
|
|
59
|
+
hooks.scheduleProjectRefresh(ctx);
|
|
60
|
+
tui.requestRender();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
dispose: () => {
|
|
65
|
+
unsubscribeBranch();
|
|
66
|
+
hooks.setRequestRender(undefined);
|
|
67
|
+
hooks.setExtensionStatusesGetter?.(undefined);
|
|
68
|
+
},
|
|
69
|
+
invalidate() {},
|
|
70
|
+
render(width: number): string[] {
|
|
71
|
+
if (width <= 0) return [""];
|
|
72
|
+
const config = getConfig();
|
|
73
|
+
const colorSource = config.colorSources.starship;
|
|
74
|
+
const iconMode = config.icons.mode;
|
|
75
|
+
const rawExtensionStatuses = footerData.getExtensionStatuses();
|
|
76
|
+
const mcpStatus = parseMcpStatus(rawExtensionStatuses.get("mcp"));
|
|
77
|
+
const { safeCwd, safeRuntime, safePackageVersion, safeCommit } = sanitizeFooterInputs(
|
|
78
|
+
ctx,
|
|
79
|
+
state,
|
|
80
|
+
);
|
|
81
|
+
const separator = renderStyleForSource(
|
|
82
|
+
theme,
|
|
83
|
+
colorSource,
|
|
84
|
+
config.colors.separator,
|
|
85
|
+
separatorText[config.separator],
|
|
86
|
+
);
|
|
87
|
+
const innerWidth = Math.max(1, width - 2);
|
|
88
|
+
const cwdLabel = renderStyleForSource(
|
|
89
|
+
theme,
|
|
90
|
+
colorSource,
|
|
91
|
+
config.colors.cwd,
|
|
92
|
+
formatCwdLabel(safeCwd, config.icons.cwd, {
|
|
93
|
+
mode: config.pathDisplay.mode,
|
|
94
|
+
depth: config.pathDisplay.depth,
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
const cwdValueLabel = renderStyleForSource(
|
|
98
|
+
theme,
|
|
99
|
+
colorSource,
|
|
100
|
+
config.colors.cwd,
|
|
101
|
+
formatCwdLabel(safeCwd, "", {
|
|
102
|
+
mode: config.pathDisplay.mode,
|
|
103
|
+
depth: config.pathDisplay.depth,
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
106
|
+
const branch = safeStatusText(state.branch, 256) || undefined;
|
|
107
|
+
const branchText = branch
|
|
108
|
+
? formatGitBranchText(branch, config.gitBranch.maxLength)
|
|
109
|
+
: undefined;
|
|
110
|
+
const contextUsage = ctx.getContextUsage();
|
|
111
|
+
const liveContext = hooks.getLiveContext?.();
|
|
112
|
+
const contextWindow = ctx.model?.contextWindow ?? contextUsage?.contextWindow;
|
|
113
|
+
const useLiveContext =
|
|
114
|
+
liveContext !== undefined && contextWindow !== undefined && contextWindow > 0;
|
|
115
|
+
const contextPercent = useLiveContext
|
|
116
|
+
? (liveContext.tokens / contextWindow) * 100
|
|
117
|
+
: contextUsage?.percent;
|
|
118
|
+
const contextLabel = buildContextDisplayLabel({
|
|
119
|
+
percent: contextPercent,
|
|
120
|
+
contextWindow,
|
|
121
|
+
style: config.contextStyle,
|
|
122
|
+
asciiGauge: iconMode === "ascii",
|
|
123
|
+
});
|
|
124
|
+
const tier = contextColorTier(contextPercent, config.contextThresholds);
|
|
125
|
+
const contextColor =
|
|
126
|
+
tier === "error"
|
|
127
|
+
? config.colors.contextError
|
|
128
|
+
: tier === "warning"
|
|
129
|
+
? config.colors.contextWarning
|
|
130
|
+
: config.colors.contextNormal;
|
|
131
|
+
const { gitColor, gitIcon, statusBlock, gitStateBlock } = buildGitStatusLabels(
|
|
132
|
+
state,
|
|
133
|
+
config,
|
|
134
|
+
theme,
|
|
135
|
+
colorSource,
|
|
136
|
+
);
|
|
137
|
+
const {
|
|
138
|
+
projectCategoryLabel,
|
|
139
|
+
sessionCategoryLabel,
|
|
140
|
+
usageCategoryLabel,
|
|
141
|
+
sessionNameLabel,
|
|
142
|
+
modelValueLabel,
|
|
143
|
+
modelStatusLabel,
|
|
144
|
+
providerId,
|
|
145
|
+
modelId,
|
|
146
|
+
thinkingLabel,
|
|
147
|
+
turnLabel,
|
|
148
|
+
cacheReadLabel,
|
|
149
|
+
cacheWriteLabel,
|
|
150
|
+
cacheHitLabel,
|
|
151
|
+
cacheDetailsLabel,
|
|
152
|
+
codexUsageLabel,
|
|
153
|
+
instructionFilesLabel,
|
|
154
|
+
agentsFilesLabel,
|
|
155
|
+
claudeFilesLabel,
|
|
156
|
+
skillsLabel,
|
|
157
|
+
activeSkillsLabel,
|
|
158
|
+
extensionsLabel,
|
|
159
|
+
configCountsLabel,
|
|
160
|
+
mcpLabel,
|
|
161
|
+
toolCountsLabel,
|
|
162
|
+
runningToolsLabel,
|
|
163
|
+
activeAgentsLabel,
|
|
164
|
+
} = buildTelemetryLabels(ctx, state, config, theme, colorSource, mcpStatus);
|
|
165
|
+
const renderVariable = (name: string): string => {
|
|
166
|
+
const canonical = FOOTER_FORMAT_ALIASES[name] ?? name;
|
|
167
|
+
switch (canonical) {
|
|
168
|
+
case "cwd":
|
|
169
|
+
return cwdLabel;
|
|
170
|
+
case "git_branch":
|
|
171
|
+
return branchText
|
|
172
|
+
? gitIcon
|
|
173
|
+
? `${gitIcon} ${gitColor(branchText)}`
|
|
174
|
+
: gitColor(branchText)
|
|
175
|
+
: "";
|
|
176
|
+
case "git_status":
|
|
177
|
+
return statusBlock;
|
|
178
|
+
case "git_state":
|
|
179
|
+
return gitStateBlock;
|
|
180
|
+
case "runtime": {
|
|
181
|
+
if (!safeRuntime) return "";
|
|
182
|
+
const symbol = resolveRuntimeSymbol(
|
|
183
|
+
safeRuntime.name,
|
|
184
|
+
safeRuntime.symbol,
|
|
185
|
+
iconMode,
|
|
186
|
+
);
|
|
187
|
+
const label = safeRuntime.version ? `${symbol} ${safeRuntime.version}` : symbol;
|
|
188
|
+
return renderStyleForSource(theme, colorSource, safeRuntime.style, label);
|
|
189
|
+
}
|
|
190
|
+
case "session_duration":
|
|
191
|
+
return state.sessionStartEpoch
|
|
192
|
+
? renderStyleForSource(
|
|
193
|
+
theme,
|
|
194
|
+
colorSource,
|
|
195
|
+
config.colors.sessionDuration,
|
|
196
|
+
buildSessionDurationLabel(state.sessionStartEpoch),
|
|
197
|
+
)
|
|
198
|
+
: "";
|
|
199
|
+
case "username":
|
|
200
|
+
return renderStyleForSource(
|
|
201
|
+
theme,
|
|
202
|
+
colorSource,
|
|
203
|
+
config.colors.username,
|
|
204
|
+
formatUsernameHostLabel(config.icons.username),
|
|
205
|
+
);
|
|
206
|
+
case "os":
|
|
207
|
+
return renderStyleForSource(
|
|
208
|
+
theme,
|
|
209
|
+
colorSource,
|
|
210
|
+
config.colors.os,
|
|
211
|
+
formatOsLabel(config.icons.os, iconMode),
|
|
212
|
+
);
|
|
213
|
+
case "time":
|
|
214
|
+
return renderStyleForSource(
|
|
215
|
+
theme,
|
|
216
|
+
colorSource,
|
|
217
|
+
config.colors.time,
|
|
218
|
+
formatTimeLabel(config.icons.time),
|
|
219
|
+
);
|
|
220
|
+
case "context":
|
|
221
|
+
return renderStyleForSource(theme, colorSource, contextColor, contextLabel);
|
|
222
|
+
case "tokens":
|
|
223
|
+
return renderStyleForSource(
|
|
224
|
+
theme,
|
|
225
|
+
colorSource,
|
|
226
|
+
config.colors.tokens,
|
|
227
|
+
state.tokenLabel,
|
|
228
|
+
);
|
|
229
|
+
case "cost":
|
|
230
|
+
return renderStyleForSource(theme, colorSource, config.colors.cost, state.costLabel);
|
|
231
|
+
case "package":
|
|
232
|
+
return formatPackageVersionSegment(
|
|
233
|
+
theme,
|
|
234
|
+
safePackageVersion,
|
|
235
|
+
colorSource,
|
|
236
|
+
iconMode,
|
|
237
|
+
config.icons.package,
|
|
238
|
+
config.colors.packageVersion,
|
|
239
|
+
);
|
|
240
|
+
case "package_version":
|
|
241
|
+
return safePackageVersion?.version
|
|
242
|
+
? renderStyleForSource(
|
|
243
|
+
theme,
|
|
244
|
+
colorSource,
|
|
245
|
+
config.colors.packageVersion,
|
|
246
|
+
safePackageVersion.version,
|
|
247
|
+
)
|
|
248
|
+
: "";
|
|
249
|
+
case "sep":
|
|
250
|
+
return renderStyleForSource(theme, colorSource, config.colors.separator, " | ");
|
|
251
|
+
case "git_commit":
|
|
252
|
+
return formatGitCommitSegment(
|
|
253
|
+
theme,
|
|
254
|
+
safeCommit,
|
|
255
|
+
config.gitCommit,
|
|
256
|
+
colorSource,
|
|
257
|
+
config.colors.gitCommit,
|
|
258
|
+
);
|
|
259
|
+
case "git_tag":
|
|
260
|
+
return config.gitCommit.showTag && safeCommit?.tag
|
|
261
|
+
? renderStyleForSource(
|
|
262
|
+
theme,
|
|
263
|
+
colorSource,
|
|
264
|
+
config.colors.gitCommit,
|
|
265
|
+
safeCommit.tag,
|
|
266
|
+
)
|
|
267
|
+
: "";
|
|
268
|
+
case "git_metrics":
|
|
269
|
+
return formatGitMetricsSegment(
|
|
270
|
+
theme,
|
|
271
|
+
state.metrics,
|
|
272
|
+
config.gitMetrics,
|
|
273
|
+
colorSource,
|
|
274
|
+
config.colors.gitMetricsAdded,
|
|
275
|
+
config.colors.gitMetricsDeleted,
|
|
276
|
+
);
|
|
277
|
+
case "git_added":
|
|
278
|
+
return state.metrics
|
|
279
|
+
? renderStyleForSource(
|
|
280
|
+
theme,
|
|
281
|
+
colorSource,
|
|
282
|
+
config.colors.gitMetricsAdded,
|
|
283
|
+
`+ ${state.metrics.added}`,
|
|
284
|
+
)
|
|
285
|
+
: "";
|
|
286
|
+
case "git_deleted":
|
|
287
|
+
return state.metrics
|
|
288
|
+
? renderStyleForSource(
|
|
289
|
+
theme,
|
|
290
|
+
colorSource,
|
|
291
|
+
config.colors.gitMetricsDeleted,
|
|
292
|
+
`− ${state.metrics.deleted}`,
|
|
293
|
+
)
|
|
294
|
+
: "";
|
|
295
|
+
case "session_name":
|
|
296
|
+
return sessionNameLabel;
|
|
297
|
+
case "model":
|
|
298
|
+
return modelStatusLabel;
|
|
299
|
+
case "provider":
|
|
300
|
+
return providerId
|
|
301
|
+
? renderStyleForSource(theme, colorSource, config.colors.runtimePrefix, providerId)
|
|
302
|
+
: "";
|
|
303
|
+
case "model_id":
|
|
304
|
+
return modelId
|
|
305
|
+
? renderStyleForSource(theme, colorSource, config.colors.runtimePrefix, modelId)
|
|
306
|
+
: "";
|
|
307
|
+
case "thinking":
|
|
308
|
+
return thinkingLabel;
|
|
309
|
+
case "turn":
|
|
310
|
+
return turnLabel;
|
|
311
|
+
case "cache_read":
|
|
312
|
+
return cacheReadLabel;
|
|
313
|
+
case "cache_write":
|
|
314
|
+
return cacheWriteLabel;
|
|
315
|
+
case "cache_hit":
|
|
316
|
+
return cacheHitLabel;
|
|
317
|
+
case "codex_usage":
|
|
318
|
+
return codexUsageLabel;
|
|
319
|
+
case "instruction_files":
|
|
320
|
+
return instructionFilesLabel;
|
|
321
|
+
case "agents_files":
|
|
322
|
+
return agentsFilesLabel;
|
|
323
|
+
case "claude_files":
|
|
324
|
+
return claudeFilesLabel;
|
|
325
|
+
case "skills":
|
|
326
|
+
return skillsLabel;
|
|
327
|
+
case "active_skills":
|
|
328
|
+
return activeSkillsLabel;
|
|
329
|
+
case "extensions":
|
|
330
|
+
return extensionsLabel;
|
|
331
|
+
case "mcp":
|
|
332
|
+
return mcpLabel;
|
|
333
|
+
case "tool_counts":
|
|
334
|
+
return toolCountsLabel;
|
|
335
|
+
case "running_tools":
|
|
336
|
+
return runningToolsLabel;
|
|
337
|
+
case "active_agents":
|
|
338
|
+
return activeAgentsLabel;
|
|
339
|
+
default:
|
|
340
|
+
return "";
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
const branchParts: string[] = [];
|
|
344
|
+
if (config.footerSegments.gitBranch) {
|
|
345
|
+
if (branchText) {
|
|
346
|
+
branchParts.push("on", gitIcon, gitColor(branchText));
|
|
347
|
+
} else if (safeCommit?.detached) {
|
|
348
|
+
// `HEAD` uses git-branch style; `(hash)` uses git-commit style
|
|
349
|
+
// (bold green) per Starship `git_commit` format.
|
|
350
|
+
branchParts.push("on", gitIcon, gitColor("HEAD"));
|
|
351
|
+
if (config.footerSegments.gitCommit && safeCommit.oid) {
|
|
352
|
+
const shortHash = safeCommit.oid.slice(0, config.gitCommit.hashLength);
|
|
353
|
+
const tag = config.gitCommit.showTag && safeCommit.tag ? safeCommit.tag : "";
|
|
354
|
+
const inner = [shortHash, tag].filter(Boolean).join(" ");
|
|
355
|
+
branchParts.push(
|
|
356
|
+
renderStyleForSource(theme, colorSource, config.colors.gitCommit, `(${inner})`),
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const gitStatusParts = config.footerSegments.gitStatus && statusBlock ? [statusBlock] : [];
|
|
362
|
+
const showGitState = config.footerSegments.gitBranch || config.footerSegments.gitStatus;
|
|
363
|
+
const gitStateParts = showGitState && gitStateBlock ? [gitStateBlock] : [];
|
|
364
|
+
const branchLabel = [...branchParts, ...gitStatusParts, ...gitStateParts]
|
|
365
|
+
.filter(Boolean)
|
|
366
|
+
.join(" ");
|
|
367
|
+
const runtimeLabel = config.footerSegments.runtime
|
|
368
|
+
? formatRuntimeSegment(
|
|
369
|
+
theme,
|
|
370
|
+
safeRuntime,
|
|
371
|
+
config.colors.runtimePrefix,
|
|
372
|
+
colorSource,
|
|
373
|
+
iconMode,
|
|
374
|
+
)
|
|
375
|
+
: "";
|
|
376
|
+
const packageVersionLabel = config.footerSegments.packageVersion
|
|
377
|
+
? formatPackageVersionSegment(
|
|
378
|
+
theme,
|
|
379
|
+
safePackageVersion,
|
|
380
|
+
colorSource,
|
|
381
|
+
iconMode,
|
|
382
|
+
config.icons.package,
|
|
383
|
+
config.colors.packageVersion,
|
|
384
|
+
)
|
|
385
|
+
: "";
|
|
386
|
+
// Skip standalone gitCommit when hash is already folded into the
|
|
387
|
+
// branch display on detached HEAD.
|
|
388
|
+
const hashFoldedIntoBranch = safeCommit?.detached && config.footerSegments.gitBranch;
|
|
389
|
+
const gitCommitLabel =
|
|
390
|
+
config.footerSegments.gitCommit && !hashFoldedIntoBranch
|
|
391
|
+
? formatGitCommitSegment(
|
|
392
|
+
theme,
|
|
393
|
+
safeCommit,
|
|
394
|
+
config.gitCommit,
|
|
395
|
+
colorSource,
|
|
396
|
+
config.colors.gitCommit,
|
|
397
|
+
)
|
|
398
|
+
: "";
|
|
399
|
+
const gitMetricsLabel = config.footerSegments.gitMetrics
|
|
400
|
+
? formatGitMetricsSegment(
|
|
401
|
+
theme,
|
|
402
|
+
state.metrics,
|
|
403
|
+
config.gitMetrics,
|
|
404
|
+
colorSource,
|
|
405
|
+
config.colors.gitMetricsAdded,
|
|
406
|
+
config.colors.gitMetricsDeleted,
|
|
407
|
+
)
|
|
408
|
+
: "";
|
|
409
|
+
|
|
410
|
+
const sessionDurationSegment = (() => {
|
|
411
|
+
if (!config.footerSegments.sessionDuration || !state.sessionStartEpoch) return "";
|
|
412
|
+
const timeLabel = buildSessionDurationLabel(state.sessionStartEpoch);
|
|
413
|
+
const prefix = renderStyleForSource(theme, colorSource, "", "up for");
|
|
414
|
+
const time = renderStyleForSource(
|
|
415
|
+
theme,
|
|
416
|
+
colorSource,
|
|
417
|
+
config.colors.sessionDuration,
|
|
418
|
+
timeLabel,
|
|
419
|
+
);
|
|
420
|
+
return `${prefix} ${time}`;
|
|
421
|
+
})();
|
|
422
|
+
const usernameSegment = config.footerSegments.username
|
|
423
|
+
? renderStyleForSource(
|
|
424
|
+
theme,
|
|
425
|
+
colorSource,
|
|
426
|
+
config.colors.username,
|
|
427
|
+
formatUsernameHostLabel(config.icons.username),
|
|
428
|
+
)
|
|
429
|
+
: "";
|
|
430
|
+
const osSegment = config.footerSegments.os
|
|
431
|
+
? renderStyleForSource(
|
|
432
|
+
theme,
|
|
433
|
+
colorSource,
|
|
434
|
+
config.colors.os,
|
|
435
|
+
formatOsLabel(config.icons.os, iconMode),
|
|
436
|
+
)
|
|
437
|
+
: "";
|
|
438
|
+
const timeSegment = config.footerSegments.time
|
|
439
|
+
? renderStyleForSource(
|
|
440
|
+
theme,
|
|
441
|
+
colorSource,
|
|
442
|
+
config.colors.time,
|
|
443
|
+
formatTimeLabel(config.icons.time),
|
|
444
|
+
)
|
|
445
|
+
: "";
|
|
446
|
+
const projectSegments = [
|
|
447
|
+
projectCategoryLabel,
|
|
448
|
+
config.footerSegments.cwd ? cwdValueLabel : "",
|
|
449
|
+
branchLabel,
|
|
450
|
+
gitCommitLabel,
|
|
451
|
+
gitMetricsLabel,
|
|
452
|
+
packageVersionLabel,
|
|
453
|
+
runtimeLabel,
|
|
454
|
+
config.footerSegments.configCounts ? configCountsLabel : "",
|
|
455
|
+
osSegment,
|
|
456
|
+
usernameSegment,
|
|
457
|
+
].filter(Boolean);
|
|
458
|
+
const sessionSegments = [
|
|
459
|
+
sessionCategoryLabel,
|
|
460
|
+
config.footerSegments.sessionName ? sessionNameLabel : "",
|
|
461
|
+
config.footerSegments.model ? modelValueLabel : "",
|
|
462
|
+
config.footerSegments.thinking ? thinkingLabel : "",
|
|
463
|
+
config.footerSegments.turnCount ? turnLabel : "",
|
|
464
|
+
config.footerSegments.skills ? skillsLabel : "",
|
|
465
|
+
config.footerSegments.mcp ? mcpLabel : "",
|
|
466
|
+
config.footerSegments.toolActivity ? runningToolsLabel || toolCountsLabel : "",
|
|
467
|
+
config.footerSegments.agentActivity ? activeAgentsLabel : "",
|
|
468
|
+
].filter(Boolean);
|
|
469
|
+
const usageSegments = [
|
|
470
|
+
usageCategoryLabel,
|
|
471
|
+
config.footerSegments.context
|
|
472
|
+
? renderStyleForSource(theme, colorSource, contextColor, contextLabel)
|
|
473
|
+
: "",
|
|
474
|
+
config.footerSegments.tokens
|
|
475
|
+
? renderStyleForSource(theme, colorSource, config.colors.tokens, state.tokenLabel)
|
|
476
|
+
: "",
|
|
477
|
+
config.footerSegments.cacheDetails ? cacheDetailsLabel : "",
|
|
478
|
+
config.footerSegments.cost
|
|
479
|
+
? renderStyleForSource(theme, colorSource, config.colors.cost, state.costLabel)
|
|
480
|
+
: "",
|
|
481
|
+
config.footerSegments.codexUsage ? codexUsageLabel : "",
|
|
482
|
+
sessionDurationSegment,
|
|
483
|
+
timeSegment,
|
|
484
|
+
].filter(Boolean);
|
|
485
|
+
|
|
486
|
+
const formatNeedsMcp = /\$\{?mcp\b/.test(config.footerFormat);
|
|
487
|
+
const dedicatedMcpVisible = Boolean(
|
|
488
|
+
mcpStatus && (config.footerFormat ? formatNeedsMcp : config.footerSegments.mcp),
|
|
489
|
+
);
|
|
490
|
+
const extensionStatusSource = dedicatedMcpVisible
|
|
491
|
+
? new Map([...rawExtensionStatuses].filter(([key]) => key !== "mcp"))
|
|
492
|
+
: rawExtensionStatuses;
|
|
493
|
+
const extensionStatuses = collectExtensionStatusSegments(extensionStatusSource, config);
|
|
494
|
+
const renderExtensionStatus = (segment: ExtensionStatusSegment) =>
|
|
495
|
+
segment.colorMode === "original"
|
|
496
|
+
? segment.text
|
|
497
|
+
: renderStyleForSource(theme, colorSource, config.colors.extensionStatus, segment.text);
|
|
498
|
+
const extensionLeft = extensionStatuses.left.map(renderExtensionStatus);
|
|
499
|
+
const extensionMiddle = extensionStatuses.middle.map(renderExtensionStatus);
|
|
500
|
+
const extensionRight = extensionStatuses.right.map(renderExtensionStatus);
|
|
501
|
+
const frame = (content: string) => {
|
|
502
|
+
const framed = width > 2 ? ` ${truncateToWidth(content, width - 2, "")} ` : content;
|
|
503
|
+
return truncateToWidth(framed, width, "");
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
if (config.footerFormat) {
|
|
507
|
+
const {
|
|
508
|
+
left: fmtLeft,
|
|
509
|
+
middle: fmtMiddle,
|
|
510
|
+
right: fmtRight,
|
|
511
|
+
} = renderFormatSplit(parseFooterFormat(config.footerFormat), renderVariable);
|
|
512
|
+
const templateMiddle = stripOrphanSeparators(fmtMiddle);
|
|
513
|
+
const content = composeFooterContent(
|
|
514
|
+
stripOrphanSeparators(fmtLeft),
|
|
515
|
+
stripOrphanSeparators(fmtRight),
|
|
516
|
+
extensionLeft,
|
|
517
|
+
templateMiddle ? [templateMiddle, ...extensionMiddle] : extensionMiddle,
|
|
518
|
+
extensionRight,
|
|
519
|
+
separator,
|
|
520
|
+
innerWidth,
|
|
521
|
+
);
|
|
522
|
+
return [frame(content)];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const contents = composeCategorizedFooterRows(
|
|
526
|
+
{
|
|
527
|
+
project: projectSegments,
|
|
528
|
+
session: [
|
|
529
|
+
...sessionSegments,
|
|
530
|
+
...extensionLeft,
|
|
531
|
+
...extensionMiddle,
|
|
532
|
+
...extensionRight,
|
|
533
|
+
],
|
|
534
|
+
usage: usageSegments,
|
|
535
|
+
},
|
|
536
|
+
separator,
|
|
537
|
+
innerWidth,
|
|
538
|
+
);
|
|
539
|
+
return contents.map(frame);
|
|
540
|
+
},
|
|
541
|
+
};
|
|
542
|
+
});
|
|
543
|
+
}
|