fix(app): last turn changes rendered in review pane (#12182)

This commit is contained in:
Adam
2026-02-04 13:50:56 -06:00
committed by GitHub
parent 9436cb575b
commit 222bddc41a
19 changed files with 395 additions and 392 deletions

View File

@@ -28,6 +28,7 @@ import { Dialog } from "@opencode-ai/ui/dialog"
import { InlineInput } from "@opencode-ai/ui/inline-input" import { InlineInput } from "@opencode-ai/ui/inline-input"
import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { Tabs } from "@opencode-ai/ui/tabs" import { Tabs } from "@opencode-ai/ui/tabs"
import { Select } from "@opencode-ai/ui/select"
import { useCodeComponent } from "@opencode-ai/ui/context/code" import { useCodeComponent } from "@opencode-ai/ui/context/code"
import { LineComment as LineCommentView, LineCommentEditor } from "@opencode-ai/ui/line-comment" import { LineComment as LineCommentView, LineCommentEditor } from "@opencode-ai/ui/line-comment"
import { SessionTurn } from "@opencode-ai/ui/session-turn" import { SessionTurn } from "@opencode-ai/ui/session-turn"
@@ -54,7 +55,7 @@ import { useCommand } from "@/context/command"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { useNavigate, useParams } from "@solidjs/router" import { useNavigate, useParams } from "@solidjs/router"
import { UserMessage } from "@opencode-ai/sdk/v2" import { UserMessage } from "@opencode-ai/sdk/v2"
import type { FileDiff } from "@opencode-ai/sdk/v2/client" import type { FileDiff } from "@opencode-ai/sdk/v2"
import { useSDK } from "@/context/sdk" import { useSDK } from "@/context/sdk"
import { usePrompt } from "@/context/prompt" import { usePrompt } from "@/context/prompt"
import { useComments, type LineComment } from "@/context/comments" import { useComments, type LineComment } from "@/context/comments"
@@ -104,6 +105,8 @@ const setSessionHandoff = (key: string, patch: Partial<HandoffSession>) => {
} }
interface SessionReviewTabProps { interface SessionReviewTabProps {
title?: JSX.Element
empty?: JSX.Element
diffs: () => FileDiff[] diffs: () => FileDiff[]
view: () => ReturnType<ReturnType<typeof useLayout>["view"]> view: () => ReturnType<ReturnType<typeof useLayout>["view"]>
diffStyle: DiffStyle diffStyle: DiffStyle
@@ -220,6 +223,8 @@ function SessionReviewTab(props: SessionReviewTabProps) {
return ( return (
<SessionReview <SessionReview
title={props.title}
empty={props.empty}
scrollRef={(el) => { scrollRef={(el) => {
scroll = el scroll = el
props.onScrollRef?.(el) props.onScrollRef?.(el)
@@ -709,10 +714,14 @@ export default function Page() {
messageId: undefined as string | undefined, messageId: undefined as string | undefined,
turnStart: 0, turnStart: 0,
mobileTab: "session" as "session" | "changes", mobileTab: "session" as "session" | "changes",
changes: "session" as "session" | "turn",
newSessionWorktree: "main", newSessionWorktree: "main",
promptHeight: 0, promptHeight: 0,
}) })
const turnDiffs = createMemo(() => lastUserMessage()?.summary?.diffs ?? [])
const reviewDiffs = createMemo(() => (store.changes === "session" ? diffs() : turnDiffs()))
const renderedUserMessages = createMemo( const renderedUserMessages = createMemo(
() => { () => {
const msgs = visibleUserMessages() const msgs = visibleUserMessages()
@@ -894,6 +903,7 @@ export default function Page() {
() => { () => {
setStore("messageId", undefined) setStore("messageId", undefined)
setStore("expanded", {}) setStore("expanded", {})
setStore("changes", "session")
setUi("autoCreated", false) setUi("autoCreated", false)
}, },
{ defer: true }, { defer: true },
@@ -1428,17 +1438,64 @@ export default function Page() {
setFileTreeTab("all") setFileTreeTab("all")
} }
const changesOptions = ["session", "turn"] as const
const changesOptionsList = [...changesOptions]
const changesTitle = () => (
<Select
options={changesOptionsList}
current={store.changes}
label={(option) =>
option === "session" ? language.t("ui.sessionReview.title") : language.t("ui.sessionReview.title.lastTurn")
}
onSelect={(option) => option && setStore("changes", option)}
variant="ghost"
size="large"
triggerStyle={{ "font-size": "var(--font-size-large)" }}
/>
)
const emptyTurn = () => (
<div class="h-full pb-30 flex flex-col items-center justify-center text-center gap-6">
<Mark class="w-14 opacity-10" />
<div class="text-14-regular text-text-weak max-w-56">{language.t("session.review.noChanges")}</div>
</div>
)
const reviewPanel = () => ( const reviewPanel = () => (
<div class="flex flex-col h-full overflow-hidden bg-background-stronger contain-strict"> <div class="flex flex-col h-full overflow-hidden bg-background-stronger contain-strict">
<div class="relative pt-2 flex-1 min-h-0 overflow-hidden"> <div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
<Switch> <Switch>
<Match when={store.changes === "turn" && !!params.id}>
<SessionReviewTab
title={changesTitle()}
empty={emptyTurn()}
diffs={reviewDiffs}
view={view}
diffStyle={layout.review.diffStyle()}
onDiffStyleChange={layout.review.setDiffStyle}
onScrollRef={(el) => setTree("reviewScroll", el)}
focusedFile={tree.activeDiff}
onLineComment={(comment) => addCommentToContext({ ...comment, origin: "review" })}
comments={comments.all()}
focusedComment={comments.focus()}
onFocusedCommentChange={comments.setFocus}
onViewFile={(path) => {
showAllFiles()
const value = file.tab(path)
tabs().open(value)
file.load(path)
}}
/>
</Match>
<Match when={hasReview()}> <Match when={hasReview()}>
<Show <Show
when={diffsReady()} when={diffsReady()}
fallback={<div class="px-6 py-4 text-text-weak">{language.t("session.review.loadingChanges")}</div>} fallback={<div class="px-6 py-4 text-text-weak">{language.t("session.review.loadingChanges")}</div>}
> >
<SessionReviewTab <SessionReviewTab
diffs={diffs} title={changesTitle()}
diffs={reviewDiffs}
view={view} view={view}
diffStyle={layout.review.diffStyle()} diffStyle={layout.review.diffStyle()}
onDiffStyleChange={layout.review.setDiffStyle} onDiffStyleChange={layout.review.setDiffStyle}
@@ -2138,6 +2195,31 @@ export default function Page() {
fallback={ fallback={
<div class="relative h-full overflow-hidden"> <div class="relative h-full overflow-hidden">
<Switch> <Switch>
<Match when={store.changes === "turn" && !!params.id}>
<SessionReviewTab
title={changesTitle()}
empty={emptyTurn()}
diffs={reviewDiffs}
view={view}
diffStyle="unified"
focusedFile={tree.activeDiff}
onLineComment={(comment) => addCommentToContext({ ...comment, origin: "review" })}
comments={comments.all()}
focusedComment={comments.focus()}
onFocusedCommentChange={comments.setFocus}
onViewFile={(path) => {
showAllFiles()
const value = file.tab(path)
tabs().open(value)
file.load(path)
}}
classes={{
root: "pb-[calc(var(--prompt-height,8rem)+32px)]",
header: "px-4",
container: "px-4",
}}
/>
</Match>
<Match when={hasReview()}> <Match when={hasReview()}>
<Show <Show
when={diffsReady()} when={diffsReady()}
@@ -2148,7 +2230,8 @@ export default function Page() {
} }
> >
<SessionReviewTab <SessionReviewTab
diffs={diffs} title={changesTitle()}
diffs={reviewDiffs}
view={view} view={view}
diffStyle="unified" diffStyle="unified"
focusedFile={tree.activeDiff} focusedFile={tree.activeDiff}

View File

@@ -10,9 +10,9 @@
display: none; display: none;
} }
/* [data-slot="session-review-container"] { */ [data-slot="session-review-container"] {
/* height: 100%; */ flex: 1 1 auto;
/* } */ }
[data-slot="session-review-header"] { [data-slot="session-review-header"] {
position: sticky; position: sticky;

View File

@@ -36,6 +36,8 @@ export type SessionReviewLineComment = {
export type SessionReviewFocus = { file: string; id: string } export type SessionReviewFocus = { file: string; id: string }
export interface SessionReviewProps { export interface SessionReviewProps {
title?: JSX.Element
empty?: JSX.Element
split?: boolean split?: boolean
diffStyle?: SessionReviewDiffStyle diffStyle?: SessionReviewDiffStyle
onDiffStyleChange?: (diffStyle: SessionReviewDiffStyle) => void onDiffStyleChange?: (diffStyle: SessionReviewDiffStyle) => void
@@ -184,6 +186,7 @@ export const SessionReview = (props: SessionReviewProps) => {
const open = () => props.open ?? store.open const open = () => props.open ?? store.open
const diffStyle = () => props.diffStyle ?? (props.split ? "split" : "unified") const diffStyle = () => props.diffStyle ?? (props.split ? "split" : "unified")
const hasDiffs = () => props.diffs.length > 0
const handleChange = (open: string[]) => { const handleChange = (open: string[]) => {
props.onOpenChange?.(open) props.onOpenChange?.(open)
@@ -287,9 +290,9 @@ export const SessionReview = (props: SessionReviewProps) => {
[props.classes?.header ?? ""]: !!props.classes?.header, [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"> <div data-slot="session-review-actions">
<Show when={props.onDiffStyleChange}> <Show when={hasDiffs() && props.onDiffStyleChange}>
<RadioGroup <RadioGroup
options={["unified", "split"] as const} options={["unified", "split"] as const}
current={diffStyle()} current={diffStyle()}
@@ -300,12 +303,14 @@ export const SessionReview = (props: SessionReviewProps) => {
onSelect={(style) => style && props.onDiffStyleChange?.(style)} onSelect={(style) => style && props.onDiffStyleChange?.(style)}
/> />
</Show> </Show>
<Show when={hasDiffs()}>
<Button size="normal" icon="chevron-grabber-vertical" onClick={handleExpandOrCollapseAll}> <Button size="normal" icon="chevron-grabber-vertical" onClick={handleExpandOrCollapseAll}>
<Switch> <Switch>
<Match when={open().length > 0}>{i18n.t("ui.sessionReview.collapseAll")}</Match> <Match when={open().length > 0}>{i18n.t("ui.sessionReview.collapseAll")}</Match>
<Match when={true}>{i18n.t("ui.sessionReview.expandAll")}</Match> <Match when={true}>{i18n.t("ui.sessionReview.expandAll")}</Match>
</Switch> </Switch>
</Button> </Button>
</Show>
{props.actions} {props.actions}
</div> </div>
</div> </div>
@@ -315,6 +320,7 @@ export const SessionReview = (props: SessionReviewProps) => {
[props.classes?.container ?? ""]: !!props.classes?.container, [props.classes?.container ?? ""]: !!props.classes?.container,
}} }}
> >
<Show when={hasDiffs()} fallback={props.empty}>
<Accordion multiple value={open()} onChange={handleChange}> <Accordion multiple value={open()} onChange={handleChange}>
<For each={props.diffs}> <For each={props.diffs}>
{(diff) => { {(diff) => {
@@ -631,6 +637,7 @@ export const SessionReview = (props: SessionReviewProps) => {
}} }}
</For> </For>
</Accordion> </Accordion>
</Show>
</div> </div>
</div> </div>
) )

View File

@@ -8,25 +8,16 @@ import {
TextPart, TextPart,
ToolPart, ToolPart,
} from "@opencode-ai/sdk/v2/client" } from "@opencode-ai/sdk/v2/client"
import { type FileDiff } from "@opencode-ai/sdk/v2"
import { useData } from "../context" import { useData } from "../context"
import { useDiffComponent } from "../context/diff"
import { type UiI18nKey, type UiI18nParams, useI18n } from "../context/i18n" import { type UiI18nKey, type UiI18nParams, useI18n } from "../context/i18n"
import { findLast } from "@opencode-ai/util/array" import { findLast } from "@opencode-ai/util/array"
import { getDirectory, getFilename } from "@opencode-ai/util/path"
import { Binary } from "@opencode-ai/util/binary" import { Binary } from "@opencode-ai/util/binary"
import { createEffect, createMemo, createSignal, For, Match, on, onCleanup, ParentProps, Show, Switch } from "solid-js" 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 { Message, Part } from "./message-part"
import { Markdown } from "./markdown" 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 { IconButton } from "./icon-button"
import { Card } from "./card" import { Card } from "./card"
import { Dynamic } from "solid-js/web"
import { Button } from "./button" import { Button } from "./button"
import { Spinner } from "./spinner" import { Spinner } from "./spinner"
import { Tooltip } from "./tooltip" import { Tooltip } from "./tooltip"
@@ -143,7 +134,6 @@ export function SessionTurn(
) { ) {
const i18n = useI18n() const i18n = useI18n()
const data = useData() const data = useData()
const diffComponent = useDiffComponent()
const emptyMessages: MessageType[] = [] const emptyMessages: MessageType[] = []
const emptyParts: PartType[] = [] const emptyParts: PartType[] = []
@@ -153,7 +143,6 @@ export function SessionTurn(
const emptyPermissionParts: { part: ToolPart; message: AssistantMessage }[] = [] const emptyPermissionParts: { part: ToolPart; message: AssistantMessage }[] = []
const emptyQuestions: QuestionRequest[] = [] const emptyQuestions: QuestionRequest[] = []
const emptyQuestionParts: { part: ToolPart; message: AssistantMessage }[] = [] const emptyQuestionParts: { part: ToolPart; message: AssistantMessage }[] = []
const emptyDiffs: FileDiff[] = []
const idle = { type: "idle" as const } const idle = { type: "idle" as const }
const allMessages = createMemo(() => data.store.message[props.sessionID] ?? emptyMessages) const allMessages = createMemo(() => data.store.message[props.sessionID] ?? emptyMessages)
@@ -409,8 +398,7 @@ export function SessionTurn(
const response = createMemo(() => lastTextPart()?.text) const response = createMemo(() => lastTextPart()?.text)
const responsePartId = createMemo(() => lastTextPart()?.id) const responsePartId = createMemo(() => lastTextPart()?.id)
const messageDiffs = createMemo(() => message()?.summary?.diffs ?? emptyDiffs) const hasDiffs = createMemo(() => (message()?.summary?.diffs?.length ?? 0) > 0)
const hasDiffs = createMemo(() => messageDiffs().length > 0)
const hideResponsePart = createMemo(() => !working() && !!responsePartId()) const hideResponsePart = createMemo(() => !working() && !!responsePartId())
const [copied, setCopied] = createSignal(false) const [copied, setCopied] = createSignal(false)
@@ -476,28 +464,12 @@ export function SessionTurn(
updateStickyHeight(sticky.getBoundingClientRect().height) updateStickyHeight(sticky.getBoundingClientRect().height)
}) })
const diffInit = 20
const diffBatch = 20
const [store, setStore] = createStore({ const [store, setStore] = createStore({
retrySeconds: 0, retrySeconds: 0,
diffsOpen: [] as string[],
diffLimit: diffInit,
status: rawStatus(), status: rawStatus(),
duration: duration(), duration: duration(),
}) })
createEffect(
on(
() => message()?.id,
() => {
setStore("diffsOpen", [])
setStore("diffLimit", diffInit)
},
{ defer: true },
),
)
createEffect(() => { createEffect(() => {
const r = retry() const r = retry()
if (!r) { if (!r) {
@@ -727,7 +699,7 @@ export function SessionTurn(
<div class="sr-only" aria-live="polite"> <div class="sr-only" aria-live="polite">
{!working() && response() ? response() : ""} {!working() && response() ? response() : ""}
</div> </div>
<Show when={!working() && (response() || hasDiffs())}> <Show when={!working() && response()}>
<div data-slot="session-turn-summary-section"> <div data-slot="session-turn-summary-section">
<div data-slot="session-turn-summary-header"> <div data-slot="session-turn-summary-header">
<h2 data-slot="session-turn-summary-title">{i18n.t("ui.sessionTurn.summary.response")}</h2> <h2 data-slot="session-turn-summary-title">{i18n.t("ui.sessionTurn.summary.response")}</h2>
@@ -760,80 +732,6 @@ export function SessionTurn(
</Show> </Show>
</div> </div>
</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> </div>
</Show> </Show>
<Show when={error() && !props.stepsExpanded}> <Show when={error() && !props.stepsExpanded}>

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "تغييرات الجلسة", "ui.sessionReview.title": "تغييرات الجلسة",
"ui.sessionReview.title.lastTurn": "تغييرات آخر دور",
"ui.sessionReview.diffStyle.unified": "موجد", "ui.sessionReview.diffStyle.unified": "موجد",
"ui.sessionReview.diffStyle.split": "منقسم", "ui.sessionReview.diffStyle.split": "منقسم",
"ui.sessionReview.expandAll": "توسيع الكل", "ui.sessionReview.expandAll": "توسيع الكل",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "Alterações da sessão", "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.unified": "Unificado",
"ui.sessionReview.diffStyle.split": "Dividido", "ui.sessionReview.diffStyle.split": "Dividido",
"ui.sessionReview.expandAll": "Expandir tudo", "ui.sessionReview.expandAll": "Expandir tudo",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "Sessionsændringer", "ui.sessionReview.title": "Sessionsændringer",
"ui.sessionReview.title.lastTurn": "Ændringer fra sidste tur",
"ui.sessionReview.diffStyle.unified": "Samlet", "ui.sessionReview.diffStyle.unified": "Samlet",
"ui.sessionReview.diffStyle.split": "Opdelt", "ui.sessionReview.diffStyle.split": "Opdelt",
"ui.sessionReview.expandAll": "Udvid alle", "ui.sessionReview.expandAll": "Udvid alle",

View File

@@ -4,6 +4,7 @@ type Keys = keyof typeof en
export const dict = { export const dict = {
"ui.sessionReview.title": "Sitzungsänderungen", "ui.sessionReview.title": "Sitzungsänderungen",
"ui.sessionReview.title.lastTurn": "Änderungen der letzten Runde",
"ui.sessionReview.diffStyle.unified": "Vereinheitlicht", "ui.sessionReview.diffStyle.unified": "Vereinheitlicht",
"ui.sessionReview.diffStyle.split": "Geteilt", "ui.sessionReview.diffStyle.split": "Geteilt",
"ui.sessionReview.expandAll": "Alle erweitern", "ui.sessionReview.expandAll": "Alle erweitern",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "Session changes", "ui.sessionReview.title": "Session changes",
"ui.sessionReview.title.lastTurn": "Last turn changes",
"ui.sessionReview.diffStyle.unified": "Unified", "ui.sessionReview.diffStyle.unified": "Unified",
"ui.sessionReview.diffStyle.split": "Split", "ui.sessionReview.diffStyle.split": "Split",
"ui.sessionReview.expandAll": "Expand all", "ui.sessionReview.expandAll": "Expand all",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "Cambios de la sesión", "ui.sessionReview.title": "Cambios de la sesión",
"ui.sessionReview.title.lastTurn": "Cambios del último turno",
"ui.sessionReview.diffStyle.unified": "Unificado", "ui.sessionReview.diffStyle.unified": "Unificado",
"ui.sessionReview.diffStyle.split": "Dividido", "ui.sessionReview.diffStyle.split": "Dividido",
"ui.sessionReview.expandAll": "Expandir todo", "ui.sessionReview.expandAll": "Expandir todo",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "Modifications de la session", "ui.sessionReview.title": "Modifications de la session",
"ui.sessionReview.title.lastTurn": "Modifications du dernier tour",
"ui.sessionReview.diffStyle.unified": "Unifié", "ui.sessionReview.diffStyle.unified": "Unifié",
"ui.sessionReview.diffStyle.split": "Divisé", "ui.sessionReview.diffStyle.split": "Divisé",
"ui.sessionReview.expandAll": "Tout développer", "ui.sessionReview.expandAll": "Tout développer",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "セッションの変更", "ui.sessionReview.title": "セッションの変更",
"ui.sessionReview.title.lastTurn": "前回ターンの変更",
"ui.sessionReview.diffStyle.unified": "Unified", "ui.sessionReview.diffStyle.unified": "Unified",
"ui.sessionReview.diffStyle.split": "Split", "ui.sessionReview.diffStyle.split": "Split",
"ui.sessionReview.expandAll": "すべて展開", "ui.sessionReview.expandAll": "すべて展開",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "세션 변경 사항", "ui.sessionReview.title": "세션 변경 사항",
"ui.sessionReview.title.lastTurn": "마지막 턴 변경 사항",
"ui.sessionReview.diffStyle.unified": "통합 보기", "ui.sessionReview.diffStyle.unified": "통합 보기",
"ui.sessionReview.diffStyle.split": "분할 보기", "ui.sessionReview.diffStyle.split": "분할 보기",
"ui.sessionReview.expandAll": "모두 펼치기", "ui.sessionReview.expandAll": "모두 펼치기",

View File

@@ -3,6 +3,7 @@ type Keys = keyof typeof en
export const dict: Record<Keys, string> = { export const dict: Record<Keys, string> = {
"ui.sessionReview.title": "Sesjonsendringer", "ui.sessionReview.title": "Sesjonsendringer",
"ui.sessionReview.title.lastTurn": "Endringer i siste tur",
"ui.sessionReview.diffStyle.unified": "Samlet", "ui.sessionReview.diffStyle.unified": "Samlet",
"ui.sessionReview.diffStyle.split": "Delt", "ui.sessionReview.diffStyle.split": "Delt",
"ui.sessionReview.expandAll": "Utvid alle", "ui.sessionReview.expandAll": "Utvid alle",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "Zmiany w sesji", "ui.sessionReview.title": "Zmiany w sesji",
"ui.sessionReview.title.lastTurn": "Zmiany z ostatniej tury",
"ui.sessionReview.diffStyle.unified": "Ujednolicony", "ui.sessionReview.diffStyle.unified": "Ujednolicony",
"ui.sessionReview.diffStyle.split": "Podzielony", "ui.sessionReview.diffStyle.split": "Podzielony",
"ui.sessionReview.expandAll": "Rozwiń wszystko", "ui.sessionReview.expandAll": "Rozwiń wszystko",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "Изменения сессии", "ui.sessionReview.title": "Изменения сессии",
"ui.sessionReview.title.lastTurn": "Изменения последнего хода",
"ui.sessionReview.diffStyle.unified": "Объединённый", "ui.sessionReview.diffStyle.unified": "Объединённый",
"ui.sessionReview.diffStyle.split": "Разделённый", "ui.sessionReview.diffStyle.split": "Разделённый",
"ui.sessionReview.expandAll": "Развернуть всё", "ui.sessionReview.expandAll": "Развернуть всё",

View File

@@ -1,5 +1,6 @@
export const dict = { export const dict = {
"ui.sessionReview.title": "การเปลี่ยนแปลงเซสชัน", "ui.sessionReview.title": "การเปลี่ยนแปลงเซสชัน",
"ui.sessionReview.title.lastTurn": "การเปลี่ยนแปลงของเทิร์นล่าสุด",
"ui.sessionReview.diffStyle.unified": "แบบรวม", "ui.sessionReview.diffStyle.unified": "แบบรวม",
"ui.sessionReview.diffStyle.split": "แบบแยก", "ui.sessionReview.diffStyle.split": "แบบแยก",
"ui.sessionReview.expandAll": "ขยายทั้งหมด", "ui.sessionReview.expandAll": "ขยายทั้งหมด",

View File

@@ -4,6 +4,7 @@ type Keys = keyof typeof en
export const dict = { export const dict = {
"ui.sessionReview.title": "会话变更", "ui.sessionReview.title": "会话变更",
"ui.sessionReview.title.lastTurn": "上一轮变更",
"ui.sessionReview.diffStyle.unified": "统一", "ui.sessionReview.diffStyle.unified": "统一",
"ui.sessionReview.diffStyle.split": "拆分", "ui.sessionReview.diffStyle.split": "拆分",
"ui.sessionReview.expandAll": "全部展开", "ui.sessionReview.expandAll": "全部展开",

View File

@@ -4,6 +4,7 @@ type Keys = keyof typeof en
export const dict = { export const dict = {
"ui.sessionReview.title": "工作階段變更", "ui.sessionReview.title": "工作階段變更",
"ui.sessionReview.title.lastTurn": "上一輪變更",
"ui.sessionReview.diffStyle.unified": "整合", "ui.sessionReview.diffStyle.unified": "整合",
"ui.sessionReview.diffStyle.split": "拆分", "ui.sessionReview.diffStyle.split": "拆分",
"ui.sessionReview.expandAll": "全部展開", "ui.sessionReview.expandAll": "全部展開",