@narumitw/pi-statusline 0.50.0 → 0.50.2
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/README.md +29 -209
- package/dist/chunks/{chunk-74QZGJ7L.js → chunk-LARLXEUL.ts} +18 -97
- package/dist/chunks/chunk-LARLXEUL.ts.map +7 -0
- package/dist/chunks/{commands-C2G2G7C6.js → commands-KDWSOSZZ.ts} +14 -58
- package/dist/chunks/commands-KDWSOSZZ.ts.map +7 -0
- package/dist/index.ts +21 -76
- package/dist/index.ts.map +2 -2
- package/docs/configuration.md +194 -0
- package/package.json +51 -51
- package/src/ansi.ts +18 -24
- package/src/command-contract.ts +6 -6
- package/src/commands.ts +759 -818
- package/src/directory.ts +27 -31
- package/src/extension-status.ts +186 -219
- package/src/git-status.ts +103 -111
- package/src/information-profiles.ts +12 -31
- package/src/powerline.ts +120 -127
- package/src/presets/candy.ts +1 -7
- package/src/presets/create-ramp.ts +21 -23
- package/src/presets/custom.ts +7 -7
- package/src/presets/forest.ts +1 -7
- package/src/presets/index.ts +30 -32
- package/src/presets/mono.ts +1 -7
- package/src/presets/neon.ts +1 -7
- package/src/presets/ocean.ts +1 -7
- package/src/presets/sunset.ts +1 -7
- package/src/presets/tokyo-night.ts +9 -9
- package/src/presets/types.ts +5 -5
- package/src/render.ts +277 -306
- package/src/settings.ts +516 -568
- package/src/statusline.ts +347 -372
- package/src/types.ts +34 -42
- package/src/usage.ts +40 -40
- package/dist/chunks/chunk-74QZGJ7L.js.map +0 -7
- package/dist/chunks/commands-C2G2G7C6.js.map +0 -7
package/src/git-status.ts
CHANGED
|
@@ -4,145 +4,137 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
4
4
|
const GIT_STATUS_TIMEOUT_MS = 3_000;
|
|
5
5
|
|
|
6
6
|
export interface GitStatusSummary {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
root?: string;
|
|
8
|
+
ahead: number;
|
|
9
|
+
behind: number;
|
|
10
|
+
staged: number;
|
|
11
|
+
modified: number;
|
|
12
|
+
untracked: number;
|
|
13
|
+
conflicts: number;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export async function readGitStatus(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
pi: ExtensionAPI,
|
|
18
|
+
cwd: string,
|
|
19
|
+
signal?: AbortSignal,
|
|
20
20
|
): Promise<GitStatusSummary | undefined> {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
21
|
+
const [statusResult, rootResult] = await Promise.all([
|
|
22
|
+
pi.exec("git", ["--no-optional-locks", "status", "--porcelain=v1", "--branch", "--untracked-files=normal"], {
|
|
23
|
+
cwd,
|
|
24
|
+
timeout: GIT_STATUS_TIMEOUT_MS,
|
|
25
|
+
signal,
|
|
26
|
+
}),
|
|
27
|
+
pi
|
|
28
|
+
.exec("git", ["rev-parse", "--path-format=absolute", "--show-toplevel"], {
|
|
29
|
+
cwd,
|
|
30
|
+
timeout: GIT_STATUS_TIMEOUT_MS,
|
|
31
|
+
signal,
|
|
32
|
+
})
|
|
33
|
+
.catch(() => undefined),
|
|
34
|
+
]);
|
|
35
|
+
if (statusResult.code !== 0 || statusResult.killed) return undefined;
|
|
36
|
+
const summary = parseGitStatusPorcelain(statusResult.stdout);
|
|
37
|
+
if (rootResult && rootResult.code === 0 && !rootResult.killed) {
|
|
38
|
+
const root = parseGitRoot(rootResult.stdout);
|
|
39
|
+
if (root) summary.root = root;
|
|
40
|
+
}
|
|
41
|
+
return summary;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export function parseGitStatusPorcelain(output: string): GitStatusSummary {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
45
|
+
const summary: GitStatusSummary = {
|
|
46
|
+
ahead: 0,
|
|
47
|
+
behind: 0,
|
|
48
|
+
staged: 0,
|
|
49
|
+
modified: 0,
|
|
50
|
+
untracked: 0,
|
|
51
|
+
conflicts: 0,
|
|
52
|
+
};
|
|
53
|
+
for (const line of output.split(/\r?\n/)) {
|
|
54
|
+
if (!line) continue;
|
|
55
|
+
if (line.startsWith("## ")) {
|
|
56
|
+
const ahead = line.match(/\bahead (\d+)/u);
|
|
57
|
+
const behind = line.match(/\bbehind (\d+)/u);
|
|
58
|
+
summary.ahead = ahead ? Number(ahead[1]) : 0;
|
|
59
|
+
summary.behind = behind ? Number(behind[1]) : 0;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const indexStatus = line[0] ?? " ";
|
|
63
|
+
const worktreeStatus = line[1] ?? " ";
|
|
64
|
+
if (indexStatus === "?" && worktreeStatus === "?") {
|
|
65
|
+
summary.untracked += 1;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (isConflictStatus(indexStatus, worktreeStatus)) {
|
|
69
|
+
summary.conflicts += 1;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (isChangedStatus(indexStatus)) summary.staged += 1;
|
|
73
|
+
if (isChangedStatus(worktreeStatus)) summary.modified += 1;
|
|
74
|
+
}
|
|
75
|
+
return summary;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function isConflictStatus(indexStatus: string, worktreeStatus: string): boolean {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
return (
|
|
80
|
+
(indexStatus === "D" && worktreeStatus === "D") ||
|
|
81
|
+
(indexStatus === "A" && worktreeStatus === "A") ||
|
|
82
|
+
indexStatus === "U" ||
|
|
83
|
+
worktreeStatus === "U"
|
|
84
|
+
);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function isChangedStatus(status: string): boolean {
|
|
88
|
-
|
|
88
|
+
return status !== " " && status !== "?" && status !== "!";
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
export function parseGitRoot(output: string): string | undefined {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
const root = output.split(/\r?\n/u)[0]?.trim();
|
|
93
|
+
return root && isAbsolute(root) ? root : undefined;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
export function formatGitStatusSummary(summary: GitStatusSummary | undefined): string {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
97
|
+
if (!summary) return "";
|
|
98
|
+
const tokens = [
|
|
99
|
+
["⇡", summary.ahead],
|
|
100
|
+
["⇣", summary.behind],
|
|
101
|
+
["+", summary.staged],
|
|
102
|
+
["~", summary.modified],
|
|
103
|
+
["?", summary.untracked],
|
|
104
|
+
["!", summary.conflicts],
|
|
105
|
+
] as const;
|
|
106
|
+
return tokens
|
|
107
|
+
.filter(([, count]) => count > 0)
|
|
108
|
+
.map(([prefix, count]) => `${prefix}${formatCount(count)}`)
|
|
109
|
+
.join(" ");
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
export function formatGitBranchValue(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
): string {
|
|
117
|
-
if (!branch) return "no-git";
|
|
118
|
-
const suffixes = [formatGitStatusSummary(status), pr ? `(${pr})` : ""].filter(Boolean);
|
|
119
|
-
return suffixes.length > 0 ? `${branch} ${suffixes.join(" ")}` : branch;
|
|
112
|
+
export function formatGitBranchValue(branch: string | null, status: GitStatusSummary | undefined, pr?: string): string {
|
|
113
|
+
if (!branch) return "no-git";
|
|
114
|
+
const suffixes = [formatGitStatusSummary(status), pr ? `(${pr})` : ""].filter(Boolean);
|
|
115
|
+
return suffixes.length > 0 ? `${branch} ${suffixes.join(" ")}` : branch;
|
|
120
116
|
}
|
|
121
117
|
|
|
122
|
-
export function formatGitBranchText(
|
|
123
|
-
|
|
124
|
-
status: GitStatusSummary | undefined,
|
|
125
|
-
pr?: string,
|
|
126
|
-
): string {
|
|
127
|
-
return `🌿 ${formatGitBranchValue(branch, status, pr)}`;
|
|
118
|
+
export function formatGitBranchText(branch: string | null, status: GitStatusSummary | undefined, pr?: string): string {
|
|
119
|
+
return `🌿 ${formatGitBranchValue(branch, status, pr)}`;
|
|
128
120
|
}
|
|
129
121
|
|
|
130
122
|
export function gitStatusSummaryEqual(
|
|
131
|
-
|
|
132
|
-
|
|
123
|
+
left: GitStatusSummary | undefined,
|
|
124
|
+
right: GitStatusSummary | undefined,
|
|
133
125
|
): boolean {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
126
|
+
if (!left || !right) return left === right;
|
|
127
|
+
return (
|
|
128
|
+
left.root === right.root &&
|
|
129
|
+
left.ahead === right.ahead &&
|
|
130
|
+
left.behind === right.behind &&
|
|
131
|
+
left.staged === right.staged &&
|
|
132
|
+
left.modified === right.modified &&
|
|
133
|
+
left.untracked === right.untracked &&
|
|
134
|
+
left.conflicts === right.conflicts
|
|
135
|
+
);
|
|
144
136
|
}
|
|
145
137
|
|
|
146
138
|
function formatCount(value: number): string {
|
|
147
|
-
|
|
139
|
+
return Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 }).format(value);
|
|
148
140
|
}
|
|
@@ -4,37 +4,18 @@ export const INFORMATION_PROFILE_NAMES = ["minimal", "balanced", "detailed"] as
|
|
|
4
4
|
export type InformationProfileName = (typeof INFORMATION_PROFILE_NAMES)[number];
|
|
5
5
|
export type InformationProfile = InformationProfileName | "custom";
|
|
6
6
|
|
|
7
|
-
export const INFORMATION_PROFILES: Readonly<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
balanced: ["model", "thinking", "cwd", "branch", "tools", "context", "time"],
|
|
12
|
-
detailed: [
|
|
13
|
-
"provider",
|
|
14
|
-
"model",
|
|
15
|
-
"thinking",
|
|
16
|
-
"cwd",
|
|
17
|
-
"branch",
|
|
18
|
-
"tools",
|
|
19
|
-
"context",
|
|
20
|
-
"tokens",
|
|
21
|
-
"cache",
|
|
22
|
-
"cost",
|
|
23
|
-
"time",
|
|
24
|
-
],
|
|
7
|
+
export const INFORMATION_PROFILES: Readonly<Record<InformationProfileName, readonly SegmentName[]>> = {
|
|
8
|
+
minimal: ["model", "cwd", "branch", "context"],
|
|
9
|
+
balanced: ["model", "thinking", "cwd", "branch", "tools", "context", "time"],
|
|
10
|
+
detailed: ["provider", "model", "thinking", "cwd", "branch", "tools", "context", "tokens", "cache", "cost", "time"],
|
|
25
11
|
};
|
|
26
12
|
|
|
27
|
-
export function inferInformationProfile(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
) {
|
|
36
|
-
return name;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return "custom";
|
|
13
|
+
export function inferInformationProfile(segments: readonly ConfigSegmentName[]): InformationProfile {
|
|
14
|
+
for (const name of INFORMATION_PROFILE_NAMES) {
|
|
15
|
+
const profile = INFORMATION_PROFILES[name];
|
|
16
|
+
if (segments.length === profile.length && segments.every((segment, index) => segment === profile[index])) {
|
|
17
|
+
return name;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return "custom";
|
|
40
21
|
}
|
package/src/powerline.ts
CHANGED
|
@@ -4,173 +4,166 @@ import { ansiStyle } from "./ansi.js";
|
|
|
4
4
|
import { resolvePreset } from "./presets/index.js";
|
|
5
5
|
import type { BlockColors, PowerlinePreset } from "./presets/types.js";
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
LINE_BREAK_SEGMENT_NAME,
|
|
8
|
+
type PalettePreset,
|
|
9
|
+
type PowerlineBlockName,
|
|
10
|
+
type RenderItem,
|
|
11
|
+
type RenderSegment,
|
|
12
|
+
type SegmentPalette,
|
|
13
|
+
type SeparatorName,
|
|
14
|
+
type StatuslineConfig,
|
|
15
15
|
} from "./types.js";
|
|
16
16
|
|
|
17
17
|
interface PowerlineBlock {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
baseBlock: PowerlineBlockName;
|
|
19
|
+
colors: BlockColors;
|
|
20
|
+
segments: RenderSegment[];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function renderPowerlineStatusline(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
width: number,
|
|
25
|
+
items: RenderItem[],
|
|
26
|
+
config: Pick<StatuslineConfig, "palettePreset" | "palette" | "density" | "separator">,
|
|
27
|
+
trueColor = true,
|
|
28
28
|
): string {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
if (items.length === 0 || width <= 0) return "";
|
|
30
|
+
return splitLines(items)
|
|
31
|
+
.map((segments) => fitPowerlineSegments(segments, width, config, trueColor))
|
|
32
|
+
.join("\n");
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function splitLines(items: RenderItem[]): RenderSegment[][] {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const lines: RenderSegment[][] = [[]];
|
|
37
|
+
for (const item of items) {
|
|
38
|
+
if (item.name === LINE_BREAK_SEGMENT_NAME) lines.push([]);
|
|
39
|
+
else lines.at(-1)?.push(item);
|
|
40
|
+
}
|
|
41
|
+
return lines;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const SEGMENT_RETENTION_PRIORITY: Readonly<Record<RenderSegment["name"], number>> = {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
context: 120,
|
|
46
|
+
model: 110,
|
|
47
|
+
branch: 100,
|
|
48
|
+
tools: 90,
|
|
49
|
+
cwd: 80,
|
|
50
|
+
thinking: 70,
|
|
51
|
+
cost: 60,
|
|
52
|
+
provider: 50,
|
|
53
|
+
cache: 45,
|
|
54
|
+
tokens: 40,
|
|
55
|
+
time: 30,
|
|
56
|
+
turn: 20,
|
|
57
|
+
brand: 10,
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
function fitPowerlineSegments(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
segments: readonly RenderSegment[],
|
|
62
|
+
width: number,
|
|
63
|
+
config: Pick<StatuslineConfig, "palettePreset" | "palette" | "density" | "separator">,
|
|
64
|
+
trueColor: boolean,
|
|
65
65
|
): string {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
66
|
+
if (segments.length === 0) return "";
|
|
67
|
+
const fitted = [...segments];
|
|
68
|
+
while (fitted.length > 1) {
|
|
69
|
+
const rendered = joinPowerlineSegments(fitted, config, trueColor);
|
|
70
|
+
if (visibleWidth(rendered) <= width) return rendered;
|
|
71
|
+
let removalIndex = 0;
|
|
72
|
+
for (let index = 1; index < fitted.length; index += 1) {
|
|
73
|
+
const candidate = fitted[index];
|
|
74
|
+
const current = fitted[removalIndex];
|
|
75
|
+
if (
|
|
76
|
+
candidate &&
|
|
77
|
+
current &&
|
|
78
|
+
SEGMENT_RETENTION_PRIORITY[candidate.name] < SEGMENT_RETENTION_PRIORITY[current.name]
|
|
79
|
+
) {
|
|
80
|
+
removalIndex = index;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
fitted.splice(removalIndex, 1);
|
|
84
|
+
}
|
|
85
|
+
const rendered = joinPowerlineSegments(fitted, config, trueColor);
|
|
86
|
+
return visibleWidth(rendered) <= width ? rendered : "";
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
export function powerlineExtensionSeparator(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
_theme: Theme,
|
|
91
|
+
palettePreset: PalettePreset = "tokyo-night",
|
|
92
|
+
trueColor = true,
|
|
93
93
|
): string {
|
|
94
|
-
|
|
94
|
+
return ansiStyle(" • ", { fg: resolvePreset(palettePreset).extensionSeparator }, trueColor);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function joinPowerlineSegments(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
segments: RenderSegment[],
|
|
99
|
+
config: Pick<StatuslineConfig, "palettePreset" | "palette" | "density" | "separator">,
|
|
100
|
+
trueColor: boolean,
|
|
101
101
|
): string {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
const preset = resolvePreset(config.palettePreset);
|
|
103
|
+
const blocks = contiguousBlocks(segments, preset, config.palettePreset, config.palette);
|
|
104
|
+
let line = ansiStyle("░▒▓", { fg: preset.lead }, trueColor);
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
for (const [index, block] of blocks.entries()) {
|
|
107
|
+
const previous = index === 0 ? undefined : blocks[index - 1]?.colors;
|
|
108
|
+
if (previous) {
|
|
109
|
+
line += ansiStyle("", { fg: previous.bg, bg: block.colors.bg }, trueColor);
|
|
110
|
+
}
|
|
111
|
+
line += ansiStyle(formatBlockText(block, config), block.colors, trueColor);
|
|
112
|
+
}
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
const lastBlock = blocks.at(-1);
|
|
115
|
+
if (lastBlock) line += ansiStyle("", { fg: lastBlock.colors.bg }, trueColor);
|
|
116
|
+
return line;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
function contiguousBlocks(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
segments: RenderSegment[],
|
|
121
|
+
preset: PowerlinePreset,
|
|
122
|
+
palettePreset: PalettePreset,
|
|
123
|
+
configuredPalette: SegmentPalette,
|
|
124
124
|
): PowerlineBlock[] {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (matchesPrevious) previous.segments.push(segment);
|
|
138
|
-
else blocks.push({ baseBlock: segment.block, colors, segments: [segment] });
|
|
139
|
-
}
|
|
140
|
-
return blocks;
|
|
125
|
+
const blocks: PowerlineBlock[] = [];
|
|
126
|
+
const usesConfiguredColors = palettePreset === "custom";
|
|
127
|
+
for (const segment of segments) {
|
|
128
|
+
const colors = usesConfiguredColors ? (configuredPalette[segment.name] ?? {}) : preset.blocks[segment.block];
|
|
129
|
+
const previous = blocks.at(-1);
|
|
130
|
+
const matchesPrevious =
|
|
131
|
+
previous !== undefined &&
|
|
132
|
+
(usesConfiguredColors ? colorsEqual(previous.colors, colors) : previous.baseBlock === segment.block);
|
|
133
|
+
if (matchesPrevious) previous.segments.push(segment);
|
|
134
|
+
else blocks.push({ baseBlock: segment.block, colors, segments: [segment] });
|
|
135
|
+
}
|
|
136
|
+
return blocks;
|
|
141
137
|
}
|
|
142
138
|
|
|
143
139
|
function colorsEqual(left: BlockColors, right: BlockColors): boolean {
|
|
144
|
-
|
|
140
|
+
return left.fg === right.fg && left.bg === right.bg;
|
|
145
141
|
}
|
|
146
142
|
|
|
147
|
-
function formatBlockText(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const leading = config.density === "cozy" ? " " : " ";
|
|
154
|
-
const trailing = config.density === "cozy" ? " " : "";
|
|
155
|
-
return `${leading}${texts.join(separator)}${trailing}`;
|
|
143
|
+
function formatBlockText(block: PowerlineBlock, config: Pick<StatuslineConfig, "density" | "separator">): string {
|
|
144
|
+
const texts = block.segments.map(formatSegmentText);
|
|
145
|
+
const separator = separatorText(config.separator, config.density === "cozy");
|
|
146
|
+
const leading = config.density === "cozy" ? " " : " ";
|
|
147
|
+
const trailing = config.density === "cozy" ? " " : "";
|
|
148
|
+
return `${leading}${texts.join(separator)}${trailing}`;
|
|
156
149
|
}
|
|
157
150
|
|
|
158
151
|
function formatSegmentText(segment: RenderSegment): string {
|
|
159
|
-
|
|
152
|
+
return segment.emphasis ? `\u001b[1m${segment.text}\u001b[22m` : segment.text;
|
|
160
153
|
}
|
|
161
154
|
|
|
162
155
|
function separatorText(separator: SeparatorName, cozy: boolean): string {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
156
|
+
const padding = cozy ? " " : " ";
|
|
157
|
+
switch (separator) {
|
|
158
|
+
case "dot":
|
|
159
|
+
return `${padding}•${padding}`;
|
|
160
|
+
case "bar":
|
|
161
|
+
return `${padding}│${padding}`;
|
|
162
|
+
case "powerline":
|
|
163
|
+
return `${padding}${padding}`;
|
|
164
|
+
case "round":
|
|
165
|
+
return `${padding}❯${padding}`;
|
|
166
|
+
case "none":
|
|
167
|
+
return padding;
|
|
168
|
+
}
|
|
176
169
|
}
|
package/src/presets/candy.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
import { createRampPreset } from "./create-ramp.js";
|
|
2
2
|
|
|
3
|
-
export const CANDY_PRESET = createRampPreset([
|
|
4
|
-
"#f5c2e7",
|
|
5
|
-
"#cba6f7",
|
|
6
|
-
"#89b4fa",
|
|
7
|
-
"#745f9a",
|
|
8
|
-
"#403a5c",
|
|
9
|
-
]);
|
|
3
|
+
export const CANDY_PRESET = createRampPreset(["#f5c2e7", "#cba6f7", "#89b4fa", "#745f9a", "#403a5c"]);
|