fix: keep scene 8 transcript visible
This commit is contained in:
@@ -10,6 +10,7 @@ type AuthoringConversationProps = {
|
||||
readonly activePhase: AuthoringPhaseId;
|
||||
readonly surface: "stage" | "dock";
|
||||
readonly requestOverride?: string | undefined;
|
||||
readonly scrollMode?: "active" | "start" | undefined;
|
||||
readonly runAction?: { readonly label: string; readonly disabled: boolean; readonly run: () => void } | undefined;
|
||||
};
|
||||
|
||||
@@ -19,6 +20,7 @@ export const AuthoringConversation = ({
|
||||
activePhase,
|
||||
surface,
|
||||
requestOverride,
|
||||
scrollMode,
|
||||
runAction,
|
||||
}: AuthoringConversationProps) => (
|
||||
<AssistantOperatorThread
|
||||
@@ -26,6 +28,7 @@ export const AuthoringConversation = ({
|
||||
surface={surface}
|
||||
messages={projectPreparedAuthoringThread(throughPhase, requestOverride)}
|
||||
activeToolGroupId={authoringToolGroupId(activePhase)}
|
||||
scrollMode={scrollMode}
|
||||
ariaLabel="prepared authoring conversation"
|
||||
runAction={runAction}
|
||||
/>
|
||||
|
||||
@@ -45,6 +45,10 @@ describe("Scene8ChatEntry", () => {
|
||||
await user.click(screen.getByRole("button", { name: "Send" }));
|
||||
expect(screen.getByText(/let me inspect the available sources/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: /discover.*4 tool calls/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole("region", { name: "authoring chat entry" })).toHaveAttribute(
|
||||
"data-entry-phase",
|
||||
"submitted",
|
||||
);
|
||||
expect(screen.queryByRole("button", { name: /run prepared workflow/i })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Send" })).toBeDisabled();
|
||||
});
|
||||
|
||||
@@ -24,7 +24,11 @@ export const Scene8ChatEntry = ({ state, dispatch }: Scene8ChatEntryProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="agent-handoff-scene__entry" aria-label="authoring chat entry">
|
||||
<section
|
||||
className="agent-handoff-scene__entry"
|
||||
aria-label="authoring chat entry"
|
||||
data-entry-phase={state.phase}
|
||||
>
|
||||
<div className="agent-handoff-scene__intro">
|
||||
<span>Scene 8 · agent handoff</span>
|
||||
<h1>What should the workflow author prepare?</h1>
|
||||
@@ -62,6 +66,7 @@ export const Scene8ChatEntry = ({ state, dispatch }: Scene8ChatEntryProps) => {
|
||||
throughPhase="discover"
|
||||
activePhase="discover"
|
||||
surface="stage"
|
||||
scrollMode="start"
|
||||
requestOverride={state.request}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -179,6 +179,53 @@ describe("AssistantOperatorThread", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("can keep a first-stage group anchored at the start of the transcript", async () => {
|
||||
const setScrollTop = vi.fn();
|
||||
const originalDescriptor = Object.getOwnPropertyDescriptor(HTMLDivElement.prototype, "scrollTop");
|
||||
const messages: ReadonlyArray<AgentMessage> = [
|
||||
{
|
||||
id: "assistant-start-scroll-text",
|
||||
role: "assistant",
|
||||
parts: [
|
||||
{ type: "text", text: "The first authoring turn is visible." },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "authoring-discover-tools",
|
||||
role: "assistant",
|
||||
parts: [
|
||||
{ type: "tool-call", call: { id: "authoring-discover-command-0", name: "workflow.sources.list", input: { phase: "discover" } } },
|
||||
{ type: "tool-result", result: { callId: "authoring-discover-command-0", name: "workflow.sources.list", status: "success", output: {} } },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
Object.defineProperty(HTMLDivElement.prototype, "scrollTop", {
|
||||
configurable: true,
|
||||
get: () => 0,
|
||||
set: setScrollTop,
|
||||
});
|
||||
render(
|
||||
<AssistantOperatorThread
|
||||
mode="full"
|
||||
surface="stage"
|
||||
messages={messages}
|
||||
activeToolGroupId="authoring-discover"
|
||||
scrollMode="start"
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(setScrollTop).toHaveBeenCalledWith(0));
|
||||
} finally {
|
||||
if (originalDescriptor) {
|
||||
Object.defineProperty(HTMLDivElement.prototype, "scrollTop", originalDescriptor);
|
||||
} else {
|
||||
delete (HTMLDivElement.prototype as { scrollTop?: number }).scrollTop;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("pairs a lone tool call with its result", () => {
|
||||
const messages: ReadonlyArray<AgentMessage> = [
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@ type AssistantOperatorThreadProps = {
|
||||
readonly mode: "hidden" | "full" | "rail" | "dock";
|
||||
readonly messages: ReadonlyArray<AgentMessage>;
|
||||
readonly runAction?: { readonly label: string; readonly disabled: boolean; readonly run: () => void } | undefined;
|
||||
readonly scrollMode?: "active" | "start" | undefined;
|
||||
readonly submitApproval?: (() => void) | undefined;
|
||||
readonly requestRevision?: (() => void) | undefined;
|
||||
readonly ariaLabel?: string | undefined;
|
||||
@@ -250,6 +251,7 @@ export const AssistantOperatorThread = ({
|
||||
mode,
|
||||
messages,
|
||||
runAction,
|
||||
scrollMode = "active",
|
||||
submitApproval,
|
||||
requestRevision,
|
||||
ariaLabel = "operator conversation",
|
||||
@@ -282,13 +284,17 @@ export const AssistantOperatorThread = ({
|
||||
if (!viewport || !activeGroup) return;
|
||||
const bottomAlignedTop = activeGroup.offsetTop + activeGroup.offsetHeight - viewport.clientHeight;
|
||||
const dockCenteredTop = activeGroup.offsetTop - (viewport.clientHeight - activeGroup.offsetHeight) / 2;
|
||||
const requestedTop = surface === "dock" ? dockCenteredTop : bottomAlignedTop;
|
||||
const requestedTop = scrollMode === "start"
|
||||
? 0
|
||||
: surface === "dock"
|
||||
? dockCenteredTop
|
||||
: bottomAlignedTop;
|
||||
const top = Math.min(
|
||||
Math.max(0, requestedTop),
|
||||
Math.max(0, viewport.scrollHeight - viewport.clientHeight),
|
||||
);
|
||||
viewport.scrollTop = top;
|
||||
}, [activeToolGroupId, projected, surface]);
|
||||
}, [activeToolGroupId, projected, scrollMode, surface]);
|
||||
|
||||
const setToolGroupOpen = useCallback((groupId: string, open: boolean) => {
|
||||
setToolGroupOverrides((current) => {
|
||||
|
||||
@@ -3042,6 +3042,30 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.agent-handoff-scene__entry[data-entry-phase="submitted"] {
|
||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||||
}
|
||||
|
||||
.agent-handoff-scene__entry[data-entry-phase="submitted"] .agent-handoff-scene__entry-thread {
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.agent-handoff-scene__entry[data-entry-phase="submitted"] .agent-handoff-scene__composer {
|
||||
grid-row: 3;
|
||||
gap: 0.4rem;
|
||||
padding: 0.65rem 1rem;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.agent-handoff-scene__entry[data-entry-phase="submitted"] .agent-handoff-scene__composer textarea {
|
||||
min-height: 3.25rem;
|
||||
max-height: 4.5rem;
|
||||
}
|
||||
|
||||
.agent-handoff-scene__entry[data-entry-phase="submitted"] .agent-handoff-scene__composer-help {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.agent-handoff-scene__intro {
|
||||
width: min(100%, 52rem);
|
||||
margin-inline: auto;
|
||||
|
||||
Reference in New Issue
Block a user