@golba98/codexa 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -18
- package/bin/codexa.js +62 -144
- package/package.json +4 -3
- package/src/app.tsx +642 -303
- package/src/commands/handler.ts +7 -18
- package/src/config/buildInfo.ts +2 -2
- package/src/config/layeredConfig.ts +1 -1
- package/src/config/runtimeConfig.ts +1 -1
- package/src/config/settings.ts +1 -1
- package/src/config/trustStore.ts +1 -1
- package/src/core/README.md +52 -0
- package/src/core/agent/loop.ts +282 -0
- package/src/core/agent/protocol.ts +211 -0
- package/src/core/agent/tools.ts +414 -0
- package/src/core/{codexExecArgs.ts → codex/codexExecArgs.ts} +2 -2
- package/src/core/{codexLaunch.ts → codex/codexLaunch.ts} +3 -3
- package/src/core/{codexPrompt.ts → codex/codexPrompt.ts} +3 -3
- package/src/core/debug/modelStateDebug.ts +34 -0
- package/src/core/executables/antigravityExecutable.ts +48 -0
- package/src/core/executables/executableResolver.ts +5 -1
- package/src/core/models/codexModelCapabilities.ts +57 -4
- package/src/core/models/codexModelsCacheSeed.ts +153 -0
- package/src/core/models/providerModelCache.ts +106 -0
- package/src/core/perf/renderDebug.ts +10 -6
- package/src/core/process/CommandRunner.ts +12 -1
- package/src/core/providerLauncher/registry.ts +64 -18
- package/src/core/providerLauncher/types.ts +12 -9
- package/src/core/providerLauncher/workspaceConfig.ts +41 -26
- package/src/core/providerRuntime/anthropic.ts +10 -6
- package/src/core/providerRuntime/antigravity.ts +461 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +724 -446
- package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
- package/src/core/providerRuntime/contextMetadata.ts +12 -24
- package/src/core/providerRuntime/local.ts +129 -51
- package/src/core/providerRuntime/mistralVibe.ts +663 -0
- package/src/core/providerRuntime/models.ts +40 -15
- package/src/core/providerRuntime/reasoning.ts +9 -6
- package/src/core/providerRuntime/registry.ts +76 -4
- package/src/core/providerRuntime/types.ts +20 -14
- package/src/core/providers/codexSubprocess.ts +2 -2
- package/src/core/providers/types.ts +1 -1
- package/src/core/{attachments.ts → shared/attachments.ts} +27 -4
- package/src/core/{cleanupFastFail.ts → shared/cleanupFastFail.ts} +1 -1
- package/src/core/{hollowResponseFormat.ts → shared/hollowResponseFormat.ts} +1 -1
- package/src/core/terminal/clearFrameBoundary.ts +814 -0
- package/src/core/terminal/frameLock.ts +109 -0
- package/src/core/terminal/inkRenderReset.ts +123 -0
- package/src/core/terminal/terminalControl.ts +22 -0
- package/src/core/terminal/terminalTitle.ts +16 -102
- package/src/core/{channel.ts → version/channel.ts} +1 -1
- package/src/core/{updateCheck.ts → version/updateCheck.ts} +10 -5
- package/src/core/{launchContext.ts → workspace/launchContext.ts} +9 -16
- package/src/core/{planStorage.ts → workspace/planStorage.ts} +2 -2
- package/src/core/{workspaceGuard.ts → workspace/workspaceGuard.ts} +97 -13
- package/src/headless/execRunner.ts +2 -2
- package/src/index.tsx +66 -98
- package/src/session/appSession.ts +10 -7
- package/src/session/chatLifecycle.ts +1 -1
- package/src/session/liveRenderScheduler.ts +1 -1
- package/src/session/types.ts +1 -1
- package/src/test/runtimeTestUtils.ts +90 -0
- package/src/ui/{ActivityBars.tsx → chrome/ActivityBars.tsx} +1 -1
- package/src/ui/{ActivityIndicator.tsx → chrome/ActivityIndicator.tsx} +3 -3
- package/src/ui/{AnimatedStatusText.tsx → chrome/AnimatedStatusText.tsx} +3 -3
- package/src/ui/chrome/AppShell.tsx +672 -0
- package/src/ui/{BottomComposer.tsx → chrome/BottomComposer.tsx} +159 -158
- package/src/ui/{DashCard.tsx → chrome/DashCard.tsx} +2 -2
- package/src/ui/{RunFooter.tsx → chrome/RunFooter.tsx} +3 -3
- package/src/ui/chrome/RuntimeStatusBar.tsx +108 -0
- package/src/ui/{Spinner.tsx → chrome/Spinner.tsx} +1 -1
- package/src/ui/{TopHeader.tsx → chrome/TopHeader.tsx} +54 -38
- package/src/ui/{UpdateAvailableCard.tsx → chrome/UpdateAvailableCard.tsx} +5 -5
- package/src/ui/{commandNormalize.ts → input/commandNormalize.ts} +1 -1
- package/src/ui/{focus.ts → input/focus.ts} +1 -1
- package/src/ui/{inputBuffer.ts → input/inputBuffer.ts} +3 -3
- package/src/ui/layout.ts +298 -24
- package/src/ui/{AttachmentImportPanel.tsx → panels/AttachmentImportPanel.tsx} +3 -3
- package/src/ui/{AuthPanel.tsx → panels/AuthPanel.tsx} +5 -5
- package/src/ui/{BackendPicker.tsx → panels/BackendPicker.tsx} +2 -2
- package/src/ui/{ModePicker.tsx → panels/ModePicker.tsx} +2 -2
- package/src/ui/{ModelPicker.tsx → panels/ModelPicker.tsx} +2 -2
- package/src/ui/{ModelPickerScreen.tsx → panels/ModelPickerScreen.tsx} +224 -42
- package/src/ui/{ModelReasoningPicker.tsx → panels/ModelReasoningPicker.tsx} +5 -5
- package/src/ui/{Panel.tsx → panels/Panel.tsx} +1 -1
- package/src/ui/{PermissionsPanel.tsx → panels/PermissionsPanel.tsx} +3 -3
- package/src/ui/{PlanActionPicker.tsx → panels/PlanActionPicker.tsx} +2 -2
- package/src/ui/{PlanReviewPanel.tsx → panels/PlanReviewPanel.tsx} +5 -5
- package/src/ui/panels/ProviderPicker.tsx +737 -0
- package/src/ui/{ReasoningPicker.tsx → panels/ReasoningPicker.tsx} +3 -3
- package/src/ui/{SelectionPanel.tsx → panels/SelectionPanel.tsx} +5 -1
- package/src/ui/{SettingsPanel.tsx → panels/SettingsPanel.tsx} +4 -4
- package/src/ui/{TextEntryPanel.tsx → panels/TextEntryPanel.tsx} +4 -4
- package/src/ui/{ThemePicker.tsx → panels/ThemePicker.tsx} +2 -2
- package/src/ui/{UpdatePromptPanel.tsx → panels/UpdatePromptPanel.tsx} +17 -23
- package/src/ui/{Markdown.tsx → render/Markdown.tsx} +2 -2
- package/src/ui/{diffRenderer.ts → render/diffRenderer.ts} +1 -1
- package/src/ui/{logoVariants.ts → render/logoVariants.ts} +4 -8
- package/src/ui/{modeDisplay.ts → render/modeDisplay.ts} +2 -2
- package/src/ui/{outputPipeline.ts → render/outputPipeline.ts} +2 -2
- package/src/ui/{runtimeDisplay.ts → render/runtimeDisplay.ts} +23 -10
- package/src/ui/{textLayout.ts → render/textLayout.ts} +15 -4
- package/src/ui/{ActionRequiredBlock.tsx → timeline/ActionRequiredBlock.tsx} +3 -3
- package/src/ui/{AgentBlock.tsx → timeline/AgentBlock.tsx} +10 -10
- package/src/ui/{StaticIntroItem.tsx → timeline/StaticIntroItem.tsx} +2 -2
- package/src/ui/{ThinkingBlock.tsx → timeline/ThinkingBlock.tsx} +4 -4
- package/src/ui/{Timeline.tsx → timeline/Timeline.tsx} +1620 -1471
- package/src/ui/timeline/TranscriptShell.tsx +322 -0
- package/src/ui/{TurnGroup.tsx → timeline/TurnGroup.tsx} +15 -15
- package/src/ui/timeline/layoutListWindow.ts +145 -0
- package/src/ui/{progressEntries.ts → timeline/progressEntries.ts} +3 -3
- package/src/ui/{runActivityView.ts → timeline/runActivityView.ts} +1 -1
- package/src/ui/{timelineMeasure.ts → timeline/timelineMeasure.ts} +206 -134
- package/src/core/codex.ts +0 -124
- package/src/ui/AppShell.tsx +0 -706
- package/src/ui/ProviderPicker.tsx +0 -321
- package/src/ui/StaticTranscriptItem.tsx +0 -56
- /package/src/core/{inputDebug.ts → debug/inputDebug.ts} +0 -0
- /package/src/core/{clipboard.ts → shared/clipboard.ts} +0 -0
- /package/src/core/{githubDiagnostics.ts → shared/githubDiagnostics.ts} +0 -0
- /package/src/core/{projectInstructions.ts → workspace/projectInstructions.ts} +0 -0
- /package/src/core/{workspaceActivity.ts → workspace/workspaceActivity.ts} +0 -0
- /package/src/core/{workspaceRoot.ts → workspace/workspaceRoot.ts} +0 -0
- /package/src/ui/{busyStatusAnimation.ts → chrome/busyStatusAnimation.ts} +0 -0
- /package/src/ui/{slashCommands.ts → input/slashCommands.ts} +0 -0
- /package/src/ui/{terminalAnswerFormat.ts → render/terminalAnswerFormat.ts} +0 -0
package/src/ui/AppShell.tsx
DELETED
|
@@ -1,706 +0,0 @@
|
|
|
1
|
-
import React, { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { Box, Text, useStdin } from "ink";
|
|
3
|
-
import { useTheme } from "./theme.js";
|
|
4
|
-
import type { RuntimeSummary } from "../config/runtimeConfig.js";
|
|
5
|
-
import type { CodexAuthState } from "../core/auth/codexAuth.js";
|
|
6
|
-
import { HEADER_CONFIG_DEFAULTS, type HeaderConfig } from "../config/settings.js";
|
|
7
|
-
import * as renderDebug from "../core/perf/renderDebug.js";
|
|
8
|
-
import type { Screen, TimelineEvent, UIState } from "../session/types.js";
|
|
9
|
-
import { isBusy } from "../session/types.js";
|
|
10
|
-
import {
|
|
11
|
-
getShellHeight,
|
|
12
|
-
getShellWidth,
|
|
13
|
-
type Layout,
|
|
14
|
-
type LayoutMode,
|
|
15
|
-
} from "./layout.js";
|
|
16
|
-
import {
|
|
17
|
-
buildActiveRenderItems,
|
|
18
|
-
buildStaticRenderItems,
|
|
19
|
-
buildTimelineItems,
|
|
20
|
-
parseTimelineNavigationInput,
|
|
21
|
-
Timeline,
|
|
22
|
-
TimelineRowView,
|
|
23
|
-
type TimelineItem,
|
|
24
|
-
} from "./Timeline.js";
|
|
25
|
-
import { buildNativeTranscriptParts, type NativeTranscriptRowItem, type TimelineRow } from "./timelineMeasure.js";
|
|
26
|
-
import type { TerminalSelectionProfile } from "../core/terminal/terminalSelection.js";
|
|
27
|
-
import { MemoizedTopHeader, measureTopHeaderRows, type UpdateAvailableInfo } from "./TopHeader.js";
|
|
28
|
-
|
|
29
|
-
const COMPACT_HEADER_TO_COMPOSER_GAP_ROWS = 1;
|
|
30
|
-
const MEDIUM_HEADER_TO_COMPOSER_GAP_ROWS = 1;
|
|
31
|
-
const TALL_HEADER_TO_COMPOSER_GAP_ROWS = 1;
|
|
32
|
-
|
|
33
|
-
// ─── Types & constants ────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
type AppShellLayout = Layout & { layoutEpoch?: number };
|
|
36
|
-
type NativeStaticItem =
|
|
37
|
-
{ type: "rows" } & NativeTranscriptRowItem;
|
|
38
|
-
|
|
39
|
-
export interface AppShellProps {
|
|
40
|
-
layout: AppShellLayout;
|
|
41
|
-
screen: Screen;
|
|
42
|
-
authState: CodexAuthState;
|
|
43
|
-
workspaceLabel: string;
|
|
44
|
-
workspaceRoot?: string | null;
|
|
45
|
-
runtimeSummary?: RuntimeSummary | null;
|
|
46
|
-
staticEvents: TimelineEvent[];
|
|
47
|
-
activeEvents: TimelineEvent[];
|
|
48
|
-
uiState: UIState;
|
|
49
|
-
panel: React.ReactNode;
|
|
50
|
-
mainPanel?: React.ReactNode;
|
|
51
|
-
mainPanelMode?: "viewport" | "full-output";
|
|
52
|
-
composer: React.ReactNode;
|
|
53
|
-
composerRows: number;
|
|
54
|
-
panelHint?: React.ReactNode;
|
|
55
|
-
verboseMode?: boolean;
|
|
56
|
-
mouseCapture?: boolean;
|
|
57
|
-
onMouseActivity?: () => void;
|
|
58
|
-
selectionProfile?: TerminalSelectionProfile;
|
|
59
|
-
clearCount?: number;
|
|
60
|
-
headerConfig?: HeaderConfig;
|
|
61
|
-
updateAvailable?: UpdateAvailableInfo | null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ─── Helpers & subcomponents ─────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
export function isCrampedViewport(rows: number | undefined): boolean {
|
|
67
|
-
return (rows ?? 24) <= 24;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function calculateNativeSpacerRows({
|
|
71
|
-
shellRows,
|
|
72
|
-
introRows,
|
|
73
|
-
composerRows,
|
|
74
|
-
staticRows,
|
|
75
|
-
liveRows,
|
|
76
|
-
}: {
|
|
77
|
-
shellRows: number;
|
|
78
|
-
introRows: number;
|
|
79
|
-
composerRows: number;
|
|
80
|
-
staticRows: number;
|
|
81
|
-
liveRows: number;
|
|
82
|
-
}): number {
|
|
83
|
-
const availableBodyRows = Math.max(0, shellRows - introRows - composerRows);
|
|
84
|
-
const visibleBodyContentRows = Math.max(0, staticRows) + Math.max(0, liveRows);
|
|
85
|
-
return Math.max(0, availableBodyRows - visibleBodyContentRows);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function calculateColdStartSpacerRows({
|
|
89
|
-
shellRows,
|
|
90
|
-
headerRows,
|
|
91
|
-
composerRows,
|
|
92
|
-
layoutMode,
|
|
93
|
-
availableRows,
|
|
94
|
-
}: {
|
|
95
|
-
shellRows: number;
|
|
96
|
-
headerRows: number;
|
|
97
|
-
composerRows: number;
|
|
98
|
-
layoutMode: LayoutMode;
|
|
99
|
-
availableRows: number;
|
|
100
|
-
}): number {
|
|
101
|
-
if (availableRows <= 0) return 0;
|
|
102
|
-
|
|
103
|
-
const rowsAfterHeaderAndComposer = Math.max(0, shellRows - headerRows - composerRows);
|
|
104
|
-
const preferredRows = layoutMode === "micro" || shellRows <= 18
|
|
105
|
-
? 1
|
|
106
|
-
: shellRows >= 36
|
|
107
|
-
? TALL_HEADER_TO_COMPOSER_GAP_ROWS
|
|
108
|
-
: shellRows >= 28
|
|
109
|
-
? MEDIUM_HEADER_TO_COMPOSER_GAP_ROWS
|
|
110
|
-
: COMPACT_HEADER_TO_COMPOSER_GAP_ROWS;
|
|
111
|
-
|
|
112
|
-
return Math.max(0, Math.min(availableRows, rowsAfterHeaderAndComposer, preferredRows));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function calculateHeaderToContentGapRows(layout: Layout): number {
|
|
116
|
-
if (layout.mode === "micro" || layout.rows <= 18) return 0;
|
|
117
|
-
return 1;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function NativeRowsItem({ rows }: { rows: TimelineRow[] }) {
|
|
121
|
-
return (
|
|
122
|
-
<Box flexDirection="column">
|
|
123
|
-
{rows.map((row) => (
|
|
124
|
-
<TimelineRowView key={row.key} row={row} />
|
|
125
|
-
))}
|
|
126
|
-
</Box>
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function NativePauseBar({ unseenRows }: { unseenRows: number }) {
|
|
131
|
-
return (
|
|
132
|
-
<Box width="100%" paddingX={1}>
|
|
133
|
-
<Text dimColor>
|
|
134
|
-
{unseenRows > 0
|
|
135
|
-
? `↓ ${unseenRows} new rows · End to follow`
|
|
136
|
-
: "↓ New output · End to follow"}
|
|
137
|
-
</Text>
|
|
138
|
-
</Box>
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ─── Component ────────────────────────────────────────────────────────────────
|
|
143
|
-
|
|
144
|
-
function AppShellInner({
|
|
145
|
-
layout,
|
|
146
|
-
screen,
|
|
147
|
-
authState,
|
|
148
|
-
workspaceLabel,
|
|
149
|
-
workspaceRoot = null,
|
|
150
|
-
runtimeSummary = null,
|
|
151
|
-
staticEvents,
|
|
152
|
-
activeEvents,
|
|
153
|
-
uiState,
|
|
154
|
-
panel,
|
|
155
|
-
mainPanel,
|
|
156
|
-
mainPanelMode = "viewport",
|
|
157
|
-
composer,
|
|
158
|
-
composerRows,
|
|
159
|
-
panelHint,
|
|
160
|
-
verboseMode = false,
|
|
161
|
-
mouseCapture = false,
|
|
162
|
-
onMouseActivity,
|
|
163
|
-
selectionProfile,
|
|
164
|
-
clearCount = 0,
|
|
165
|
-
headerConfig = HEADER_CONFIG_DEFAULTS,
|
|
166
|
-
updateAvailable = null,
|
|
167
|
-
}: AppShellProps) {
|
|
168
|
-
const theme = useTheme();
|
|
169
|
-
renderDebug.useRenderDebug("AppShell", {
|
|
170
|
-
cols: layout.cols,
|
|
171
|
-
rows: layout.rows,
|
|
172
|
-
mode: layout.mode,
|
|
173
|
-
layoutEpoch: layout.layoutEpoch,
|
|
174
|
-
screen,
|
|
175
|
-
authState,
|
|
176
|
-
workspaceLabel,
|
|
177
|
-
workspaceRoot,
|
|
178
|
-
runtimeSummary,
|
|
179
|
-
staticEvents,
|
|
180
|
-
activeEvents,
|
|
181
|
-
uiState,
|
|
182
|
-
composer,
|
|
183
|
-
composerRows,
|
|
184
|
-
verboseMode,
|
|
185
|
-
});
|
|
186
|
-
renderDebug.useLifecycleDebug("AppShell", {
|
|
187
|
-
screen,
|
|
188
|
-
cols: layout.cols,
|
|
189
|
-
rows: layout.rows,
|
|
190
|
-
mode: layout.mode,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const shellWidth = getShellWidth(layout.cols);
|
|
194
|
-
const shellHeight = getShellHeight(layout.rows);
|
|
195
|
-
const headerRows = measureTopHeaderRows(layout, headerConfig, !!updateAvailable);
|
|
196
|
-
const headerToContentGapRows = calculateHeaderToContentGapRows(layout);
|
|
197
|
-
const showComposer = screen === "main";
|
|
198
|
-
const showMainPanel = screen === "main" && mainPanel !== undefined && mainPanel !== null;
|
|
199
|
-
const showMainPanelFullOutput = showMainPanel && mainPanelMode === "full-output";
|
|
200
|
-
const showTimeline = screen === "main" && !showMainPanel;
|
|
201
|
-
const showPanelStage = screen !== "main";
|
|
202
|
-
const hasUserPrompt = useMemo(
|
|
203
|
-
() => staticEvents.some((e) => e.type === "user") || activeEvents.some((e) => e.type === "user"),
|
|
204
|
-
[staticEvents, activeEvents],
|
|
205
|
-
);
|
|
206
|
-
const previousMeasurements = useRef<{
|
|
207
|
-
timelineRows: number;
|
|
208
|
-
composerRows: number;
|
|
209
|
-
shellHeight: number;
|
|
210
|
-
shellWidth: number;
|
|
211
|
-
} | null>(null);
|
|
212
|
-
|
|
213
|
-
// ── Native mode scroll-pause state ────────────────────────────────────────
|
|
214
|
-
// When the user presses Page Up or Home during streaming, we freeze nativeAllRows
|
|
215
|
-
// so Ink's lastOutputHeight stays constant and the terminal stops auto-scrolling.
|
|
216
|
-
const [nativePaused, setNativePaused] = useState(false);
|
|
217
|
-
const nativePausedRef = useRef(false);
|
|
218
|
-
const frozenNativeRowsRef = useRef<TimelineRow[]>([]);
|
|
219
|
-
const frozenLiveRowCountRef = useRef(0);
|
|
220
|
-
const { stdin } = useStdin();
|
|
221
|
-
|
|
222
|
-
useEffect(() => {
|
|
223
|
-
if (mouseCapture) return;
|
|
224
|
-
if (!isBusy(uiState) && nativePausedRef.current) {
|
|
225
|
-
setNativePaused(false);
|
|
226
|
-
nativePausedRef.current = false;
|
|
227
|
-
}
|
|
228
|
-
}, [mouseCapture, uiState]);
|
|
229
|
-
|
|
230
|
-
useEffect(() => {
|
|
231
|
-
if (mouseCapture || !stdin) return;
|
|
232
|
-
|
|
233
|
-
function handleScrollKeys(chunk: Buffer | string) {
|
|
234
|
-
const raw = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
235
|
-
const actions = parseTimelineNavigationInput(raw);
|
|
236
|
-
if (actions.length === 0) return;
|
|
237
|
-
|
|
238
|
-
const wantsUp = actions.includes("pageUp") || actions.includes("home");
|
|
239
|
-
const wantsDown = actions.includes("pageDown") || actions.includes("end");
|
|
240
|
-
|
|
241
|
-
if (wantsDown && nativePausedRef.current) {
|
|
242
|
-
setNativePaused(false);
|
|
243
|
-
nativePausedRef.current = false;
|
|
244
|
-
} else if (wantsUp && !nativePausedRef.current && isBusy(uiState)) {
|
|
245
|
-
setNativePaused(true);
|
|
246
|
-
nativePausedRef.current = true;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
stdin.on("data", handleScrollKeys);
|
|
251
|
-
return () => { stdin.off("data", handleScrollKeys); };
|
|
252
|
-
}, [mouseCapture, stdin, uiState]);
|
|
253
|
-
// ── End native mode scroll-pause state ────────────────────────────────────
|
|
254
|
-
|
|
255
|
-
const effectiveShowComposer = showComposer;
|
|
256
|
-
const effectiveComposerRows = effectiveShowComposer ? composerRows : 0;
|
|
257
|
-
const panelHintRows = showPanelStage && panelHint ? 2 : 0;
|
|
258
|
-
const canUseColdStartGap = effectiveShowComposer && !hasUserPrompt && screen === "main" && !showMainPanel;
|
|
259
|
-
|
|
260
|
-
// Timeline/panel owns all vertical space between the live header and fixed composer.
|
|
261
|
-
const coldStartAvailableRows = Math.max(
|
|
262
|
-
0,
|
|
263
|
-
shellHeight - headerRows - headerToContentGapRows - effectiveComposerRows - panelHintRows - 2,
|
|
264
|
-
);
|
|
265
|
-
const coldStartComposerGapRows = canUseColdStartGap
|
|
266
|
-
? calculateColdStartSpacerRows({
|
|
267
|
-
shellRows: shellHeight,
|
|
268
|
-
headerRows,
|
|
269
|
-
composerRows: effectiveComposerRows,
|
|
270
|
-
layoutMode: layout.mode,
|
|
271
|
-
availableRows: coldStartAvailableRows,
|
|
272
|
-
})
|
|
273
|
-
: 0;
|
|
274
|
-
const fixedComposerLeadGapRows = mouseCapture ? coldStartComposerGapRows : 0;
|
|
275
|
-
const calculatedTimelineRowsRaw = shellHeight
|
|
276
|
-
- headerRows
|
|
277
|
-
- headerToContentGapRows
|
|
278
|
-
- effectiveComposerRows
|
|
279
|
-
- panelHintRows
|
|
280
|
-
- fixedComposerLeadGapRows;
|
|
281
|
-
const calculatedTimelineRows = Math.max(2, calculatedTimelineRowsRaw);
|
|
282
|
-
|
|
283
|
-
const { finalShellHeight, finalShellWidth, finalTimelineRows } = useMemo(() => {
|
|
284
|
-
const prev = previousMeasurements.current;
|
|
285
|
-
const isValid = shellHeight > 0
|
|
286
|
-
&& shellWidth > 0
|
|
287
|
-
&& Number.isFinite(shellHeight)
|
|
288
|
-
&& Number.isFinite(shellWidth)
|
|
289
|
-
&& Number.isFinite(calculatedTimelineRowsRaw)
|
|
290
|
-
&& calculatedTimelineRowsRaw >= 2;
|
|
291
|
-
|
|
292
|
-
if (!isValid && prev) {
|
|
293
|
-
renderDebug.traceEvent("layout", "measurementFallback", {
|
|
294
|
-
reason: "invalid-shell-or-timeline-rows",
|
|
295
|
-
shellHeight,
|
|
296
|
-
shellWidth,
|
|
297
|
-
calculatedTimelineRowsRaw,
|
|
298
|
-
previousTimelineRows: prev.timelineRows,
|
|
299
|
-
});
|
|
300
|
-
return {
|
|
301
|
-
finalShellHeight: prev.shellHeight,
|
|
302
|
-
finalShellWidth: prev.shellWidth,
|
|
303
|
-
finalTimelineRows: prev.timelineRows,
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (!isValid) {
|
|
308
|
-
renderDebug.traceEvent("layout", "measurementFallback", {
|
|
309
|
-
reason: "invalid-initial-shell-or-timeline-rows",
|
|
310
|
-
shellHeight,
|
|
311
|
-
shellWidth,
|
|
312
|
-
calculatedTimelineRowsRaw,
|
|
313
|
-
clampedTimelineRows: calculatedTimelineRows,
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return {
|
|
318
|
-
finalShellHeight: shellHeight,
|
|
319
|
-
finalShellWidth: shellWidth,
|
|
320
|
-
finalTimelineRows: Math.max(2, calculatedTimelineRows),
|
|
321
|
-
};
|
|
322
|
-
}, [shellHeight, shellWidth, calculatedTimelineRows, calculatedTimelineRowsRaw]);
|
|
323
|
-
|
|
324
|
-
const nativeTranscriptParts = useMemo(() => {
|
|
325
|
-
if (mouseCapture) {
|
|
326
|
-
return { staticItems: [], liveRows: [] };
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const staticItems = buildTimelineItems(staticEvents);
|
|
330
|
-
const activeItems = buildTimelineItems(activeEvents);
|
|
331
|
-
const turnIds = [...staticItems, ...activeItems]
|
|
332
|
-
.filter((item): item is Extract<TimelineItem, { type: "turn" }> => item.type === "turn")
|
|
333
|
-
.map((item) => item.turnId);
|
|
334
|
-
|
|
335
|
-
const parts = buildNativeTranscriptParts(
|
|
336
|
-
[
|
|
337
|
-
...buildStaticRenderItems(staticItems, turnIds, null, null, null),
|
|
338
|
-
...buildActiveRenderItems(activeItems, turnIds, uiState),
|
|
339
|
-
],
|
|
340
|
-
{
|
|
341
|
-
totalWidth: finalShellWidth,
|
|
342
|
-
verboseMode,
|
|
343
|
-
debugLabel: "app-shell-native",
|
|
344
|
-
workspaceRoot,
|
|
345
|
-
},
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
// If we're not supposed to show the timeline yet (e.g. during early mount
|
|
349
|
-
// or when in a full-panel mode), we still calculate the static parts
|
|
350
|
-
// but hide the live rows. This keeps the committed row array stable,
|
|
351
|
-
// preventing unnecessary re-renders.
|
|
352
|
-
if (!showTimeline) {
|
|
353
|
-
return { ...parts, liveRows: [] };
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
return parts;
|
|
357
|
-
}, [activeEvents, finalShellWidth, mouseCapture, showTimeline, staticEvents, uiState, verboseMode, workspaceRoot]);
|
|
358
|
-
|
|
359
|
-
// In native mode the root box is content-sized (no fixed height), so without an
|
|
360
|
-
// explicit spacer the composer appears immediately after the intro instead of being
|
|
361
|
-
// anchored near the terminal bottom. The spacer fills the gap between whatever
|
|
362
|
-
// live content exists and where the composer should sit.
|
|
363
|
-
const nativeStaticTranscriptRows = useMemo(
|
|
364
|
-
() => nativeTranscriptParts.staticItems.reduce((total, item) => total + item.rows.length, 0),
|
|
365
|
-
[nativeTranscriptParts.staticItems],
|
|
366
|
-
);
|
|
367
|
-
const nativeSpacerRows = useMemo(() => {
|
|
368
|
-
if (mouseCapture || !effectiveShowComposer || showMainPanel) return 0;
|
|
369
|
-
const rows = calculateNativeSpacerRows({
|
|
370
|
-
shellRows: finalShellHeight,
|
|
371
|
-
introRows: headerRows + headerToContentGapRows,
|
|
372
|
-
composerRows: effectiveComposerRows,
|
|
373
|
-
staticRows: nativeStaticTranscriptRows,
|
|
374
|
-
liveRows: nativeTranscriptParts.liveRows.length,
|
|
375
|
-
});
|
|
376
|
-
// Before the user sends their first prompt, keep a small fixed gap so the
|
|
377
|
-
// composer sits near the logo without a large blank area in between.
|
|
378
|
-
// This cap persists across model/auth system events so the layout stays
|
|
379
|
-
// stable after config changes on cold start.
|
|
380
|
-
if (!hasUserPrompt) {
|
|
381
|
-
return calculateColdStartSpacerRows({
|
|
382
|
-
shellRows: finalShellHeight,
|
|
383
|
-
headerRows,
|
|
384
|
-
composerRows: effectiveComposerRows,
|
|
385
|
-
layoutMode: layout.mode,
|
|
386
|
-
availableRows: rows,
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
return rows;
|
|
390
|
-
}, [mouseCapture, effectiveShowComposer, showMainPanel, finalShellHeight, headerRows, headerToContentGapRows, effectiveComposerRows, layout.mode, nativeStaticTranscriptRows, nativeTranscriptParts.liveRows.length, hasUserPrompt]);
|
|
391
|
-
|
|
392
|
-
// In native mode (no SGR capture), committed rows still render inside the
|
|
393
|
-
// body below the live header. Do not use Ink's static output component here
|
|
394
|
-
// because it permanently prepends static output above live output, which
|
|
395
|
-
// would place transcript content before the header regardless of JSX order.
|
|
396
|
-
const nativeStaticAllItems = useMemo<NativeStaticItem[]>(
|
|
397
|
-
() => {
|
|
398
|
-
if (mouseCapture) return [];
|
|
399
|
-
return nativeTranscriptParts.staticItems.map((item) => ({ ...item, type: "rows" as const }));
|
|
400
|
-
},
|
|
401
|
-
[mouseCapture, clearCount, nativeTranscriptParts.staticItems],
|
|
402
|
-
);
|
|
403
|
-
const nativeAllRows = useMemo<TimelineRow[]>(
|
|
404
|
-
() => {
|
|
405
|
-
if (mouseCapture) return [];
|
|
406
|
-
|
|
407
|
-
// When the user has paused auto-follow (pressed Page Up while busy),
|
|
408
|
-
// return the frozen snapshot so Ink's lastOutputHeight stays constant
|
|
409
|
-
// and the terminal stops auto-scrolling to the cursor on each frame.
|
|
410
|
-
if (nativePaused) {
|
|
411
|
-
return frozenNativeRowsRef.current;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const allStaticRows = nativeStaticAllItems.flatMap((item) => item.rows);
|
|
415
|
-
// Trim old static rows so lastOutputHeight stays bounded to ~2 terminal heights.
|
|
416
|
-
// Rows that fall off the top are already in terminal scrollback.
|
|
417
|
-
const maxStaticRows = finalShellHeight > 0 ? finalShellHeight * 2 : allStaticRows.length;
|
|
418
|
-
const trimmedStaticRows = allStaticRows.slice(Math.max(0, allStaticRows.length - maxStaticRows));
|
|
419
|
-
|
|
420
|
-
const rows = [
|
|
421
|
-
...trimmedStaticRows,
|
|
422
|
-
...(showTimeline ? nativeTranscriptParts.liveRows : []),
|
|
423
|
-
];
|
|
424
|
-
frozenLiveRowCountRef.current = showTimeline ? nativeTranscriptParts.liveRows.length : 0;
|
|
425
|
-
frozenNativeRowsRef.current = rows;
|
|
426
|
-
return rows;
|
|
427
|
-
},
|
|
428
|
-
[mouseCapture, nativePaused, nativeStaticAllItems, nativeTranscriptParts.liveRows, showTimeline, finalShellHeight],
|
|
429
|
-
);
|
|
430
|
-
|
|
431
|
-
// When paused, count how many live rows have arrived since the freeze point.
|
|
432
|
-
const nativeUnseenRows = nativePaused
|
|
433
|
-
? Math.max(0, (showTimeline ? nativeTranscriptParts.liveRows.length : 0) - frozenLiveRowCountRef.current)
|
|
434
|
-
: 0;
|
|
435
|
-
|
|
436
|
-
renderDebug.traceEvent("layout", "nativeTranscript", {
|
|
437
|
-
nativeMode: !mouseCapture,
|
|
438
|
-
mouseCapture,
|
|
439
|
-
showTimeline,
|
|
440
|
-
activeEvents: activeEvents.length,
|
|
441
|
-
staticEvents: staticEvents.length,
|
|
442
|
-
staticItems: nativeStaticAllItems.length,
|
|
443
|
-
liveRows: nativeTranscriptParts.liveRows.length,
|
|
444
|
-
contentSized: true,
|
|
445
|
-
finalTimelineRows,
|
|
446
|
-
composerRows: effectiveComposerRows,
|
|
447
|
-
headerRows,
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
renderDebug.traceLayoutValidity("AppShell", {
|
|
451
|
-
cols: layout.cols,
|
|
452
|
-
rows: layout.rows,
|
|
453
|
-
shellWidth,
|
|
454
|
-
shellHeight,
|
|
455
|
-
timelineRows: finalTimelineRows,
|
|
456
|
-
calculatedTimelineRowsRaw,
|
|
457
|
-
composerRows: effectiveComposerRows,
|
|
458
|
-
});
|
|
459
|
-
if (!Number.isFinite(calculatedTimelineRowsRaw) || calculatedTimelineRowsRaw <= 0) {
|
|
460
|
-
renderDebug.traceBlankFrame("AppShell", {
|
|
461
|
-
reason: "invalid-available-timeline-rows",
|
|
462
|
-
availableTimelineRows: calculatedTimelineRowsRaw,
|
|
463
|
-
finalTimelineRows,
|
|
464
|
-
composerRows: effectiveComposerRows,
|
|
465
|
-
shellHeight: finalShellHeight,
|
|
466
|
-
screen,
|
|
467
|
-
uiStateKind: uiState.kind,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
useEffect(() => {
|
|
472
|
-
const previous = previousMeasurements.current;
|
|
473
|
-
const changed: string[] = [];
|
|
474
|
-
if (!previous) {
|
|
475
|
-
changed.push("mount");
|
|
476
|
-
} else {
|
|
477
|
-
if (previous.timelineRows !== finalTimelineRows) changed.push("availableTimelineRows");
|
|
478
|
-
if (previous.composerRows !== effectiveComposerRows) changed.push("composerRows");
|
|
479
|
-
if (previous.shellHeight !== finalShellHeight) changed.push("height");
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (changed.length > 0) {
|
|
483
|
-
renderDebug.traceEvent("layout", "measurementUpdate", {
|
|
484
|
-
reason: changed.join(","),
|
|
485
|
-
availableTimelineRows: finalTimelineRows,
|
|
486
|
-
rawAvailableTimelineRows: calculatedTimelineRowsRaw,
|
|
487
|
-
composerRows: effectiveComposerRows,
|
|
488
|
-
shellHeight: finalShellHeight,
|
|
489
|
-
showComposer,
|
|
490
|
-
showTimeline,
|
|
491
|
-
showMainPanelFullOutput,
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
previousMeasurements.current = {
|
|
496
|
-
timelineRows: finalTimelineRows,
|
|
497
|
-
composerRows: effectiveComposerRows,
|
|
498
|
-
shellHeight: finalShellHeight,
|
|
499
|
-
shellWidth: finalShellWidth,
|
|
500
|
-
};
|
|
501
|
-
}, [calculatedTimelineRowsRaw, effectiveComposerRows, finalShellHeight, finalShellWidth, showComposer, showMainPanelFullOutput, showTimeline, finalTimelineRows]);
|
|
502
|
-
|
|
503
|
-
const clonedComposer = React.isValidElement(composer)
|
|
504
|
-
? React.cloneElement(composer as React.ReactElement<{ selectionProfile?: TerminalSelectionProfile }>, { selectionProfile })
|
|
505
|
-
: composer;
|
|
506
|
-
|
|
507
|
-
if (showMainPanelFullOutput) {
|
|
508
|
-
return (
|
|
509
|
-
<Box flexDirection="column" width="100%">
|
|
510
|
-
<Box flexDirection="column" width={finalShellWidth}>
|
|
511
|
-
<MemoizedTopHeader
|
|
512
|
-
authState={authState}
|
|
513
|
-
workspaceLabel={workspaceLabel}
|
|
514
|
-
layout={layout}
|
|
515
|
-
runtimeSummary={runtimeSummary}
|
|
516
|
-
headerConfig={headerConfig}
|
|
517
|
-
updateAvailable={updateAvailable}
|
|
518
|
-
/>
|
|
519
|
-
|
|
520
|
-
{headerToContentGapRows > 0 && (
|
|
521
|
-
<Box height={headerToContentGapRows} />
|
|
522
|
-
)}
|
|
523
|
-
|
|
524
|
-
{mainPanel}
|
|
525
|
-
|
|
526
|
-
{showComposer && (
|
|
527
|
-
<Box flexDirection="column" flexShrink={0}>
|
|
528
|
-
{composer}
|
|
529
|
-
</Box>
|
|
530
|
-
)}
|
|
531
|
-
</Box>
|
|
532
|
-
</Box>
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Native mode: no fixed shell height — content-sized so Ink's lastOutputHeight stays small.
|
|
537
|
-
// All visible content remains in one live tree so the header is always the
|
|
538
|
-
// first physical output region.
|
|
539
|
-
if (!mouseCapture) {
|
|
540
|
-
return (
|
|
541
|
-
<Box flexDirection="column" width={finalShellWidth}>
|
|
542
|
-
<MemoizedTopHeader
|
|
543
|
-
authState={authState}
|
|
544
|
-
workspaceLabel={workspaceLabel}
|
|
545
|
-
layout={layout}
|
|
546
|
-
runtimeSummary={runtimeSummary}
|
|
547
|
-
headerConfig={headerConfig}
|
|
548
|
-
updateAvailable={updateAvailable}
|
|
549
|
-
/>
|
|
550
|
-
|
|
551
|
-
{headerToContentGapRows > 0 && (
|
|
552
|
-
<Box height={headerToContentGapRows} />
|
|
553
|
-
)}
|
|
554
|
-
|
|
555
|
-
{nativeAllRows.length > 0 && (
|
|
556
|
-
<NativeRowsItem rows={nativeAllRows} />
|
|
557
|
-
)}
|
|
558
|
-
|
|
559
|
-
{nativePaused && isBusy(uiState) && (
|
|
560
|
-
<NativePauseBar unseenRows={nativeUnseenRows} />
|
|
561
|
-
)}
|
|
562
|
-
|
|
563
|
-
{showMainPanel && (
|
|
564
|
-
<Box flexDirection="column" paddingY={1} justifyContent="center">
|
|
565
|
-
{mainPanel}
|
|
566
|
-
</Box>
|
|
567
|
-
)}
|
|
568
|
-
|
|
569
|
-
{showPanelStage && (
|
|
570
|
-
<Box
|
|
571
|
-
flexDirection="column"
|
|
572
|
-
overflow="hidden"
|
|
573
|
-
paddingY={1}
|
|
574
|
-
>
|
|
575
|
-
{panel}
|
|
576
|
-
</Box>
|
|
577
|
-
)}
|
|
578
|
-
|
|
579
|
-
{nativeSpacerRows > 0 && (
|
|
580
|
-
<Box height={nativeSpacerRows} />
|
|
581
|
-
)}
|
|
582
|
-
|
|
583
|
-
{effectiveShowComposer && (
|
|
584
|
-
<Box flexDirection="column" flexShrink={0}>
|
|
585
|
-
{composer}
|
|
586
|
-
</Box>
|
|
587
|
-
)}
|
|
588
|
-
|
|
589
|
-
{showPanelStage && panelHint}
|
|
590
|
-
</Box>
|
|
591
|
-
);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
return (
|
|
595
|
-
<Box flexDirection="column" width="100%" height={finalShellHeight}>
|
|
596
|
-
<Box flexDirection="column" width={finalShellWidth}>
|
|
597
|
-
<MemoizedTopHeader
|
|
598
|
-
authState={authState}
|
|
599
|
-
workspaceLabel={workspaceLabel}
|
|
600
|
-
layout={layout}
|
|
601
|
-
runtimeSummary={runtimeSummary}
|
|
602
|
-
headerConfig={headerConfig}
|
|
603
|
-
updateAvailable={updateAvailable}
|
|
604
|
-
/>
|
|
605
|
-
|
|
606
|
-
{headerToContentGapRows > 0 && (
|
|
607
|
-
<Box height={headerToContentGapRows} />
|
|
608
|
-
)}
|
|
609
|
-
|
|
610
|
-
{/* Keep Timeline always mounted so its viewport scroll state survives panel open/close.
|
|
611
|
-
display="none" removes it from yoga layout (0 height) without unmounting. */}
|
|
612
|
-
<Box
|
|
613
|
-
flexDirection="column"
|
|
614
|
-
height={finalTimelineRows}
|
|
615
|
-
overflow="hidden"
|
|
616
|
-
display={showTimeline ? "flex" : "none"}
|
|
617
|
-
>
|
|
618
|
-
<Timeline
|
|
619
|
-
key={`timeline-${clearCount}`}
|
|
620
|
-
staticEvents={staticEvents}
|
|
621
|
-
activeEvents={activeEvents}
|
|
622
|
-
layout={layout}
|
|
623
|
-
uiState={uiState}
|
|
624
|
-
viewportRows={finalTimelineRows}
|
|
625
|
-
verboseMode={verboseMode}
|
|
626
|
-
authState={authState}
|
|
627
|
-
workspaceLabel={workspaceLabel}
|
|
628
|
-
workspaceRoot={workspaceRoot}
|
|
629
|
-
mouseCapture={mouseCapture}
|
|
630
|
-
onMouseActivity={onMouseActivity}
|
|
631
|
-
contentSized
|
|
632
|
-
/>
|
|
633
|
-
</Box>
|
|
634
|
-
|
|
635
|
-
{showMainPanel && (
|
|
636
|
-
<Box flexDirection="column" height={finalTimelineRows} overflow="hidden" justifyContent="center">
|
|
637
|
-
{mainPanel}
|
|
638
|
-
</Box>
|
|
639
|
-
)}
|
|
640
|
-
|
|
641
|
-
{showPanelStage && (
|
|
642
|
-
<Box flexDirection="column" flexGrow={1} overflow="hidden" paddingY={1}>
|
|
643
|
-
{panel}
|
|
644
|
-
</Box>
|
|
645
|
-
)}
|
|
646
|
-
|
|
647
|
-
{fixedComposerLeadGapRows > 0 && (
|
|
648
|
-
<Box height={fixedComposerLeadGapRows} />
|
|
649
|
-
)}
|
|
650
|
-
|
|
651
|
-
{effectiveShowComposer && (
|
|
652
|
-
<Box flexDirection="column" flexShrink={0}>
|
|
653
|
-
{clonedComposer}
|
|
654
|
-
</Box>
|
|
655
|
-
)}
|
|
656
|
-
|
|
657
|
-
{showPanelStage && panelHint}
|
|
658
|
-
</Box>
|
|
659
|
-
</Box>
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
/**
|
|
664
|
-
* Memoized AppShell — prevents re-renders when irrelevant App state changes.
|
|
665
|
-
*
|
|
666
|
-
* The App component re-renders on every streaming delta (via dispatchSession),
|
|
667
|
-
* cursor move, and conversationChars update. AppShell itself only needs to
|
|
668
|
-
* re-render when the layout, screen, event lists, uiState, or composer
|
|
669
|
-
* layout rows actually change.
|
|
670
|
-
*
|
|
671
|
-
* `composer` must remain in the comparator because MemoizedBottomComposer
|
|
672
|
-
* receives value/cursor updates through this prop; without it the composer
|
|
673
|
-
* would display stale input. Non-main panels are compared so picker content can
|
|
674
|
-
* refresh while the active screen stays unchanged, such as model discovery
|
|
675
|
-
* replacing the loading model picker with the interactive picker.
|
|
676
|
-
*/
|
|
677
|
-
export const AppShell = memo(AppShellInner, (prev, next) => {
|
|
678
|
-
const panelPropsEqual = next.screen === "main"
|
|
679
|
-
? prev.mainPanel === next.mainPanel
|
|
680
|
-
: (prev.panel === next.panel && prev.panelHint === next.panelHint);
|
|
681
|
-
|
|
682
|
-
return (
|
|
683
|
-
prev.layout.cols === next.layout.cols &&
|
|
684
|
-
prev.layout.rows === next.layout.rows &&
|
|
685
|
-
prev.layout.mode === next.layout.mode &&
|
|
686
|
-
prev.layout.layoutEpoch === next.layout.layoutEpoch &&
|
|
687
|
-
prev.screen === next.screen &&
|
|
688
|
-
prev.authState === next.authState &&
|
|
689
|
-
prev.workspaceLabel === next.workspaceLabel &&
|
|
690
|
-
prev.workspaceRoot === next.workspaceRoot &&
|
|
691
|
-
prev.runtimeSummary === next.runtimeSummary &&
|
|
692
|
-
prev.staticEvents === next.staticEvents &&
|
|
693
|
-
prev.activeEvents === next.activeEvents &&
|
|
694
|
-
prev.uiState === next.uiState &&
|
|
695
|
-
prev.composerRows === next.composerRows &&
|
|
696
|
-
prev.composer === next.composer &&
|
|
697
|
-
prev.mainPanel === next.mainPanel &&
|
|
698
|
-
prev.mainPanelMode === next.mainPanelMode &&
|
|
699
|
-
prev.verboseMode === next.verboseMode &&
|
|
700
|
-
prev.mouseCapture === next.mouseCapture &&
|
|
701
|
-
prev.onMouseActivity === next.onMouseActivity &&
|
|
702
|
-
prev.clearCount === next.clearCount &&
|
|
703
|
-
prev.updateAvailable === next.updateAvailable &&
|
|
704
|
-
panelPropsEqual
|
|
705
|
-
);
|
|
706
|
-
});
|