Wave 2 prep: LedgerEvent.ref, demo bridge protocol, artifact stubs, sendText in ChatActionsContext
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
import type { ArtifactPartProps } from "@/src/core/contracts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demo artifact card (W6): sandboxed iframe rendering DemoResult.html with
|
||||||
|
* the auto-injected resize/error bridge (contracts.ts "Demo iframe bridge").
|
||||||
|
* While input-available: job activity card (poll /api/jobs).
|
||||||
|
*/
|
||||||
|
export function DemoCard(_props: ArtifactPartProps) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import type { ComponentType } from "react";
|
import type { ComponentType } from "react";
|
||||||
import type { ArtifactPartProps } from "@/src/core/contracts";
|
import type { ArtifactPartProps } from "@/src/core/contracts";
|
||||||
|
import { DemoCard } from "@/components/artifacts/demo-card";
|
||||||
import { JobCard } from "@/components/artifacts/job-card";
|
import { JobCard } from "@/components/artifacts/job-card";
|
||||||
|
import { PlanDivider } from "@/components/artifacts/plan-divider";
|
||||||
|
import { VisualCard } from "@/components/artifacts/visual-card";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Artifact renderer registry — the extension point for tool-part UI.
|
* Artifact renderer registry — the extension point for tool-part UI.
|
||||||
@@ -16,5 +19,9 @@ import { JobCard } from "@/components/artifacts/job-card";
|
|||||||
*/
|
*/
|
||||||
export const artifactRenderers: Record<string, ComponentType<ArtifactPartProps>> = {
|
export const artifactRenderers: Record<string, ComponentType<ArtifactPartProps>> = {
|
||||||
research: JobCard,
|
research: JobCard,
|
||||||
// W6: "demo" → sandboxed iframe card, "visual" → svg card
|
// Phase divider where the plan lands (W5 fills in the component):
|
||||||
|
plan_init: PlanDivider,
|
||||||
|
// Artifacts (W6 fills in the components):
|
||||||
|
demo: DemoCard,
|
||||||
|
visual: VisualCard,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { ArtifactPartProps } from "@/src/core/contracts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase divider rendered where plan_init lands in the transcript (W5).
|
||||||
|
* Probe → teach transition marker per vision §2 "Two phases".
|
||||||
|
*/
|
||||||
|
export function PlanDivider(_props: ArtifactPartProps) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { ArtifactPartProps } from "@/src/core/contracts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visual artifact card (W6): renders VisualResult.svg inline.
|
||||||
|
* While input-available: job activity card (poll /api/jobs).
|
||||||
|
*/
|
||||||
|
export function VisualCard(_props: ArtifactPartProps) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
+3
-1
@@ -37,6 +37,8 @@ const COLUMN = "mx-auto w-full max-w-3xl px-4";
|
|||||||
export const ChatActionsContext = createContext<{
|
export const ChatActionsContext = createContext<{
|
||||||
chatId: string;
|
chatId: string;
|
||||||
syncFromServer: () => Promise<void>;
|
syncFromServer: () => Promise<void>;
|
||||||
|
/** Send a user message into the session (demo error-fix loop, W6). */
|
||||||
|
sendText: (text: string) => void;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
/** useLayoutEffect that is silent during SSR (client components still render on the server in Next). */
|
/** useLayoutEffect that is silent during SSR (client components still render on the server in Next). */
|
||||||
@@ -143,7 +145,7 @@ export function Chat({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatActionsContext.Provider value={{ chatId, syncFromServer }}>
|
<ChatActionsContext.Provider value={{ chatId, syncFromServer, sendText: (t: string) => void sendMessage({ text: t }) }}>
|
||||||
<div className="flex h-dvh flex-col">
|
<div className="flex h-dvh flex-col">
|
||||||
<header className="flex items-center gap-3 px-3 py-2">
|
<header className="flex items-center gap-3 px-3 py-2">
|
||||||
<SidebarToggle />
|
<SidebarToggle />
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ export interface LedgerEvent {
|
|||||||
kind: LedgerEventKind;
|
kind: LedgerEventKind;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
at: string;
|
at: string;
|
||||||
|
/** Idempotency key: the quiz part's toolCallId or `taught:<treeNodeId>` —
|
||||||
|
* re-emission of the same chat must never double-record an event. */
|
||||||
|
ref?: string;
|
||||||
/** The learner's quiz note — diagnostic gold (vision §1 calibration). */
|
/** The learner's quiz note — diagnostic gold (vision §1 calibration). */
|
||||||
note?: string;
|
note?: string;
|
||||||
/** The quiz question text, when nodeId is null (later attribution input). */
|
/** The quiz question text, when nodeId is null (later attribution input). */
|
||||||
@@ -206,6 +209,24 @@ export interface VisualResult {
|
|||||||
notes?: string;
|
notes?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Demo iframe bridge (W6) ────────────────────────────────────────────────
|
||||||
|
// postMessage protocol between the sandboxed demo iframe and its parent card.
|
||||||
|
// The bridge script is auto-injected into every demo HTML by the demo-card
|
||||||
|
// renderer (never the model's job): ResizeObserver → DEMO_RESIZE with the
|
||||||
|
// document height; window.onerror → DEMO_ERROR with the message.
|
||||||
|
|
||||||
|
export interface DemoResizeMessage {
|
||||||
|
type: "demo-resize";
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DemoErrorMessage {
|
||||||
|
type: "demo-error";
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DemoBridgeMessage = DemoResizeMessage | DemoErrorMessage;
|
||||||
|
|
||||||
// ─── Artifact renderer registry (client extension point) ────────────────────
|
// ─── Artifact renderer registry (client extension point) ────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user