@gajae-code/coding-agent 0.9.5 → 0.10.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/CHANGELOG.md +54 -0
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/daemon-cli.d.ts +2 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/malloc-env-guard.d.ts +65 -0
- package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +3 -1
- package/dist/types/cli/session-picker.d.ts +4 -3
- package/dist/types/cli/update-cli.d.ts +22 -3
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/commands/daemon.d.ts +6 -2
- package/dist/types/commands/launch.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +1 -1
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +47 -7
- package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
- package/dist/types/coordinator-mcp/server.d.ts +67 -6
- package/dist/types/daemon/control-types.d.ts +10 -0
- package/dist/types/daemon/operator-contract.d.ts +47 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- package/dist/types/gjc-runtime/launch-tmux.d.ts +26 -7
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +34 -3
- package/dist/types/gjc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +309 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +49 -17
- package/dist/types/internal-urls/local-protocol.d.ts +13 -9
- package/dist/types/main.d.ts +66 -2
- package/dist/types/modes/acp/acp-agent.d.ts +19 -2
- package/dist/types/modes/acp/acp-mode.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +59 -0
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
- package/dist/types/modes/components/pet-selector.d.ts +11 -0
- package/dist/types/modes/components/session-selector.d.ts +22 -9
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +4 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +24 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
- package/dist/types/modes/theme/defaults/index.d.ts +568 -568
- package/dist/types/modes/types.d.ts +28 -2
- package/dist/types/modes/utils/abort-message.d.ts +1 -1
- package/dist/types/modes/utils/irc-message.d.ts +38 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
- package/dist/types/notifications/config.d.ts +34 -0
- package/dist/types/notifications/daemon-paths.d.ts +7 -0
- package/dist/types/notifications/index.d.ts +27 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
- package/dist/types/notifications/notification-service.d.ts +144 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
- package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +130 -3
- package/dist/types/notifications/telegram-reference.d.ts +19 -2
- package/dist/types/notifications/topic-registry.d.ts +24 -4
- package/dist/types/registry/agent-registry.d.ts +2 -0
- package/dist/types/rlm/index.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +26 -2
- package/dist/types/session/cache-economics.d.ts +79 -0
- package/dist/types/session/provider-safety-stop.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +124 -4
- package/dist/types/session/session-storage.d.ts +155 -13
- package/dist/types/setup/credential-auto-import.d.ts +2 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/tools/ask-answer-registry.d.ts +5 -0
- package/dist/types/tools/ask.d.ts +25 -1
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
- package/dist/types/tools/index.d.ts +58 -6
- package/dist/types/tools/telegram-send.d.ts +7 -0
- package/dist/types/utils/external-editor.d.ts +2 -0
- package/package.json +9 -9
- package/scripts/compile-args.ts +8 -2
- package/scripts/g004-tmux-smoke.ts +196 -36
- package/src/cli/daemon-cli.ts +19 -29
- package/src/cli/malloc-env-guard.test.ts +104 -0
- package/src/cli/malloc-env-guard.ts +149 -0
- package/src/cli/notify-cli.ts +78 -14
- package/src/cli/session-picker.ts +23 -40
- package/src/cli/update-cli.ts +56 -37
- package/src/cli.ts +50 -10
- package/src/commands/daemon.ts +19 -5
- package/src/commands/harness.ts +515 -47
- package/src/commands/launch.ts +11 -3
- package/src/commands/notify.ts +5 -1
- package/src/commands/session.ts +6 -1
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-profile-activation.ts +1 -1
- package/src/config/model-profiles.ts +23 -23
- package/src/config/settings-schema.ts +41 -1
- package/src/coordinator-mcp/model-preset.ts +100 -0
- package/src/coordinator-mcp/server.ts +2653 -425
- package/src/daemon/control-types.ts +11 -0
- package/src/daemon/operator-contract.ts +121 -0
- package/src/deep-interview/render-middleware.ts +67 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
- package/src/gjc-runtime/launch-tmux.ts +702 -135
- package/src/gjc-runtime/session-state-sidecar.ts +756 -135
- package/src/gjc-runtime/team-runtime.ts +6 -0
- package/src/gjc-runtime/tmux-common.ts +26 -7
- package/src/gjc-runtime/tmux-gc.ts +52 -9
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +185 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +2060 -0
- package/src/gjc-runtime/tmux-sessions.ts +871 -68
- package/src/internal-urls/docs-index.generated.ts +9 -8
- package/src/internal-urls/local-protocol.ts +29 -10
- package/src/main.ts +298 -75
- package/src/modes/acp/acp-agent.ts +728 -98
- package/src/modes/acp/acp-mode.ts +29 -2
- package/src/modes/components/assistant-message.ts +32 -3
- package/src/modes/components/bash-execution.ts +43 -13
- package/src/modes/components/custom-editor.ts +5 -0
- package/src/modes/components/gajae-pet-widget.ts +360 -0
- package/src/modes/components/hook-selector.ts +12 -0
- package/src/modes/components/irc-sidebar.ts +206 -0
- package/src/modes/components/pet-selector.ts +55 -0
- package/src/modes/components/session-selector.ts +222 -221
- package/src/modes/components/settings-selector.ts +10 -0
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/components/user-message.ts +26 -9
- package/src/modes/controllers/command-controller.ts +28 -2
- package/src/modes/controllers/event-controller.ts +159 -36
- package/src/modes/controllers/extension-ui-controller.ts +36 -17
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +62 -15
- package/src/modes/interactive-mode.ts +183 -7
- package/src/modes/irc-observation-ledger.ts +127 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
- package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
- package/src/modes/types.ts +34 -2
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/irc-message.ts +138 -0
- package/src/modes/utils/ui-helpers.ts +216 -50
- package/src/notifications/config.ts +44 -0
- package/src/notifications/daemon-paths.ts +8 -0
- package/src/notifications/index.ts +535 -75
- package/src/notifications/lifecycle-control-runtime.ts +930 -77
- package/src/notifications/notification-service.ts +710 -0
- package/src/notifications/rate-limit-pool.ts +46 -12
- package/src/notifications/telegram-daemon-cli.ts +2 -0
- package/src/notifications/telegram-daemon-control.ts +27 -2
- package/src/notifications/telegram-daemon.ts +635 -94
- package/src/notifications/telegram-reference.ts +73 -13
- package/src/notifications/topic-registry.ts +86 -9
- package/src/prompts/system/irc-peer-roster.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/irc.md +1 -1
- package/src/registry/agent-registry.ts +3 -0
- package/src/sdk.ts +52 -1
- package/src/secrets/obfuscator.ts +4 -2
- package/src/session/agent-session.ts +396 -145
- package/src/session/cache-economics.ts +275 -0
- package/src/session/provider-safety-stop.ts +9 -0
- package/src/session/session-dump-format.ts +6 -0
- package/src/session/session-manager.ts +809 -39
- package/src/session/session-storage.ts +582 -38
- package/src/slash-commands/builtin-registry.ts +143 -9
- package/src/task/executor.ts +12 -3
- package/src/task/index.ts +8 -0
- package/src/task/receipt.ts +7 -2
- package/src/task/types.ts +26 -0
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/ask.ts +355 -33
- package/src/tools/browser/tab-supervisor.ts +63 -6
- package/src/tools/browser.ts +8 -3
- package/src/tools/index.ts +81 -6
- package/src/tools/sqlite-reader.ts +8 -4
- package/src/tui/output-block.ts +18 -6
- package/src/utils/external-editor.ts +6 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Component,
|
|
3
|
+
padding,
|
|
4
|
+
renderComponentWithViewportAnchors,
|
|
5
|
+
TERMINAL,
|
|
6
|
+
truncateToWidth,
|
|
7
|
+
type ViewportAnchorProvider,
|
|
8
|
+
type ViewportAnchorRender,
|
|
9
|
+
visibleWidth,
|
|
10
|
+
withTerminalGraphicsFallback,
|
|
11
|
+
wrapTextWithAnsi,
|
|
12
|
+
} from "@gajae-code/tui";
|
|
13
|
+
import type { IrcObservationLedger, IrcObservationRecord } from "../irc-observation-ledger";
|
|
14
|
+
import { formatIrcMessageBlock, projectIrcText } from "../utils/irc-message";
|
|
15
|
+
|
|
16
|
+
export const IRC_SIDEBAR_WIDTH_RATIO = 0.3;
|
|
17
|
+
const IRC_SIDEBAR_MIN_WIDTH = 30;
|
|
18
|
+
const IRC_SEPARATOR_WIDTH = 3;
|
|
19
|
+
export const IRC_SIDEBAR_MAX_RENDER_ROWS = 2_048;
|
|
20
|
+
const IRC_SIDEBAR_MAX_RENDER_SOURCE_UTF8_BYTES = 64 * 1_024;
|
|
21
|
+
const IRC_SIDEBAR_OLDER_MESSAGES_ELISION = "… older IRC messages elided …";
|
|
22
|
+
const IRC_SIDEBAR_MESSAGE_ELISION = " … message elided …";
|
|
23
|
+
|
|
24
|
+
/** Computes transcript/sidebar widths while preserving at least half the terminal for the transcript. */
|
|
25
|
+
export function computeIrcSplitWidths(width: number): {
|
|
26
|
+
leftWidth: number;
|
|
27
|
+
separatorWidth: number;
|
|
28
|
+
rightWidth: number;
|
|
29
|
+
} {
|
|
30
|
+
const normalizedWidth = Math.max(0, Math.floor(width));
|
|
31
|
+
const transcriptFloor = Math.floor(normalizedWidth * 0.5);
|
|
32
|
+
const preferredSidebar = Math.max(IRC_SIDEBAR_MIN_WIDTH, Math.floor(normalizedWidth * IRC_SIDEBAR_WIDTH_RATIO));
|
|
33
|
+
const sidebarWidth = Math.max(
|
|
34
|
+
0,
|
|
35
|
+
Math.min(preferredSidebar, normalizedWidth - IRC_SEPARATOR_WIDTH - transcriptFloor),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// The sidebar yields entirely rather than rendering below its readable minimum.
|
|
39
|
+
if (sidebarWidth < IRC_SIDEBAR_MIN_WIDTH) {
|
|
40
|
+
return { leftWidth: normalizedWidth, separatorWidth: 0, rightWidth: 0 };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
leftWidth: normalizedWidth - IRC_SEPARATOR_WIDTH - sidebarWidth,
|
|
45
|
+
separatorWidth: IRC_SEPARATOR_WIDTH,
|
|
46
|
+
rightWidth: sidebarWidth,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function styleSender(componentTheme: IrcSidebarTheme, sender: string): string {
|
|
51
|
+
return componentTheme.fg("accent", componentTheme.bold(sender));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function renderSidebarRecord(
|
|
55
|
+
record: IrcObservationRecord,
|
|
56
|
+
width: number,
|
|
57
|
+
componentTheme: IrcSidebarTheme,
|
|
58
|
+
maxRows: number,
|
|
59
|
+
maxSourceUtf8Bytes: number,
|
|
60
|
+
): { lines: string[]; sourceUtf8Bytes: number; truncated: boolean } {
|
|
61
|
+
const bodyWidth = Math.max(1, width - 2);
|
|
62
|
+
const sourceLimit = Math.min(maxSourceUtf8Bytes, Math.max(0, maxRows - 1) * bodyWidth);
|
|
63
|
+
const clipped = projectIrcText(record.text, sourceLimit);
|
|
64
|
+
const block = formatIrcMessageBlock({ ...record, text: clipped.text });
|
|
65
|
+
const sender = styleSender(componentTheme, block.sender);
|
|
66
|
+
const time = componentTheme.fg("dim", block.time);
|
|
67
|
+
const lines = [truncateToWidth(`${sender} → ${block.recipient} · ${time}`, width)];
|
|
68
|
+
let truncated = clipped.truncated;
|
|
69
|
+
|
|
70
|
+
body: for (const bodyLine of block.bodyLines) {
|
|
71
|
+
for (const wrappedLine of wrapTextWithAnsi(bodyLine, bodyWidth)) {
|
|
72
|
+
if (lines.length >= maxRows) {
|
|
73
|
+
truncated = true;
|
|
74
|
+
break body;
|
|
75
|
+
}
|
|
76
|
+
lines.push(truncateToWidth(` ${wrappedLine}`, width));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (truncated && maxRows > 1) {
|
|
80
|
+
const marker = componentTheme.fg("dim", IRC_SIDEBAR_MESSAGE_ELISION);
|
|
81
|
+
if (lines.length >= maxRows) lines[maxRows - 1] = truncateToWidth(marker, width);
|
|
82
|
+
else lines.push(truncateToWidth(marker, width));
|
|
83
|
+
}
|
|
84
|
+
return { lines, sourceUtf8Bytes: clipped.utf8Bytes, truncated };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function renderSidebarRecords(ledger: IrcObservationLedger, width: number, componentTheme: IrcSidebarTheme): string[] {
|
|
88
|
+
if (width <= 0) return [];
|
|
89
|
+
|
|
90
|
+
const records = ledger.getSidebarRecords();
|
|
91
|
+
const newestFirstBlocks: string[][] = [];
|
|
92
|
+
let renderedRows = 0;
|
|
93
|
+
let renderedSourceUtf8Bytes = 0;
|
|
94
|
+
let omittedOlderRecords = false;
|
|
95
|
+
for (let index = records.length - 1; index >= 0; index--) {
|
|
96
|
+
const separatorRows = newestFirstBlocks.length > 0 ? 1 : 0;
|
|
97
|
+
const availableRows = IRC_SIDEBAR_MAX_RENDER_ROWS - renderedRows - separatorRows - 2;
|
|
98
|
+
const availableSourceUtf8Bytes = IRC_SIDEBAR_MAX_RENDER_SOURCE_UTF8_BYTES - renderedSourceUtf8Bytes;
|
|
99
|
+
const minimumRecordRows = records[index].text.length > 0 ? 2 : 1;
|
|
100
|
+
if (availableRows < minimumRecordRows || (availableSourceUtf8Bytes <= 0 && records[index].text.length > 0)) {
|
|
101
|
+
omittedOlderRecords = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const rendered = renderSidebarRecord(
|
|
106
|
+
records[index],
|
|
107
|
+
width,
|
|
108
|
+
componentTheme,
|
|
109
|
+
availableRows,
|
|
110
|
+
availableSourceUtf8Bytes,
|
|
111
|
+
);
|
|
112
|
+
newestFirstBlocks.push(rendered.lines);
|
|
113
|
+
renderedRows += separatorRows + rendered.lines.length;
|
|
114
|
+
renderedSourceUtf8Bytes += rendered.sourceUtf8Bytes;
|
|
115
|
+
if (rendered.truncated) {
|
|
116
|
+
omittedOlderRecords = index > 0;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const lines: string[] = [];
|
|
122
|
+
if (omittedOlderRecords) {
|
|
123
|
+
lines.push(truncateToWidth(componentTheme.fg("dim", IRC_SIDEBAR_OLDER_MESSAGES_ELISION), width));
|
|
124
|
+
}
|
|
125
|
+
for (const block of newestFirstBlocks.reverse()) {
|
|
126
|
+
if (lines.length > 0) lines.push("");
|
|
127
|
+
lines.push(...block);
|
|
128
|
+
}
|
|
129
|
+
return lines.slice(0, IRC_SIDEBAR_MAX_RENDER_ROWS);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface IrcSidebarTheme {
|
|
133
|
+
fg(color: "dim" | "accent", text: string): string;
|
|
134
|
+
bold(text: string): string;
|
|
135
|
+
readonly boxSharp: { readonly vertical: string };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type IrcSidebarThemeSource = IrcSidebarTheme | (() => IrcSidebarTheme);
|
|
139
|
+
|
|
140
|
+
/** Read-only IRC history alongside the active transcript. */
|
|
141
|
+
export class IrcSplitViewComponent implements ViewportAnchorProvider {
|
|
142
|
+
#visible = false;
|
|
143
|
+
|
|
144
|
+
constructor(
|
|
145
|
+
private readonly leftPane: Component,
|
|
146
|
+
private readonly ledger: IrcObservationLedger,
|
|
147
|
+
private readonly componentTheme: IrcSidebarThemeSource,
|
|
148
|
+
) {}
|
|
149
|
+
|
|
150
|
+
get visible(): boolean {
|
|
151
|
+
return this.#visible;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
effectiveSidebarVisible(width = process.stdout.columns ?? 0): boolean {
|
|
155
|
+
return this.#visible && computeIrcSplitWidths(width).rightWidth > 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
setVisible(visible: boolean): void {
|
|
159
|
+
if (this.#visible === visible) return;
|
|
160
|
+
this.#visible = visible;
|
|
161
|
+
this.invalidate();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
render(width: number): string[] {
|
|
165
|
+
return this.renderWithViewportAnchors(width).lines;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
renderWithViewportAnchors(width: number): ViewportAnchorRender {
|
|
169
|
+
if (!this.#visible) return renderComponentWithViewportAnchors(this.leftPane, width);
|
|
170
|
+
|
|
171
|
+
const componentTheme = typeof this.componentTheme === "function" ? this.componentTheme() : this.componentTheme;
|
|
172
|
+
const { leftWidth, separatorWidth, rightWidth } = computeIrcSplitWidths(width);
|
|
173
|
+
if (rightWidth === 0) return renderComponentWithViewportAnchors(this.leftPane, width);
|
|
174
|
+
|
|
175
|
+
const separatorText = componentTheme.fg("dim", ` ${componentTheme.boxSharp.vertical} `);
|
|
176
|
+
const separator = separatorWidth > 0 ? separatorText : "";
|
|
177
|
+
const leftRender = withTerminalGraphicsFallback(
|
|
178
|
+
() => renderComponentWithViewportAnchors(this.leftPane, leftWidth),
|
|
179
|
+
{ allowCursorNeutralImages: true },
|
|
180
|
+
);
|
|
181
|
+
const rightLines = renderSidebarRecords(this.ledger, rightWidth, componentTheme);
|
|
182
|
+
const lineCount = Math.max(leftRender.lines.length, rightLines.length);
|
|
183
|
+
const lines: string[] = [];
|
|
184
|
+
const anchors: ViewportAnchorRender["anchors"] = [];
|
|
185
|
+
|
|
186
|
+
const leftOffset = lineCount - leftRender.lines.length;
|
|
187
|
+
const rightOffset = lineCount - rightLines.length;
|
|
188
|
+
for (let index = 0; index < lineCount; index++) {
|
|
189
|
+
const leftIndex = index - leftOffset;
|
|
190
|
+
const leftRaw = leftRender.lines[leftIndex] ?? "";
|
|
191
|
+
const right = truncateToWidth(rightLines[index - rightOffset] ?? "", rightWidth);
|
|
192
|
+
if (TERMINAL.isImageLine(leftRaw)) {
|
|
193
|
+
lines.push(leftRaw + padding(leftWidth) + separator + right);
|
|
194
|
+
} else {
|
|
195
|
+
const left = truncateToWidth(leftRaw, leftWidth);
|
|
196
|
+
lines.push(left + padding(Math.max(0, leftWidth - visibleWidth(left))) + separator + right);
|
|
197
|
+
}
|
|
198
|
+
anchors.push(leftRender.anchors[leftIndex] ?? null);
|
|
199
|
+
}
|
|
200
|
+
return { lines, anchors };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
invalidate(): void {
|
|
204
|
+
this.leftPane.invalidate?.();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Container, PET_SKIN_IDS, PET_SKINS, type SelectItem, SelectList } from "@gajae-code/tui";
|
|
2
|
+
import { getSelectListTheme } from "../theme/theme";
|
|
3
|
+
import { DynamicBorder } from "./dynamic-border";
|
|
4
|
+
import type { PetMode } from "./gajae-pet-widget";
|
|
5
|
+
|
|
6
|
+
const PET_OPTIONS: { value: PetMode; label: string; description: string }[] = [
|
|
7
|
+
{ value: "off", label: "Off", description: "No pet" },
|
|
8
|
+
...PET_SKIN_IDS.map(id => ({
|
|
9
|
+
value: id,
|
|
10
|
+
label: PET_SKINS[id].label,
|
|
11
|
+
description: PET_SKINS[id].description,
|
|
12
|
+
})),
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Theme-style picker for the gajae pet skin (Off / RedGajae / BlueGajae). Preview
|
|
17
|
+
* fires as the selection moves; select commits, cancel restores.
|
|
18
|
+
*/
|
|
19
|
+
export class PetSelectorComponent extends Container {
|
|
20
|
+
#selectList: SelectList;
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
current: PetMode,
|
|
24
|
+
onSelect: (mode: PetMode) => void,
|
|
25
|
+
onCancel: () => void,
|
|
26
|
+
onPreview: (mode: PetMode) => void,
|
|
27
|
+
) {
|
|
28
|
+
super();
|
|
29
|
+
|
|
30
|
+
const items: SelectItem[] = PET_OPTIONS.map(option => ({
|
|
31
|
+
value: option.value,
|
|
32
|
+
label: option.label,
|
|
33
|
+
description: option.value === current ? `${option.description} (current)` : option.description,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
this.addChild(new DynamicBorder());
|
|
37
|
+
|
|
38
|
+
this.#selectList = new SelectList(items, 10, getSelectListTheme());
|
|
39
|
+
const currentIndex = PET_OPTIONS.findIndex(option => option.value === current);
|
|
40
|
+
if (currentIndex !== -1) {
|
|
41
|
+
this.#selectList.setSelectedIndex(currentIndex);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.#selectList.onSelect = item => onSelect(item.value as PetMode);
|
|
45
|
+
this.#selectList.onCancel = () => onCancel();
|
|
46
|
+
this.#selectList.onSelectionChange = item => onPreview(item.value as PetMode);
|
|
47
|
+
|
|
48
|
+
this.addChild(this.#selectList);
|
|
49
|
+
this.addChild(new DynamicBorder());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getSelectList(): SelectList {
|
|
53
|
+
return this.#selectList;
|
|
54
|
+
}
|
|
55
|
+
}
|