pi-mono/apps/p2/test/fixtures/optimistic.ts
Mario Zechner 20d86c3657 feat(p2): Chord v2 cleanup pass
p2 rewritten by the Chord v2 authoring rules (decision "App authoring rules confirmed
by the p2 audit"), in four parts merged together:
- core: session lifecycle (relocate, clone, fork, archive) and search rebuild are
  persisted jobs on a new server store; Sessions v6 with client-chosen ids and visible
  operations; Retry answers with the worker's real outcome; workers push branch,
  subagent and status state to the server; roots take the method's ctx.
- services: models, envs, ports, login, mcp, logs, sharing and voice follow external
  state (self.follow, followNative) instead of copy loops; methods answer after their
  state commits; presence from host.references.
- chat: chat mode, artifacts and prompt resources follow Durable and native state;
  shell commands are Chord jobs (shellId is the requestId); status pushed before
  send and stop answer; race-free draft claim.
- ui: optimistic updates on web (useOptimistic) and terminal (TuiScope.optimistic,
  extended in Chord with a reducer and a pending flag), pending states for long work,
  visible rollback, load waterfalls removed; merged with the one composer and
  immediate first send (ccff9876e) and the folder + button (5a31a6d5f).
docs/optimistic-ui.md describes the pattern.
2026-11-05 18:22:13 +00:00

54 lines
1.8 KiB
TypeScript

// Browser-only component fixture, bundled by `Browser.loadFixture`: a switch whose value is "server state" changed only
// by its call, shown through `useOptimisticCall`. The test settles each call from outside: `pass()` commits the new value
// before the call resolves (as Chord applies the answering host's state before its answer), `refuse(message)` rejects.
import { createElement, useState } from "react";
import { createRoot } from "react-dom/client";
import { errorText, useOptimisticCall } from "../../src/runtime/web.tsx";
type Settle = { pass: () => void; refuse: (message: string) => void };
const calls: Settle[] = [];
function Switch() {
const [server, setServer] = useState(false);
const [error, setError] = useState<string>();
const [shown, run, pending] = useOptimisticCall(
server,
(_, next: boolean) => next,
(reason) => setError(errorText(reason)),
);
const toggle = () => {
const next = !shown;
setError(undefined);
run(
next,
() =>
new Promise<void>((resolve, reject) =>
calls.push({
pass: () => {
setServer(next);
resolve();
},
refuse: (message) => reject(new Error(message)),
}),
),
);
};
return createElement(
"div",
null,
createElement("button", { id: "switch", type: "button", onClick: toggle }, shown ? "on" : "off"),
createElement("span", { id: "pending" }, pending ? "pending" : "idle"),
createElement("span", { id: "error" }, error ?? ""),
);
}
const element = document.createElement("div");
document.body.replaceChildren(element);
createRoot(element).render(createElement(Switch));
Object.assign(window, {
settleCall: (outcome: { pass: true } | { refuse: string }) => {
const call = calls.shift();
if (call && "pass" in outcome) call.pass();
else call?.refuse("refuse" in outcome ? outcome.refuse : "");
},
});