chore: generate
This commit is contained in:
@@ -719,10 +719,10 @@
|
|||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
||||||
"@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch",
|
"@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch",
|
||||||
|
"virtua@0.49.1": "patches/virtua@0.49.1.patch",
|
||||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||||
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
|
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
|
||||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||||
"virtua@0.49.1": "patches/virtua@0.49.1.patch",
|
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@opentui/core": "catalog:",
|
"@opentui/core": "catalog:",
|
||||||
|
|||||||
@@ -168,7 +168,9 @@ test.describe("regression: session timeline local row state", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await expect(wrapper.locator('[data-slot="diff-changes-additions"]').filter({ hasText: "+2" }).first()).toBeVisible({ timeout: 10_000 })
|
await expect(wrapper.locator('[data-slot="diff-changes-additions"]').filter({ hasText: "+2" }).first()).toBeVisible(
|
||||||
|
{ timeout: 10_000 },
|
||||||
|
)
|
||||||
expect(await readDiffProbe(page)).toEqual({ fileMarker: "before", shadowRoots: 0, toolMarker: "before" })
|
expect(await readDiffProbe(page)).toEqual({ fileMarker: "before", shadowRoots: 0, toolMarker: "before" })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -194,23 +196,29 @@ async function expectExpanded(locator: Locator, expected: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function readToolState(page: Page) {
|
async function readToolState(page: Page) {
|
||||||
return page.locator(`[data-timeline-part-id="${editPartID}"]`).first().evaluate((element, textPartID) => ({
|
return page
|
||||||
expanded: (() => {
|
.locator(`[data-timeline-part-id="${editPartID}"]`)
|
||||||
const trigger = element.querySelector('[data-slot="collapsible-trigger"]')
|
.first()
|
||||||
const aria = trigger?.getAttribute("aria-expanded")
|
.evaluate(
|
||||||
if (aria === "true") return true
|
(element, textPartID) => ({
|
||||||
if (aria === "false") return false
|
expanded: (() => {
|
||||||
|
const trigger = element.querySelector('[data-slot="collapsible-trigger"]')
|
||||||
|
const aria = trigger?.getAttribute("aria-expanded")
|
||||||
|
if (aria === "true") return true
|
||||||
|
if (aria === "false") return false
|
||||||
|
|
||||||
const root = element.querySelector('[data-component="collapsible"]')
|
const root = element.querySelector('[data-component="collapsible"]')
|
||||||
if (root?.hasAttribute("data-expanded")) return true
|
if (root?.hasAttribute("data-expanded")) return true
|
||||||
if (root?.hasAttribute("data-closed")) return false
|
if (root?.hasAttribute("data-closed")) return false
|
||||||
|
|
||||||
const content = element.querySelector<HTMLElement>('[data-slot="collapsible-content"]')
|
const content = element.querySelector<HTMLElement>('[data-slot="collapsible-content"]')
|
||||||
return !!content && content.getBoundingClientRect().height > 0
|
return !!content && content.getBoundingClientRect().height > 0
|
||||||
})(),
|
})(),
|
||||||
row: element.closest("[data-timeline-row]")?.getAttribute("data-timeline-row"),
|
row: element.closest("[data-timeline-row]")?.getAttribute("data-timeline-row"),
|
||||||
streamedTextVisible: !!document.querySelector(`[data-timeline-part-id="${textPartID}"]`),
|
streamedTextVisible: !!document.querySelector(`[data-timeline-part-id="${textPartID}"]`),
|
||||||
}), textPartID)
|
}),
|
||||||
|
textPartID,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installDiffProbe(page: Page) {
|
async function installDiffProbe(page: Page) {
|
||||||
@@ -231,27 +239,33 @@ async function installDiffProbe(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function markDiffProbe(page: Page) {
|
async function markDiffProbe(page: Page) {
|
||||||
await page.locator(`[data-timeline-part-id="${editPartID}"]`).first().evaluate((element) => {
|
await page
|
||||||
const tool = element as HTMLElement
|
.locator(`[data-timeline-part-id="${editPartID}"]`)
|
||||||
const file = tool.querySelector<HTMLElement>('[data-component="file"][data-mode="diff"]')
|
.first()
|
||||||
if (!file) throw new Error("missing edit diff file")
|
.evaluate((element) => {
|
||||||
|
const tool = element as HTMLElement
|
||||||
|
const file = tool.querySelector<HTMLElement>('[data-component="file"][data-mode="diff"]')
|
||||||
|
if (!file) throw new Error("missing edit diff file")
|
||||||
|
|
||||||
tool.dataset.timelineProbe = "before"
|
tool.dataset.timelineProbe = "before"
|
||||||
file.dataset.timelineProbe = "before"
|
file.dataset.timelineProbe = "before"
|
||||||
window.__timelineDiffProbe.reset()
|
window.__timelineDiffProbe.reset()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readDiffProbe(page: Page) {
|
async function readDiffProbe(page: Page) {
|
||||||
return page.locator(`[data-timeline-part-id="${editPartID}"]`).first().evaluate((element) => {
|
return page
|
||||||
const tool = element as HTMLElement
|
.locator(`[data-timeline-part-id="${editPartID}"]`)
|
||||||
const file = tool.querySelector<HTMLElement>('[data-component="file"][data-mode="diff"]')
|
.first()
|
||||||
return {
|
.evaluate((element) => {
|
||||||
fileMarker: file?.dataset.timelineProbe,
|
const tool = element as HTMLElement
|
||||||
shadowRoots: window.__timelineDiffProbe.shadowRoots(),
|
const file = tool.querySelector<HTMLElement>('[data-component="file"][data-mode="diff"]')
|
||||||
toolMarker: tool.dataset.timelineProbe,
|
return {
|
||||||
}
|
fileMarker: file?.dataset.timelineProbe,
|
||||||
})
|
shadowRoots: window.__timelineDiffProbe.shadowRoots(),
|
||||||
|
toolMarker: tool.dataset.timelineProbe,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function editPartWithAdditions(additions: number) {
|
function editPartWithAdditions(additions: number) {
|
||||||
@@ -292,10 +306,23 @@ async function mockServer(page: Page, events: EventPayload[]) {
|
|||||||
|
|
||||||
const path = url.pathname
|
const path = url.pathname
|
||||||
if (path === "/global/event") return sse(route, events.splice(0))
|
if (path === "/global/event") return sse(route, events.splice(0))
|
||||||
if (path === "/global/config" || path === "/config" || path === "/provider/auth" || path === "/mcp" || path === "/session/status") return json(route, {})
|
if (
|
||||||
if (["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/status", "/vcs/diff"].includes(path)) return json(route, [])
|
path === "/global/config" ||
|
||||||
|
path === "/config" ||
|
||||||
|
path === "/provider/auth" ||
|
||||||
|
path === "/mcp" ||
|
||||||
|
path === "/session/status"
|
||||||
|
)
|
||||||
|
return json(route, {})
|
||||||
|
if (
|
||||||
|
["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/status", "/vcs/diff"].includes(
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return json(route, [])
|
||||||
if (path === "/provider") return json(route, provider())
|
if (path === "/provider") return json(route, provider())
|
||||||
if (path === "/path") return json(route, { state: directory, config: directory, worktree: directory, directory, home: "C:/OpenCode" })
|
if (path === "/path")
|
||||||
|
return json(route, { state: directory, config: directory, worktree: directory, directory, home: "C:/OpenCode" })
|
||||||
if (path === "/project") return json(route, [project()])
|
if (path === "/project") return json(route, [project()])
|
||||||
if (path === "/project/current") return json(route, project())
|
if (path === "/project/current") return json(route, project())
|
||||||
if (path === "/agent") return json(route, [{ name: "build", mode: "primary" }])
|
if (path === "/agent") return json(route, [{ name: "build", mode: "primary" }])
|
||||||
@@ -309,11 +336,26 @@ async function mockServer(page: Page, events: EventPayload[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function project() {
|
function project() {
|
||||||
return { id: projectID, worktree: directory, vcs: "git", name: "timeline-state-regression", time: { created: 1700000000000, updated: 1700000000000 }, sandboxes: [] }
|
return {
|
||||||
|
id: projectID,
|
||||||
|
worktree: directory,
|
||||||
|
vcs: "git",
|
||||||
|
name: "timeline-state-regression",
|
||||||
|
time: { created: 1700000000000, updated: 1700000000000 },
|
||||||
|
sandboxes: [],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function session() {
|
function session() {
|
||||||
return { id: sessionID, slug: "timeline-state-regression", projectID, directory, title, version: "dev", time: { created: 1700000000000, updated: 1700000000000 } }
|
return {
|
||||||
|
id: sessionID,
|
||||||
|
slug: "timeline-state-regression",
|
||||||
|
projectID,
|
||||||
|
directory,
|
||||||
|
title,
|
||||||
|
version: "dev",
|
||||||
|
time: { created: 1700000000000, updated: 1700000000000 },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function provider() {
|
function provider() {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ const model = { providerID: "opencode", modelID: "claude-opus-4-6", variant: "ma
|
|||||||
const contextIDs = ["prt_0100_read", "prt_0101_glob", "prt_0102_grep", "prt_0103_list"]
|
const contextIDs = ["prt_0100_read", "prt_0101_glob", "prt_0102_grep", "prt_0103_list"]
|
||||||
const followingTextID = "prt_0104_text"
|
const followingTextID = "prt_0104_text"
|
||||||
|
|
||||||
type Message = { info: Record<string, unknown> & { id: string; role: "user" | "assistant" }; parts: Record<string, unknown>[] }
|
type Message = {
|
||||||
|
info: Record<string, unknown> & { id: string; role: "user" | "assistant" }
|
||||||
|
parts: Record<string, unknown>[]
|
||||||
|
}
|
||||||
|
|
||||||
const messages = [
|
const messages = [...Array.from({ length: 8 }, (_, index) => turn(index, false)).flat(), ...turn(10, true)]
|
||||||
...Array.from({ length: 8 }, (_, index) => turn(index, false)).flat(),
|
|
||||||
...turn(10, true),
|
|
||||||
]
|
|
||||||
|
|
||||||
test.describe("regression: session timeline context group resize", () => {
|
test.describe("regression: session timeline context group resize", () => {
|
||||||
test("remeasures a recent explored context group before the next paint", async ({ page }) => {
|
test("remeasures a recent explored context group before the next paint", async ({ page }) => {
|
||||||
@@ -76,7 +76,8 @@ async function sampleExpansion(page: Page) {
|
|||||||
const trigger = context?.querySelector<HTMLElement>('[data-slot="collapsible-trigger"]')
|
const trigger = context?.querySelector<HTMLElement>('[data-slot="collapsible-trigger"]')
|
||||||
const contextRow = context?.closest<HTMLElement>('[data-timeline-row="AssistantPart"]')
|
const contextRow = context?.closest<HTMLElement>('[data-timeline-row="AssistantPart"]')
|
||||||
const textRow = text?.closest<HTMLElement>('[data-timeline-row="AssistantPart"]')
|
const textRow = text?.closest<HTMLElement>('[data-timeline-row="AssistantPart"]')
|
||||||
if (!context || !text || !scroller || !trigger || !contextRow || !textRow) throw new Error("missing regression nodes")
|
if (!context || !text || !scroller || !trigger || !contextRow || !textRow)
|
||||||
|
throw new Error("missing regression nodes")
|
||||||
|
|
||||||
scroller.scrollTop = scroller.scrollHeight
|
scroller.scrollTop = scroller.scrollHeight
|
||||||
const samples: {
|
const samples: {
|
||||||
@@ -213,10 +214,17 @@ async function mockServer(page: Page) {
|
|||||||
|
|
||||||
const path = url.pathname
|
const path = url.pathname
|
||||||
if (path === "/global/event" || path === "/event") return sse(route)
|
if (path === "/global/event" || path === "/event") return sse(route)
|
||||||
if (["/global/config", "/config", "/provider/auth", "/mcp", "/session/status"].includes(path)) return json(route, {})
|
if (["/global/config", "/config", "/provider/auth", "/mcp", "/session/status"].includes(path))
|
||||||
if (["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/status", "/vcs/diff"].includes(path)) return json(route, [])
|
return json(route, {})
|
||||||
|
if (
|
||||||
|
["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/status", "/vcs/diff"].includes(
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return json(route, [])
|
||||||
if (path === "/provider") return json(route, provider())
|
if (path === "/provider") return json(route, provider())
|
||||||
if (path === "/path") return json(route, { state: directory, config: directory, worktree: directory, directory, home: "C:/OpenCode" })
|
if (path === "/path")
|
||||||
|
return json(route, { state: directory, config: directory, worktree: directory, directory, home: "C:/OpenCode" })
|
||||||
if (path === "/project") return json(route, [project()])
|
if (path === "/project") return json(route, [project()])
|
||||||
if (path === "/project/current") return json(route, project())
|
if (path === "/project/current") return json(route, project())
|
||||||
if (path === "/agent") return json(route, [{ name: "build", mode: "primary" }])
|
if (path === "/agent") return json(route, [{ name: "build", mode: "primary" }])
|
||||||
@@ -238,11 +246,26 @@ function id(prefix: string, index: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function project() {
|
function project() {
|
||||||
return { id: projectID, worktree: directory, vcs: "git", name: "context-resize-regression", time: { created: 1700000000000, updated: 1700000000000 }, sandboxes: [] }
|
return {
|
||||||
|
id: projectID,
|
||||||
|
worktree: directory,
|
||||||
|
vcs: "git",
|
||||||
|
name: "context-resize-regression",
|
||||||
|
time: { created: 1700000000000, updated: 1700000000000 },
|
||||||
|
sandboxes: [],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function session() {
|
function session() {
|
||||||
return { id: sessionID, slug: "context-resize-regression", projectID, directory, title, version: "dev", time: { created: 1700000000000, updated: 1700000000000 } }
|
return {
|
||||||
|
id: sessionID,
|
||||||
|
slug: "context-resize-regression",
|
||||||
|
projectID,
|
||||||
|
directory,
|
||||||
|
title,
|
||||||
|
version: "dev",
|
||||||
|
time: { created: 1700000000000, updated: 1700000000000 },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function provider() {
|
function provider() {
|
||||||
|
|||||||
@@ -1018,7 +1018,9 @@ export function MessageTimeline(props: {
|
|||||||
return (
|
return (
|
||||||
<ContextToolGroup
|
<ContextToolGroup
|
||||||
parts={parts()}
|
parts={parts()}
|
||||||
busy={workingTurn(row().userMessageID) && lastAssistantGroupKey().get(row().userMessageID) === row().group.key}
|
busy={
|
||||||
|
workingTurn(row().userMessageID) && lastAssistantGroupKey().get(row().userMessageID) === row().group.key
|
||||||
|
}
|
||||||
onSizeChange={measureTimeline}
|
onSizeChange={measureTimeline}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -1149,7 +1151,11 @@ export function MessageTimeline(props: {
|
|||||||
{(message) => (
|
{(message) => (
|
||||||
<div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
|
<div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
|
||||||
<div data-slot="session-turn-message-content" aria-live="off">
|
<div data-slot="session-turn-message-content" aria-live="off">
|
||||||
<Message message={message()} parts={getMsgParts(userMessageRow().userMessageID)} actions={props.actions} />
|
<Message
|
||||||
|
message={message()}
|
||||||
|
parts={getMsgParts(userMessageRow().userMessageID)}
|
||||||
|
actions={props.actions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1178,7 +1184,10 @@ export function MessageTimeline(props: {
|
|||||||
return (
|
return (
|
||||||
<TimelineRowFrame row={assistantPartRow}>
|
<TimelineRowFrame row={assistantPartRow}>
|
||||||
<div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
|
<div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
|
||||||
<div data-slot="session-turn-assistant-content" aria-hidden={workingTurn(assistantPartRow().userMessageID)}>
|
<div
|
||||||
|
data-slot="session-turn-assistant-content"
|
||||||
|
aria-hidden={workingTurn(assistantPartRow().userMessageID)}
|
||||||
|
>
|
||||||
{renderAssistantPartGroup(assistantPartRow)}
|
{renderAssistantPartGroup(assistantPartRow)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1089,8 +1089,12 @@ function DiffViewer<T>(props: DiffFileProps<T>) {
|
|||||||
return sampledChecksum(contents)
|
return sampledChecksum(contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
const before = local.before ? { ...local.before, contents: beforeContents, cacheKey: cacheKey(beforeContents) } : undefined
|
const before = local.before
|
||||||
const after = local.after ? { ...local.after, contents: afterContents, cacheKey: cacheKey(afterContents) } : undefined
|
? { ...local.before, contents: beforeContents, cacheKey: cacheKey(beforeContents) }
|
||||||
|
: undefined
|
||||||
|
const after = local.after
|
||||||
|
? { ...local.after, contents: afterContents, cacheKey: cacheKey(afterContents) }
|
||||||
|
: undefined
|
||||||
const targetChanged =
|
const targetChanged =
|
||||||
local.fileDiff !== undefined
|
local.fileDiff !== undefined
|
||||||
? instanceFileDiff !== local.fileDiff
|
? instanceFileDiff !== local.fileDiff
|
||||||
|
|||||||
@@ -2278,7 +2278,12 @@ ToolRegistry.register({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div data-component="apply-patch-file-diff">
|
<div data-component="apply-patch-file-diff">
|
||||||
<Dynamic component={fileComponent} mode="diff" virtualize={props.virtualizeDiff} fileDiff={single()!.view.fileDiff} />
|
<Dynamic
|
||||||
|
component={fileComponent}
|
||||||
|
mode="diff"
|
||||||
|
virtualize={props.virtualizeDiff}
|
||||||
|
fileDiff={single()!.view.fileDiff}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ToolFileAccordion>
|
</ToolFileAccordion>
|
||||||
</BasicTool>
|
</BasicTool>
|
||||||
|
|||||||
@@ -71,7 +71,8 @@ describe("session diff", () => {
|
|||||||
test("keeps capped header-only patches partial", () => {
|
test("keeps capped header-only patches partial", () => {
|
||||||
const fileDiff = resolveFileDiff({
|
const fileDiff = resolveFileDiff({
|
||||||
file: "a.ts",
|
file: "a.ts",
|
||||||
patch: "Index: a.ts\n===================================================================\n--- a.ts\t\n+++ a.ts\t\n",
|
patch:
|
||||||
|
"Index: a.ts\n===================================================================\n--- a.ts\t\n+++ a.ts\t\n",
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(fileDiff.name).toBe("a.ts")
|
expect(fileDiff.name).toBe("a.ts")
|
||||||
|
|||||||
@@ -92,12 +92,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card {...rest} data-kind="tool-error-card" data-open={open() ? "true" : "false"} variant="error">
|
<Card {...rest} data-kind="tool-error-card" data-open={open() ? "true" : "false"} variant="error">
|
||||||
<Collapsible
|
<Collapsible class="tool-collapsible" data-open={open() ? "true" : "false"} open={open()} onOpenChange={setOpen}>
|
||||||
class="tool-collapsible"
|
|
||||||
data-open={open() ? "true" : "false"}
|
|
||||||
open={open()}
|
|
||||||
onOpenChange={setOpen}
|
|
||||||
>
|
|
||||||
<Collapsible.Trigger>
|
<Collapsible.Trigger>
|
||||||
<div data-component="tool-trigger">
|
<div data-component="tool-trigger">
|
||||||
<div data-slot="basic-tool-tool-trigger-content">
|
<div data-slot="basic-tool-tool-trigger-content">
|
||||||
|
|||||||
Reference in New Issue
Block a user