summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app/src')
-rw-r--r--packages/app/src/components/prompt-input.tsx17
-rw-r--r--packages/app/src/components/prompt-input/submit.ts3
-rw-r--r--packages/app/src/components/terminal.tsx16
-rw-r--r--packages/app/src/context/local.tsx50
-rw-r--r--packages/app/src/pages/error.tsx10
-rw-r--r--packages/app/src/pages/session/composer/session-composer-state.ts55
-rw-r--r--packages/app/src/pages/session/composer/session-todo-dock.tsx22
-rw-r--r--packages/app/src/pages/session/terminal-panel.tsx6
-rw-r--r--packages/app/src/testing/model-selection.ts109
-rw-r--r--packages/app/src/testing/prompt.ts83
-rw-r--r--packages/app/src/testing/session-composer.ts84
-rw-r--r--packages/app/src/testing/terminal.ts119
12 files changed, 5 insertions, 569 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index eedbc91cf..8ddb10a90 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -35,7 +35,6 @@ import { useLanguage } from "@/context/language"
import { usePlatform } from "@/context/platform"
import { useSessionLayout } from "@/pages/session/session-layout"
import { createSessionTabs } from "@/pages/session/helpers"
-import { promptEnabled, promptProbe } from "@/testing/prompt"
import { createTextFragment, getCursorPosition, setCursorPosition, setRangeEdge } from "./prompt-input/editor-dom"
import { createPromptAttachments } from "./prompt-input/attachments"
import { ACCEPTED_FILE_TYPES } from "./prompt-input/files"
@@ -639,7 +638,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const handleSlashSelect = (cmd: SlashCommand | undefined) => {
if (!cmd) return
- promptProbe.select(cmd.id)
closePopover()
const images = imageAttachments()
@@ -728,21 +726,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
element?.scrollIntoView({ block: "nearest", behavior: "smooth" })
})
})
-
- if (promptEnabled()) {
- createEffect(() => {
- promptProbe.set({
- popover: store.popover,
- slash: {
- active: slashActive() ?? null,
- ids: slashFlat().map((cmd) => cmd.id),
- },
- })
- })
-
- onCleanup(() => promptProbe.clear())
- }
-
const selectPopoverActive = () => {
if (store.popover === "at") {
const items = atFlat()
diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts
index 2a3a3d0e9..ad759d08e 100644
--- a/packages/app/src/components/prompt-input/submit.ts
+++ b/packages/app/src/components/prompt-input/submit.ts
@@ -13,7 +13,6 @@ import { usePermission } from "@/context/permission"
import { type ContextItem, type ImageAttachmentPart, type Prompt, usePrompt } from "@/context/prompt"
import { useSDK } from "@/context/sdk"
import { useSync } from "@/context/sync"
-import { promptProbe } from "@/testing/prompt"
import { Identifier } from "@/utils/id"
import { Worktree as WorktreeState } from "@/utils/worktree"
import { buildRequestParts } from "./build-request-parts"
@@ -307,7 +306,6 @@ export function createPromptSubmit(input: PromptSubmitInput) {
input.addToHistory(currentPrompt, mode)
input.resetHistoryNavigation()
- promptProbe.start()
const projectDirectory = sdk.directory
const isNewSession = !params.id
@@ -427,7 +425,6 @@ export function createPromptSubmit(input: PromptSubmitInput) {
return
}
- promptProbe.submit({ sessionID: session.id, directory: sessionDirectory })
input.onSubmit?.()
if (mode === "shell") {
diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx
index c8430d8bb..96a865b9e 100644
--- a/packages/app/src/components/terminal.tsx
+++ b/packages/app/src/components/terminal.tsx
@@ -13,7 +13,6 @@ import { useSDK } from "@/context/sdk"
import { useServer } from "@/context/server"
import { monoFontFamily, useSettings } from "@/context/settings"
import type { LocalPTY } from "@/context/terminal"
-import { terminalAttr, terminalProbe } from "@/testing/terminal"
import { disposeIfDisposable, getHoveredLinkText, setOptionIfSupported } from "@/utils/runtime-adapters"
import { terminalWriter } from "@/utils/terminal-writer"
@@ -178,7 +177,6 @@ export const Terminal = (props: TerminalProps) => {
let container!: HTMLDivElement
const [local, others] = splitProps(props, ["pty", "class", "classList", "autoFocus", "onConnect", "onConnectError"])
const id = local.pty.id
- const probe = terminalProbe(id)
const restore = typeof local.pty.buffer === "string" ? local.pty.buffer : ""
const restoreSize =
restore &&
@@ -349,9 +347,6 @@ export const Terminal = (props: TerminalProps) => {
}
onMount(() => {
- probe.init()
- cleanups.push(() => probe.drop())
-
const run = async () => {
const loaded = await loadGhostty()
if (disposed) return
@@ -381,8 +376,6 @@ export const Terminal = (props: TerminalProps) => {
term = t
output = terminalWriter((data, done) =>
t.write(data, () => {
- probe.render(data)
- probe.settle()
done?.()
}),
)
@@ -534,7 +527,6 @@ export const Terminal = (props: TerminalProps) => {
const handleOpen = () => {
if (disposed) return
tries = 0
- probe.connect()
local.onConnect?.()
scheduleSize(t.cols, t.rows)
}
@@ -599,13 +591,6 @@ export const Terminal = (props: TerminalProps) => {
socket.addEventListener("close", handleClose)
}
- probe.control({
- disconnect: () => {
- if (!ws) return
- ws.close(4_000, "e2e")
- },
- })
-
open()
}
@@ -645,7 +630,6 @@ export const Terminal = (props: TerminalProps) => {
<div
ref={container}
data-component="terminal"
- {...{ [terminalAttr]: id }}
data-prevent-autofocus
tabIndex={-1}
style={{ "background-color": terminalColors().background }}
diff --git a/packages/app/src/context/local.tsx b/packages/app/src/context/local.tsx
index 1633607de..28ce2770d 100644
--- a/packages/app/src/context/local.tsx
+++ b/packages/app/src/context/local.tsx
@@ -1,11 +1,10 @@
import { createSimpleContext } from "@opencode-ai/ui/context"
import { base64Encode } from "@opencode-ai/util/encode"
import { useParams } from "@solidjs/router"
-import { batch, createEffect, createMemo, onCleanup } from "solid-js"
+import { batch, createEffect, createMemo } from "solid-js"
import { createStore } from "solid-js/store"
import { useModels } from "@/context/models"
import { useProviders } from "@/hooks/use-providers"
-import { modelEnabled, modelProbe } from "@/testing/model-selection"
import { Persist, persisted } from "@/utils/persist"
import { cycleModelVariant, getConfiguredAgentVariant, resolveModelVariant } from "./model-variant"
import { useSDK } from "./sdk"
@@ -388,53 +387,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
},
},
}
-
- if (modelEnabled()) {
- const probe = Symbol("model-probe")
-
- modelProbe.bind(probe, {
- setAgent: agent.set,
- setModel: model.set,
- setVariant: model.variant.set,
- })
-
- createEffect(() => {
- const agent = result.agent.current()
- const model = result.model.current()
- modelProbe.set(probe, {
- dir: sdk.directory,
- sessionID: id(),
- last: store.last,
- agent: agent?.name,
- model: model
- ? {
- providerID: model.provider.id,
- modelID: model.id,
- name: model.name,
- }
- : undefined,
- variant: result.model.variant.current() ?? null,
- selected: result.model.variant.selected(),
- configured: result.model.variant.configured(),
- pick: scope(),
- base: undefined,
- current: store.current,
- variants: result.model.variant.list(),
- models: result.model
- .list()
- .filter((item) => result.model.visible({ providerID: item.provider.id, modelID: item.id }))
- .map((item) => ({
- providerID: item.provider.id,
- modelID: item.id,
- name: item.name,
- })),
- agents: result.agent.list().map((item) => ({ name: item.name })),
- })
- })
-
- onCleanup(() => modelProbe.clear(probe))
- }
-
return result
},
})
diff --git a/packages/app/src/pages/error.tsx b/packages/app/src/pages/error.tsx
index 1cdc06116..11284b3d2 100644
--- a/packages/app/src/pages/error.tsx
+++ b/packages/app/src/pages/error.tsx
@@ -1,12 +1,11 @@
import { TextField } from "@opencode-ai/ui/text-field"
import { Logo } from "@opencode-ai/ui/logo"
import { Button } from "@opencode-ai/ui/button"
-import { Component, Show, onMount } from "solid-js"
+import { Component, Show } from "solid-js"
import { createStore } from "solid-js/store"
import { usePlatform } from "@/context/platform"
import { useLanguage } from "@/context/language"
import { Icon } from "@opencode-ai/ui/icon"
-import type { E2EWindow } from "@/testing/terminal"
export type InitError = {
name: string
@@ -227,13 +226,6 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
actionError: undefined as string | undefined,
})
- onMount(() => {
- const win = window as E2EWindow
- if (!win.__opencode_e2e) return
- const detail = formatError(props.error, language.t)
- console.error(`[e2e:error-boundary] ${window.location.pathname}\n${detail}`)
- })
-
async function checkForUpdates() {
if (!platform.checkUpdate) return
setStore("checking", true)
diff --git a/packages/app/src/pages/session/composer/session-composer-state.ts b/packages/app/src/pages/session/composer/session-composer-state.ts
index eab210868..525766dcf 100644
--- a/packages/app/src/pages/session/composer/session-composer-state.ts
+++ b/packages/app/src/pages/session/composer/session-composer-state.ts
@@ -1,6 +1,5 @@
-import { createEffect, createMemo, on, onCleanup, onMount } from "solid-js"
+import { createEffect, createMemo, on, onCleanup } from "solid-js"
import { createStore } from "solid-js/store"
-import { makeEventListener } from "@solid-primitives/event-listener"
import type { PermissionRequest, QuestionRequest, Todo } from "@opencode-ai/sdk/v2"
import { useParams } from "@solidjs/router"
import { showToast } from "@opencode-ai/ui/toast"
@@ -9,7 +8,6 @@ import { useLanguage } from "@/context/language"
import { usePermission } from "@/context/permission"
import { useSDK } from "@/context/sdk"
import { useSync } from "@/context/sync"
-import { composerDriver, composerEnabled, composerEvent } from "@/testing/session-composer"
import { sessionPermissionRequest, sessionQuestionRequest } from "./session-request-tree"
export const todoState = (input: {
@@ -49,49 +47,7 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
return !!permissionRequest() || !!questionRequest()
})
- const [test, setTest] = createStore({
- on: false,
- live: undefined as boolean | undefined,
- todos: undefined as Todo[] | undefined,
- })
-
- const pull = () => {
- const id = params.id
- if (!id) {
- setTest({ on: false, live: undefined, todos: undefined })
- return
- }
-
- const next = composerDriver(id)
- if (!next) {
- setTest({ on: false, live: undefined, todos: undefined })
- return
- }
-
- setTest({
- on: true,
- live: next.live,
- todos: next.todos?.map((todo) => ({ ...todo })),
- })
- }
-
- onMount(() => {
- if (!composerEnabled()) return
-
- pull()
- createEffect(on(() => params.id, pull, { defer: true }))
-
- const onEvent = (event: Event) => {
- const detail = (event as CustomEvent<{ sessionID?: string }>).detail
- if (detail?.sessionID !== params.id) return
- pull()
- }
-
- makeEventListener(window, composerEvent, onEvent)
- })
-
const todos = createMemo((): Todo[] => {
- if (test.on && test.todos !== undefined) return test.todos
const id = params.id
if (!id) return []
return globalSync.data.session_todo[id] ?? []
@@ -108,10 +64,7 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
})
const busy = createMemo(() => status().type !== "idle")
- const live = createMemo(() => {
- if (test.on && test.live !== undefined) return test.live
- return busy() || blocked()
- })
+ const live = createMemo(() => busy() || blocked())
const [store, setStore] = createStore({
responding: undefined as string | undefined,
@@ -163,10 +116,6 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
// Keep stale turn todos from reopening if the model never clears them.
const clear = () => {
- if (test.on && test.todos !== undefined) {
- setTest("todos", [])
- return
- }
const id = params.id
if (!id) return
globalSync.todo.set(id, [])
diff --git a/packages/app/src/pages/session/composer/session-todo-dock.tsx b/packages/app/src/pages/session/composer/session-todo-dock.tsx
index 2214248b1..fa8c17734 100644
--- a/packages/app/src/pages/session/composer/session-todo-dock.tsx
+++ b/packages/app/src/pages/session/composer/session-todo-dock.tsx
@@ -7,9 +7,8 @@ import { useSpring } from "@opencode-ai/ui/motion-spring"
import { TextReveal } from "@opencode-ai/ui/text-reveal"
import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
import { createResizeObserver } from "@solid-primitives/resize-observer"
-import { Index, createEffect, createMemo, onCleanup } from "solid-js"
+import { Index, createEffect, createMemo } from "solid-js"
import { createStore } from "solid-js/store"
-import { composerEnabled, composerProbe } from "@/testing/session-composer"
import { useLanguage } from "@/context/language"
const doneToken = "\u0000done\u0000"
@@ -81,8 +80,6 @@ export function SessionTodoDock(props: {
const off = createMemo(() => hide() > 0.98)
const turn = createMemo(() => Math.max(0, Math.min(1, value())))
const full = createMemo(() => Math.max(78, store.height))
- const e2e = composerEnabled()
- const probe = composerProbe(props.sessionID)
let contentRef: HTMLDivElement | undefined
createEffect(() => {
@@ -95,23 +92,6 @@ export function SessionTodoDock(props: {
createResizeObserver(el, update)
})
- createEffect(() => {
- if (!e2e) return
-
- probe.set({
- mounted: true,
- collapsed: store.collapsed,
- hidden: store.collapsed || off(),
- count: props.todos.length,
- states: props.todos.map((todo) => todo.status),
- })
- })
-
- onCleanup(() => {
- if (!e2e) return
- probe.drop()
- })
-
return (
<DockTray
data-component="session-todo-dock"
diff --git a/packages/app/src/pages/session/terminal-panel.tsx b/packages/app/src/pages/session/terminal-panel.tsx
index 1161d565a..2c2d9817f 100644
--- a/packages/app/src/pages/session/terminal-panel.tsx
+++ b/packages/app/src/pages/session/terminal-panel.tsx
@@ -19,7 +19,6 @@ import { terminalTabLabel } from "@/pages/session/terminal-label"
import { createSizing, focusTerminalById } from "@/pages/session/helpers"
import { getTerminalHandoff, setTerminalHandoff } from "@/pages/session/handoff"
import { useSessionLayout } from "@/pages/session/session-layout"
-import { terminalProbe } from "@/testing/terminal"
export function TerminalPanel() {
const delays = [120, 240]
@@ -78,12 +77,9 @@ export function TerminalPanel() {
)
const focus = (id: string) => {
- const probe = terminalProbe(id)
- probe.focus(delays.length + 1)
focusTerminalById(id)
const frame = requestAnimationFrame(() => {
- probe.step()
if (!opened()) return
if (terminal.active() !== id) return
focusTerminalById(id)
@@ -91,7 +87,6 @@ export function TerminalPanel() {
const timers = delays.map((ms) =>
window.setTimeout(() => {
- probe.step()
if (!opened()) return
if (terminal.active() !== id) return
focusTerminalById(id)
@@ -99,7 +94,6 @@ export function TerminalPanel() {
)
return () => {
- probe.focus(0)
cancelAnimationFrame(frame)
for (const timer of timers) clearTimeout(timer)
}
diff --git a/packages/app/src/testing/model-selection.ts b/packages/app/src/testing/model-selection.ts
deleted file mode 100644
index d2770fe28..000000000
--- a/packages/app/src/testing/model-selection.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-type ModelKey = {
- providerID: string
- modelID: string
-}
-
-type ModelItem = ModelKey & {
- name: string
-}
-
-type AgentItem = {
- name: string
-}
-
-type State = {
- agent?: string
- model?: ModelKey | null
- variant?: string | null
-}
-
-export type ModelProbeState = {
- dir?: string
- sessionID?: string
- last?: {
- type: "agent" | "model" | "variant"
- agent?: string
- model?: ModelKey | null
- variant?: string | null
- }
- agent?: string
- model?: (ModelKey & { name?: string }) | undefined
- variant?: string | null
- selected?: string | null
- configured?: string
- pick?: State
- base?: State
- current?: string
- variants?: string[]
- models?: ModelItem[]
- agents?: AgentItem[]
-}
-
-export type ModelWindow = Window & {
- __opencode_e2e?: {
- model?: {
- enabled?: boolean
- current?: ModelProbeState
- controls?: {
- setAgent?: (name: string | undefined) => void
- setModel?: (value: ModelKey | undefined) => void
- setVariant?: (value: string | undefined) => void
- }
- }
- }
-}
-
-const clone = (state?: State) => {
- if (!state) return undefined
- return {
- ...state,
- model: state.model ? { ...state.model } : state.model,
- }
-}
-
-let active: symbol | undefined
-
-export const modelEnabled = () => {
- if (typeof window === "undefined") return false
- return (window as ModelWindow).__opencode_e2e?.model?.enabled === true
-}
-
-const root = () => {
- if (!modelEnabled()) return
- return (window as ModelWindow).__opencode_e2e?.model
-}
-
-export const modelProbe = {
- bind(id: symbol, input: NonNullable<NonNullable<ModelWindow["__opencode_e2e"]>["model"]>["controls"]) {
- const state = root()
- if (!state) return
- active = id
- state.controls = input
- },
- set(id: symbol, input: ModelProbeState) {
- const state = root()
- if (!state || active !== id) return
- state.current = {
- ...input,
- model: input.model ? { ...input.model } : undefined,
- last: input.last
- ? {
- ...input.last,
- model: input.last.model ? { ...input.last.model } : input.last.model,
- }
- : undefined,
- pick: clone(input.pick),
- base: clone(input.base),
- variants: input.variants?.slice(),
- models: input.models?.map((item) => ({ ...item })),
- agents: input.agents?.map((item) => ({ ...item })),
- }
- },
- clear(id: symbol) {
- const state = root()
- if (!state || active !== id) return
- active = undefined
- state.current = undefined
- state.controls = undefined
- },
-}
diff --git a/packages/app/src/testing/prompt.ts b/packages/app/src/testing/prompt.ts
deleted file mode 100644
index 5102ed825..000000000
--- a/packages/app/src/testing/prompt.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import type { E2EWindow } from "./terminal"
-
-export type PromptProbeState = {
- popover: "at" | "slash" | null
- slash: {
- active: string | null
- ids: string[]
- }
- selected: string | null
- selects: number
-}
-
-export type PromptSendState = {
- started: number
- count: number
- sessionID?: string
- directory?: string
-}
-
-export const promptEnabled = () => {
- if (typeof window === "undefined") return false
- return (window as E2EWindow).__opencode_e2e?.prompt?.enabled === true
-}
-
-const root = () => {
- if (!promptEnabled()) return
- return (window as E2EWindow).__opencode_e2e?.prompt
-}
-
-export const promptProbe = {
- set(input: Omit<PromptProbeState, "selected" | "selects">) {
- const state = root()
- if (!state) return
- state.current = {
- popover: input.popover,
- slash: {
- active: input.slash.active,
- ids: [...input.slash.ids],
- },
- selected: state.current?.selected ?? null,
- selects: state.current?.selects ?? 0,
- }
- },
- select(id: string) {
- const state = root()
- if (!state) return
- const prev = state.current
- state.current = {
- popover: prev?.popover ?? null,
- slash: {
- active: prev?.slash.active ?? null,
- ids: [...(prev?.slash.ids ?? [])],
- },
- selected: id,
- selects: (prev?.selects ?? 0) + 1,
- }
- },
- clear() {
- const state = root()
- if (!state) return
- state.current = undefined
- },
- start() {
- const state = root()
- if (!state) return
- state.sent = {
- started: (state.sent?.started ?? 0) + 1,
- count: state.sent?.count ?? 0,
- sessionID: state.sent?.sessionID,
- directory: state.sent?.directory,
- }
- },
- submit(input: { sessionID: string; directory: string }) {
- const state = root()
- if (!state) return
- state.sent = {
- started: state.sent?.started ?? 0,
- count: (state.sent?.count ?? 0) + 1,
- sessionID: input.sessionID,
- directory: input.directory,
- }
- },
-}
diff --git a/packages/app/src/testing/session-composer.ts b/packages/app/src/testing/session-composer.ts
deleted file mode 100644
index 01c809e4c..000000000
--- a/packages/app/src/testing/session-composer.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import type { Todo } from "@opencode-ai/sdk/v2"
-
-export const composerEvent = "opencode:e2e:composer"
-
-export type ComposerDriverState = {
- live?: boolean
- todos?: Array<Pick<Todo, "content" | "status" | "priority">>
-}
-
-export type ComposerProbeState = {
- mounted: boolean
- collapsed: boolean
- hidden: boolean
- count: number
- states: Todo["status"][]
-}
-
-type ComposerState = {
- driver?: ComposerDriverState
- probe?: ComposerProbeState
-}
-
-export type ComposerWindow = Window & {
- __opencode_e2e?: {
- composer?: {
- enabled?: boolean
- sessions?: Record<string, ComposerState>
- }
- }
-}
-
-const clone = (driver: ComposerDriverState) => ({
- live: driver.live,
- todos: driver.todos?.map((todo) => ({ ...todo })),
-})
-
-export const composerEnabled = () => {
- if (typeof window === "undefined") return false
- return (window as ComposerWindow).__opencode_e2e?.composer?.enabled === true
-}
-
-const root = () => {
- if (!composerEnabled()) return
- const state = (window as ComposerWindow).__opencode_e2e?.composer
- if (!state) return
- state.sessions ??= {}
- return state.sessions
-}
-
-export const composerDriver = (sessionID?: string) => {
- if (!sessionID) return
- const state = root()?.[sessionID]?.driver
- if (!state) return
- return clone(state)
-}
-
-export const composerProbe = (sessionID?: string) => {
- const set = (next: ComposerProbeState) => {
- if (!sessionID) return
- const sessions = root()
- if (!sessions) return
- const prev = sessions[sessionID] ?? {}
- sessions[sessionID] = {
- ...prev,
- probe: {
- ...next,
- states: [...next.states],
- },
- }
- }
-
- return {
- set,
- drop() {
- set({
- mounted: false,
- collapsed: false,
- hidden: true,
- count: 0,
- states: [],
- })
- },
- }
-}
diff --git a/packages/app/src/testing/terminal.ts b/packages/app/src/testing/terminal.ts
deleted file mode 100644
index db8001ddf..000000000
--- a/packages/app/src/testing/terminal.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import type { ModelProbeState } from "./model-selection"
-
-export const terminalAttr = "data-pty-id"
-
-export type TerminalProbeState = {
- connected: boolean
- connects: number
- rendered: string
- settled: number
- focusing: number
-}
-
-type TerminalProbeControl = {
- disconnect?: VoidFunction
-}
-
-export type E2EWindow = Window & {
- __opencode_e2e?: {
- model?: {
- enabled?: boolean
- current?: ModelProbeState
- }
- prompt?: {
- enabled?: boolean
- current?: import("./prompt").PromptProbeState
- sent?: import("./prompt").PromptSendState
- }
- terminal?: {
- enabled?: boolean
- terminals?: Record<string, TerminalProbeState>
- controls?: Record<string, TerminalProbeControl>
- }
- }
-}
-
-const seed = (): TerminalProbeState => ({
- connected: false,
- connects: 0,
- rendered: "",
- settled: 0,
- focusing: 0,
-})
-
-const root = () => {
- if (typeof window === "undefined") return
- const state = (window as E2EWindow).__opencode_e2e?.terminal
- if (!state?.enabled) return
- return state
-}
-
-const terms = () => {
- const state = root()
- if (!state) return
- state.terminals ??= {}
- return state.terminals
-}
-
-const controls = () => {
- const state = root()
- if (!state) return
- state.controls ??= {}
- return state.controls
-}
-
-export const terminalProbe = (id: string) => {
- const set = (next: Partial<TerminalProbeState>) => {
- const state = terms()
- if (!state) return
- state[id] = { ...(state[id] ?? seed()), ...next }
- }
-
- return {
- init() {
- set(seed())
- },
- connect() {
- const state = terms()
- if (!state) return
- const prev = state[id] ?? seed()
- state[id] = {
- ...prev,
- connected: true,
- connects: prev.connects + 1,
- }
- },
- render(data: string) {
- const state = terms()
- if (!state) return
- const prev = state[id] ?? seed()
- state[id] = { ...prev, rendered: prev.rendered + data }
- },
- settle() {
- const state = terms()
- if (!state) return
- const prev = state[id] ?? seed()
- state[id] = { ...prev, settled: prev.settled + 1 }
- },
- focus(count: number) {
- set({ focusing: Math.max(0, count) })
- },
- step() {
- const state = terms()
- if (!state) return
- const prev = state[id] ?? seed()
- state[id] = { ...prev, focusing: Math.max(0, prev.focusing - 1) }
- },
- control(next: Partial<TerminalProbeControl>) {
- const state = controls()
- if (!state) return
- state[id] = { ...(state[id] ?? {}), ...next }
- },
- drop() {
- const state = terms()
- if (state) delete state[id]
- const control = controls()
- if (control) delete control[id]
- },
- }
-}