feat: add prepared authoring assistant pane

This commit is contained in:
lda
2026-07-12 06:21:33 +07:00 Verified
parent c5f9bed26d
commit bfad5d33b1
4 changed files with 152 additions and 0 deletions
@@ -0,0 +1,32 @@
import { cleanup, render, screen } from "@testing-library/react";
import { afterEach, describe, expect, it } from "vitest";
import { PresentationAssistantPane } from "./PresentationAssistantPane.js";
afterEach(cleanup);
describe("PresentationAssistantPane", () => {
it("renders a persistent prepared replay surface for the current phase", () => {
render(<PresentationAssistantPane phase="validate" />);
expect(screen.getByRole("complementary", { name: /prepared authoring assistant/i })).toBeInTheDocument();
expect(screen.getByRole("heading", { name: /authoring assistant/i })).toBeInTheDocument();
expect(screen.getByText(/current phase: validate/i)).toBeInTheDocument();
expect(screen.getByText(/prepared replay only/i)).toBeInTheDocument();
});
it("keeps the active tool group synchronized with the phase", () => {
render(<PresentationAssistantPane phase="artifact" />);
expect(screen.getByRole("button", { name: /artifact.*3 tool calls/i }))
.toHaveAttribute("aria-expanded", "true");
expect(screen.getByRole("button", { name: /validate.*2 tool calls/i }))
.toHaveAttribute("aria-expanded", "false");
});
it("does not expose a live or run action", () => {
render(<PresentationAssistantPane phase="deployment" />);
expect(screen.queryByRole("button", { name: /run|send|execute/i })).not.toBeInTheDocument();
expect(screen.queryByText(/workflow\.runs\.start/i)).not.toBeInTheDocument();
});
});
@@ -0,0 +1,45 @@
import { AuthoringConversation } from "./AuthoringConversation.js";
import type { AuthoringPhaseId } from "./authoring-recording.js";
type PresentationAssistantPaneProps = {
readonly phase: AuthoringPhaseId;
};
const phaseLabels: Readonly<Record<AuthoringPhaseId, string>> = {
discover: "Discover",
draft: "Draft",
validate: "Validate",
artifact: "Artifact",
deployment: "Deployment",
};
/**
* Stable Scene 9 boundary for the prepared assistant surface.
*
* The pane deliberately passes no action handlers: its conversation is a
* replay projection, not a live assistant runtime or authoring client.
*/
export const PresentationAssistantPane = ({ phase }: PresentationAssistantPaneProps) => (
<aside
className="presentation-assistant-pane"
aria-label="prepared authoring assistant"
data-phase={phase}
data-surface="prepared-replay"
>
<header className="presentation-assistant-pane__header">
<p className="presentation-assistant-pane__eyebrow">Prepared workflow</p>
<h2>Authoring assistant</h2>
<p>Current phase: {phaseLabels[phase]}</p>
<p className="presentation-assistant-pane__disclosure">
Prepared replay only. No live actions or RPC calls.
</p>
</header>
<div className="presentation-assistant-pane__conversation">
<AuthoringConversation
throughPhase={phase}
activePhase={phase}
surface="dock"
/>
</div>
</aside>
);