export const researchOffersFixture = String.raw` import { createRoot } from "react-dom/client"; import * as Draft from "@chopin/draft"; import type { Wire } from "../wire"; import type { ResearchRequestStore } from "../research-requests"; let source: ConversationPlan.ResearchSource = { messageId: "original", author: { kind: "member", handle: "ana" }, quote: "Investigate Jev alternatives.", start: 0, end: 28 }; let offer: ConversationPlan.ResearchOffer = { id: "offer", needId: "topic", contextId: "scope", source, brief: source.quote, status: "offered", workflow: { version: 1, revision: 0, generation: 0, generationBrief: source.quote, mode: "automatic", placementMessageId: "original", sources: [source], context: { messages: [], decisions: [] }, published: true, preparation: "ready", editedBy: [], additions: [] }, }; let entries: Chat.Entry[] = [ { id: "original", author: source.author, text: source.quote, ts: 1 }, { id: "middle", author: { kind: "member", handle: "bo" }, text: "An unrelated message", ts: 2 }, { id: "later", author: { kind: "member", handle: "ana" }, text: "Include self-hosted options.", ts: 3 }, ]; let state: ConversationPlan.State = { schemaVersion: 2, revision: 1, events: [], threads: [], queue: [], analysis: [], research: { queue: [], analysis: [], retries: [] }, researchOffers: [offer] }; let snapshot = { state, canAct: true, canExecute: true }; let listeners = new Set<() => void>(); let held = false; let pending: Array<() => void> = []; let started: string[] = []; let requests: string[] = []; let linkedRequest: import("@chopin/protocol").Research.RequestView | undefined; let researchStore = { subscribe(listener: () => void) { listeners.add(listener); return () => listeners.delete(listener); }, get: () => linkedRequest, retain: () => () => {}, async retry(id: string) { requests.push("retry:" + id); linkedRequest = { ...linkedRequest!, state: "pending", stage: "queued", error: undefined }; publish(); return linkedRequest; }, } as unknown as ResearchRequestStore; function publish() { state = { ...state, revision: state.revision + 1, researchOffers: [structuredClone(offer)] }; snapshot = { ...snapshot, state }; for (let listener of listeners) listener(); } function edit(operation: ConversationPlan.ResearchEdit["operation"]) { if (offer.status !== "offered") return; if (operation.kind === "begin" && !offer.workflow!.draft) { offer.workflow!.draft = Draft.binary(Draft.create(offer.brief)); offer.workflow!.mode = "human"; } else if (operation.kind === "patch") { let model = Draft.apply(Draft.restore(offer.workflow!.draft!), operation.patch); offer.workflow!.draft = Draft.binary(model); offer.brief = Draft.read(model); } offer.workflow!.revision++; publish(); } function View({ handle }: { handle: string }) { let value = useSyncExternalStore(listener => { listeners.add(listener); return () => { listeners.delete(listener); }; }, () => snapshot); let controls: ResearchOfferControls = { links: linkedRequest ? { [offer.id]: { status: "linked", researchRequestId: linkedRequest.id } } : {}, busy: new Set(), errors: {}, canAct: value.canAct, canExecute: value.canExecute, canCheckLink: false, store: researchStore, wire: wires[handle], onAction: (_id, choice) => { if (choice === "research") { started.push(offer.brief); offer.status = "accepted"; } if (choice === "dismiss") offer.status = "dismissed"; publish(); }, onRetryLink() {}, }; return
{}} conversationPlan={value.state} researchOffers={controls} />
; } function wire(): Wire { return { connected: true, on: () => () => {}, send() {}, ask: (_kind: string, payload: ConversationPlan.ResearchEdit) => new Promise(resolve => { requests.push(payload.operation.kind); let execute = () => { edit(payload.operation); resolve({ offer: structuredClone(offer), revision: state.revision }); }; if (held && payload.operation.kind === "patch") pending.push(execute); else execute(); }), } as unknown as Wire; } let wires: Record = { ana: wire(), bo: wire() }; window.researchOffersProbe = { hold() { held = true; }, release() { held = false; for (let execute of pending.splice(0)) execute(); }, move() { offer.workflow!.placementMessageId = "later"; offer.workflow!.revision++; publish(); }, remote(text: string) { let model = Draft.restore(offer.workflow!.draft!).fork(); edit({ kind: "patch", patch: Draft.change(model, text)! }); }, capabilities(canAct: boolean, canExecute: boolean) { snapshot = { ...snapshot, canAct, canExecute }; publish(); }, failResearch() { offer.status = "accepted"; linkedRequest = { id: "request", channelId: "channel", question: offer.brief, sources: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), state: "failed", stage: "failed", error: "A public web-search request timed out." }; publish(); }, snapshot() { return { brief: offer.brief, started: [...started], requests: [...requests], pending: pending.length }; }, }; createRoot(document.getElementById("fixture")!).render(<>); `;