From d723cf890699b6733fd667e5f92fbf056248485f Mon Sep 17 00:00:00 2001 From: Ame <123734885+luokerenx4@users.noreply.github.com> Date: Sun, 6 Sep 2026 12:33:57 +0800 Subject: [PATCH 1/4] Polish shared conversation transcript and message actions --- docs/ui-interaction-and-motion.md | 8 +++ .../conversation/ConversationTranscript.tsx | 11 +++- .../conversation/ConversationView.tsx | 2 +- .../conversation/MessageActions.spec.tsx | 45 +++++++++++++++ .../conversation/MessageActions.tsx | 38 +++++++++++++ .../components/conversation/conversation.css | 55 +++++++++---------- 6 files changed, 127 insertions(+), 32 deletions(-) create mode 100644 ui/src/components/conversation/MessageActions.spec.tsx create mode 100644 ui/src/components/conversation/MessageActions.tsx diff --git a/docs/ui-interaction-and-motion.md b/docs/ui-interaction-and-motion.md index 3c78e5d64..80288fdb8 100644 --- a/docs/ui-interaction-and-motion.md +++ b/docs/ui-interaction-and-motion.md @@ -172,6 +172,14 @@ the Harness launch page; its context, controls and details are caller-owned slots, not embedded Pi selectors. Existing `oa-harness-composer-*` styling seams remain the shared visual material. Messages and composer use a 46rem reading measure, with local scrolling for wide output and wrapping toolbar controls. +User messages use a quiet, borderless bubble; assistant prose sits directly on +the canvas. Execution summaries are lightweight disclosure rows, with an inset +rail for individual actions rather than nested activity cards. Preserve the +shared Markdown table scroll wrapper instead of overriding table display. +Completed text has a copy action that copies only the displayed message, not +reasoning or tool payloads. Older actions reveal on hover or keyboard focus; +the latest message and touch surfaces keep them visible. Clipboard failures +are actionable, and interrupted tools must say incomplete rather than completed. The normalized types in this folder are ephemeral presentation data, not a new persisted transcript or execution protocol. An adapter converts wire messages diff --git a/ui/src/components/conversation/ConversationTranscript.tsx b/ui/src/components/conversation/ConversationTranscript.tsx index c509dd168..e292e4c68 100644 --- a/ui/src/components/conversation/ConversationTranscript.tsx +++ b/ui/src/components/conversation/ConversationTranscript.tsx @@ -2,18 +2,22 @@ import { useEffect, useState, type ReactElement } from 'react' import { Check, ChevronRight, CircleAlert, CircleDashed, LoaderCircle } from 'lucide-react' import { MarkdownContent } from '../MarkdownContent' import type { ConversationActivity, ConversationContent, ConversationItem, ConversationToolStep } from './types' +import { MessageActions } from './MessageActions' export function ConversationTranscriptItem({ item, working, + latest = false, }: { readonly item: ConversationItem readonly working: boolean + readonly latest?: boolean }): ReactElement { if (item.kind === 'user') { return ( -
+
+ {item.content.some(block => block.kind === 'markdown') && block.kind === 'markdown' ? [block.text] : []).join('\n\n')} />}
) } @@ -25,7 +29,7 @@ export function ConversationTranscriptItem({ ) } return ( -
+
{item.progress.map((text, index) => (
@@ -33,6 +37,7 @@ export function ConversationTranscriptItem({ {item.activity && } {item.final &&
}
+ {!working && item.final && }
) } @@ -115,7 +120,7 @@ function ConversationToolStepView({ step, working }: { readonly step: Conversati : } {step.name} - {summary ?? (step.status === 'running' ? 'Running…' : 'Completed')} + {summary ?? (step.status === 'running' ? (working ? 'Running…' : 'Incomplete') : failed ? 'Failed' : 'Completed')} {resultChars !== null && {formatChars(resultChars)}}