@herbertgao/pi-extensions 2026.8.3 → 2026.8.4
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 +41 -0
- package/examples/pi-footer.json +275 -0
- package/node_modules/pi-footer/CHANGELOG.md +145 -0
- package/node_modules/pi-footer/LICENSE +21 -0
- package/node_modules/pi-footer/README.md +354 -0
- package/node_modules/pi-footer/package.json +68 -0
- package/node_modules/pi-footer/src/cache.ts +106 -0
- package/node_modules/pi-footer/src/colors.ts +237 -0
- package/node_modules/pi-footer/src/config.ts +202 -0
- package/node_modules/pi-footer/src/event-widgets.ts +41 -0
- package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
- package/node_modules/pi-footer/src/git.ts +135 -0
- package/node_modules/pi-footer/src/index.ts +268 -0
- package/node_modules/pi-footer/src/metrics.ts +141 -0
- package/node_modules/pi-footer/src/presets.ts +383 -0
- package/node_modules/pi-footer/src/render.ts +114 -0
- package/node_modules/pi-footer/src/separators.ts +52 -0
- package/node_modules/pi-footer/src/types.ts +122 -0
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
- package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
- package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
- package/node_modules/pi-footer/src/ui/events.ts +35 -0
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
- package/node_modules/pi-footer/src/ui/fields.ts +252 -0
- package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
- package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
- package/node_modules/pi-footer/src/ui/layout.ts +17 -0
- package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
- package/node_modules/pi-footer/src/ui/model.ts +64 -0
- package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
- package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
- package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
- package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
- package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
- package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
- package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
- package/node_modules/pi-footer/src/ui/screen.ts +243 -0
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
- package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
- package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
- package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
- package/node_modules/pi-footer/src/ui/theme.ts +48 -0
- package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
- package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
- package/node_modules/pi-footer/src/ui.ts +66 -0
- package/node_modules/pi-footer/src/widgets/context.ts +28 -0
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
- package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
- package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
- package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
- package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
- package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
- package/node_modules/pi-footer/src/widgets/options.ts +122 -0
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
- package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/store.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/types.ts +181 -0
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
- package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
- package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
- package/package.json +7 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const TextVerbosityWidget = defineWidget({
|
|
4
|
+
type: "text-verbosity",
|
|
5
|
+
label: "Text Verbosity",
|
|
6
|
+
category: "Core",
|
|
7
|
+
description: "Text verbosity for models/providers that support it",
|
|
8
|
+
dependencies: ["textVerbosity"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "📝", nerd: "", text: "verbosity" },
|
|
13
|
+
defaultStyle: { fg: "cyan", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(ctx.textVerbosity);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const ThinkingLevelWidget = defineWidget({
|
|
4
|
+
type: "thinking-level",
|
|
5
|
+
label: "Thinking Level",
|
|
6
|
+
category: "Core",
|
|
7
|
+
description: "Pi reasoning/thinking level for reasoning-capable models",
|
|
8
|
+
dependencies: ["thinkingLevel"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "🧠", nerd: "", text: "thinking" },
|
|
13
|
+
defaultStyle: { fg: "magenta", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(ctx.thinkingLevel);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitAheadBehindWidget = defineWidget({
|
|
4
|
+
type: "git-ahead-behind",
|
|
5
|
+
label: "Git Ahead/Behind",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Ahead/behind upstream counts",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "↕️", nerd: "", text: "upstream" },
|
|
13
|
+
defaultStyle: { fg: "cyan", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(ctx.git.isRepo ? `↑${ctx.git.ahead} ↓${ctx.git.behind}` : "");
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
const GIT_BRANCH_DISPLAY_STYLES = {
|
|
4
|
+
default: {
|
|
5
|
+
label: "Default",
|
|
6
|
+
list: "default",
|
|
7
|
+
},
|
|
8
|
+
"round-brackets": {
|
|
9
|
+
label: "Round brackets",
|
|
10
|
+
list: "brackets",
|
|
11
|
+
},
|
|
12
|
+
custom: {
|
|
13
|
+
label: "Custom surround text",
|
|
14
|
+
list: "custom",
|
|
15
|
+
},
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
type GitBranchDisplayStyle = keyof typeof GIT_BRANCH_DISPLAY_STYLES;
|
|
19
|
+
|
|
20
|
+
const GIT_BRANCH_DISPLAY_STYLE_VALUES = Object.keys(
|
|
21
|
+
GIT_BRANCH_DISPLAY_STYLES,
|
|
22
|
+
) as GitBranchDisplayStyle[];
|
|
23
|
+
|
|
24
|
+
export const GitBranchWidget = defineWidget({
|
|
25
|
+
type: "git-branch",
|
|
26
|
+
label: "Git Branch",
|
|
27
|
+
category: "Git",
|
|
28
|
+
description: "Current Git branch",
|
|
29
|
+
dependencies: ["git"],
|
|
30
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
31
|
+
baseOptionDefaults: { text: "" },
|
|
32
|
+
properties: [
|
|
33
|
+
{
|
|
34
|
+
id: "gitBranchDisplayStyle",
|
|
35
|
+
label: "Display style",
|
|
36
|
+
kind: "choice",
|
|
37
|
+
description: "Branch name display style",
|
|
38
|
+
default: "default",
|
|
39
|
+
options: {
|
|
40
|
+
choices: GIT_BRANCH_DISPLAY_STYLE_VALUES.map((style) => ({
|
|
41
|
+
id: style,
|
|
42
|
+
label: GIT_BRANCH_DISPLAY_STYLES[style].label,
|
|
43
|
+
list: GIT_BRANCH_DISPLAY_STYLES[style].list,
|
|
44
|
+
})),
|
|
45
|
+
showInWidgets: true,
|
|
46
|
+
showInColors: false,
|
|
47
|
+
listProperty: "display",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "surroundLeft",
|
|
52
|
+
label: "Surround left",
|
|
53
|
+
kind: "text",
|
|
54
|
+
description: "Text before the branch name when custom display is enabled",
|
|
55
|
+
default: "",
|
|
56
|
+
showWhen: { property: "gitBranchDisplayStyle", equals: "custom" },
|
|
57
|
+
options: { showInWidgets: false, showInColors: false },
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "surroundRight",
|
|
61
|
+
label: "Surround right",
|
|
62
|
+
kind: "text",
|
|
63
|
+
description: "Text after the branch name when custom display is enabled",
|
|
64
|
+
default: "",
|
|
65
|
+
showWhen: { property: "gitBranchDisplayStyle", equals: "custom" },
|
|
66
|
+
options: { showInWidgets: false, showInColors: false },
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
icons: { emoji: "🌿", nerd: "", text: "git" },
|
|
70
|
+
defaultStyle: { fg: "magenta", bg: "default", bold: false },
|
|
71
|
+
render({ ctx, options, renderWidget }) {
|
|
72
|
+
const branch = ctx.git.branch;
|
|
73
|
+
if (!branch) return renderWidget("");
|
|
74
|
+
|
|
75
|
+
if (options.gitBranchDisplayStyle === "round-brackets") return renderWidget(`(${branch})`);
|
|
76
|
+
if (options.gitBranchDisplayStyle === "custom") {
|
|
77
|
+
return renderWidget(`${options.surroundLeft}${branch}${options.surroundRight}`);
|
|
78
|
+
}
|
|
79
|
+
return renderWidget(branch);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitCleanStatusWidget = defineWidget({
|
|
4
|
+
type: "git-clean",
|
|
5
|
+
label: "Git Clean Status",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Clean/dirty state",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "✅", nerd: "", text: "git" },
|
|
13
|
+
defaultStyle: { fg: "green", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
if (!ctx.git.isRepo) return renderWidget("");
|
|
16
|
+
const changes = ctx.git.staged + ctx.git.unstaged + ctx.git.untracked;
|
|
17
|
+
return renderWidget(changes === 0 ? "clean" : "dirty");
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitDeletionsWidget = defineWidget({
|
|
4
|
+
type: "git-deletions",
|
|
5
|
+
label: "Git Deletions",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Uncommitted deletion count",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenZero", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "➖", nerd: "-", text: "del" },
|
|
13
|
+
defaultStyle: { fg: "red", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(`${ctx.git.deletions}`);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitDiffWidget = defineWidget({
|
|
4
|
+
type: "git-diff",
|
|
5
|
+
label: "Git Diff",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Insertion/deletion diff summary",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [
|
|
12
|
+
{
|
|
13
|
+
id: "gitDiffMode",
|
|
14
|
+
label: "Display",
|
|
15
|
+
kind: "choice",
|
|
16
|
+
description: "Git diff display mode",
|
|
17
|
+
default: "plain",
|
|
18
|
+
options: {
|
|
19
|
+
choices: [
|
|
20
|
+
{ id: "plain", label: "Plain (+/-)", list: "Plain (+/-)" },
|
|
21
|
+
{ id: "compact", label: "Compact (+n,-n)", list: "Compact (+n,-n)" },
|
|
22
|
+
],
|
|
23
|
+
showInWidgets: true,
|
|
24
|
+
showInColors: false,
|
|
25
|
+
listProperty: "display",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
icons: { emoji: "📈", nerd: "", text: "diff" },
|
|
30
|
+
defaultStyle: { fg: "yellow", bg: "default", bold: false },
|
|
31
|
+
render({ ctx, options, renderWidget }) {
|
|
32
|
+
return renderWidget(
|
|
33
|
+
options.gitDiffMode === "compact"
|
|
34
|
+
? `(+${ctx.git.insertions},-${ctx.git.deletions})`
|
|
35
|
+
: `+${ctx.git.insertions}/-${ctx.git.deletions}`,
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitInsertionsWidget = defineWidget({
|
|
4
|
+
type: "git-insertions",
|
|
5
|
+
label: "Git Insertions",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Uncommitted insertion count",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenZero", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "➕", nerd: "+", text: "ins" },
|
|
13
|
+
defaultStyle: { fg: "green", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(`${ctx.git.insertions}`);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitRemoteWidget = defineWidget({
|
|
4
|
+
type: "git-remote",
|
|
5
|
+
label: "Git Remote",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Origin remote",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "🌐", nerd: "", text: "remote" },
|
|
13
|
+
defaultStyle: { fg: "cyan", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(ctx.git.remote ?? "");
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitRootDirWidget = defineWidget({
|
|
4
|
+
type: "git-root",
|
|
5
|
+
label: "Git Root Dir",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Repository root directory name",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "📦", nerd: "", text: "repo" },
|
|
13
|
+
defaultStyle: { fg: "cyan", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(ctx.git.root ?? "");
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitShaWidget = defineWidget({
|
|
4
|
+
type: "git-sha",
|
|
5
|
+
label: "Git SHA",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Short HEAD commit SHA",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "🔖", nerd: "", text: "sha" },
|
|
13
|
+
defaultStyle: { fg: "brightBlack", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(ctx.git.sha ?? "");
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitStagedWidget = defineWidget({
|
|
4
|
+
type: "git-staged",
|
|
5
|
+
label: "Git Staged Files",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Staged file count",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenZero", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "➕", nerd: "+", text: "staged" },
|
|
13
|
+
defaultStyle: { fg: "green", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(`${ctx.git.staged}`);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitStatusWidget = defineWidget({
|
|
4
|
+
type: "git-status",
|
|
5
|
+
label: "Git Status",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Staged/unstaged/untracked counts",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenEmpty", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "🔀", nerd: "", text: "git" },
|
|
13
|
+
defaultStyle: { fg: "yellow", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(
|
|
16
|
+
ctx.git.isRepo ? `+${ctx.git.staged} ±${ctx.git.unstaged} ?${ctx.git.untracked}` : "",
|
|
17
|
+
);
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitUnstagedWidget = defineWidget({
|
|
4
|
+
type: "git-unstaged",
|
|
5
|
+
label: "Git Unstaged Files",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Unstaged file count",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenZero", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "📝", nerd: "±", text: "unstaged" },
|
|
13
|
+
defaultStyle: { fg: "yellow", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(`${ctx.git.unstaged}`);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const GitUntrackedWidget = defineWidget({
|
|
4
|
+
type: "git-untracked",
|
|
5
|
+
label: "Git Untracked Files",
|
|
6
|
+
category: "Git",
|
|
7
|
+
description: "Untracked file count",
|
|
8
|
+
dependencies: ["git"],
|
|
9
|
+
baseOptions: ["raw", "hideWhenZero", "icon", "text"],
|
|
10
|
+
baseOptionDefaults: { text: "" },
|
|
11
|
+
properties: [],
|
|
12
|
+
icons: { emoji: "❓", nerd: "?", text: "untracked" },
|
|
13
|
+
defaultStyle: { fg: "red", bg: "default", bold: false },
|
|
14
|
+
render({ ctx, renderWidget }) {
|
|
15
|
+
return renderWidget(`${ctx.git.untracked}`);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { applyColors, stripAnsi } from "../colors.js";
|
|
2
|
+
import type { WidgetEntry, WidgetOptions } from "../types.js";
|
|
3
|
+
import type { WidgetSpecUnion } from "./registry.js";
|
|
4
|
+
import type {
|
|
5
|
+
BaseWidgetContext,
|
|
6
|
+
ContextFor,
|
|
7
|
+
OptionsFor,
|
|
8
|
+
TypedWidgetRenderArgs,
|
|
9
|
+
Widget,
|
|
10
|
+
WidgetContext,
|
|
11
|
+
WidgetRenderOptions,
|
|
12
|
+
} from "./types.js";
|
|
13
|
+
|
|
14
|
+
export class WidgetInstance<TSpec extends WidgetSpecUnion = WidgetSpecUnion> implements Widget {
|
|
15
|
+
constructor(
|
|
16
|
+
readonly spec: TSpec,
|
|
17
|
+
readonly entry: WidgetEntry,
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
get id(): string {
|
|
21
|
+
return this.entry.id;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get type(): WidgetEntry["type"] {
|
|
25
|
+
return this.entry.type;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get enabled(): boolean {
|
|
29
|
+
return this.entry.enabled;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
set enabled(value: boolean) {
|
|
33
|
+
this.entry.enabled = value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get options(): WidgetOptions {
|
|
37
|
+
return this.entry.options;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
set options(value: WidgetOptions) {
|
|
41
|
+
this.entry.options = value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// TODO(widget-spec): remove casts once render context and hydrated entries preserve concrete spec types.
|
|
45
|
+
render(ctx: WidgetContext): string | undefined {
|
|
46
|
+
const spec = this.spec as unknown as TSpec & {
|
|
47
|
+
render(args: TypedWidgetRenderArgs<TSpec>): string | undefined;
|
|
48
|
+
};
|
|
49
|
+
return spec.render({
|
|
50
|
+
ctx: ctx as ContextFor<TSpec>,
|
|
51
|
+
options: this.options as OptionsFor<TSpec>,
|
|
52
|
+
renderWidget: (value, renderOptions) => {
|
|
53
|
+
return renderWidgetValue(this.entry, value, ctx, {
|
|
54
|
+
...renderOptions,
|
|
55
|
+
icons: renderOptions?.icons ?? this.spec.icons,
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
toggle(enabled = !this.enabled): void {
|
|
62
|
+
this.enabled = enabled;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
update(options: Partial<WidgetOptions>): void {
|
|
66
|
+
this.options = { ...this.options, ...options };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
toEntry(): WidgetEntry {
|
|
70
|
+
return {
|
|
71
|
+
id: this.id,
|
|
72
|
+
type: this.type,
|
|
73
|
+
enabled: this.enabled,
|
|
74
|
+
options: { ...this.options },
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function renderWidgetValue(
|
|
80
|
+
entry: WidgetEntry,
|
|
81
|
+
value: string | undefined,
|
|
82
|
+
ctx: BaseWidgetContext,
|
|
83
|
+
renderOptions: WidgetRenderOptions = {},
|
|
84
|
+
): string | undefined {
|
|
85
|
+
// The persisted option fields the renderer reads (style + base options) — entry.options is
|
|
86
|
+
// already WidgetOptions, which is a superset, so no narrowing is needed.
|
|
87
|
+
const options = entry.options;
|
|
88
|
+
if (!entry.enabled) return undefined;
|
|
89
|
+
|
|
90
|
+
const rawValue = value ?? "";
|
|
91
|
+
if (rawValue.length === 0 && options.hideWhenEmpty) return undefined;
|
|
92
|
+
if (rawValue === "0" && options.hideWhenZero) return undefined;
|
|
93
|
+
|
|
94
|
+
const fallbackValue = rawValue.length === 0 ? (options.text ?? "-") : rawValue;
|
|
95
|
+
const displayValue = renderOptions.stripIncomingStyles ? stripAnsi(fallbackValue) : fallbackValue;
|
|
96
|
+
const label = renderOptions.icons?.[ctx.iconMode];
|
|
97
|
+
const unstyled =
|
|
98
|
+
options.raw === true || ctx.minimalist
|
|
99
|
+
? displayValue
|
|
100
|
+
: options.icon
|
|
101
|
+
? `${options.icon}${displayValue}`
|
|
102
|
+
: label
|
|
103
|
+
? `${label} ${displayValue}`
|
|
104
|
+
: displayValue;
|
|
105
|
+
const styled =
|
|
106
|
+
renderOptions.preservedTrimStyles && !renderOptions.stripIncomingStyles
|
|
107
|
+
? `${renderOptions.preservedTrimStyles}${unstyled}`
|
|
108
|
+
: unstyled;
|
|
109
|
+
|
|
110
|
+
return applyColors(
|
|
111
|
+
styled,
|
|
112
|
+
renderOptions.fg ?? options.fg,
|
|
113
|
+
renderOptions.bg ?? options.bg,
|
|
114
|
+
renderOptions.bold ?? options.bold,
|
|
115
|
+
ctx.colorLevel,
|
|
116
|
+
ctx.theme,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const CustomTextWidget = defineWidget({
|
|
4
|
+
type: "custom-text",
|
|
5
|
+
label: "Custom Text",
|
|
6
|
+
category: "Custom/Layout",
|
|
7
|
+
description: "User-defined text segment",
|
|
8
|
+
dependencies: [],
|
|
9
|
+
baseOptions: ["text"],
|
|
10
|
+
// Custom text only exposes the shared `text` field, with a widget-specific default.
|
|
11
|
+
baseOptionDefaults: { text: "custom" },
|
|
12
|
+
properties: [],
|
|
13
|
+
icons: { emoji: "", nerd: "", text: "" },
|
|
14
|
+
defaultStyle: { fg: "default", bg: "default", bold: false },
|
|
15
|
+
render({ options, renderWidget }) {
|
|
16
|
+
return renderWidget(options.text);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const FlexSeparatorWidget = defineWidget({
|
|
4
|
+
type: "flex-separator",
|
|
5
|
+
label: "Flex Separator",
|
|
6
|
+
category: "Custom/Layout",
|
|
7
|
+
description: "Push following widgets to the right",
|
|
8
|
+
dependencies: [],
|
|
9
|
+
baseOptions: [],
|
|
10
|
+
baseOptionDefaults: {},
|
|
11
|
+
properties: [
|
|
12
|
+
{
|
|
13
|
+
id: "hideWhenEmpty",
|
|
14
|
+
label: "Hide when empty",
|
|
15
|
+
kind: "boolean",
|
|
16
|
+
description: "Hide empty flex separator output",
|
|
17
|
+
default: true,
|
|
18
|
+
options: {
|
|
19
|
+
// Hidden implementation detail: flex separators are structural and should not render text,
|
|
20
|
+
// while the visible UI remains only Enabled as it was before migration.
|
|
21
|
+
showInFields: false,
|
|
22
|
+
showInWidgets: false,
|
|
23
|
+
showInColors: false,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
icons: { emoji: "", nerd: "", text: "" },
|
|
28
|
+
defaultStyle: { fg: "default", bg: "default", bold: false },
|
|
29
|
+
render({ renderWidget }) {
|
|
30
|
+
return renderWidget("");
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { WIDGET_SEPARATOR_VALUES, widgetSeparatorText } from "../../separators.js";
|
|
2
|
+
import { defineWidget } from "../types.js";
|
|
3
|
+
|
|
4
|
+
export const SeparatorWidget = defineWidget({
|
|
5
|
+
type: "separator",
|
|
6
|
+
label: "Separator",
|
|
7
|
+
category: "Custom/Layout",
|
|
8
|
+
description: "Predefined or custom separator segment",
|
|
9
|
+
dependencies: [],
|
|
10
|
+
baseOptions: [],
|
|
11
|
+
baseOptionDefaults: {},
|
|
12
|
+
properties: [
|
|
13
|
+
{
|
|
14
|
+
id: "hideWhenEmpty",
|
|
15
|
+
label: "Hide when empty",
|
|
16
|
+
kind: "boolean",
|
|
17
|
+
description: "Hide empty separator output",
|
|
18
|
+
default: true,
|
|
19
|
+
options: {
|
|
20
|
+
// Hidden implementation detail: `separator=none` and empty custom text must render no segment,
|
|
21
|
+
// while the visible UI remains only Enabled/Separator/Text as it was before migration.
|
|
22
|
+
showInFields: false,
|
|
23
|
+
showInWidgets: false,
|
|
24
|
+
showInColors: false,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "separator",
|
|
29
|
+
label: "Separator",
|
|
30
|
+
kind: "choice",
|
|
31
|
+
description: "Separator style",
|
|
32
|
+
default: "pipe",
|
|
33
|
+
options: {
|
|
34
|
+
choices: WIDGET_SEPARATOR_VALUES.map((id) => ({ id, label: id, list: id })),
|
|
35
|
+
listProperty: "separator",
|
|
36
|
+
showInColors: true,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "text",
|
|
41
|
+
label: "Text",
|
|
42
|
+
kind: "text",
|
|
43
|
+
description: "Custom separator text",
|
|
44
|
+
default: "|",
|
|
45
|
+
showWhen: { property: "separator", equals: "custom" },
|
|
46
|
+
options: {
|
|
47
|
+
quoteValue: true,
|
|
48
|
+
showInColors: true,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
icons: { emoji: "", nerd: "", text: "" },
|
|
53
|
+
defaultStyle: { fg: "default", bg: "default", bold: false },
|
|
54
|
+
render({ options, renderWidget }) {
|
|
55
|
+
return renderWidget(widgetSeparatorText(options.separator, options.text));
|
|
56
|
+
},
|
|
57
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineWidget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const SpacerWidget = defineWidget({
|
|
4
|
+
type: "spacer",
|
|
5
|
+
label: "Spacer",
|
|
6
|
+
category: "Custom/Layout",
|
|
7
|
+
description: "Fixed-width blank spacer",
|
|
8
|
+
dependencies: [],
|
|
9
|
+
baseOptions: [],
|
|
10
|
+
baseOptionDefaults: {},
|
|
11
|
+
properties: [
|
|
12
|
+
{
|
|
13
|
+
id: "width",
|
|
14
|
+
label: "Width",
|
|
15
|
+
kind: "number",
|
|
16
|
+
description: "Spacer width",
|
|
17
|
+
default: 2,
|
|
18
|
+
options: {
|
|
19
|
+
min: 1,
|
|
20
|
+
max: 40,
|
|
21
|
+
listProperty: "width",
|
|
22
|
+
showInColors: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
icons: { emoji: "", nerd: "", text: "" },
|
|
27
|
+
defaultStyle: { fg: "default", bg: "default", bold: false },
|
|
28
|
+
render({ options, renderWidget }) {
|
|
29
|
+
return renderWidget(" ".repeat(options.width));
|
|
30
|
+
},
|
|
31
|
+
});
|