mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-06 00:12:11 +00:00
fix(app): stale keyed show errors
This commit is contained in:
parent
e9568999c3
commit
a71b11caca
4 changed files with 315 additions and 249 deletions
|
|
@ -445,6 +445,57 @@ export async function seedSessionPermission(
|
||||||
return { id: result.id }
|
return { id: result.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function seedSessionTask(
|
||||||
|
sdk: ReturnType<typeof createSdk>,
|
||||||
|
input: {
|
||||||
|
sessionID: string
|
||||||
|
description: string
|
||||||
|
prompt: string
|
||||||
|
subagentType?: string
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const text = [
|
||||||
|
"Your only valid response is one task tool call.",
|
||||||
|
`Use this JSON input: ${JSON.stringify({
|
||||||
|
description: input.description,
|
||||||
|
prompt: input.prompt,
|
||||||
|
subagent_type: input.subagentType ?? "general",
|
||||||
|
})}`,
|
||||||
|
"Do not output plain text.",
|
||||||
|
"Wait for the task to start and return the child session id.",
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
const result = await seed({
|
||||||
|
sdk,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
prompt: text,
|
||||||
|
timeout: 90_000,
|
||||||
|
probe: async () => {
|
||||||
|
const messages = await sdk.session.messages({ sessionID: input.sessionID, limit: 50 }).then((x) => x.data ?? [])
|
||||||
|
const part = messages
|
||||||
|
.flatMap((message) => message.parts)
|
||||||
|
.find((part) => {
|
||||||
|
if (part.type !== "tool" || part.tool !== "task") return false
|
||||||
|
if (part.state.input?.description !== input.description) return false
|
||||||
|
return typeof part.state.metadata?.sessionId === "string" && part.state.metadata.sessionId.length > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!part) return
|
||||||
|
const id = part.state.metadata?.sessionId
|
||||||
|
if (typeof id !== "string" || !id) return
|
||||||
|
const child = await sdk.session
|
||||||
|
.get({ sessionID: id })
|
||||||
|
.then((x) => x.data)
|
||||||
|
.catch(() => undefined)
|
||||||
|
if (!child?.id) return
|
||||||
|
return { sessionID: id }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!result) throw new Error("Timed out seeding task tool")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
export async function seedSessionTodos(
|
export async function seedSessionTodos(
|
||||||
sdk: ReturnType<typeof createSdk>,
|
sdk: ReturnType<typeof createSdk>,
|
||||||
input: {
|
input: {
|
||||||
|
|
|
||||||
37
packages/app/e2e/session/session-child-navigation.spec.ts
Normal file
37
packages/app/e2e/session/session-child-navigation.spec.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { seedSessionTask, withSession } from "../actions"
|
||||||
|
import { test, expect } from "../fixtures"
|
||||||
|
|
||||||
|
test("task tool child-session link does not trigger stale show errors", async ({ page, sdk, gotoSession }) => {
|
||||||
|
test.setTimeout(120_000)
|
||||||
|
|
||||||
|
const errs: string[] = []
|
||||||
|
const onError = (err: Error) => {
|
||||||
|
errs.push(err.message)
|
||||||
|
}
|
||||||
|
page.on("pageerror", onError)
|
||||||
|
|
||||||
|
await withSession(sdk, `e2e child nav ${Date.now()}`, async (session) => {
|
||||||
|
const child = await seedSessionTask(sdk, {
|
||||||
|
sessionID: session.id,
|
||||||
|
description: "Open child session",
|
||||||
|
prompt: "Search the repository for AssistantParts and then reply with exactly CHILD_OK.",
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gotoSession(session.id)
|
||||||
|
|
||||||
|
const link = page
|
||||||
|
.locator("a.subagent-link")
|
||||||
|
.filter({ hasText: /open child session/i })
|
||||||
|
.first()
|
||||||
|
await expect(link).toBeVisible({ timeout: 30_000 })
|
||||||
|
await link.click()
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(new RegExp(`/session/${child.sessionID}(?:[/?#]|$)`), { timeout: 30_000 })
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
expect(errs).toEqual([])
|
||||||
|
} finally {
|
||||||
|
page.off("pageerror", onError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -527,19 +527,15 @@ export function AssistantParts(props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={message()}>
|
<Show when={message()}>
|
||||||
{(msg) => (
|
<Show when={part()}>
|
||||||
<Show when={part()}>
|
<Part
|
||||||
{(p) => (
|
part={part()!}
|
||||||
<Part
|
message={message()!}
|
||||||
part={p()}
|
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||||
message={msg()}
|
turnDurationMs={props.turnDurationMs}
|
||||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
defaultOpen={partDefaultOpen(part()!, props.shellToolDefaultOpen, props.editToolDefaultOpen)}
|
||||||
turnDurationMs={props.turnDurationMs}
|
/>
|
||||||
defaultOpen={partDefaultOpen(p(), props.shellToolDefaultOpen, props.editToolDefaultOpen)}
|
</Show>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Show>
|
|
||||||
)}
|
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
})()}
|
})()}
|
||||||
|
|
@ -741,13 +737,11 @@ export function AssistantMessageDisplay(props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={part()}>
|
<Show when={part()}>
|
||||||
{(p) => (
|
<Part
|
||||||
<Part
|
part={part()!}
|
||||||
part={p()}
|
message={props.message}
|
||||||
message={props.message}
|
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
})()}
|
})()}
|
||||||
|
|
@ -1410,11 +1404,9 @@ ToolRegistry.register({
|
||||||
trigger={{ title: i18n.t("ui.tool.list"), subtitle: getDirectory(props.input.path || "/") }}
|
trigger={{ title: i18n.t("ui.tool.list"), subtitle: getDirectory(props.input.path || "/") }}
|
||||||
>
|
>
|
||||||
<Show when={props.output}>
|
<Show when={props.output}>
|
||||||
{(output) => (
|
<div data-component="tool-output" data-scrollable>
|
||||||
<div data-component="tool-output" data-scrollable>
|
<Markdown text={props.output!} />
|
||||||
<Markdown text={output()} />
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Show>
|
</Show>
|
||||||
</BasicTool>
|
</BasicTool>
|
||||||
)
|
)
|
||||||
|
|
@ -1436,11 +1428,9 @@ ToolRegistry.register({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show when={props.output}>
|
<Show when={props.output}>
|
||||||
{(output) => (
|
<div data-component="tool-output" data-scrollable>
|
||||||
<div data-component="tool-output" data-scrollable>
|
<Markdown text={props.output!} />
|
||||||
<Markdown text={output()} />
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Show>
|
</Show>
|
||||||
</BasicTool>
|
</BasicTool>
|
||||||
)
|
)
|
||||||
|
|
@ -1465,11 +1455,9 @@ ToolRegistry.register({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show when={props.output}>
|
<Show when={props.output}>
|
||||||
{(output) => (
|
<div data-component="tool-output" data-scrollable>
|
||||||
<div data-component="tool-output" data-scrollable>
|
<Markdown text={props.output!} />
|
||||||
<Markdown text={output()} />
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Show>
|
</Show>
|
||||||
</BasicTool>
|
</BasicTool>
|
||||||
)
|
)
|
||||||
|
|
@ -1613,16 +1601,14 @@ ToolRegistry.register({
|
||||||
<Show when={description()}>
|
<Show when={description()}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={href()}>
|
<Match when={href()}>
|
||||||
{(url) => (
|
<a
|
||||||
<a
|
data-slot="basic-tool-tool-subtitle"
|
||||||
data-slot="basic-tool-tool-subtitle"
|
class="clickable subagent-link"
|
||||||
class="clickable subagent-link"
|
href={href()!}
|
||||||
href={url()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
onClick={(e) => e.stopPropagation()}
|
>
|
||||||
>
|
{description()}
|
||||||
{description()}
|
</a>
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<span data-slot="basic-tool-tool-subtitle">{description()}</span>
|
<span data-slot="basic-tool-tool-subtitle">{description()}</span>
|
||||||
|
|
@ -1747,7 +1733,9 @@ ToolRegistry.register({
|
||||||
<ToolFileAccordion
|
<ToolFileAccordion
|
||||||
path={path()}
|
path={path()}
|
||||||
actions={
|
actions={
|
||||||
<Show when={!pending() && props.metadata.filediff}>{(diff) => <DiffChanges changes={diff()} />}</Show>
|
<Show when={!pending() && props.metadata.filediff}>
|
||||||
|
<DiffChanges changes={props.metadata.filediff!} />
|
||||||
|
</Show>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div data-component="edit-content">
|
<div data-component="edit-content">
|
||||||
|
|
@ -1974,74 +1962,72 @@ ToolRegistry.register({
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(file) => (
|
<div data-component="apply-patch-tool">
|
||||||
<div data-component="apply-patch-tool">
|
<BasicTool
|
||||||
<BasicTool
|
{...props}
|
||||||
{...props}
|
icon="code-lines"
|
||||||
icon="code-lines"
|
defer
|
||||||
defer
|
trigger={
|
||||||
trigger={
|
<div data-component="edit-trigger">
|
||||||
<div data-component="edit-trigger">
|
<div data-slot="message-part-title-area">
|
||||||
<div data-slot="message-part-title-area">
|
<div data-slot="message-part-title">
|
||||||
<div data-slot="message-part-title">
|
<span data-slot="message-part-title-text">
|
||||||
<span data-slot="message-part-title-text">
|
<TextShimmer text={i18n.t("ui.tool.patch")} active={pending()} />
|
||||||
<TextShimmer text={i18n.t("ui.tool.patch")} active={pending()} />
|
</span>
|
||||||
</span>
|
|
||||||
<Show when={!pending()}>
|
|
||||||
<span data-slot="message-part-title-filename">{getFilename(file().relativePath)}</span>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<Show when={!pending() && file().relativePath.includes("/")}>
|
|
||||||
<div data-slot="message-part-path">
|
|
||||||
<span data-slot="message-part-directory">{getDirectory(file().relativePath)}</span>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<div data-slot="message-part-actions">
|
|
||||||
<Show when={!pending()}>
|
<Show when={!pending()}>
|
||||||
<DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} />
|
<span data-slot="message-part-title-filename">{getFilename(single()!.relativePath)}</span>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={!pending() && single()!.relativePath.includes("/")}>
|
||||||
|
<div data-slot="message-part-path">
|
||||||
|
<span data-slot="message-part-directory">{getDirectory(single()!.relativePath)}</span>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
<div data-slot="message-part-actions">
|
||||||
|
<Show when={!pending()}>
|
||||||
|
<DiffChanges changes={{ additions: single()!.additions, deletions: single()!.deletions }} />
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ToolFileAccordion
|
||||||
|
path={single()!.relativePath}
|
||||||
|
actions={
|
||||||
|
<Switch>
|
||||||
|
<Match when={single()!.type === "add"}>
|
||||||
|
<span data-slot="apply-patch-change" data-type="added">
|
||||||
|
{i18n.t("ui.patch.action.created")}
|
||||||
|
</span>
|
||||||
|
</Match>
|
||||||
|
<Match when={single()!.type === "delete"}>
|
||||||
|
<span data-slot="apply-patch-change" data-type="removed">
|
||||||
|
{i18n.t("ui.patch.action.deleted")}
|
||||||
|
</span>
|
||||||
|
</Match>
|
||||||
|
<Match when={single()!.type === "move"}>
|
||||||
|
<span data-slot="apply-patch-change" data-type="modified">
|
||||||
|
{i18n.t("ui.patch.action.moved")}
|
||||||
|
</span>
|
||||||
|
</Match>
|
||||||
|
<Match when={true}>
|
||||||
|
<DiffChanges changes={{ additions: single()!.additions, deletions: single()!.deletions }} />
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ToolFileAccordion
|
<div data-component="apply-patch-file-diff">
|
||||||
path={file().relativePath}
|
<Dynamic
|
||||||
actions={
|
component={fileComponent}
|
||||||
<Switch>
|
mode="diff"
|
||||||
<Match when={file().type === "add"}>
|
before={{ name: single()!.filePath, contents: single()!.before }}
|
||||||
<span data-slot="apply-patch-change" data-type="added">
|
after={{ name: single()!.movePath ?? single()!.filePath, contents: single()!.after }}
|
||||||
{i18n.t("ui.patch.action.created")}
|
/>
|
||||||
</span>
|
</div>
|
||||||
</Match>
|
</ToolFileAccordion>
|
||||||
<Match when={file().type === "delete"}>
|
</BasicTool>
|
||||||
<span data-slot="apply-patch-change" data-type="removed">
|
</div>
|
||||||
{i18n.t("ui.patch.action.deleted")}
|
|
||||||
</span>
|
|
||||||
</Match>
|
|
||||||
<Match when={file().type === "move"}>
|
|
||||||
<span data-slot="apply-patch-change" data-type="modified">
|
|
||||||
{i18n.t("ui.patch.action.moved")}
|
|
||||||
</span>
|
|
||||||
</Match>
|
|
||||||
<Match when={true}>
|
|
||||||
<DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} />
|
|
||||||
</Match>
|
|
||||||
</Switch>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div data-component="apply-patch-file-diff">
|
|
||||||
<Dynamic
|
|
||||||
component={fileComponent}
|
|
||||||
mode="diff"
|
|
||||||
before={{ name: file().filePath, contents: file().before }}
|
|
||||||
after={{ name: file().movePath ?? file().filePath, contents: file().after }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ToolFileAccordion>
|
|
||||||
</BasicTool>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -388,157 +388,149 @@ export function SessionTurn(
|
||||||
>
|
>
|
||||||
<div onClick={autoScroll.handleInteraction}>
|
<div onClick={autoScroll.handleInteraction}>
|
||||||
<Show when={message()}>
|
<Show when={message()}>
|
||||||
{(msg) => (
|
<div
|
||||||
<div
|
ref={autoScroll.contentRef}
|
||||||
ref={autoScroll.contentRef}
|
data-message={message()!.id}
|
||||||
data-message={msg().id}
|
data-slot="session-turn-message-container"
|
||||||
data-slot="session-turn-message-container"
|
class={props.classes?.container}
|
||||||
class={props.classes?.container}
|
>
|
||||||
>
|
<div data-slot="session-turn-message-content" aria-live="off">
|
||||||
<div data-slot="session-turn-message-content" aria-live="off">
|
<Message message={message()!} parts={parts()} interrupted={interrupted()} queued={queued()} />
|
||||||
<Message message={msg()} parts={parts()} interrupted={interrupted()} queued={queued()} />
|
</div>
|
||||||
|
<Show when={compaction()}>
|
||||||
|
<div data-slot="session-turn-compaction">
|
||||||
|
<Part part={compaction()!} message={message()!} hideDetails />
|
||||||
</div>
|
</div>
|
||||||
<Show when={compaction()}>
|
</Show>
|
||||||
{(part) => (
|
<Show when={assistantMessages().length > 0}>
|
||||||
<div data-slot="session-turn-compaction">
|
<div data-slot="session-turn-assistant-content" aria-hidden={working()}>
|
||||||
<Part part={part()} message={msg()} hideDetails />
|
<AssistantParts
|
||||||
</div>
|
messages={assistantMessages()}
|
||||||
)}
|
showAssistantCopyPartID={assistantCopyPartID()}
|
||||||
</Show>
|
turnDurationMs={turnDurationMs()}
|
||||||
<Show when={assistantMessages().length > 0}>
|
working={working()}
|
||||||
<div data-slot="session-turn-assistant-content" aria-hidden={working()}>
|
showReasoningSummaries={showReasoningSummaries()}
|
||||||
<AssistantParts
|
shellToolDefaultOpen={props.shellToolDefaultOpen}
|
||||||
messages={assistantMessages()}
|
editToolDefaultOpen={props.editToolDefaultOpen}
|
||||||
showAssistantCopyPartID={assistantCopyPartID()}
|
/>
|
||||||
turnDurationMs={turnDurationMs()}
|
</div>
|
||||||
working={working()}
|
</Show>
|
||||||
showReasoningSummaries={showReasoningSummaries()}
|
<Show when={showThinking()}>
|
||||||
shellToolDefaultOpen={props.shellToolDefaultOpen}
|
<div data-slot="session-turn-thinking">
|
||||||
editToolDefaultOpen={props.editToolDefaultOpen}
|
<TextShimmer text={i18n.t("ui.sessionTurn.status.thinking")} />
|
||||||
|
<Show when={!showReasoningSummaries()}>
|
||||||
|
<TextReveal
|
||||||
|
text={reasoningHeading()}
|
||||||
|
class="session-turn-thinking-heading"
|
||||||
|
travel={25}
|
||||||
|
duration={700}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Show>
|
||||||
</Show>
|
</div>
|
||||||
<Show when={showThinking()}>
|
</Show>
|
||||||
<div data-slot="session-turn-thinking">
|
<SessionRetry status={status()} show={active()} />
|
||||||
<TextShimmer text={i18n.t("ui.sessionTurn.status.thinking")} />
|
<Show when={edited() > 0 && !working()}>
|
||||||
<Show when={!showReasoningSummaries()}>
|
<div data-slot="session-turn-diffs">
|
||||||
<TextReveal
|
<Collapsible open={open()} onOpenChange={setOpen} variant="ghost">
|
||||||
text={reasoningHeading()}
|
<Collapsible.Trigger>
|
||||||
class="session-turn-thinking-heading"
|
<div data-component="session-turn-diffs-trigger">
|
||||||
travel={25}
|
<div data-slot="session-turn-diffs-title">
|
||||||
duration={700}
|
<span data-slot="session-turn-diffs-label">{i18n.t("ui.sessionReview.change.modified")}</span>
|
||||||
/>
|
<span data-slot="session-turn-diffs-count">
|
||||||
</Show>
|
{edited()} {i18n.t(edited() === 1 ? "ui.common.file.one" : "ui.common.file.other")}
|
||||||
</div>
|
</span>
|
||||||
</Show>
|
<div data-slot="session-turn-diffs-meta">
|
||||||
<SessionRetry status={status()} show={active()} />
|
<DiffChanges changes={diffs()} variant="bars" />
|
||||||
<Show when={edited() > 0 && !working()}>
|
<Collapsible.Arrow />
|
||||||
<div data-slot="session-turn-diffs">
|
|
||||||
<Collapsible open={open()} onOpenChange={setOpen} variant="ghost">
|
|
||||||
<Collapsible.Trigger>
|
|
||||||
<div data-component="session-turn-diffs-trigger">
|
|
||||||
<div data-slot="session-turn-diffs-title">
|
|
||||||
<span data-slot="session-turn-diffs-label">
|
|
||||||
{i18n.t("ui.sessionReview.change.modified")}
|
|
||||||
</span>
|
|
||||||
<span data-slot="session-turn-diffs-count">
|
|
||||||
{edited()} {i18n.t(edited() === 1 ? "ui.common.file.one" : "ui.common.file.other")}
|
|
||||||
</span>
|
|
||||||
<div data-slot="session-turn-diffs-meta">
|
|
||||||
<DiffChanges changes={diffs()} variant="bars" />
|
|
||||||
<Collapsible.Arrow />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Collapsible.Trigger>
|
</div>
|
||||||
<Collapsible.Content>
|
</Collapsible.Trigger>
|
||||||
<Show when={open()}>
|
<Collapsible.Content>
|
||||||
<div data-component="session-turn-diffs-content">
|
<Show when={open()}>
|
||||||
<Accordion
|
<div data-component="session-turn-diffs-content">
|
||||||
multiple
|
<Accordion
|
||||||
style={{ "--sticky-accordion-offset": "40px" }}
|
multiple
|
||||||
value={expanded()}
|
style={{ "--sticky-accordion-offset": "40px" }}
|
||||||
onChange={(value) => setExpanded(Array.isArray(value) ? value : value ? [value] : [])}
|
value={expanded()}
|
||||||
>
|
onChange={(value) => setExpanded(Array.isArray(value) ? value : value ? [value] : [])}
|
||||||
<For each={diffs()}>
|
>
|
||||||
{(diff) => {
|
<For each={diffs()}>
|
||||||
const active = createMemo(() => expanded().includes(diff.file))
|
{(diff) => {
|
||||||
const [visible, setVisible] = createSignal(false)
|
const active = createMemo(() => expanded().includes(diff.file))
|
||||||
|
const [visible, setVisible] = createSignal(false)
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
on(
|
on(
|
||||||
active,
|
active,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
setVisible(false)
|
setVisible(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (!active()) return
|
if (!active()) return
|
||||||
setVisible(true)
|
setVisible(true)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{ defer: true },
|
{ defer: true },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion.Item value={diff.file}>
|
<Accordion.Item value={diff.file}>
|
||||||
<StickyAccordionHeader>
|
<StickyAccordionHeader>
|
||||||
<Accordion.Trigger>
|
<Accordion.Trigger>
|
||||||
<div data-slot="session-turn-diff-trigger">
|
<div data-slot="session-turn-diff-trigger">
|
||||||
<span data-slot="session-turn-diff-path">
|
<span data-slot="session-turn-diff-path">
|
||||||
<Show when={diff.file.includes("/")}>
|
<Show when={diff.file.includes("/")}>
|
||||||
<span data-slot="session-turn-diff-directory">
|
<span data-slot="session-turn-diff-directory">
|
||||||
{`\u202A${getDirectory(diff.file)}\u202C`}
|
{`\u202A${getDirectory(diff.file)}\u202C`}
|
||||||
</span>
|
|
||||||
</Show>
|
|
||||||
<span data-slot="session-turn-diff-filename">
|
|
||||||
{getFilename(diff.file)}
|
|
||||||
</span>
|
</span>
|
||||||
|
</Show>
|
||||||
|
<span data-slot="session-turn-diff-filename">{getFilename(diff.file)}</span>
|
||||||
|
</span>
|
||||||
|
<div data-slot="session-turn-diff-meta">
|
||||||
|
<span data-slot="session-turn-diff-changes">
|
||||||
|
<DiffChanges changes={diff} />
|
||||||
|
</span>
|
||||||
|
<span data-slot="session-turn-diff-chevron">
|
||||||
|
<Icon name="chevron-down" size="small" />
|
||||||
</span>
|
</span>
|
||||||
<div data-slot="session-turn-diff-meta">
|
|
||||||
<span data-slot="session-turn-diff-changes">
|
|
||||||
<DiffChanges changes={diff} />
|
|
||||||
</span>
|
|
||||||
<span data-slot="session-turn-diff-chevron">
|
|
||||||
<Icon name="chevron-down" size="small" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Accordion.Trigger>
|
</div>
|
||||||
</StickyAccordionHeader>
|
</Accordion.Trigger>
|
||||||
<Accordion.Content>
|
</StickyAccordionHeader>
|
||||||
<Show when={visible()}>
|
<Accordion.Content>
|
||||||
<div data-slot="session-turn-diff-view" data-scrollable>
|
<Show when={visible()}>
|
||||||
<Dynamic
|
<div data-slot="session-turn-diff-view" data-scrollable>
|
||||||
component={fileComponent}
|
<Dynamic
|
||||||
mode="diff"
|
component={fileComponent}
|
||||||
before={{ name: diff.file, contents: diff.before }}
|
mode="diff"
|
||||||
after={{ name: diff.file, contents: diff.after }}
|
before={{ name: diff.file, contents: diff.before }}
|
||||||
/>
|
after={{ name: diff.file, contents: diff.after }}
|
||||||
</div>
|
/>
|
||||||
</Show>
|
</div>
|
||||||
</Accordion.Content>
|
</Show>
|
||||||
</Accordion.Item>
|
</Accordion.Content>
|
||||||
)
|
</Accordion.Item>
|
||||||
}}
|
)
|
||||||
</For>
|
}}
|
||||||
</Accordion>
|
</For>
|
||||||
</div>
|
</Accordion>
|
||||||
</Show>
|
</div>
|
||||||
</Collapsible.Content>
|
</Show>
|
||||||
</Collapsible>
|
</Collapsible.Content>
|
||||||
</div>
|
</Collapsible>
|
||||||
</Show>
|
</div>
|
||||||
<Show when={error()}>
|
</Show>
|
||||||
<Card variant="error" class="error-card">
|
<Show when={error()}>
|
||||||
{errorText()}
|
<Card variant="error" class="error-card">
|
||||||
</Card>
|
{errorText()}
|
||||||
</Show>
|
</Card>
|
||||||
</div>
|
</Show>
|
||||||
)}
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue