diff options
| author | Adam <[email protected]> | 2026-02-04 13:50:56 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-04 19:50:56 +0000 |
| commit | 222bddc41a945aec8f18536bda09c0e596748bbd (patch) | |
| tree | d12a648ec5e5ec735acafe918c71cda4143960df /packages/ui | |
| parent | 9436cb575bf64ba5867511c998d1e1c51782173c (diff) | |
| download | opencode-222bddc41a945aec8f18536bda09c0e596748bbd.tar.gz opencode-222bddc41a945aec8f18536bda09c0e596748bbd.zip | |
fix(app): last turn changes rendered in review pane (#12182)
Diffstat (limited to 'packages/ui')
| -rw-r--r-- | packages/ui/src/components/session-review.css | 6 | ||||
| -rw-r--r-- | packages/ui/src/components/session-review.tsx | 611 | ||||
| -rw-r--r-- | packages/ui/src/components/session-turn.tsx | 106 | ||||
| -rw-r--r-- | packages/ui/src/i18n/ar.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/br.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/da.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/de.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/en.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/es.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/fr.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/ja.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/ko.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/no.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/pl.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/ru.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/th.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/zh.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/i18n/zht.ts | 1 |
18 files changed, 329 insertions, 409 deletions
diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 363343f91..df6df4649 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -10,9 +10,9 @@ display: none; } - /* [data-slot="session-review-container"] { */ - /* height: 100%; */ - /* } */ + [data-slot="session-review-container"] { + flex: 1 1 auto; + } [data-slot="session-review-header"] { position: sticky; diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 84ec934e2..70d9fe802 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -36,6 +36,8 @@ export type SessionReviewLineComment = { export type SessionReviewFocus = { file: string; id: string } export interface SessionReviewProps { + title?: JSX.Element + empty?: JSX.Element split?: boolean diffStyle?: SessionReviewDiffStyle onDiffStyleChange?: (diffStyle: SessionReviewDiffStyle) => void @@ -184,6 +186,7 @@ export const SessionReview = (props: SessionReviewProps) => { const open = () => props.open ?? store.open const diffStyle = () => props.diffStyle ?? (props.split ? "split" : "unified") + const hasDiffs = () => props.diffs.length > 0 const handleChange = (open: string[]) => { props.onOpenChange?.(open) @@ -287,9 +290,9 @@ export const SessionReview = (props: SessionReviewProps) => { [props.classes?.header ?? ""]: !!props.classes?.header, }} > - <div data-slot="session-review-title">{i18n.t("ui.sessionReview.title")}</div> + <div data-slot="session-review-title">{props.title ?? i18n.t("ui.sessionReview.title")}</div> <div data-slot="session-review-actions"> - <Show when={props.onDiffStyleChange}> + <Show when={hasDiffs() && props.onDiffStyleChange}> <RadioGroup options={["unified", "split"] as const} current={diffStyle()} @@ -300,12 +303,14 @@ export const SessionReview = (props: SessionReviewProps) => { onSelect={(style) => style && props.onDiffStyleChange?.(style)} /> </Show> - <Button size="normal" icon="chevron-grabber-vertical" onClick={handleExpandOrCollapseAll}> - <Switch> - <Match when={open().length > 0}>{i18n.t("ui.sessionReview.collapseAll")}</Match> - <Match when={true}>{i18n.t("ui.sessionReview.expandAll")}</Match> - </Switch> - </Button> + <Show when={hasDiffs()}> + <Button size="normal" icon="chevron-grabber-vertical" onClick={handleExpandOrCollapseAll}> + <Switch> + <Match when={open().length > 0}>{i18n.t("ui.sessionReview.collapseAll")}</Match> + <Match when={true}>{i18n.t("ui.sessionReview.expandAll")}</Match> + </Switch> + </Button> + </Show> {props.actions} </div> </div> @@ -315,322 +320,324 @@ export const SessionReview = (props: SessionReviewProps) => { [props.classes?.container ?? ""]: !!props.classes?.container, }} > - <Accordion multiple value={open()} onChange={handleChange}> - <For each={props.diffs}> - {(diff) => { - let wrapper: HTMLDivElement | undefined - - const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === diff.file)) - const commentedLines = createMemo(() => comments().map((c) => c.selection)) - - const beforeText = () => (typeof diff.before === "string" ? diff.before : "") - const afterText = () => (typeof diff.after === "string" ? diff.after : "") - - const isAdded = () => beforeText().length === 0 && afterText().length > 0 - const isDeleted = () => afterText().length === 0 && beforeText().length > 0 - const isImage = () => isImageFile(diff.file) - const isAudio = () => isAudioFile(diff.file) - - const diffImageSrc = dataUrlFromValue(diff.after) ?? dataUrlFromValue(diff.before) - const [imageSrc, setImageSrc] = createSignal<string | undefined>(diffImageSrc) - const [imageStatus, setImageStatus] = createSignal<"idle" | "loading" | "error">("idle") - - const diffAudioSrc = dataUrlFromValue(diff.after) ?? dataUrlFromValue(diff.before) - const [audioSrc, setAudioSrc] = createSignal<string | undefined>(diffAudioSrc) - const [audioStatus, setAudioStatus] = createSignal<"idle" | "loading" | "error">("idle") - const [audioMime, setAudioMime] = createSignal<string | undefined>(undefined) - - const selectedLines = createMemo(() => { - const current = selection() - if (!current || current.file !== diff.file) return null - return current.range - }) - - const draftRange = createMemo(() => { - const current = commenting() - if (!current || current.file !== diff.file) return null - return current.range - }) - - const [draft, setDraft] = createSignal("") - const [positions, setPositions] = createSignal<Record<string, number>>({}) - const [draftTop, setDraftTop] = createSignal<number | undefined>(undefined) - - const getRoot = () => { - const el = wrapper - if (!el) return - - const host = el.querySelector("diffs-container") - if (!(host instanceof HTMLElement)) return - return host.shadowRoot ?? undefined - } - - const updateAnchors = () => { - const el = wrapper - if (!el) return - - const root = getRoot() - if (!root) return - - const next: Record<string, number> = {} - for (const item of comments()) { - const marker = findMarker(root, item.selection) - if (!marker) continue - next[item.id] = markerTop(el, marker) + <Show when={hasDiffs()} fallback={props.empty}> + <Accordion multiple value={open()} onChange={handleChange}> + <For each={props.diffs}> + {(diff) => { + let wrapper: HTMLDivElement | undefined + + const comments = createMemo(() => (props.comments ?? []).filter((c) => c.file === diff.file)) + const commentedLines = createMemo(() => comments().map((c) => c.selection)) + + const beforeText = () => (typeof diff.before === "string" ? diff.before : "") + const afterText = () => (typeof diff.after === "string" ? diff.after : "") + + const isAdded = () => beforeText().length === 0 && afterText().length > 0 + const isDeleted = () => afterText().length === 0 && beforeText().length > 0 + const isImage = () => isImageFile(diff.file) + const isAudio = () => isAudioFile(diff.file) + + const diffImageSrc = dataUrlFromValue(diff.after) ?? dataUrlFromValue(diff.before) + const [imageSrc, setImageSrc] = createSignal<string | undefined>(diffImageSrc) + const [imageStatus, setImageStatus] = createSignal<"idle" | "loading" | "error">("idle") + + const diffAudioSrc = dataUrlFromValue(diff.after) ?? dataUrlFromValue(diff.before) + const [audioSrc, setAudioSrc] = createSignal<string | undefined>(diffAudioSrc) + const [audioStatus, setAudioStatus] = createSignal<"idle" | "loading" | "error">("idle") + const [audioMime, setAudioMime] = createSignal<string | undefined>(undefined) + + const selectedLines = createMemo(() => { + const current = selection() + if (!current || current.file !== diff.file) return null + return current.range + }) + + const draftRange = createMemo(() => { + const current = commenting() + if (!current || current.file !== diff.file) return null + return current.range + }) + + const [draft, setDraft] = createSignal("") + const [positions, setPositions] = createSignal<Record<string, number>>({}) + const [draftTop, setDraftTop] = createSignal<number | undefined>(undefined) + + const getRoot = () => { + const el = wrapper + if (!el) return + + const host = el.querySelector("diffs-container") + if (!(host instanceof HTMLElement)) return + return host.shadowRoot ?? undefined } - setPositions(next) - const range = draftRange() - if (!range) { - setDraftTop(undefined) - return + const updateAnchors = () => { + const el = wrapper + if (!el) return + + const root = getRoot() + if (!root) return + + const next: Record<string, number> = {} + for (const item of comments()) { + const marker = findMarker(root, item.selection) + if (!marker) continue + next[item.id] = markerTop(el, marker) + } + setPositions(next) + + const range = draftRange() + if (!range) { + setDraftTop(undefined) + return + } + + const marker = findMarker(root, range) + if (!marker) { + setDraftTop(undefined) + return + } + + setDraftTop(markerTop(el, marker)) } - const marker = findMarker(root, range) - if (!marker) { - setDraftTop(undefined) - return + const scheduleAnchors = () => { + requestAnimationFrame(updateAnchors) } - setDraftTop(markerTop(el, marker)) - } - - const scheduleAnchors = () => { - requestAnimationFrame(updateAnchors) - } - - createEffect(() => { - comments() - scheduleAnchors() - }) - - createEffect(() => { - const range = draftRange() - if (!range) return - setDraft("") - scheduleAnchors() - }) - - createEffect(() => { - if (!open().includes(diff.file)) return - if (!isImage()) return - if (imageSrc()) return - if (imageStatus() !== "idle") return - - const reader = props.readFile - if (!reader) return - - setImageStatus("loading") - reader(diff.file) - .then((result) => { - const src = dataUrl(result) - if (!src) { + createEffect(() => { + comments() + scheduleAnchors() + }) + + createEffect(() => { + const range = draftRange() + if (!range) return + setDraft("") + scheduleAnchors() + }) + + createEffect(() => { + if (!open().includes(diff.file)) return + if (!isImage()) return + if (imageSrc()) return + if (imageStatus() !== "idle") return + + const reader = props.readFile + if (!reader) return + + setImageStatus("loading") + reader(diff.file) + .then((result) => { + const src = dataUrl(result) + if (!src) { + setImageStatus("error") + return + } + setImageSrc(src) + setImageStatus("idle") + }) + .catch(() => { setImageStatus("error") - return - } - setImageSrc(src) - setImageStatus("idle") - }) - .catch(() => { - setImageStatus("error") - }) - }) - - createEffect(() => { - if (!open().includes(diff.file)) return - if (!isAudio()) return - if (audioSrc()) return - if (audioStatus() !== "idle") return - - const reader = props.readFile - if (!reader) return - - setAudioStatus("loading") - reader(diff.file) - .then((result) => { - const src = dataUrl(result) - if (!src) { + }) + }) + + createEffect(() => { + if (!open().includes(diff.file)) return + if (!isAudio()) return + if (audioSrc()) return + if (audioStatus() !== "idle") return + + const reader = props.readFile + if (!reader) return + + setAudioStatus("loading") + reader(diff.file) + .then((result) => { + const src = dataUrl(result) + if (!src) { + setAudioStatus("error") + return + } + setAudioMime(normalizeMimeType(result?.mimeType)) + setAudioSrc(src) + setAudioStatus("idle") + }) + .catch(() => { setAudioStatus("error") - return - } - setAudioMime(normalizeMimeType(result?.mimeType)) - setAudioSrc(src) - setAudioStatus("idle") - }) - .catch(() => { - setAudioStatus("error") - }) - }) - - const handleLineSelected = (range: SelectedLineRange | null) => { - if (!props.onLineComment) return - - if (!range) { - setSelection(null) - return - } + }) + }) - setSelection({ file: diff.file, range }) - } + const handleLineSelected = (range: SelectedLineRange | null) => { + if (!props.onLineComment) return - const handleLineSelectionEnd = (range: SelectedLineRange | null) => { - if (!props.onLineComment) return + if (!range) { + setSelection(null) + return + } - if (!range) { - setCommenting(null) - return + setSelection({ file: diff.file, range }) } - setSelection({ file: diff.file, range }) - setCommenting({ file: diff.file, range }) - } + const handleLineSelectionEnd = (range: SelectedLineRange | null) => { + if (!props.onLineComment) return - const openComment = (comment: SessionReviewComment) => { - setOpened({ file: comment.file, id: comment.id }) - setSelection({ file: comment.file, range: comment.selection }) - } + if (!range) { + setCommenting(null) + return + } - const isCommentOpen = (comment: SessionReviewComment) => { - const current = opened() - if (!current) return false - return current.file === comment.file && current.id === comment.id - } + setSelection({ file: diff.file, range }) + setCommenting({ file: diff.file, range }) + } - return ( - <Accordion.Item - value={diff.file} - id={diffId(diff.file)} - data-file={diff.file} - data-slot="session-review-accordion-item" - data-selected={props.focusedFile === diff.file ? "" : undefined} - > - <StickyAccordionHeader> - <Accordion.Trigger> - <div data-slot="session-review-trigger-content"> - <div data-slot="session-review-file-info"> - <FileIcon node={{ path: diff.file, type: "file" }} /> - <div data-slot="session-review-file-name-container"> - <Show when={diff.file.includes("/")}> - <span data-slot="session-review-directory">{`\u202A${getDirectory(diff.file)}\u202C`}</span> - </Show> - <span data-slot="session-review-filename">{getFilename(diff.file)}</span> - <Show when={props.onViewFile}> - <button - data-slot="session-review-view-button" - type="button" - onClick={(e) => { - e.stopPropagation() - props.onViewFile?.(diff.file) - }} - > - <Icon name="eye" size="small" /> - </button> - </Show> + const openComment = (comment: SessionReviewComment) => { + setOpened({ file: comment.file, id: comment.id }) + setSelection({ file: comment.file, range: comment.selection }) + } + + const isCommentOpen = (comment: SessionReviewComment) => { + const current = opened() + if (!current) return false + return current.file === comment.file && current.id === comment.id + } + + return ( + <Accordion.Item + value={diff.file} + id={diffId(diff.file)} + data-file={diff.file} + data-slot="session-review-accordion-item" + data-selected={props.focusedFile === diff.file ? "" : undefined} + > + <StickyAccordionHeader> + <Accordion.Trigger> + <div data-slot="session-review-trigger-content"> + <div data-slot="session-review-file-info"> + <FileIcon node={{ path: diff.file, type: "file" }} /> + <div data-slot="session-review-file-name-container"> + <Show when={diff.file.includes("/")}> + <span data-slot="session-review-directory">{`\u202A${getDirectory(diff.file)}\u202C`}</span> + </Show> + <span data-slot="session-review-filename">{getFilename(diff.file)}</span> + <Show when={props.onViewFile}> + <button + data-slot="session-review-view-button" + type="button" + onClick={(e) => { + e.stopPropagation() + props.onViewFile?.(diff.file) + }} + > + <Icon name="eye" size="small" /> + </button> + </Show> + </div> + </div> + <div data-slot="session-review-trigger-actions"> + <Switch> + <Match when={isAdded()}> + <span data-slot="session-review-change" data-type="added"> + {i18n.t("ui.sessionReview.change.added")} + </span> + </Match> + <Match when={isDeleted()}> + <span data-slot="session-review-change" data-type="removed"> + {i18n.t("ui.sessionReview.change.removed")} + </span> + </Match> + <Match when={true}> + <DiffChanges changes={diff} /> + </Match> + </Switch> + <Icon name="chevron-grabber-vertical" size="small" /> </div> </div> - <div data-slot="session-review-trigger-actions"> - <Switch> - <Match when={isAdded()}> - <span data-slot="session-review-change" data-type="added"> - {i18n.t("ui.sessionReview.change.added")} - </span> - </Match> - <Match when={isDeleted()}> - <span data-slot="session-review-change" data-type="removed"> - {i18n.t("ui.sessionReview.change.removed")} - </span> - </Match> - <Match when={true}> - <DiffChanges changes={diff} /> - </Match> - </Switch> - <Icon name="chevron-grabber-vertical" size="small" /> - </div> - </div> - </Accordion.Trigger> - </StickyAccordionHeader> - <Accordion.Content data-slot="session-review-accordion-content"> - <div - data-slot="session-review-diff-wrapper" - ref={(el) => { - wrapper = el - anchors.set(diff.file, el) - scheduleAnchors() - }} - > - <Dynamic - component={diffComponent} - preloadedDiff={diff.preloaded} - diffStyle={diffStyle()} - onRendered={() => { - props.onDiffRendered?.() + </Accordion.Trigger> + </StickyAccordionHeader> + <Accordion.Content data-slot="session-review-accordion-content"> + <div + data-slot="session-review-diff-wrapper" + ref={(el) => { + wrapper = el + anchors.set(diff.file, el) scheduleAnchors() }} - enableLineSelection={props.onLineComment != null} - onLineSelected={handleLineSelected} - onLineSelectionEnd={handleLineSelectionEnd} - selectedLines={selectedLines()} - commentedLines={commentedLines()} - before={{ - name: diff.file!, - contents: typeof diff.before === "string" ? diff.before : "", - }} - after={{ - name: diff.file!, - contents: typeof diff.after === "string" ? diff.after : "", - }} - /> - - <For each={comments()}> - {(comment) => ( - <LineComment - id={comment.id} - top={positions()[comment.id]} - onMouseEnter={() => setSelection({ file: comment.file, range: comment.selection })} - onClick={() => { - if (isCommentOpen(comment)) { - setOpened(null) - return - } - - openComment(comment) - }} - open={isCommentOpen(comment)} - comment={comment.comment} - selection={selectionLabel(comment.selection)} - /> - )} - </For> - - <Show when={draftRange()}> - {(range) => ( - <Show when={draftTop() !== undefined}> - <LineCommentEditor - top={draftTop()} - value={draft()} - selection={selectionLabel(range())} - onInput={setDraft} - onCancel={() => setCommenting(null)} - onSubmit={(comment) => { - props.onLineComment?.({ - file: diff.file, - selection: range(), - comment, - preview: selectionPreview(diff, range()), - }) - setCommenting(null) + > + <Dynamic + component={diffComponent} + preloadedDiff={diff.preloaded} + diffStyle={diffStyle()} + onRendered={() => { + props.onDiffRendered?.() + scheduleAnchors() + }} + enableLineSelection={props.onLineComment != null} + onLineSelected={handleLineSelected} + onLineSelectionEnd={handleLineSelectionEnd} + selectedLines={selectedLines()} + commentedLines={commentedLines()} + before={{ + name: diff.file!, + contents: typeof diff.before === "string" ? diff.before : "", + }} + after={{ + name: diff.file!, + contents: typeof diff.after === "string" ? diff.after : "", + }} + /> + + <For each={comments()}> + {(comment) => ( + <LineComment + id={comment.id} + top={positions()[comment.id]} + onMouseEnter={() => setSelection({ file: comment.file, range: comment.selection })} + onClick={() => { + if (isCommentOpen(comment)) { + setOpened(null) + return + } + + openComment(comment) }} + open={isCommentOpen(comment)} + comment={comment.comment} + selection={selectionLabel(comment.selection)} /> - </Show> - )} - </Show> - </div> - </Accordion.Content> - </Accordion.Item> - ) - }} - </For> - </Accordion> + )} + </For> + + <Show when={draftRange()}> + {(range) => ( + <Show when={draftTop() !== undefined}> + <LineCommentEditor + top={draftTop()} + value={draft()} + selection={selectionLabel(range())} + onInput={setDraft} + onCancel={() => setCommenting(null)} + onSubmit={(comment) => { + props.onLineComment?.({ + file: diff.file, + selection: range(), + comment, + preview: selectionPreview(diff, range()), + }) + setCommenting(null) + }} + /> + </Show> + )} + </Show> + </div> + </Accordion.Content> + </Accordion.Item> + ) + }} + </For> + </Accordion> + </Show> </div> </div> ) diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index d878bd245..7c5694ba5 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -8,25 +8,16 @@ import { TextPart, ToolPart, } from "@opencode-ai/sdk/v2/client" -import { type FileDiff } from "@opencode-ai/sdk/v2" import { useData } from "../context" -import { useDiffComponent } from "../context/diff" import { type UiI18nKey, type UiI18nParams, useI18n } from "../context/i18n" import { findLast } from "@opencode-ai/util/array" -import { getDirectory, getFilename } from "@opencode-ai/util/path" import { Binary } from "@opencode-ai/util/binary" import { createEffect, createMemo, createSignal, For, Match, on, onCleanup, ParentProps, Show, Switch } from "solid-js" -import { DiffChanges } from "./diff-changes" import { Message, Part } from "./message-part" import { Markdown } from "./markdown" -import { Accordion } from "./accordion" -import { StickyAccordionHeader } from "./sticky-accordion-header" -import { FileIcon } from "./file-icon" -import { Icon } from "./icon" import { IconButton } from "./icon-button" import { Card } from "./card" -import { Dynamic } from "solid-js/web" import { Button } from "./button" import { Spinner } from "./spinner" import { Tooltip } from "./tooltip" @@ -143,7 +134,6 @@ export function SessionTurn( ) { const i18n = useI18n() const data = useData() - const diffComponent = useDiffComponent() const emptyMessages: MessageType[] = [] const emptyParts: PartType[] = [] @@ -153,7 +143,6 @@ export function SessionTurn( const emptyPermissionParts: { part: ToolPart; message: AssistantMessage }[] = [] const emptyQuestions: QuestionRequest[] = [] const emptyQuestionParts: { part: ToolPart; message: AssistantMessage }[] = [] - const emptyDiffs: FileDiff[] = [] const idle = { type: "idle" as const } const allMessages = createMemo(() => data.store.message[props.sessionID] ?? emptyMessages) @@ -409,8 +398,7 @@ export function SessionTurn( const response = createMemo(() => lastTextPart()?.text) const responsePartId = createMemo(() => lastTextPart()?.id) - const messageDiffs = createMemo(() => message()?.summary?.diffs ?? emptyDiffs) - const hasDiffs = createMemo(() => messageDiffs().length > 0) + const hasDiffs = createMemo(() => (message()?.summary?.diffs?.length ?? 0) > 0) const hideResponsePart = createMemo(() => !working() && !!responsePartId()) const [copied, setCopied] = createSignal(false) @@ -476,28 +464,12 @@ export function SessionTurn( updateStickyHeight(sticky.getBoundingClientRect().height) }) - const diffInit = 20 - const diffBatch = 20 - const [store, setStore] = createStore({ retrySeconds: 0, - diffsOpen: [] as string[], - diffLimit: diffInit, status: rawStatus(), duration: duration(), }) - createEffect( - on( - () => message()?.id, - () => { - setStore("diffsOpen", []) - setStore("diffLimit", diffInit) - }, - { defer: true }, - ), - ) - createEffect(() => { const r = retry() if (!r) { @@ -727,7 +699,7 @@ export function SessionTurn( <div class="sr-only" aria-live="polite"> {!working() && response() ? response() : ""} </div> - <Show when={!working() && (response() || hasDiffs())}> + <Show when={!working() && response()}> <div data-slot="session-turn-summary-section"> <div data-slot="session-turn-summary-header"> <h2 data-slot="session-turn-summary-title">{i18n.t("ui.sessionTurn.summary.response")}</h2> @@ -760,80 +732,6 @@ export function SessionTurn( </Show> </div> </div> - <Accordion - data-slot="session-turn-accordion" - multiple - value={store.diffsOpen} - onChange={(value) => { - if (!Array.isArray(value)) return - setStore("diffsOpen", value) - }} - > - <For each={messageDiffs().slice(0, store.diffLimit)}> - {(diff) => ( - <Accordion.Item value={diff.file}> - <StickyAccordionHeader> - <Accordion.Trigger> - <div data-slot="session-turn-accordion-trigger-content"> - <div data-slot="session-turn-file-info"> - <FileIcon - node={{ path: diff.file, type: "file" }} - data-slot="session-turn-file-icon" - /> - <div data-slot="session-turn-file-path"> - <Show when={diff.file.includes("/")}> - <span data-slot="session-turn-directory"> - {`\u202A${getDirectory(diff.file)}\u202C`} - </span> - </Show> - <span data-slot="session-turn-filename">{getFilename(diff.file)}</span> - </div> - </div> - <div data-slot="session-turn-accordion-actions"> - <DiffChanges changes={diff} /> - <Icon name="chevron-grabber-vertical" size="small" /> - </div> - </div> - </Accordion.Trigger> - </StickyAccordionHeader> - <Accordion.Content data-slot="session-turn-accordion-content"> - <Show when={store.diffsOpen.includes(diff.file!)}> - <Dynamic - component={diffComponent} - before={{ - name: diff.file!, - contents: diff.before!, - }} - after={{ - name: diff.file!, - contents: diff.after!, - }} - /> - </Show> - </Accordion.Content> - </Accordion.Item> - )} - </For> - </Accordion> - <Show when={messageDiffs().length > store.diffLimit}> - <Button - data-slot="session-turn-accordion-more" - variant="ghost" - size="small" - onClick={() => { - const total = messageDiffs().length - setStore("diffLimit", (limit) => { - const next = limit + diffBatch - if (next > total) return total - return next - }) - }} - > - {i18n.t("ui.sessionTurn.diff.showMore", { - count: messageDiffs().length - store.diffLimit, - })} - </Button> - </Show> </div> </Show> <Show when={error() && !props.stepsExpanded}> diff --git a/packages/ui/src/i18n/ar.ts b/packages/ui/src/i18n/ar.ts index fc99bdacb..d1abf5de3 100644 --- a/packages/ui/src/i18n/ar.ts +++ b/packages/ui/src/i18n/ar.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "تغييرات الجلسة", + "ui.sessionReview.title.lastTurn": "تغييرات آخر دور", "ui.sessionReview.diffStyle.unified": "موجد", "ui.sessionReview.diffStyle.split": "منقسم", "ui.sessionReview.expandAll": "توسيع الكل", diff --git a/packages/ui/src/i18n/br.ts b/packages/ui/src/i18n/br.ts index fdec9138a..36bef2650 100644 --- a/packages/ui/src/i18n/br.ts +++ b/packages/ui/src/i18n/br.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "Alterações da sessão", + "ui.sessionReview.title.lastTurn": "Alterações do último turno", "ui.sessionReview.diffStyle.unified": "Unificado", "ui.sessionReview.diffStyle.split": "Dividido", "ui.sessionReview.expandAll": "Expandir tudo", diff --git a/packages/ui/src/i18n/da.ts b/packages/ui/src/i18n/da.ts index fbe34e03f..0142d161f 100644 --- a/packages/ui/src/i18n/da.ts +++ b/packages/ui/src/i18n/da.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "Sessionsændringer", + "ui.sessionReview.title.lastTurn": "Ændringer fra sidste tur", "ui.sessionReview.diffStyle.unified": "Samlet", "ui.sessionReview.diffStyle.split": "Opdelt", "ui.sessionReview.expandAll": "Udvid alle", diff --git a/packages/ui/src/i18n/de.ts b/packages/ui/src/i18n/de.ts index d74cd5d22..e004233f6 100644 --- a/packages/ui/src/i18n/de.ts +++ b/packages/ui/src/i18n/de.ts @@ -4,6 +4,7 @@ type Keys = keyof typeof en export const dict = { "ui.sessionReview.title": "Sitzungsänderungen", + "ui.sessionReview.title.lastTurn": "Änderungen der letzten Runde", "ui.sessionReview.diffStyle.unified": "Vereinheitlicht", "ui.sessionReview.diffStyle.split": "Geteilt", "ui.sessionReview.expandAll": "Alle erweitern", diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts index 8c0f09cb3..a92a498c9 100644 --- a/packages/ui/src/i18n/en.ts +++ b/packages/ui/src/i18n/en.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "Session changes", + "ui.sessionReview.title.lastTurn": "Last turn changes", "ui.sessionReview.diffStyle.unified": "Unified", "ui.sessionReview.diffStyle.split": "Split", "ui.sessionReview.expandAll": "Expand all", diff --git a/packages/ui/src/i18n/es.ts b/packages/ui/src/i18n/es.ts index 3f71a98ac..283548bb3 100644 --- a/packages/ui/src/i18n/es.ts +++ b/packages/ui/src/i18n/es.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "Cambios de la sesión", + "ui.sessionReview.title.lastTurn": "Cambios del último turno", "ui.sessionReview.diffStyle.unified": "Unificado", "ui.sessionReview.diffStyle.split": "Dividido", "ui.sessionReview.expandAll": "Expandir todo", diff --git a/packages/ui/src/i18n/fr.ts b/packages/ui/src/i18n/fr.ts index 0ec70509a..e9ab8bf8a 100644 --- a/packages/ui/src/i18n/fr.ts +++ b/packages/ui/src/i18n/fr.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "Modifications de la session", + "ui.sessionReview.title.lastTurn": "Modifications du dernier tour", "ui.sessionReview.diffStyle.unified": "Unifié", "ui.sessionReview.diffStyle.split": "Divisé", "ui.sessionReview.expandAll": "Tout développer", diff --git a/packages/ui/src/i18n/ja.ts b/packages/ui/src/i18n/ja.ts index fd3f24ab3..4da2578d3 100644 --- a/packages/ui/src/i18n/ja.ts +++ b/packages/ui/src/i18n/ja.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "セッションの変更", + "ui.sessionReview.title.lastTurn": "前回ターンの変更", "ui.sessionReview.diffStyle.unified": "Unified", "ui.sessionReview.diffStyle.split": "Split", "ui.sessionReview.expandAll": "すべて展開", diff --git a/packages/ui/src/i18n/ko.ts b/packages/ui/src/i18n/ko.ts index e419f730a..9fb120b5e 100644 --- a/packages/ui/src/i18n/ko.ts +++ b/packages/ui/src/i18n/ko.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "세션 변경 사항", + "ui.sessionReview.title.lastTurn": "마지막 턴 변경 사항", "ui.sessionReview.diffStyle.unified": "통합 보기", "ui.sessionReview.diffStyle.split": "분할 보기", "ui.sessionReview.expandAll": "모두 펼치기", diff --git a/packages/ui/src/i18n/no.ts b/packages/ui/src/i18n/no.ts index 4433b8114..e578e3cdf 100644 --- a/packages/ui/src/i18n/no.ts +++ b/packages/ui/src/i18n/no.ts @@ -3,6 +3,7 @@ type Keys = keyof typeof en export const dict: Record<Keys, string> = { "ui.sessionReview.title": "Sesjonsendringer", + "ui.sessionReview.title.lastTurn": "Endringer i siste tur", "ui.sessionReview.diffStyle.unified": "Samlet", "ui.sessionReview.diffStyle.split": "Delt", "ui.sessionReview.expandAll": "Utvid alle", diff --git a/packages/ui/src/i18n/pl.ts b/packages/ui/src/i18n/pl.ts index efe4bf6cf..0690a7581 100644 --- a/packages/ui/src/i18n/pl.ts +++ b/packages/ui/src/i18n/pl.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "Zmiany w sesji", + "ui.sessionReview.title.lastTurn": "Zmiany z ostatniej tury", "ui.sessionReview.diffStyle.unified": "Ujednolicony", "ui.sessionReview.diffStyle.split": "Podzielony", "ui.sessionReview.expandAll": "Rozwiń wszystko", diff --git a/packages/ui/src/i18n/ru.ts b/packages/ui/src/i18n/ru.ts index 60e63455d..d5a5b59fa 100644 --- a/packages/ui/src/i18n/ru.ts +++ b/packages/ui/src/i18n/ru.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "Изменения сессии", + "ui.sessionReview.title.lastTurn": "Изменения последнего хода", "ui.sessionReview.diffStyle.unified": "Объединённый", "ui.sessionReview.diffStyle.split": "Разделённый", "ui.sessionReview.expandAll": "Развернуть всё", diff --git a/packages/ui/src/i18n/th.ts b/packages/ui/src/i18n/th.ts index 9c7e6fae5..097a2dab0 100644 --- a/packages/ui/src/i18n/th.ts +++ b/packages/ui/src/i18n/th.ts @@ -1,5 +1,6 @@ export const dict = { "ui.sessionReview.title": "การเปลี่ยนแปลงเซสชัน", + "ui.sessionReview.title.lastTurn": "การเปลี่ยนแปลงของเทิร์นล่าสุด", "ui.sessionReview.diffStyle.unified": "แบบรวม", "ui.sessionReview.diffStyle.split": "แบบแยก", "ui.sessionReview.expandAll": "ขยายทั้งหมด", diff --git a/packages/ui/src/i18n/zh.ts b/packages/ui/src/i18n/zh.ts index 4ea477792..25f36b3cd 100644 --- a/packages/ui/src/i18n/zh.ts +++ b/packages/ui/src/i18n/zh.ts @@ -4,6 +4,7 @@ type Keys = keyof typeof en export const dict = { "ui.sessionReview.title": "会话变更", + "ui.sessionReview.title.lastTurn": "上一轮变更", "ui.sessionReview.diffStyle.unified": "统一", "ui.sessionReview.diffStyle.split": "拆分", "ui.sessionReview.expandAll": "全部展开", diff --git a/packages/ui/src/i18n/zht.ts b/packages/ui/src/i18n/zht.ts index c5ef99cda..ebc096cbf 100644 --- a/packages/ui/src/i18n/zht.ts +++ b/packages/ui/src/i18n/zht.ts @@ -4,6 +4,7 @@ type Keys = keyof typeof en export const dict = { "ui.sessionReview.title": "工作階段變更", + "ui.sessionReview.title.lastTurn": "上一輪變更", "ui.sessionReview.diffStyle.unified": "整合", "ui.sessionReview.diffStyle.split": "拆分", "ui.sessionReview.expandAll": "全部展開", |
