@ggui-ai/ui-visual-tester 0.23.0 → 0.24.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 +50 -15
- package/package.json +4 -4
|
@@ -28577,6 +28577,7 @@
|
|
|
28577
28577
|
buildWireConfig: () => buildWireConfig,
|
|
28578
28578
|
connectionSource: () => connectionSource,
|
|
28579
28579
|
useAction: () => useAction,
|
|
28580
|
+
useActionSpent: () => useActionSpent,
|
|
28580
28581
|
useApp: () => useApp,
|
|
28581
28582
|
useAuth: () => useAuth,
|
|
28582
28583
|
useContract: () => useContract,
|
|
@@ -28667,8 +28668,19 @@
|
|
|
28667
28668
|
}, [actionName, dispatch, onDispatchSuppressed]);
|
|
28668
28669
|
}
|
|
28669
28670
|
|
|
28670
|
-
// ../wire/dist/
|
|
28671
|
+
// ../wire/dist/action-spent.js
|
|
28671
28672
|
var import_react35 = __toESM(require_react(), 1);
|
|
28673
|
+
var ActionSpentContext = (0, import_react35.createContext)(null);
|
|
28674
|
+
var noSubscription = () => () => void 0;
|
|
28675
|
+
function useActionSpent(actionName) {
|
|
28676
|
+
const source = (0, import_react35.useContext)(ActionSpentContext);
|
|
28677
|
+
const subscribe = (0, import_react35.useCallback)((listener) => source === null ? noSubscription() : source.subscribe(listener), [source]);
|
|
28678
|
+
const read = (0, import_react35.useCallback)(() => source === null ? false : source.isSpent(actionName), [source, actionName]);
|
|
28679
|
+
return (0, import_react35.useSyncExternalStore)(subscribe, read, read);
|
|
28680
|
+
}
|
|
28681
|
+
|
|
28682
|
+
// ../wire/dist/useGguiContext.js
|
|
28683
|
+
var import_react36 = __toESM(require_react(), 1);
|
|
28672
28684
|
function useGguiContext(slotName) {
|
|
28673
28685
|
const contextName = slotName.length === 0 ? "Context" : slotName.charAt(0).toUpperCase() + slotName.slice(1) + "Context";
|
|
28674
28686
|
const registry = globalThis.__ggui__?.contexts;
|
|
@@ -28676,19 +28688,19 @@
|
|
|
28676
28688
|
if (!ctx) {
|
|
28677
28689
|
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.`);
|
|
28678
28690
|
}
|
|
28679
|
-
return (0,
|
|
28691
|
+
return (0, import_react36.useContext)(ctx);
|
|
28680
28692
|
}
|
|
28681
28693
|
|
|
28682
28694
|
// ../wire/dist/useStream.js
|
|
28683
|
-
var
|
|
28695
|
+
var import_react37 = __toESM(require_react(), 1);
|
|
28684
28696
|
function useStream(channelName) {
|
|
28685
28697
|
const { subscribe } = useWireContext();
|
|
28686
|
-
const [latest, setLatest] = (0,
|
|
28687
|
-
const [all, setAll] = (0,
|
|
28688
|
-
const [isComplete, setIsComplete] = (0,
|
|
28689
|
-
const allRef = (0,
|
|
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);
|
|
28690
28702
|
allRef.current = all;
|
|
28691
|
-
(0,
|
|
28703
|
+
(0, import_react37.useEffect)(() => {
|
|
28692
28704
|
return subscribe(channelName, (delivery) => {
|
|
28693
28705
|
const typed = delivery.payload;
|
|
28694
28706
|
setLatest(typed);
|
|
@@ -28726,7 +28738,7 @@
|
|
|
28726
28738
|
}
|
|
28727
28739
|
|
|
28728
28740
|
// ../wire/dist/useRender.js
|
|
28729
|
-
var
|
|
28741
|
+
var import_react38 = __toESM(require_react(), 1);
|
|
28730
28742
|
|
|
28731
28743
|
// ../wire/dist/connection-store.js
|
|
28732
28744
|
var ConnectionWriterConflictError = class extends Error {
|
|
@@ -28823,7 +28835,7 @@
|
|
|
28823
28835
|
// ../wire/dist/useRender.js
|
|
28824
28836
|
function useRender() {
|
|
28825
28837
|
const { render } = useWireContext();
|
|
28826
|
-
const isConnected = (0,
|
|
28838
|
+
const isConnected = (0, import_react38.useSyncExternalStore)(connectionSource.subscribe, connectionSource.getSnapshot, connectionSource.getSnapshot);
|
|
28827
28839
|
return { sessionId: render.sessionId, isConnected };
|
|
28828
28840
|
}
|
|
28829
28841
|
|
|
@@ -29407,7 +29419,24 @@
|
|
|
29407
29419
|
});
|
|
29408
29420
|
const validateEnvelope = opts.validateEnvelope ?? validateOutboundActionEnvelope;
|
|
29409
29421
|
const spentOneShots = /* @__PURE__ */ new Set();
|
|
29422
|
+
const isSpent = (actionName) => opts.getActiveActionSpec()?.[actionName]?.oneShot === true && (spentOneShots.has(actionName) || opts.getSpentOneShots?.()?.includes(actionName) === true);
|
|
29423
|
+
const spentListeners = /* @__PURE__ */ new Set();
|
|
29424
|
+
const notifySpent = () => {
|
|
29425
|
+
for (const listener of [...spentListeners])
|
|
29426
|
+
listener();
|
|
29427
|
+
};
|
|
29428
|
+
const actionSpent = {
|
|
29429
|
+
isSpent,
|
|
29430
|
+
subscribe: (listener) => {
|
|
29431
|
+
spentListeners.add(listener);
|
|
29432
|
+
return () => {
|
|
29433
|
+
spentListeners.delete(listener);
|
|
29434
|
+
};
|
|
29435
|
+
}
|
|
29436
|
+
};
|
|
29437
|
+
opts.spentInputsChanged?.(notifySpent);
|
|
29410
29438
|
return {
|
|
29439
|
+
actionSpent,
|
|
29411
29440
|
app: opts.app,
|
|
29412
29441
|
render: opts.render,
|
|
29413
29442
|
auth: opts.auth,
|
|
@@ -29417,8 +29446,7 @@
|
|
|
29417
29446
|
if (actionSpec === void 0)
|
|
29418
29447
|
opts.onActionSpecAbsent?.(actionName);
|
|
29419
29448
|
const isOneShot = actionSpec?.[actionName]?.oneShot === true;
|
|
29420
|
-
|
|
29421
|
-
if (isOneShot && spent) {
|
|
29449
|
+
if (isSpent(actionName)) {
|
|
29422
29450
|
console.warn(`[ggui] dispatch('${actionName}') suppressed: this action is declared oneShot and already fired for this render. A second gesture is NOT sent to the agent \u2014 the submitted control is spent for the render's lifetime. If the action is meant to repeat, remove \`oneShot\` from its contract entry.`);
|
|
29423
29451
|
opts.onDispatchSuppressed?.({
|
|
29424
29452
|
reason: "one-shot-spent",
|
|
@@ -29443,8 +29471,10 @@
|
|
|
29443
29471
|
opts.onViolation(new ClientContractViolationError("outbound-action", result.violations));
|
|
29444
29472
|
return;
|
|
29445
29473
|
}
|
|
29446
|
-
if (isOneShot)
|
|
29474
|
+
if (isOneShot && !spentOneShots.has(actionName)) {
|
|
29447
29475
|
spentOneShots.add(actionName);
|
|
29476
|
+
notifySpent();
|
|
29477
|
+
}
|
|
29448
29478
|
opts.emitEnvelope(envelope);
|
|
29449
29479
|
},
|
|
29450
29480
|
subscribe: (channelName, handler) => {
|
|
@@ -32439,8 +32469,8 @@
|
|
|
32439
32469
|
|
|
32440
32470
|
// ../design/dist/themes/ThemeProvider.js
|
|
32441
32471
|
var import_jsx_runtime58 = __toESM(require_jsx_runtime(), 1);
|
|
32442
|
-
var
|
|
32443
|
-
var ThemeContext = (0,
|
|
32472
|
+
var import_react39 = __toESM(require_react(), 1);
|
|
32473
|
+
var ThemeContext = (0, import_react39.createContext)(null);
|
|
32444
32474
|
|
|
32445
32475
|
// ../design/dist/rendering/css-tokens.js
|
|
32446
32476
|
var EXPANDED_FRAME = { outerRadiusVar: "--ggui-shape-radius-xl", insetPx: 16 };
|
|
@@ -32459,6 +32489,11 @@
|
|
|
32459
32489
|
"useGguiContext",
|
|
32460
32490
|
"useContract",
|
|
32461
32491
|
"useWireContext",
|
|
32492
|
+
// ggui#1223: a `oneShot` action's spent state, as data. It is served here
|
|
32493
|
+
// one release BEFORE the triad teaches it, because a card that imports a name
|
|
32494
|
+
// its runtime's shim lacks fails at module link. A card generated to use it
|
|
32495
|
+
// must never meet a runtime without it.
|
|
32496
|
+
"useActionSpent",
|
|
32462
32497
|
// Provider component
|
|
32463
32498
|
"GguiWireProvider"
|
|
32464
32499
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggui-ai/ui-visual-tester",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.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.
|
|
57
|
-
"@ggui-ai/
|
|
58
|
-
"@ggui-ai/
|
|
56
|
+
"@ggui-ai/design": "0.24.0",
|
|
57
|
+
"@ggui-ai/protocol": "0.24.0",
|
|
58
|
+
"@ggui-ai/wire": "0.24.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@playwright/test": "^1.60.0",
|