@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.
- package/fixture/fixture-runtime.js +71 -12
- package/package.json +4 -4
|
@@ -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/
|
|
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,
|
|
28703
|
+
return (0, import_react37.useContext)(ctx);
|
|
28692
28704
|
}
|
|
28693
28705
|
|
|
28694
28706
|
// ../wire/dist/useStream.js
|
|
28695
|
-
var
|
|
28707
|
+
var import_react38 = __toESM(require_react(), 1);
|
|
28696
28708
|
function useStream(channelName) {
|
|
28697
28709
|
const { subscribe } = useWireContext();
|
|
28698
|
-
const [latest, setLatest] = (0,
|
|
28699
|
-
const [all, setAll] = (0,
|
|
28700
|
-
const [isComplete, setIsComplete] = (0,
|
|
28701
|
-
const allRef = (0,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
32473
|
-
var ThemeContext = (0,
|
|
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.
|
|
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/
|
|
57
|
-
"@ggui-ai/
|
|
58
|
-
"@ggui-ai/
|
|
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",
|