@golba98/codexa 1.0.1
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 +320 -0
- package/bin/codexa.js +445 -0
- package/package.json +45 -0
- package/scripts/audit-codexa-capabilities.mjs +466 -0
- package/scripts/smoke-terminal-bench.mjs +35 -0
- package/src/app.tsx +4561 -0
- package/src/appRenderStability.test.ts +131 -0
- package/src/commands/handler.test.ts +643 -0
- package/src/commands/handler.ts +875 -0
- package/src/config/launchArgs.test.ts +158 -0
- package/src/config/launchArgs.ts +186 -0
- package/src/config/layeredConfig.test.ts +143 -0
- package/src/config/layeredConfig.ts +836 -0
- package/src/config/persistence.test.ts +110 -0
- package/src/config/persistence.ts +311 -0
- package/src/config/runtimeConfig.test.ts +218 -0
- package/src/config/runtimeConfig.ts +554 -0
- package/src/config/settings.test.ts +155 -0
- package/src/config/settings.ts +401 -0
- package/src/config/toml-serialize.ts +98 -0
- package/src/config/trustStore.test.ts +29 -0
- package/src/config/trustStore.ts +68 -0
- package/src/core/attachments.test.ts +155 -0
- package/src/core/attachments.ts +71 -0
- package/src/core/auth/codexAuth.test.ts +68 -0
- package/src/core/auth/codexAuth.ts +359 -0
- package/src/core/cleanupFastFail.test.ts +76 -0
- package/src/core/cleanupFastFail.ts +67 -0
- package/src/core/clipboard.ts +24 -0
- package/src/core/codex.ts +124 -0
- package/src/core/codexExecArgs.test.ts +195 -0
- package/src/core/codexExecArgs.ts +152 -0
- package/src/core/codexLaunch.test.ts +205 -0
- package/src/core/codexLaunch.ts +162 -0
- package/src/core/codexPrompt.test.ts +252 -0
- package/src/core/codexPrompt.ts +428 -0
- package/src/core/executables/claudeExecutable.ts +63 -0
- package/src/core/executables/codexExecutable.test.ts +212 -0
- package/src/core/executables/codexExecutable.ts +159 -0
- package/src/core/executables/executableResolver.test.ts +129 -0
- package/src/core/executables/executableResolver.ts +138 -0
- package/src/core/executables/geminiExecutable.test.ts +116 -0
- package/src/core/executables/geminiExecutable.ts +78 -0
- package/src/core/executables/pathSanityScan.test.ts +47 -0
- package/src/core/githubDiagnostics.test.ts +92 -0
- package/src/core/githubDiagnostics.ts +222 -0
- package/src/core/hollowResponseFormat.test.ts +58 -0
- package/src/core/hollowResponseFormat.ts +39 -0
- package/src/core/inputDebug.ts +51 -0
- package/src/core/launchContext.test.ts +157 -0
- package/src/core/launchContext.ts +266 -0
- package/src/core/models/codexCapabilities.test.ts +45 -0
- package/src/core/models/codexCapabilities.ts +95 -0
- package/src/core/models/codexModelCapabilities.test.ts +246 -0
- package/src/core/models/codexModelCapabilities.ts +571 -0
- package/src/core/models/modelSpecs.test.ts +283 -0
- package/src/core/models/modelSpecs.ts +300 -0
- package/src/core/perf/profiler.ts +125 -0
- package/src/core/perf/renderDebug.test.ts +230 -0
- package/src/core/perf/renderDebug.ts +373 -0
- package/src/core/planStorage.test.ts +143 -0
- package/src/core/planStorage.ts +141 -0
- package/src/core/process/CommandRunner.test.ts +105 -0
- package/src/core/process/CommandRunner.ts +269 -0
- package/src/core/process/processValidation.ts +101 -0
- package/src/core/projectInstructions.test.ts +50 -0
- package/src/core/projectInstructions.ts +54 -0
- package/src/core/providerLauncher/launcher.test.ts +238 -0
- package/src/core/providerLauncher/launcher.ts +203 -0
- package/src/core/providerLauncher/registry.test.ts +324 -0
- package/src/core/providerLauncher/registry.ts +253 -0
- package/src/core/providerLauncher/types.ts +84 -0
- package/src/core/providerLauncher/workspaceConfig.test.ts +638 -0
- package/src/core/providerLauncher/workspaceConfig.ts +407 -0
- package/src/core/providerRuntime/anthropic.test.ts +1120 -0
- package/src/core/providerRuntime/anthropic.ts +576 -0
- package/src/core/providerRuntime/capabilityProfile.test.ts +311 -0
- package/src/core/providerRuntime/capabilityProfile.ts +288 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +446 -0
- package/src/core/providerRuntime/contextMetadata.test.ts +468 -0
- package/src/core/providerRuntime/contextMetadata.ts +409 -0
- package/src/core/providerRuntime/gemini.test.ts +437 -0
- package/src/core/providerRuntime/gemini.ts +784 -0
- package/src/core/providerRuntime/lmstudio.test.ts +168 -0
- package/src/core/providerRuntime/lmstudio.ts +118 -0
- package/src/core/providerRuntime/local.test.ts +787 -0
- package/src/core/providerRuntime/local.ts +754 -0
- package/src/core/providerRuntime/models.ts +150 -0
- package/src/core/providerRuntime/reasoning.ts +17 -0
- package/src/core/providerRuntime/registry.test.ts +233 -0
- package/src/core/providerRuntime/registry.ts +203 -0
- package/src/core/providerRuntime/types.ts +103 -0
- package/src/core/providers/codexJsonStream.test.ts +148 -0
- package/src/core/providers/codexJsonStream.ts +305 -0
- package/src/core/providers/codexSubprocess.test.ts +68 -0
- package/src/core/providers/codexSubprocess.ts +372 -0
- package/src/core/providers/codexTranscript.test.ts +284 -0
- package/src/core/providers/codexTranscript.ts +695 -0
- package/src/core/providers/openaiNative.ts +13 -0
- package/src/core/providers/registry.ts +21 -0
- package/src/core/providers/types.ts +59 -0
- package/src/core/terminal/terminalCapabilities.test.ts +93 -0
- package/src/core/terminal/terminalCapabilities.ts +100 -0
- package/src/core/terminal/terminalControl.test.ts +75 -0
- package/src/core/terminal/terminalControl.ts +147 -0
- package/src/core/terminal/terminalSanitize.test.ts +22 -0
- package/src/core/terminal/terminalSanitize.ts +147 -0
- package/src/core/terminal/terminalSelection.test.ts +42 -0
- package/src/core/terminal/terminalSelection.ts +66 -0
- package/src/core/terminal/terminalTitle.test.ts +328 -0
- package/src/core/terminal/terminalTitle.ts +483 -0
- package/src/core/workspaceActivity.test.ts +163 -0
- package/src/core/workspaceActivity.ts +380 -0
- package/src/core/workspaceGuard.test.ts +151 -0
- package/src/core/workspaceGuard.ts +288 -0
- package/src/core/workspaceRoot.test.ts +23 -0
- package/src/core/workspaceRoot.ts +47 -0
- package/src/exec.test.ts +13 -0
- package/src/exec.ts +72 -0
- package/src/headless/execArgs.test.ts +147 -0
- package/src/headless/execArgs.ts +294 -0
- package/src/headless/execRunner.test.ts +434 -0
- package/src/headless/execRunner.ts +304 -0
- package/src/index.test.tsx +618 -0
- package/src/index.tsx +296 -0
- package/src/session/appSession.test.ts +897 -0
- package/src/session/appSession.ts +761 -0
- package/src/session/chatLifecycle.test.ts +64 -0
- package/src/session/chatLifecycle.ts +951 -0
- package/src/session/liveRenderScheduler.test.ts +201 -0
- package/src/session/liveRenderScheduler.ts +214 -0
- package/src/session/planFlow.test.ts +103 -0
- package/src/session/planFlow.ts +149 -0
- package/src/session/planTranscript.test.ts +65 -0
- package/src/session/planTranscript.ts +15 -0
- package/src/session/promptRunSchedule.test.ts +36 -0
- package/src/session/promptRunSchedule.ts +26 -0
- package/src/session/types.ts +228 -0
- package/src/test/runtimeTestUtils.ts +14 -0
- package/src/types/react-dom.d.ts +3 -0
- package/src/ui/ActionRequiredBlock.tsx +38 -0
- package/src/ui/ActivityBars.tsx +68 -0
- package/src/ui/ActivityIndicator.test.tsx +58 -0
- package/src/ui/ActivityIndicator.tsx +58 -0
- package/src/ui/AgentBlock.test.ts +6 -0
- package/src/ui/AgentBlock.tsx +130 -0
- package/src/ui/AnimatedStatusText.test.ts +16 -0
- package/src/ui/AnimatedStatusText.tsx +69 -0
- package/src/ui/AppShell.test.tsx +1739 -0
- package/src/ui/AppShell.tsx +698 -0
- package/src/ui/AttachmentImportPanel.test.tsx +204 -0
- package/src/ui/AttachmentImportPanel.tsx +98 -0
- package/src/ui/AuthPanel.tsx +113 -0
- package/src/ui/BackendPicker.tsx +28 -0
- package/src/ui/BottomComposer.test.ts +674 -0
- package/src/ui/BottomComposer.tsx +1028 -0
- package/src/ui/CodexLogo.tsx +55 -0
- package/src/ui/DashCard.tsx +82 -0
- package/src/ui/Markdown.test.ts +157 -0
- package/src/ui/Markdown.tsx +310 -0
- package/src/ui/ModePicker.tsx +27 -0
- package/src/ui/ModelPicker.tsx +31 -0
- package/src/ui/ModelPickerProviderScope.test.tsx +411 -0
- package/src/ui/ModelPickerScreen.test.tsx +99 -0
- package/src/ui/ModelPickerScreen.tsx +416 -0
- package/src/ui/ModelPickerState.test.tsx +151 -0
- package/src/ui/ModelReasoningPicker.test.tsx +447 -0
- package/src/ui/ModelReasoningPicker.tsx +458 -0
- package/src/ui/Panel.tsx +51 -0
- package/src/ui/PermissionsPanel.tsx +78 -0
- package/src/ui/PlanActionPicker.tsx +119 -0
- package/src/ui/PlanReviewPanel.test.tsx +267 -0
- package/src/ui/PlanReviewPanel.tsx +212 -0
- package/src/ui/PromptCardBorder.test.tsx +161 -0
- package/src/ui/ProviderPicker.test.tsx +289 -0
- package/src/ui/ProviderPicker.tsx +321 -0
- package/src/ui/ProviderShortcut.test.tsx +143 -0
- package/src/ui/ReasoningPicker.tsx +46 -0
- package/src/ui/RunFooter.tsx +65 -0
- package/src/ui/SelectionPanel.tsx +67 -0
- package/src/ui/SettingsPanel.test.tsx +233 -0
- package/src/ui/SettingsPanel.tsx +156 -0
- package/src/ui/Spinner.tsx +25 -0
- package/src/ui/StaticIntroItem.tsx +54 -0
- package/src/ui/StaticTranscriptItem.tsx +56 -0
- package/src/ui/TextEntryPanel.tsx +139 -0
- package/src/ui/ThemePicker.tsx +31 -0
- package/src/ui/ThinkingBlock.tsx +100 -0
- package/src/ui/Timeline.test.ts +2067 -0
- package/src/ui/Timeline.tsx +1472 -0
- package/src/ui/TimelineNavigation.test.tsx +201 -0
- package/src/ui/TopHeader.test.tsx +239 -0
- package/src/ui/TopHeader.tsx +257 -0
- package/src/ui/TurnGroup.test.tsx +365 -0
- package/src/ui/TurnGroup.tsx +657 -0
- package/src/ui/busyStatusAnimation.test.ts +30 -0
- package/src/ui/busyStatusAnimation.ts +11 -0
- package/src/ui/commandNormalize.test.ts +142 -0
- package/src/ui/commandNormalize.ts +66 -0
- package/src/ui/diffRenderer.test.ts +102 -0
- package/src/ui/diffRenderer.ts +116 -0
- package/src/ui/focus.ts +61 -0
- package/src/ui/focusFlow.test.tsx +1098 -0
- package/src/ui/inputBuffer.test.ts +151 -0
- package/src/ui/inputBuffer.ts +203 -0
- package/src/ui/layout.test.ts +145 -0
- package/src/ui/layout.ts +287 -0
- package/src/ui/modeDisplay.test.ts +42 -0
- package/src/ui/modeDisplay.ts +52 -0
- package/src/ui/outputPipeline.ts +64 -0
- package/src/ui/progressEntries.ts +156 -0
- package/src/ui/runActivityView.test.ts +89 -0
- package/src/ui/runActivityView.ts +37 -0
- package/src/ui/runLifecycleView.test.tsx +237 -0
- package/src/ui/slashCommands.ts +41 -0
- package/src/ui/statusRenderIsolation.test.tsx +654 -0
- package/src/ui/terminalAnswerFormat.test.ts +19 -0
- package/src/ui/terminalAnswerFormat.ts +128 -0
- package/src/ui/textLayout.test.ts +18 -0
- package/src/ui/textLayout.ts +338 -0
- package/src/ui/theme.tsx +395 -0
- package/src/ui/themeFlow.test.ts +53 -0
- package/src/ui/themeFlow.ts +41 -0
- package/src/ui/timelineMeasure.ts +3088 -0
- package/src/ui/timelineMeasureCache.test.ts +986 -0
- package/src/ui/useThrottledValue.ts +31 -0
package/src/index.tsx
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, type Instance, type RenderOptions } from "ink";
|
|
3
|
+
import { App } from "./app.js";
|
|
4
|
+
import { parseLaunchArgs, type LaunchArgs } from "./config/launchArgs.js";
|
|
5
|
+
import { loadSettings } from "./config/persistence.js";
|
|
6
|
+
import { APP_NAME, formatTerminalTitlePath } from "./config/settings.js";
|
|
7
|
+
import { getTerminalCapability } from "./core/terminal/terminalCapabilities.js";
|
|
8
|
+
import * as renderDebug from "./core/perf/renderDebug.js";
|
|
9
|
+
import { MIN_VIEWPORT_COLS, MIN_VIEWPORT_ROWS } from "./ui/layout.js";
|
|
10
|
+
import {
|
|
11
|
+
reassertIntendedTerminalTitle,
|
|
12
|
+
setIntendedTerminalTitle,
|
|
13
|
+
startTerminalTitleStartupGuard,
|
|
14
|
+
} from "./core/terminal/terminalTitle.js";
|
|
15
|
+
import { resolveWorkspaceRoot } from "./core/workspaceRoot.js";
|
|
16
|
+
import {
|
|
17
|
+
createTerminalModeController,
|
|
18
|
+
TERMINAL_SEQUENCES,
|
|
19
|
+
traceTerminalClear,
|
|
20
|
+
writeTerminalControl,
|
|
21
|
+
} from "./core/terminal/terminalControl.js";
|
|
22
|
+
|
|
23
|
+
type RenderHandle = Pick<Instance, "clear" | "cleanup" | "waitUntilExit">;
|
|
24
|
+
const KITTY_KEYBOARD_OPTIONS: RenderOptions["kittyKeyboard"] = {
|
|
25
|
+
mode: "auto",
|
|
26
|
+
flags: ["disambiguateEscapeCodes"],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Typed subset of the internal Ink class instance we access to disable Ink's
|
|
31
|
+
* built-in resize listener. Post-startup repainting is driven by React layout
|
|
32
|
+
* state, not by forcing Ink's private render buffers.
|
|
33
|
+
*/
|
|
34
|
+
interface InkInstance {
|
|
35
|
+
unsubscribeResize?: () => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the real Ink class instance via Ink's internal WeakMap<stdout, Ink>.
|
|
40
|
+
* Returns null if resolution fails (e.g. different Ink version, test mocks).
|
|
41
|
+
*/
|
|
42
|
+
function resolveInkInstance(stdout: AppStdout): InkInstance | null {
|
|
43
|
+
try {
|
|
44
|
+
// Bun doesn't resolve bare subpath imports like "ink/build/instances.js"
|
|
45
|
+
// so we resolve ink's main entry first, then derive the sibling path.
|
|
46
|
+
const { createRequire } = require("node:module");
|
|
47
|
+
const req = createRequire(import.meta.url);
|
|
48
|
+
const inkMain = req.resolve("ink");
|
|
49
|
+
const instancesPath = inkMain.replace(/index\.js$/, "instances.js");
|
|
50
|
+
const instances = req(instancesPath);
|
|
51
|
+
const weakMap: WeakMap<object, InkInstance> =
|
|
52
|
+
instances.default ?? instances;
|
|
53
|
+
return weakMap.get(stdout as object) ?? null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface AppStdout {
|
|
60
|
+
isTTY: boolean;
|
|
61
|
+
write: (chunk: string) => boolean;
|
|
62
|
+
on: (event: "resize" | string, listener: (...args: unknown[]) => void) => void;
|
|
63
|
+
off: (event: "resize" | string, listener: (...args: unknown[]) => void) => void;
|
|
64
|
+
columns?: number;
|
|
65
|
+
rows?: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface StartAppDependencies {
|
|
69
|
+
stdin: Pick<NodeJS.ReadStream, "isTTY">;
|
|
70
|
+
stdout: AppStdout;
|
|
71
|
+
stderr: Pick<NodeJS.WriteStream, "write"> & Partial<Pick<NodeJS.WriteStream, "isTTY">>;
|
|
72
|
+
env: Record<string, string | undefined>;
|
|
73
|
+
platform: NodeJS.Platform;
|
|
74
|
+
argv: string[];
|
|
75
|
+
renderApp: (node: React.ReactElement, options?: RenderOptions) => RenderHandle;
|
|
76
|
+
resolveInkInstanceForStdout: (stdout: AppStdout) => InkInstance | null;
|
|
77
|
+
registerExitHandler: (handler: () => void) => void;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface StartAppResult {
|
|
81
|
+
started: boolean;
|
|
82
|
+
exitCode: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface ActiveRootState {
|
|
86
|
+
cleanup: () => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let activeRoot: ActiveRootState | null = null;
|
|
90
|
+
|
|
91
|
+
function debugLaunch(env: Record<string, string | undefined>, write: (chunk: string, source: string) => boolean, fields: Record<string, unknown>): void {
|
|
92
|
+
if (env.CODEXA_DEBUG_LAUNCH !== "1") {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
write(`[codexa:launch] ${JSON.stringify(fields)}\n`, "src/index.tsx:launchDebug");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function hasInvalidRestoreDimensions(stdout: Pick<AppStdout, "columns" | "rows">): boolean {
|
|
100
|
+
const cols = stdout.columns;
|
|
101
|
+
const rows = stdout.rows;
|
|
102
|
+
return !Number.isFinite(cols) || !Number.isFinite(rows)
|
|
103
|
+
|| (cols ?? 0) < MIN_VIEWPORT_COLS || (rows ?? 0) < MIN_VIEWPORT_ROWS;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function startApp({
|
|
107
|
+
stdin = process.stdin,
|
|
108
|
+
stdout = process.stdout,
|
|
109
|
+
stderr = process.stderr,
|
|
110
|
+
env = process.env,
|
|
111
|
+
platform = process.platform,
|
|
112
|
+
argv = process.argv.slice(2),
|
|
113
|
+
renderApp = render,
|
|
114
|
+
resolveInkInstanceForStdout = resolveInkInstance,
|
|
115
|
+
registerExitHandler = (handler) => {
|
|
116
|
+
process.on("exit", handler);
|
|
117
|
+
},
|
|
118
|
+
}: Partial<StartAppDependencies> = {}): StartAppResult {
|
|
119
|
+
renderDebug.configureRenderDebug(env);
|
|
120
|
+
|
|
121
|
+
if (activeRoot) {
|
|
122
|
+
return { started: true, exitCode: 0 };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const terminal = createTerminalModeController((chunk) => stdout.write(chunk));
|
|
126
|
+
const writeStdout = (chunk: string, source: string): boolean => terminal.write(chunk, source);
|
|
127
|
+
|
|
128
|
+
const writeStderr = (chunk: string, source: string): boolean => {
|
|
129
|
+
return writeTerminalControl((value) => stderr.write(value), "stderr", source, chunk);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
debugLaunch(env, writeStderr, {
|
|
133
|
+
phase: "startApp",
|
|
134
|
+
resolvedLaunchMode: "interactive-ui",
|
|
135
|
+
stdinIsTTY: Boolean(stdin.isTTY),
|
|
136
|
+
stdoutIsTTY: Boolean(stdout.isTTY),
|
|
137
|
+
stderrIsTTY: Boolean(stderr.isTTY),
|
|
138
|
+
TERM: env.TERM,
|
|
139
|
+
WT_SESSION: env.WT_SESSION,
|
|
140
|
+
TERM_PROGRAM: env.TERM_PROGRAM,
|
|
141
|
+
argv,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const capability = getTerminalCapability({
|
|
145
|
+
stdinIsTTY: Boolean(stdin.isTTY),
|
|
146
|
+
stdoutIsTTY: Boolean(stdout.isTTY),
|
|
147
|
+
platform,
|
|
148
|
+
env,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (!capability.supported) {
|
|
152
|
+
writeStderr(`${capability.message}\n`, "src/index.tsx:unsupportedTerminal");
|
|
153
|
+
return { started: false, exitCode: 1 };
|
|
154
|
+
}
|
|
155
|
+
if (capability.warning) {
|
|
156
|
+
writeStderr(`${capability.warning}\n`, "src/index.tsx:terminalCapabilityWarning");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const parsedLaunchArgs = parseLaunchArgs(argv);
|
|
160
|
+
if (!parsedLaunchArgs.ok) {
|
|
161
|
+
writeStderr(`${parsedLaunchArgs.error}\n`, "src/index.tsx:launchArgs");
|
|
162
|
+
return { started: false, exitCode: 1 };
|
|
163
|
+
}
|
|
164
|
+
const launchArgs: LaunchArgs = parsedLaunchArgs.value;
|
|
165
|
+
setIntendedTerminalTitle(env.CODEXA_INITIAL_TERMINAL_TITLE ?? APP_NAME, {
|
|
166
|
+
force: true,
|
|
167
|
+
reason: "index-safe-fallback-title",
|
|
168
|
+
write: (chunk) => writeStdout(chunk, "src/index.tsx:title.fallback"),
|
|
169
|
+
});
|
|
170
|
+
const stopStartupTitleGuard = startTerminalTitleStartupGuard({
|
|
171
|
+
write: (chunk) => writeStdout(chunk, "src/index.tsx:title.startupGuard"),
|
|
172
|
+
reason: "index-startup-guard",
|
|
173
|
+
intervalMs: 150,
|
|
174
|
+
durationMs: 3500,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Clear the screen and move cursor to home before rendering so no stale
|
|
178
|
+
// content from a previous process (e.g. bun --watch restart) ghosts above
|
|
179
|
+
// the new frame. We stay in the normal screen buffer (no \x1b[?1049h) to
|
|
180
|
+
// preserve terminal scrollback and allow mouse text selection.
|
|
181
|
+
// NOTE: Mouse reporting (\x1b[?1000h / \x1b[?1006h) is NOT enabled here.
|
|
182
|
+
// It is managed exclusively by the React app (app.tsx) and defaults to OFF
|
|
183
|
+
// so native terminal drag-selection and copy work without any special steps.
|
|
184
|
+
traceTerminalClear("src/index.tsx:startup", { mode: "hard" });
|
|
185
|
+
writeStdout(TERMINAL_SEQUENCES.hardRepaint, "src/index.tsx:startup");
|
|
186
|
+
const startupWorkspaceRoot = resolveWorkspaceRoot();
|
|
187
|
+
const startupSettings = loadSettings();
|
|
188
|
+
const startupTitle =
|
|
189
|
+
formatTerminalTitlePath(startupWorkspaceRoot, startupSettings.ui.terminalTitleMode) || APP_NAME;
|
|
190
|
+
setIntendedTerminalTitle(startupTitle, {
|
|
191
|
+
force: true,
|
|
192
|
+
reason: "startup-title",
|
|
193
|
+
write: (chunk) => writeStdout(chunk, "src/index.tsx:startup.title"),
|
|
194
|
+
});
|
|
195
|
+
reassertIntendedTerminalTitle({
|
|
196
|
+
write: (chunk) => writeStdout(chunk, "src/index.tsx:startup.titleReady"),
|
|
197
|
+
reason: "startup-title-ready",
|
|
198
|
+
});
|
|
199
|
+
terminal.setBracketedPaste(true, "src/index.tsx:startup.bracketedPaste");
|
|
200
|
+
|
|
201
|
+
let cleanupDone = false;
|
|
202
|
+
let repaintArmed = false;
|
|
203
|
+
let renderHandle: RenderHandle | null = null;
|
|
204
|
+
let inkInstance: InkInstance | null = null;
|
|
205
|
+
|
|
206
|
+
const onResize = () => {
|
|
207
|
+
renderDebug.traceEvent("terminal", "resize", {
|
|
208
|
+
cols: stdout.columns,
|
|
209
|
+
rows: stdout.rows,
|
|
210
|
+
invalid: hasInvalidRestoreDimensions(stdout),
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
if (hasInvalidRestoreDimensions(stdout)) {
|
|
214
|
+
// Transient invalid dimensions (e.g. during maximize/restore on
|
|
215
|
+
// Windows). Keep the previous frame visible and let useTerminalViewport
|
|
216
|
+
// preserve the last renderable layout until valid dimensions return.
|
|
217
|
+
repaintArmed = true;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (repaintArmed) {
|
|
222
|
+
repaintArmed = false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// useTerminalViewport owns resize-driven React state. Do not clear the
|
|
226
|
+
// terminal, reset Ink output caches, or force Ink renders here; those
|
|
227
|
+
// imperative paths can produce a transient empty frame during streaming.
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const cleanup = () => {
|
|
231
|
+
if (cleanupDone) return;
|
|
232
|
+
cleanupDone = true;
|
|
233
|
+
stopStartupTitleGuard();
|
|
234
|
+
stdout.off("resize", onResize);
|
|
235
|
+
renderHandle?.cleanup();
|
|
236
|
+
// Restore terminal state: disable mouse reporting and bracketed paste.
|
|
237
|
+
terminal.setMouseReporting(false, "src/index.tsx:cleanup.mouse");
|
|
238
|
+
terminal.setBracketedPaste(false, "src/index.tsx:cleanup.bracketedPaste");
|
|
239
|
+
terminal.resetModes();
|
|
240
|
+
activeRoot = null;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const handleFatal = (error: unknown) => {
|
|
244
|
+
cleanup();
|
|
245
|
+
if (error instanceof Error) {
|
|
246
|
+
writeStderr(`${error.stack || error.message}\n`, "src/index.tsx:fatalError");
|
|
247
|
+
} else if (error) {
|
|
248
|
+
writeStderr(`${String(error)}\n`, "src/index.tsx:fatalUnknown");
|
|
249
|
+
}
|
|
250
|
+
process.exit(1);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const handleSignal = () => {
|
|
254
|
+
cleanup();
|
|
255
|
+
process.exit(0);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
stdout.on("resize", onResize);
|
|
259
|
+
registerExitHandler(cleanup);
|
|
260
|
+
|
|
261
|
+
// Ensure clean teardown on signals and unhandled failures to prevent
|
|
262
|
+
// leaving the terminal in mouse-reporting mode.
|
|
263
|
+
process.on("SIGINT", handleSignal);
|
|
264
|
+
process.on("SIGTERM", handleSignal);
|
|
265
|
+
process.on("uncaughtException", handleFatal);
|
|
266
|
+
process.on("unhandledRejection", handleFatal);
|
|
267
|
+
|
|
268
|
+
renderHandle = renderApp(<App launchArgs={launchArgs} />, {
|
|
269
|
+
kittyKeyboard: KITTY_KEYBOARD_OPTIONS,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Resolve the real Ink class instance to get access to lastOutput,
|
|
273
|
+
// onRender, calculateLayout, etc. Gracefully degrades to null in tests.
|
|
274
|
+
inkInstance = resolveInkInstanceForStdout(stdout);
|
|
275
|
+
|
|
276
|
+
// Remove Ink's own resize handler so the app is the sole resize handler.
|
|
277
|
+
// This eliminates the race where Ink's resized() fires alongside our
|
|
278
|
+
// onResize, causing interleaved renders that desync logUpdate.
|
|
279
|
+
if (inkInstance?.unsubscribeResize) {
|
|
280
|
+
inkInstance.unsubscribeResize();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
void renderHandle.waitUntilExit().finally(cleanup);
|
|
284
|
+
|
|
285
|
+
activeRoot = { cleanup };
|
|
286
|
+
return { started: true, exitCode: 0 };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const isMainModule = Boolean((import.meta as ImportMeta & { main?: boolean }).main);
|
|
290
|
+
|
|
291
|
+
if (isMainModule) {
|
|
292
|
+
const result = startApp();
|
|
293
|
+
if (!result.started) {
|
|
294
|
+
process.exitCode = result.exitCode;
|
|
295
|
+
}
|
|
296
|
+
}
|