@ggui-ai/ui-gen 0.11.0 → 0.13.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/dist/adapters/generation-dispatch.js +180 -18
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/advanced/index.d.ts +1 -1
- package/dist/advanced/index.js +180 -18
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +146 -0
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.js +7 -7
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.js +27 -11
- package/dist/check/index.js.map +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +1 -1
- package/dist/harness/check/runtime-render/index.js +146 -0
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +126 -0
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
- package/dist/harness/index.js +7 -7
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/runtime.js +7 -7
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.js +7 -7
- package/dist/harness/types-public.js.map +1 -1
- package/dist/index.js +180 -18
- package/dist/index.js.map +1 -1
- package/dist/provider-adapter-contract.js +1161 -387
- package/dist/provider-adapter-contract.js.map +1 -1
- package/dist/{render-check-BZoIE8-u.d.ts → render-check-WfOVO9kU.d.ts} +1 -1
- package/package.json +13 -13
- package/src/boilerplate/system-prompt.ts +7 -7
- package/src/check/run-tier0.ts +35 -16
- package/src/harness/check/runtime-render/adapter.ts +28 -0
- package/src/harness/check/runtime-render/render-check.ts +224 -1
|
@@ -713,6 +713,22 @@ async function runRenderCheckInProcess(input) {
|
|
|
713
713
|
if (issue) issues.push(issue);
|
|
714
714
|
}
|
|
715
715
|
}
|
|
716
|
+
if (input.contract?.actionSpec) {
|
|
717
|
+
for (const [name, entry] of Object.entries(input.contract.actionSpec)) {
|
|
718
|
+
const idField = idLikeRequiredStringField(
|
|
719
|
+
entry.schema
|
|
720
|
+
);
|
|
721
|
+
if (idField === null) continue;
|
|
722
|
+
const issue = await checkSelectionIdentity({
|
|
723
|
+
container,
|
|
724
|
+
probe,
|
|
725
|
+
user,
|
|
726
|
+
actionName: name,
|
|
727
|
+
idField
|
|
728
|
+
});
|
|
729
|
+
if (issue) issues.push(issue);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
716
732
|
if (input.contract?.propsSpec) {
|
|
717
733
|
for (const issue of checkPropCoverage({
|
|
718
734
|
container,
|
|
@@ -722,6 +738,48 @@ async function runRenderCheckInProcess(input) {
|
|
|
722
738
|
issues.push(issue);
|
|
723
739
|
}
|
|
724
740
|
}
|
|
741
|
+
if (input.contract?.propsSpec) {
|
|
742
|
+
const propsSpec5c = input.contract.propsSpec;
|
|
743
|
+
const normalize = (s) => (s ?? "").replace(/\s+/g, " ").trim();
|
|
744
|
+
const baseText = normalize(container.textContent);
|
|
745
|
+
for (const [propName, entry] of Object.entries(propsSpec5c.properties)) {
|
|
746
|
+
if (!entry.required) continue;
|
|
747
|
+
const orig = input.mockupProps[propName];
|
|
748
|
+
if (typeof orig !== "string" && typeof orig !== "number") continue;
|
|
749
|
+
const marker = pickScalarMarker(orig);
|
|
750
|
+
if (marker === null || !baseText.includes(marker)) continue;
|
|
751
|
+
const varied = typeof orig === "string" ? "Zzyzx Qw" : orig + 137731;
|
|
752
|
+
const variedProbe = createProbe();
|
|
753
|
+
const variedConfig = createProbeWireConfig(variedProbe);
|
|
754
|
+
let variedResult;
|
|
755
|
+
try {
|
|
756
|
+
variedResult = render(
|
|
757
|
+
React.createElement(GguiWireProvider, {
|
|
758
|
+
config: variedConfig,
|
|
759
|
+
children: React.createElement(ProbeErrorBoundary, {
|
|
760
|
+
children: React.createElement(Component, {
|
|
761
|
+
...input.mockupProps,
|
|
762
|
+
[propName]: varied
|
|
763
|
+
})
|
|
764
|
+
})
|
|
765
|
+
})
|
|
766
|
+
);
|
|
767
|
+
} catch {
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
await flushPromises();
|
|
771
|
+
const variedText = normalize(variedResult.container.textContent);
|
|
772
|
+
variedResult.unmount();
|
|
773
|
+
if (variedText === baseText) {
|
|
774
|
+
issues.push({
|
|
775
|
+
check: "prop-sensitivity",
|
|
776
|
+
outcome: "failed",
|
|
777
|
+
subject: propName,
|
|
778
|
+
reason: `props.${propName} is displayed ('${marker}' appears in the DOM) but changing its value to '${String(varied)}' left the rendered output BYTE-IDENTICAL \u2014 the value is baked into the component instead of derived from the prop. A cached reuse of this blueprint under a different ${propName} would show the original value.`
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
725
783
|
if (input.contract?.streamSpec) {
|
|
726
784
|
const streamSpec = input.contract.streamSpec;
|
|
727
785
|
for (const [name] of Object.entries(streamSpec)) {
|
|
@@ -1053,6 +1111,74 @@ async function checkActionWiring(input) {
|
|
|
1053
1111
|
}
|
|
1054
1112
|
};
|
|
1055
1113
|
}
|
|
1114
|
+
function idLikeRequiredStringField(schema) {
|
|
1115
|
+
if (schema === void 0) return null;
|
|
1116
|
+
const properties = schema["properties"];
|
|
1117
|
+
const required = schema["required"];
|
|
1118
|
+
if (properties === null || typeof properties !== "object") return null;
|
|
1119
|
+
if (!Array.isArray(required)) return null;
|
|
1120
|
+
for (const [propName, propSchema] of Object.entries(
|
|
1121
|
+
properties
|
|
1122
|
+
)) {
|
|
1123
|
+
if (!/id$/i.test(propName)) continue;
|
|
1124
|
+
if (!required.includes(propName)) continue;
|
|
1125
|
+
const t = propSchema !== null && typeof propSchema === "object" ? propSchema["type"] : void 0;
|
|
1126
|
+
if (t === "string") return propName;
|
|
1127
|
+
}
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
async function checkSelectionIdentity(input) {
|
|
1131
|
+
const { container, probe, user, actionName, idField } = input;
|
|
1132
|
+
const clickables = Array.from(
|
|
1133
|
+
container.querySelectorAll('button, [role="button"]')
|
|
1134
|
+
);
|
|
1135
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
1136
|
+
for (const el of clickables) {
|
|
1137
|
+
const label = (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
1138
|
+
if (label.length === 0) continue;
|
|
1139
|
+
const group = byLabel.get(label);
|
|
1140
|
+
if (group) group.push(el);
|
|
1141
|
+
else byLabel.set(label, [el]);
|
|
1142
|
+
}
|
|
1143
|
+
for (const [label, group] of byLabel) {
|
|
1144
|
+
if (group.length < 2) continue;
|
|
1145
|
+
const payloadFor = async (el) => {
|
|
1146
|
+
const before = probe.getFireLog().length;
|
|
1147
|
+
try {
|
|
1148
|
+
await user.click(el);
|
|
1149
|
+
} catch {
|
|
1150
|
+
return void 0;
|
|
1151
|
+
}
|
|
1152
|
+
await flushPromises();
|
|
1153
|
+
const events = probe.getFireLog().slice(before).filter(
|
|
1154
|
+
(e) => e.kind === "action.fired" && e.name === actionName
|
|
1155
|
+
);
|
|
1156
|
+
return events.length > 0 ? events[events.length - 1].payload : void 0;
|
|
1157
|
+
};
|
|
1158
|
+
const first = await payloadFor(group[0]);
|
|
1159
|
+
const second = await payloadFor(group[1]);
|
|
1160
|
+
if (first === void 0 || second === void 0) continue;
|
|
1161
|
+
const idOf = (payload) => {
|
|
1162
|
+
if (payload === null || typeof payload !== "object") return void 0;
|
|
1163
|
+
const v = payload[idField];
|
|
1164
|
+
return typeof v === "string" ? v : void 0;
|
|
1165
|
+
};
|
|
1166
|
+
const firstId = idOf(first);
|
|
1167
|
+
const secondId = idOf(second);
|
|
1168
|
+
if (firstId === void 0 || secondId === void 0) continue;
|
|
1169
|
+
if (firstId === secondId) {
|
|
1170
|
+
return {
|
|
1171
|
+
check: "selection-identity",
|
|
1172
|
+
outcome: "failed",
|
|
1173
|
+
subject: actionName,
|
|
1174
|
+
reason: `Two clickable elements share the visible label '${label}', and clicking EACH fired '${actionName}' with the same ${idField} ('${firstId}') \u2014 the action payload is keyed on the display value, not the element's identity. Distinct cells must send their own ${idField} (the contract requires it), or selecting one "${label}" acts on every "${label}".`,
|
|
1175
|
+
elementHint: describeElement(group[0])
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1056
1182
|
async function simulateAndCheck(input) {
|
|
1057
1183
|
const { container, user, probe, wiringKind, actionName, actionLabel, eventKind } = input;
|
|
1058
1184
|
const candidates = findCandidateElements(container, wiringKind, actionName, actionLabel);
|