{
- if (e.key === 'Enter' || e.key === ' ') {
- e.preventDefault()
- onAction()
- }
- }}
>
{showDevice && (
-
+
{record.displayDeviceLabel}
-
+
)}
- {record.tabName}
+ {record.tabName}
-
+
{paneKinds.map((kind) => {
const Icon = paneKindIcon(kind)
return (
@@ -349,9 +342,9 @@ function TabCard({
·
{formatRelativeTime(timestamp, now)}
-
+
-
-
-
+
+
)
}
diff --git a/src/components/TerminalView.tsx b/src/components/TerminalView.tsx
index 36393dd5..2f3b4310 100644
--- a/src/components/TerminalView.tsx
+++ b/src/components/TerminalView.tsx
@@ -52,6 +52,10 @@ import {
type Osc52Event,
type Osc52Policy,
} from '@/lib/terminal-osc52'
+import {
+ createTerminalStartupProbeState,
+ extractTerminalStartupProbes,
+} from '@/lib/terminal-startup-probes'
import { ContextIds } from '@/components/context-menu/context-menu-constants'
import { resolveTerminalFontFamily } from '@/lib/terminal-fonts'
import { ConnectionErrorOverlay } from '@/components/terminal/ConnectionErrorOverlay'
@@ -83,6 +87,7 @@ export const RATE_LIMIT_RETRY_MAX_MS = 12000
const KEYBOARD_INSET_ACTIVATION_PX = 80
const MOBILE_KEYBAR_HEIGHT_PX = 40
const MOBILE_KEY_REPEAT_INITIAL_DELAY_MS = 320
+const STARTUP_PROBE_OSC11_QUERY = '\u001b]11;?\u0007'
function isClaudeTurnSubmit(data: string): boolean {
return data.includes('\r') || data.includes('\n')
@@ -96,10 +101,62 @@ const DEFAULT_MIN_CONTRAST_RATIO = 1
const MAX_LAST_SENT_VIEWPORT_CACHE_ENTRIES = 200
const TRUNCATED_REPLAY_BYTES = 128 * 1024
+type StartupProbeReplayDiscardState = {
+ remainder: string | null
+ buffered: string
+}
+
function resolveMinimumContrastRatio(theme?: { isDark?: boolean } | null): number {
return theme?.isDark === false ? LIGHT_THEME_MIN_CONTRAST_RATIO : DEFAULT_MIN_CONTRAST_RATIO
}
+function consumeStartupProbeReplayDiscard(raw: string, state: StartupProbeReplayDiscardState): string {
+ const remainder = state.remainder
+ if (!remainder) {
+ state.buffered = ''
+ return raw
+ }
+
+ let matched = state.buffered
+ let index = 0
+ while (
+ index < raw.length
+ && matched.length < remainder.length
+ && raw[index] === remainder[matched.length]
+ ) {
+ matched += raw[index]
+ index += 1
+ }
+
+ if (matched.length === remainder.length) {
+ state.remainder = null
+ state.buffered = ''
+ return raw.slice(index)
+ }
+
+ if (index < raw.length) {
+ state.remainder = null
+ state.buffered = ''
+ return `${matched}${raw.slice(index)}`
+ }
+
+ if (index === raw.length) {
+ state.buffered = matched
+ return ''
+ }
+
+ return raw
+}
+
+function getStartupProbeReplayRemainder(pending: string): string | null {
+ if (!pending || pending === STARTUP_PROBE_OSC11_QUERY) {
+ return null
+ }
+ return STARTUP_PROBE_OSC11_QUERY.startsWith(pending)
+ ? STARTUP_PROBE_OSC11_QUERY.slice(pending.length)
+ : null
+}
+
function deferTerminalPointerMutation(callback: () => void): void {
// xterm link activation runs inside element-level mouse handlers while it may
// still have document-level mouseup/move listeners in flight. Reparenting the
@@ -270,7 +327,13 @@ export default function TerminalView({ tabId, paneId, paneContent, hidden }: Ter
const restoreRequestIdRef = useRef