apex-code 0.0.1-alpha.4 → 0.0.1-alpha.5
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 +49 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -2
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +20 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +91 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +17 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +10 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/lsp/client.d.ts +76 -0
- package/dist/core/lsp/client.d.ts.map +1 -0
- package/dist/core/lsp/client.js +469 -0
- package/dist/core/lsp/client.js.map +1 -0
- package/dist/core/lsp/diagnostics.d.ts +47 -0
- package/dist/core/lsp/diagnostics.d.ts.map +1 -0
- package/dist/core/lsp/diagnostics.js +224 -0
- package/dist/core/lsp/diagnostics.js.map +1 -0
- package/dist/core/lsp/pool.d.ts +48 -0
- package/dist/core/lsp/pool.d.ts.map +1 -0
- package/dist/core/lsp/pool.js +150 -0
- package/dist/core/lsp/pool.js.map +1 -0
- package/dist/core/lsp/registry.d.ts +49 -0
- package/dist/core/lsp/registry.d.ts.map +1 -0
- package/dist/core/lsp/registry.js +215 -0
- package/dist/core/lsp/registry.js.map +1 -0
- package/dist/core/lsp/status.d.ts +23 -0
- package/dist/core/lsp/status.d.ts.map +1 -0
- package/dist/core/lsp/status.js +35 -0
- package/dist/core/lsp/status.js.map +1 -0
- package/dist/core/lsp/sync.d.ts +13 -0
- package/dist/core/lsp/sync.d.ts.map +1 -0
- package/dist/core/lsp/sync.js +71 -0
- package/dist/core/lsp/sync.js.map +1 -0
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/sdk.d.ts +4 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/contract.d.ts +29 -2
- package/dist/core/tools/contract.d.ts.map +1 -1
- package/dist/core/tools/contract.js.map +1 -1
- package/dist/core/tools/diagnostics.d.ts +39 -0
- package/dist/core/tools/diagnostics.d.ts.map +1 -0
- package/dist/core/tools/diagnostics.js +62 -0
- package/dist/core/tools/diagnostics.js.map +1 -0
- package/dist/core/tools/edit.d.ts +10 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +17 -8
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts +15 -2
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +6 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/lsp.d.ts +62 -0
- package/dist/core/tools/lsp.d.ts.map +1 -0
- package/dist/core/tools/lsp.js +216 -0
- package/dist/core/tools/lsp.js.map +1 -0
- package/dist/core/tools/write.d.ts +10 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +16 -3
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +49 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +235 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +2 -1
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/splash-header.d.ts +41 -0
- package/dist/modes/interactive/components/splash-header.d.ts.map +1 -0
- package/dist/modes/interactive/components/splash-header.js +145 -0
- package/dist/modes/interactive/components/splash-header.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +17 -14
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/apex.json +94 -0
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +6 -1
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/themes/apex-logo.d.ts +21 -0
- package/dist/themes/apex-logo.d.ts.map +1 -0
- package/dist/themes/apex-logo.js +31 -0
- package/dist/themes/apex-logo.js.map +1 -0
- package/dist/utils/changelog.d.ts +7 -2
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +82 -13
- package/dist/utils/changelog.js.map +1 -1
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -2
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
import { APEX_PEAK_LOGO, APEX_PEAK_LOGO_ASCII, APEX_PEAK_LOGO_COMPACT } from "../../../themes/apex-logo.js";
|
|
3
|
+
import { theme } from "../theme/theme.js";
|
|
4
|
+
import { formatCwdForFooter } from "./footer.js";
|
|
5
|
+
const GUTTER = 4;
|
|
6
|
+
const LABEL_WIDTH = 9;
|
|
7
|
+
/** Smallest value column worth rendering; below this the metadata column is dropped. */
|
|
8
|
+
const MIN_VALUE_WIDTH = 8;
|
|
9
|
+
/**
|
|
10
|
+
* Keep the rightmost path segments that fit, so the leaf directory stays visible.
|
|
11
|
+
*
|
|
12
|
+
* `truncateToWidth` clips the tail, which for a path hides the part that
|
|
13
|
+
* identifies the session. This clips the head instead.
|
|
14
|
+
*/
|
|
15
|
+
function truncatePathTail(path, maxWidth) {
|
|
16
|
+
if (maxWidth <= 0) {
|
|
17
|
+
return "";
|
|
18
|
+
}
|
|
19
|
+
if (visibleWidth(path) <= maxWidth) {
|
|
20
|
+
return path;
|
|
21
|
+
}
|
|
22
|
+
const ellipsis = "…/";
|
|
23
|
+
const budget = Math.max(0, maxWidth - visibleWidth(ellipsis));
|
|
24
|
+
const segments = path.split("/");
|
|
25
|
+
let tail = "";
|
|
26
|
+
for (let index = segments.length - 1; index >= 0; index--) {
|
|
27
|
+
const candidate = tail ? `${segments[index]}/${tail}` : segments[index];
|
|
28
|
+
if (visibleWidth(candidate) > budget) {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
tail = candidate;
|
|
32
|
+
}
|
|
33
|
+
// A single segment longer than the whole budget: clip it from the left.
|
|
34
|
+
return tail ? ellipsis + tail : ellipsis + trailingByWidth(path, budget);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The widest suffix of `text` that fits `maxWidth` terminal columns.
|
|
38
|
+
*
|
|
39
|
+
* Walks grapheme clusters from the right rather than using `String.slice`, whose
|
|
40
|
+
* UTF-16 offsets would cut an astral character in half — emitting a lone
|
|
41
|
+
* surrogate — and would miscount every double-width character on the way.
|
|
42
|
+
*/
|
|
43
|
+
function trailingByWidth(text, maxWidth) {
|
|
44
|
+
if (maxWidth <= 0) {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
const graphemes = [...new Intl.Segmenter(undefined, { granularity: "grapheme" }).segment(text)].map((entry) => entry.segment);
|
|
48
|
+
let width = 0;
|
|
49
|
+
let index = graphemes.length;
|
|
50
|
+
while (index > 0) {
|
|
51
|
+
const next = width + visibleWidth(graphemes[index - 1]);
|
|
52
|
+
if (next > maxWidth) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
width = next;
|
|
56
|
+
index -= 1;
|
|
57
|
+
}
|
|
58
|
+
return graphemes.slice(index).join("");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Startup header: brand mark on the left, runtime metadata on the right.
|
|
62
|
+
*
|
|
63
|
+
* Values are read through getters rather than captured at construction, so the
|
|
64
|
+
* model fills in once the session binds without an explicit refresh.
|
|
65
|
+
*
|
|
66
|
+
* The metadata column is dropped when the terminal cannot hold it, and the mark
|
|
67
|
+
* falls back to a compact variant rather than being truncated mid-glyph.
|
|
68
|
+
*/
|
|
69
|
+
export class ApexSplashHeader {
|
|
70
|
+
version;
|
|
71
|
+
getModelId;
|
|
72
|
+
getCwd;
|
|
73
|
+
verboseInstructions;
|
|
74
|
+
options;
|
|
75
|
+
constructor(version, getModelId, getCwd, verboseInstructions, options = {}) {
|
|
76
|
+
this.version = version;
|
|
77
|
+
this.getModelId = getModelId;
|
|
78
|
+
this.getCwd = getCwd;
|
|
79
|
+
this.verboseInstructions = verboseInstructions;
|
|
80
|
+
this.options = options;
|
|
81
|
+
}
|
|
82
|
+
invalidate() {
|
|
83
|
+
// Render output is derived from current theme and session state.
|
|
84
|
+
}
|
|
85
|
+
render(width) {
|
|
86
|
+
const safeWidth = Math.max(1, width);
|
|
87
|
+
const paddingX = safeWidth > 1 ? 1 : 0;
|
|
88
|
+
const contentWidth = Math.max(1, safeWidth - paddingX * 2);
|
|
89
|
+
const logoLines = this.selectLogo(contentWidth);
|
|
90
|
+
const logoWidth = logoLines.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);
|
|
91
|
+
const metaWidth = contentWidth - logoWidth - GUTTER;
|
|
92
|
+
const showMeta = metaWidth >= LABEL_WIDTH + MIN_VALUE_WIDTH;
|
|
93
|
+
const valueWidth = Math.max(1, metaWidth - LABEL_WIDTH);
|
|
94
|
+
const metaLines = showMeta ? this.buildMetadata(valueWidth) : [];
|
|
95
|
+
const metaStart = Math.max(0, Math.floor((logoLines.length - metaLines.length) / 2));
|
|
96
|
+
const lines = this.options.topPadding ? [this.padLine("", safeWidth, paddingX)] : [];
|
|
97
|
+
logoLines.forEach((line, index) => {
|
|
98
|
+
const meta = index >= metaStart && index < metaStart + metaLines.length ? metaLines[index - metaStart] : "";
|
|
99
|
+
const gap = showMeta ? " ".repeat(Math.max(0, logoWidth - visibleWidth(line) + GUTTER)) : "";
|
|
100
|
+
const content = truncateToWidth(theme.fg("text", line) + gap + meta, contentWidth, "");
|
|
101
|
+
lines.push(this.padLine(content, safeWidth, paddingX));
|
|
102
|
+
});
|
|
103
|
+
// The hint is product copy rather than a runtime fact, so it sits under the
|
|
104
|
+
// whole block at full width instead of inside the narrow metadata column,
|
|
105
|
+
// where it would truncate even on a wide terminal.
|
|
106
|
+
const hint = this.options.getHint?.();
|
|
107
|
+
if (hint) {
|
|
108
|
+
lines.push(this.padLine("", safeWidth, paddingX));
|
|
109
|
+
lines.push(this.padLine(theme.fg("dim", truncateToWidth(hint, contentWidth)), safeWidth, paddingX));
|
|
110
|
+
}
|
|
111
|
+
if (this.verboseInstructions) {
|
|
112
|
+
lines.push(this.padLine("", safeWidth, paddingX));
|
|
113
|
+
for (const instruction of this.verboseInstructions.split("\n")) {
|
|
114
|
+
lines.push(this.padLine(truncateToWidth(instruction, contentWidth), safeWidth, paddingX));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return lines;
|
|
118
|
+
}
|
|
119
|
+
selectLogo(contentWidth) {
|
|
120
|
+
if (this.options.logo) {
|
|
121
|
+
return this.options.logo.split("\n");
|
|
122
|
+
}
|
|
123
|
+
if (this.options.getSymbolPreset?.() === "ascii") {
|
|
124
|
+
return APEX_PEAK_LOGO_ASCII.split("\n");
|
|
125
|
+
}
|
|
126
|
+
const full = APEX_PEAK_LOGO.split("\n");
|
|
127
|
+
const fullWidth = full.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);
|
|
128
|
+
return contentWidth >= fullWidth ? full : APEX_PEAK_LOGO_COMPACT.split("\n");
|
|
129
|
+
}
|
|
130
|
+
buildMetadata(valueWidth) {
|
|
131
|
+
const labelled = (label, value) => theme.fg("dim", label.padEnd(LABEL_WIDTH)) + theme.fg("muted", truncateToWidth(value, valueWidth));
|
|
132
|
+
const cwd = formatCwdForFooter(this.getCwd(), process.env.HOME || process.env.USERPROFILE);
|
|
133
|
+
return [
|
|
134
|
+
labelled("version", `v${this.version}`),
|
|
135
|
+
labelled("model", this.getModelId() ?? "—"),
|
|
136
|
+
theme.fg("dim", "cwd".padEnd(LABEL_WIDTH)) + theme.fg("muted", truncatePathTail(cwd, valueWidth)),
|
|
137
|
+
...(this.options.getExtraMetadata?.() ?? []).map((line) => labelled(line.label, line.value)),
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
padLine(content, safeWidth, paddingX) {
|
|
141
|
+
const trailing = Math.max(0, safeWidth - paddingX - visibleWidth(content));
|
|
142
|
+
return " ".repeat(paddingX) + content + " ".repeat(trailing);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=splash-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splash-header.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/splash-header.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport { APEX_PEAK_LOGO, APEX_PEAK_LOGO_ASCII, APEX_PEAK_LOGO_COMPACT } from \"../../../themes/apex-logo.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { formatCwdForFooter } from \"./footer.ts\";\n\n/** One `label value` row in the metadata column. */\nexport interface SplashHeaderMetadataLine {\n\tlabel: string;\n\tvalue: string;\n}\n\nexport interface ApexSplashHeaderOptions {\n\t/** Rows appended after `cwd`, for extensions that want to contribute context. */\n\tgetExtraMetadata?: () => readonly SplashHeaderMetadataLine[];\n\t/** Hint rendered under the metadata block. Return undefined for none. */\n\tgetHint?: () => string | undefined;\n\t/** Resolves `terminal.symbolPreset`. Defaults to unicode. */\n\tgetSymbolPreset?: () => \"unicode\" | \"ascii\";\n\t/** Emit a leading blank line above the mark. */\n\ttopPadding?: boolean;\n\t/** Override the mark. Used by tests and by extensions that rebrand. */\n\tlogo?: string;\n}\n\nconst GUTTER = 4;\nconst LABEL_WIDTH = 9;\n/** Smallest value column worth rendering; below this the metadata column is dropped. */\nconst MIN_VALUE_WIDTH = 8;\n\n/**\n * Keep the rightmost path segments that fit, so the leaf directory stays visible.\n *\n * `truncateToWidth` clips the tail, which for a path hides the part that\n * identifies the session. This clips the head instead.\n */\nfunction truncatePathTail(path: string, maxWidth: number): string {\n\tif (maxWidth <= 0) {\n\t\treturn \"\";\n\t}\n\tif (visibleWidth(path) <= maxWidth) {\n\t\treturn path;\n\t}\n\tconst ellipsis = \"…/\";\n\tconst budget = Math.max(0, maxWidth - visibleWidth(ellipsis));\n\tconst segments = path.split(\"/\");\n\tlet tail = \"\";\n\tfor (let index = segments.length - 1; index >= 0; index--) {\n\t\tconst candidate = tail ? `${segments[index]}/${tail}` : segments[index];\n\t\tif (visibleWidth(candidate) > budget) {\n\t\t\tbreak;\n\t\t}\n\t\ttail = candidate;\n\t}\n\t// A single segment longer than the whole budget: clip it from the left.\n\treturn tail ? ellipsis + tail : ellipsis + trailingByWidth(path, budget);\n}\n\n/**\n * The widest suffix of `text` that fits `maxWidth` terminal columns.\n *\n * Walks grapheme clusters from the right rather than using `String.slice`, whose\n * UTF-16 offsets would cut an astral character in half — emitting a lone\n * surrogate — and would miscount every double-width character on the way.\n */\nfunction trailingByWidth(text: string, maxWidth: number): string {\n\tif (maxWidth <= 0) {\n\t\treturn \"\";\n\t}\n\tconst graphemes = [...new Intl.Segmenter(undefined, { granularity: \"grapheme\" }).segment(text)].map(\n\t\t(entry) => entry.segment,\n\t);\n\tlet width = 0;\n\tlet index = graphemes.length;\n\twhile (index > 0) {\n\t\tconst next = width + visibleWidth(graphemes[index - 1]);\n\t\tif (next > maxWidth) {\n\t\t\tbreak;\n\t\t}\n\t\twidth = next;\n\t\tindex -= 1;\n\t}\n\treturn graphemes.slice(index).join(\"\");\n}\n\n/**\n * Startup header: brand mark on the left, runtime metadata on the right.\n *\n * Values are read through getters rather than captured at construction, so the\n * model fills in once the session binds without an explicit refresh.\n *\n * The metadata column is dropped when the terminal cannot hold it, and the mark\n * falls back to a compact variant rather than being truncated mid-glyph.\n */\nexport class ApexSplashHeader implements Component {\n\tprivate readonly version: string;\n\tprivate readonly getModelId: () => string | undefined;\n\tprivate readonly getCwd: () => string;\n\tprivate readonly verboseInstructions: string | undefined;\n\tprivate readonly options: ApexSplashHeaderOptions;\n\n\tconstructor(\n\t\tversion: string,\n\t\tgetModelId: () => string | undefined,\n\t\tgetCwd: () => string,\n\t\tverboseInstructions?: string,\n\t\toptions: ApexSplashHeaderOptions = {},\n\t) {\n\t\tthis.version = version;\n\t\tthis.getModelId = getModelId;\n\t\tthis.getCwd = getCwd;\n\t\tthis.verboseInstructions = verboseInstructions;\n\t\tthis.options = options;\n\t}\n\n\tinvalidate(): void {\n\t\t// Render output is derived from current theme and session state.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, width);\n\t\tconst paddingX = safeWidth > 1 ? 1 : 0;\n\t\tconst contentWidth = Math.max(1, safeWidth - paddingX * 2);\n\n\t\tconst logoLines = this.selectLogo(contentWidth);\n\t\tconst logoWidth = logoLines.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);\n\n\t\tconst metaWidth = contentWidth - logoWidth - GUTTER;\n\t\tconst showMeta = metaWidth >= LABEL_WIDTH + MIN_VALUE_WIDTH;\n\t\tconst valueWidth = Math.max(1, metaWidth - LABEL_WIDTH);\n\t\tconst metaLines = showMeta ? this.buildMetadata(valueWidth) : [];\n\t\tconst metaStart = Math.max(0, Math.floor((logoLines.length - metaLines.length) / 2));\n\n\t\tconst lines = this.options.topPadding ? [this.padLine(\"\", safeWidth, paddingX)] : [];\n\n\t\tlogoLines.forEach((line, index) => {\n\t\t\tconst meta = index >= metaStart && index < metaStart + metaLines.length ? metaLines[index - metaStart] : \"\";\n\t\t\tconst gap = showMeta ? \" \".repeat(Math.max(0, logoWidth - visibleWidth(line) + GUTTER)) : \"\";\n\t\t\tconst content = truncateToWidth(theme.fg(\"text\", line) + gap + meta, contentWidth, \"\");\n\t\t\tlines.push(this.padLine(content, safeWidth, paddingX));\n\t\t});\n\n\t\t// The hint is product copy rather than a runtime fact, so it sits under the\n\t\t// whole block at full width instead of inside the narrow metadata column,\n\t\t// where it would truncate even on a wide terminal.\n\t\tconst hint = this.options.getHint?.();\n\t\tif (hint) {\n\t\t\tlines.push(this.padLine(\"\", safeWidth, paddingX));\n\t\t\tlines.push(this.padLine(theme.fg(\"dim\", truncateToWidth(hint, contentWidth)), safeWidth, paddingX));\n\t\t}\n\n\t\tif (this.verboseInstructions) {\n\t\t\tlines.push(this.padLine(\"\", safeWidth, paddingX));\n\t\t\tfor (const instruction of this.verboseInstructions.split(\"\\n\")) {\n\t\t\t\tlines.push(this.padLine(truncateToWidth(instruction, contentWidth), safeWidth, paddingX));\n\t\t\t}\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\tprivate selectLogo(contentWidth: number): string[] {\n\t\tif (this.options.logo) {\n\t\t\treturn this.options.logo.split(\"\\n\");\n\t\t}\n\t\tif (this.options.getSymbolPreset?.() === \"ascii\") {\n\t\t\treturn APEX_PEAK_LOGO_ASCII.split(\"\\n\");\n\t\t}\n\t\tconst full = APEX_PEAK_LOGO.split(\"\\n\");\n\t\tconst fullWidth = full.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);\n\t\treturn contentWidth >= fullWidth ? full : APEX_PEAK_LOGO_COMPACT.split(\"\\n\");\n\t}\n\n\tprivate buildMetadata(valueWidth: number): string[] {\n\t\tconst labelled = (label: string, value: string) =>\n\t\t\ttheme.fg(\"dim\", label.padEnd(LABEL_WIDTH)) + theme.fg(\"muted\", truncateToWidth(value, valueWidth));\n\n\t\tconst cwd = formatCwdForFooter(this.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\t\treturn [\n\t\t\tlabelled(\"version\", `v${this.version}`),\n\t\t\tlabelled(\"model\", this.getModelId() ?? \"—\"),\n\t\t\ttheme.fg(\"dim\", \"cwd\".padEnd(LABEL_WIDTH)) + theme.fg(\"muted\", truncatePathTail(cwd, valueWidth)),\n\t\t\t...(this.options.getExtraMetadata?.() ?? []).map((line) => labelled(line.label, line.value)),\n\t\t];\n\t}\n\n\tprivate padLine(content: string, safeWidth: number, paddingX: number): string {\n\t\tconst trailing = Math.max(0, safeWidth - paddingX - visibleWidth(content));\n\t\treturn \" \".repeat(paddingX) + content + \" \".repeat(trailing);\n\t}\n}\n"]}
|