mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-17 12:56:41 +02:00
delete all e2e tests (#22501)
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, [])
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -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: [],
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user