@ggui-ai/ui-visual-tester 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28577,6 +28577,7 @@
28577
28577
  buildWireConfig: () => buildWireConfig,
28578
28578
  connectionSource: () => connectionSource,
28579
28579
  useAction: () => useAction,
28580
+ useActionPending: () => useActionPending,
28580
28581
  useActionSpent: () => useActionSpent,
28581
28582
  useApp: () => useApp,
28582
28583
  useAuth: () => useAuth,
@@ -28679,8 +28680,19 @@
28679
28680
  return (0, import_react35.useSyncExternalStore)(subscribe, read, read);
28680
28681
  }
28681
28682
 
28682
- // ../wire/dist/useGguiContext.js
28683
+ // ../wire/dist/action-pending.js
28683
28684
  var import_react36 = __toESM(require_react(), 1);
28685
+ var ActionPendingContext = (0, import_react36.createContext)(null);
28686
+ var noSubscription2 = () => () => void 0;
28687
+ function useActionPending(actionName) {
28688
+ const source = (0, import_react36.useContext)(ActionPendingContext);
28689
+ const subscribe = (0, import_react36.useCallback)((listener) => source === null ? noSubscription2() : source.subscribe(listener), [source]);
28690
+ const read = (0, import_react36.useCallback)(() => source === null ? false : source.isPending(actionName), [source, actionName]);
28691
+ return (0, import_react36.useSyncExternalStore)(subscribe, read, read);
28692
+ }
28693
+
28694
+ // ../wire/dist/useGguiContext.js
28695
+ var import_react37 = __toESM(require_react(), 1);
28684
28696
  function useGguiContext(slotName) {
28685
28697
  const contextName = slotName.length === 0 ? "Context" : slotName.charAt(0).toUpperCase() + slotName.slice(1) + "Context";
28686
28698
  const registry = globalThis.__ggui__?.contexts;
@@ -28688,19 +28700,19 @@
28688
28700
  if (!ctx) {
28689
28701
  throw new Error(`[ggui] useGguiContext('${slotName}'): no Context registered. The contract must declare 'contextSpec.${slotName}' AND the runtime must have synthesized the per-slot React Context at boot. If you see this in production code generated by the LLM, the boilerplate's destructure line drifted from the contract.`);
28690
28702
  }
28691
- return (0, import_react36.useContext)(ctx);
28703
+ return (0, import_react37.useContext)(ctx);
28692
28704
  }
28693
28705
 
28694
28706
  // ../wire/dist/useStream.js
28695
- var import_react37 = __toESM(require_react(), 1);
28707
+ var import_react38 = __toESM(require_react(), 1);
28696
28708
  function useStream(channelName) {
28697
28709
  const { subscribe } = useWireContext();
28698
- const [latest, setLatest] = (0, import_react37.useState)(null);
28699
- const [all, setAll] = (0, import_react37.useState)([]);
28700
- const [isComplete, setIsComplete] = (0, import_react37.useState)(false);
28701
- const allRef = (0, import_react37.useRef)(all);
28710
+ const [latest, setLatest] = (0, import_react38.useState)(null);
28711
+ const [all, setAll] = (0, import_react38.useState)([]);
28712
+ const [isComplete, setIsComplete] = (0, import_react38.useState)(false);
28713
+ const allRef = (0, import_react38.useRef)(all);
28702
28714
  allRef.current = all;
28703
- (0, import_react37.useEffect)(() => {
28715
+ (0, import_react38.useEffect)(() => {
28704
28716
  return subscribe(channelName, (delivery) => {
28705
28717
  const typed = delivery.payload;
28706
28718
  setLatest(typed);
@@ -28738,7 +28750,7 @@
28738
28750
  }
28739
28751
 
28740
28752
  // ../wire/dist/useRender.js
28741
- var import_react38 = __toESM(require_react(), 1);
28753
+ var import_react39 = __toESM(require_react(), 1);
28742
28754
 
28743
28755
  // ../wire/dist/connection-store.js
28744
28756
  var ConnectionWriterConflictError = class extends Error {
@@ -28835,7 +28847,7 @@
28835
28847
  // ../wire/dist/useRender.js
28836
28848
  function useRender() {
28837
28849
  const { render } = useWireContext();
28838
- const isConnected = (0, import_react38.useSyncExternalStore)(connectionSource.subscribe, connectionSource.getSnapshot, connectionSource.getSnapshot);
28850
+ const isConnected = (0, import_react39.useSyncExternalStore)(connectionSource.subscribe, connectionSource.getSnapshot, connectionSource.getSnapshot);
28839
28851
  return { sessionId: render.sessionId, isConnected };
28840
28852
  }
28841
28853
 
@@ -29411,6 +29423,7 @@
29411
29423
  }
29412
29424
  }
29413
29425
  };
29426
+ var DEFAULT_ACTION_PENDING_BOUND_MS = 2e4;
29414
29427
  function buildWireConfig(opts) {
29415
29428
  let internalSeq = 0;
29416
29429
  const nextClientSeq = opts.nextClientSeq ?? (() => {
@@ -29435,8 +29448,43 @@
29435
29448
  }
29436
29449
  };
29437
29450
  opts.spentInputsChanged?.(notifySpent);
29451
+ const pendingBoundMs = opts.actionPendingBoundMs ?? DEFAULT_ACTION_PENDING_BOUND_MS;
29452
+ const pending = /* @__PURE__ */ new Map();
29453
+ const pendingListeners = /* @__PURE__ */ new Set();
29454
+ const notifyPending = () => {
29455
+ for (const listener of [...pendingListeners])
29456
+ listener();
29457
+ };
29458
+ const markPending = (actionName) => {
29459
+ const previous = pending.get(actionName);
29460
+ if (previous !== void 0)
29461
+ clearTimeout(previous);
29462
+ pending.set(actionName, setTimeout(() => {
29463
+ pending.delete(actionName);
29464
+ notifyPending();
29465
+ }, pendingBoundMs));
29466
+ notifyPending();
29467
+ };
29468
+ const actionPending = {
29469
+ isPending: (actionName) => pending.has(actionName),
29470
+ subscribe: (listener) => {
29471
+ pendingListeners.add(listener);
29472
+ return () => {
29473
+ pendingListeners.delete(listener);
29474
+ };
29475
+ }
29476
+ };
29477
+ opts.pendingInputsChanged?.(() => {
29478
+ if (pending.size === 0)
29479
+ return;
29480
+ for (const timer of pending.values())
29481
+ clearTimeout(timer);
29482
+ pending.clear();
29483
+ notifyPending();
29484
+ });
29438
29485
  return {
29439
29486
  actionSpent,
29487
+ actionPending,
29440
29488
  app: opts.app,
29441
29489
  render: opts.render,
29442
29490
  auth: opts.auth,
@@ -29476,6 +29524,7 @@
29476
29524
  notifySpent();
29477
29525
  }
29478
29526
  opts.emitEnvelope(envelope);
29527
+ markPending(actionName);
29479
29528
  },
29480
29529
  subscribe: (channelName, handler) => {
29481
29530
  return opts.streamBus.subscribe(channelName, (env) => {
@@ -32469,12 +32518,18 @@
32469
32518
 
32470
32519
  // ../design/dist/themes/ThemeProvider.js
32471
32520
  var import_jsx_runtime58 = __toESM(require_jsx_runtime(), 1);
32472
- var import_react39 = __toESM(require_react(), 1);
32473
- var ThemeContext = (0, import_react39.createContext)(null);
32521
+ var import_react40 = __toESM(require_react(), 1);
32522
+ var ThemeContext = (0, import_react40.createContext)(null);
32474
32523
 
32475
32524
  // ../design/dist/rendering/css-tokens.js
32476
32525
  var EXPANDED_FRAME = { outerRadiusVar: "--ggui-shape-radius-xl", insetPx: 16 };
32477
32526
  var EXPANDED_FRAME_INNER_RADIUS = `max(0px, calc(var(${EXPANDED_FRAME.outerRadiusVar}) - ${EXPANDED_FRAME.insetPx}px))`;
32527
+ var EXPANDED_FRAME_CHROME = {
32528
+ radius: `var(${EXPANDED_FRAME.outerRadiusVar})`,
32529
+ hairline: "color-mix(in srgb, var(--ggui-color-onGround) 6%, transparent)",
32530
+ shadowVar: "--ggui-shape-shadow-sm",
32531
+ ground: "var(--ggui-color-neutral-50)"
32532
+ };
32478
32533
 
32479
32534
  // ../design/dist/rendering/rewrite-imports.js
32480
32535
  var WIRE_EXPORTS = [
@@ -32494,6 +32549,10 @@
32494
32549
  // its runtime's shim lacks fails at module link. A card generated to use it
32495
32550
  // must never meet a runtime without it.
32496
32551
  "useActionSpent",
32552
+ // ggui#1398: whether a dispatched action is waiting for the agent's answer,
32553
+ // as data. Served one release before the triad teaches it, for the same
32554
+ // reason as `useActionSpent` above.
32555
+ "useActionPending",
32497
32556
  // Provider component
32498
32557
  "GguiWireProvider"
32499
32558
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/ui-visual-tester",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "Behavioral validator for ggui-generated component code. Renders a component in real Chromium via Playwright, clicks each action declared in its contract, and verifies the action actually does something — mutating bound state or dispatching to the agent. Catches buttons wired to no-op handlers that structural validators miss. Playwright is an optional peer dependency, injected by the caller.",
5
5
  "keywords": [
6
6
  "ggui",
@@ -53,9 +53,9 @@
53
53
  "dependencies": {
54
54
  "react": "^19.0.0",
55
55
  "react-dom": "^19.0.0",
56
- "@ggui-ai/design": "0.24.0",
57
- "@ggui-ai/protocol": "0.24.0",
58
- "@ggui-ai/wire": "0.24.0"
56
+ "@ggui-ai/protocol": "0.25.0",
57
+ "@ggui-ai/wire": "0.25.0",
58
+ "@ggui-ai/design": "0.25.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@playwright/test": "^1.60.0",