feat(app): restore to message and fork session (#17092)

This commit is contained in:
Adam
2026-03-11 16:34:48 -05:00
committed by GitHub
parent 58f45ae22b
commit fbd9b7cf4f
9 changed files with 328 additions and 6 deletions

View File

@@ -129,12 +129,20 @@ function DiagnosticsDisplay(props: { diagnostics: Diagnostic[] }): JSX.Element {
export interface MessageProps {
message: MessageType
parts: PartType[]
actions?: UserActions
showAssistantCopyPartID?: string | null
interrupted?: boolean
queued?: boolean
showReasoningSummaries?: boolean
}
export type SessionAction = (input: { sessionID: string; messageID: string }) => Promise<void> | void
export type UserActions = {
fork?: SessionAction
revert?: SessionAction
}
export interface MessagePartProps {
part: PartType
message: MessageType
@@ -676,6 +684,7 @@ export function Message(props: MessageProps) {
<UserMessageDisplay
message={userMessage() as UserMessage}
parts={props.parts}
actions={props.actions}
interrupted={props.interrupted}
queued={props.queued}
/>
@@ -872,6 +881,7 @@ function ContextToolGroup(props: { parts: ToolPart[]; busy?: boolean }) {
export function UserMessageDisplay(props: {
message: UserMessage
parts: PartType[]
actions?: UserActions
interrupted?: boolean
queued?: boolean
}) {
@@ -879,6 +889,7 @@ export function UserMessageDisplay(props: {
const dialog = useDialog()
const i18n = useI18n()
const [copied, setCopied] = createSignal(false)
const [busy, setBusy] = createSignal<"fork" | "revert" | undefined>()
const textPart = createMemo(
() => props.parts?.find((p) => p.type === "text" && !(p as TextPart).synthetic) as TextPart | undefined,
@@ -945,6 +956,22 @@ export function UserMessageDisplay(props: {
setTimeout(() => setCopied(false), 2000)
}
const run = (kind: "fork" | "revert") => {
const act = kind === "fork" ? props.actions?.fork : props.actions?.revert
if (!act || busy()) return
setBusy(kind)
void Promise.resolve()
.then(() =>
act({
sessionID: props.message.sessionID,
messageID: props.message.id,
}),
)
.finally(() => {
if (busy() === kind) setBusy(undefined)
})
}
return (
<div data-component="user-message" data-interrupted={props.interrupted ? "" : undefined}>
<Show when={attachments().length > 0}>
@@ -1012,6 +1039,38 @@ export function UserMessageDisplay(props: {
</Show>
</span>
</Show>
<Show when={props.actions?.fork}>
<Tooltip value={i18n.t("ui.message.forkMessage")} placement="top" gutter={4}>
<IconButton
icon="fork"
size="normal"
variant="ghost"
disabled={!!busy()}
onMouseDown={(e) => e.preventDefault()}
onClick={(event) => {
event.stopPropagation()
run("fork")
}}
aria-label={i18n.t("ui.message.forkMessage")}
/>
</Tooltip>
</Show>
<Show when={props.actions?.revert}>
<Tooltip value={i18n.t("ui.message.revertMessage")} placement="top" gutter={4}>
<IconButton
icon="reset"
size="normal"
variant="ghost"
disabled={!!busy()}
onMouseDown={(e) => e.preventDefault()}
onClick={(event) => {
event.stopPropagation()
run("revert")
}}
aria-label={i18n.t("ui.message.revertMessage")}
/>
</Tooltip>
</Show>
<Tooltip
value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyMessage")}
placement="top"