@ggui-ai/ui-gen 0.9.0 → 0.11.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.d.ts +9 -2
- package/dist/adapters/generation-dispatch.js +693 -89
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +28 -11
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +5 -3
- package/dist/advanced/index.js +714 -101
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +267 -3
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.js +14 -2
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +1 -1
- package/dist/check/index.js +11 -1
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-B9kV4U9h.d.ts → create-ui-generator-CF0EL0Li.d.ts} +13 -1
- package/dist/{dispatch-Deo9qzrG.d.ts → dispatch-D75M-OfZ.d.ts} +1 -1
- package/dist/evaluation/axis-checks/index.d.ts +3 -3
- package/dist/evaluation/axis-checks/index.js +101 -0
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +1 -1
- package/dist/evaluation/axis-checks/registry.js +101 -0
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +46 -3
- package/dist/evaluation/index.js +358 -17
- package/dist/evaluation/index.js.map +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +2 -2
- package/dist/harness/check/runtime-render/index.js +268 -3
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.d.ts +2 -0
- package/dist/harness/check/runtime-render/render-check-worker.js +1332 -0
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -0
- package/dist/harness/index.d.ts +2 -2
- package/dist/harness/index.js +263 -18
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/llm-trace-sink.d.ts +1 -1
- package/dist/harness/llm-trace-sink.js.map +1 -1
- package/dist/harness/prompts.js +17 -3
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +1 -1
- package/dist/harness/runtime.d.ts +9 -2
- package/dist/harness/runtime.js +20 -6
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +1 -1
- package/dist/harness/types-public.js +14 -2
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +708 -95
- package/dist/index.js.map +1 -1
- package/dist/{llm-router-1TyI0_TV.d.ts → llm-router-BEVtbAb9.d.ts} +61 -4
- package/dist/patch.d.ts +1 -1
- package/dist/provider-adapter.d.ts +16 -6
- package/dist/provider-adapter.js.map +1 -1
- package/dist/providers/index.d.ts +19 -6
- package/dist/providers/index.js.map +1 -1
- package/dist/{render-check-CNZjHD5g.d.ts → render-check-BZoIE8-u.d.ts} +21 -1
- package/dist/{types-public-C36k1q6s.d.ts → types-public-DIpjC-OA.d.ts} +1 -1
- package/dist/workflows.d.ts +1 -1
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +9 -0
- package/src/adapters/openrouter/client.ts +2 -2
- package/src/adapters/openrouter/types.ts +10 -0
- package/src/boilerplate/generate.ts +10 -2
- package/src/boilerplate/json-schema-ts.ts +22 -2
- package/src/boilerplate/system-prompt.ts +1 -0
- package/src/compile.ts +1 -1
- package/src/contract-context.ts +10 -2
- package/src/create-ui-generator.ts +14 -0
- package/src/design-system-docs.ts +6 -4
- package/src/evaluation/axis-checks/checks/state-ui-affordance.ts +162 -0
- package/src/evaluation/axis-checks/registry.ts +2 -0
- package/src/evaluation/index.ts +11 -0
- package/src/evaluation/llm-evaluator.ts +14 -1
- package/src/evaluation/visual-evaluator.ts +44 -65
- package/src/harness/check/runtime-render/adapter.ts +18 -0
- package/src/harness/check/runtime-render/index.ts +8 -0
- package/src/harness/check/runtime-render/prepare-mockup.ts +14 -0
- package/src/harness/check/runtime-render/production-act-shim.ts +158 -0
- package/src/harness/check/runtime-render/render-check-host.ts +201 -0
- package/src/harness/check/runtime-render/render-check-worker.ts +80 -0
- package/src/harness/check/runtime-render/render-check.ts +112 -1
- package/src/harness/coding/run-eval-round.ts +61 -5
- package/src/harness/index.ts +1 -1
- package/src/harness/llm-router.ts +397 -18
- package/src/harness/llm-trace-sink.ts +2 -1
- package/src/harness/runtime.ts +7 -0
- package/src/index.ts +5 -5
- package/src/provider-adapter.ts +16 -6
- package/src/providers/bedrock.ts +5 -4
- package/src/providers/index.ts +16 -4
- package/src/tools/render-check.ts +19 -7
|
@@ -1,7 +1,199 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
4
|
+
import { runSandboxed } from '@ggui-ai/sandbox';
|
|
1
5
|
import { listContractGadgets, HOOK_NAME_RE } from '@ggui-ai/protocol';
|
|
2
6
|
import ts from 'typescript';
|
|
3
7
|
|
|
4
|
-
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __esm = (fn, res, err) => function __init() {
|
|
11
|
+
if (err) throw err[0];
|
|
12
|
+
try {
|
|
13
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
14
|
+
} catch (e) {
|
|
15
|
+
throw err = [e], e;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var __export = (target, all) => {
|
|
19
|
+
for (var name in all)
|
|
20
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/harness/check/runtime-render/render-check-host.ts
|
|
24
|
+
var render_check_host_exports = {};
|
|
25
|
+
__export(render_check_host_exports, {
|
|
26
|
+
mapSandboxResultToCheckResult: () => mapSandboxResultToCheckResult,
|
|
27
|
+
runRenderCheckViaWorker: () => runRenderCheckViaWorker
|
|
28
|
+
});
|
|
29
|
+
function resolveWorkerSpawn() {
|
|
30
|
+
const jsCandidates = [
|
|
31
|
+
// Sibling — when import.meta.url is already .../harness/check/runtime-render/.
|
|
32
|
+
new URL(`./${WORKER_BASENAME}.js`, import.meta.url),
|
|
33
|
+
// From dist/index.js.
|
|
34
|
+
new URL(`./harness/check/runtime-render/${WORKER_BASENAME}.js`, import.meta.url),
|
|
35
|
+
// From dist/harness/index.js.
|
|
36
|
+
new URL(`./check/runtime-render/${WORKER_BASENAME}.js`, import.meta.url),
|
|
37
|
+
// From dist/check/index.js, dist/advanced/index.js, dist/<flat>.js.
|
|
38
|
+
new URL(`../harness/check/runtime-render/${WORKER_BASENAME}.js`, import.meta.url)
|
|
39
|
+
];
|
|
40
|
+
for (const jsUrl of jsCandidates) {
|
|
41
|
+
const jsPath = fileURLToPath(jsUrl);
|
|
42
|
+
if (existsSync(jsPath)) {
|
|
43
|
+
return { command: process.execPath, args: [jsPath] };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const tsCandidates = [
|
|
47
|
+
new URL(`./${WORKER_BASENAME}.ts`, import.meta.url),
|
|
48
|
+
new URL(`../harness/check/runtime-render/${WORKER_BASENAME}.ts`, import.meta.url)
|
|
49
|
+
];
|
|
50
|
+
for (const tsUrl of tsCandidates) {
|
|
51
|
+
const tsPath = fileURLToPath(tsUrl);
|
|
52
|
+
if (existsSync(tsPath)) {
|
|
53
|
+
const require_ = createRequire(import.meta.url);
|
|
54
|
+
const tsxLoader = pathToFileURL(require_.resolve("tsx")).href;
|
|
55
|
+
return { command: process.execPath, args: ["--import", tsxLoader, tsPath] };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const tried = [...jsCandidates, ...tsCandidates].map((u) => fileURLToPath(u)).join(", ");
|
|
59
|
+
throw new Error(
|
|
60
|
+
`render-check: isolated worker not found at any of: ${tried}. Did \`pnpm build\` run?`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function failed(reason, t0) {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
issues: [{ check: "render-no-throw", outcome: "failed", reason }],
|
|
67
|
+
stats: { actionsChecked: 0, streamsChecked: 0, renderMs: Date.now() - t0 }
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function unverified(reason, t0) {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
issues: [{ check: "render-no-throw", outcome: "unverified", reason }],
|
|
74
|
+
stats: { actionsChecked: 0, streamsChecked: 0, renderMs: Date.now() - t0 }
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function mapSandboxResultToCheckResult(result, t0) {
|
|
78
|
+
if (result.outcome === "timeout") {
|
|
79
|
+
return failed(
|
|
80
|
+
`render check timed out after ${CHECK_TIMEOUT_MS}ms in the isolated worker (likely infinite loop or runaway effect in the component).`,
|
|
81
|
+
t0
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (result.outcome === "overflow-stdout" || result.outcome === "overflow-stderr") {
|
|
85
|
+
return failed(
|
|
86
|
+
`render check worker produced excessive output (${result.outcome}) \u2014 the component is likely in a pathological logging or error loop.`,
|
|
87
|
+
t0
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (result.outcome === "spawn-error") {
|
|
91
|
+
return unverified(
|
|
92
|
+
`render check worker failed to start \u2014 ${result.errorMessage}`,
|
|
93
|
+
t0
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (result.outcome === "canceled") {
|
|
97
|
+
return unverified("render check worker was canceled before completing.", t0);
|
|
98
|
+
}
|
|
99
|
+
if (result.outcome !== "exit") {
|
|
100
|
+
return unverified(
|
|
101
|
+
`render check worker ended with unexpected sandbox outcome '${result.outcome}'.`,
|
|
102
|
+
t0
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
if (result.exitCode !== 0) {
|
|
106
|
+
const tail = result.stderr.trim() || result.stdout.trim();
|
|
107
|
+
return unverified(
|
|
108
|
+
`render check worker exited ${result.exitCode}${tail ? ` \u2014 ${tail.slice(0, 600)}` : ""}`,
|
|
109
|
+
t0
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const stdout = result.stdout.trim();
|
|
113
|
+
if (stdout.length === 0) {
|
|
114
|
+
return unverified("render check worker exited without producing a verdict.", t0);
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return JSON.parse(stdout);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return unverified(
|
|
120
|
+
`render check worker verdict was not valid JSON \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
121
|
+
t0
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function runRenderCheckViaWorker(input) {
|
|
126
|
+
const t0 = Date.now();
|
|
127
|
+
let spawn;
|
|
128
|
+
try {
|
|
129
|
+
spawn = resolveWorkerSpawn();
|
|
130
|
+
} catch (err) {
|
|
131
|
+
return unverified(err instanceof Error ? err.message : String(err), t0);
|
|
132
|
+
}
|
|
133
|
+
const result = await runSandboxed({
|
|
134
|
+
command: spawn.command,
|
|
135
|
+
args: spawn.args,
|
|
136
|
+
timeoutMs: CHECK_TIMEOUT_MS,
|
|
137
|
+
maxStdoutBytes: CHECK_STDOUT_CAP,
|
|
138
|
+
nodeHeapMb: CHECK_NODE_HEAP_MB,
|
|
139
|
+
stdin: JSON.stringify(input),
|
|
140
|
+
// NODE_ENV steers React's production vs development build — keep
|
|
141
|
+
// parity with the caller; everything else stays on the sandbox's
|
|
142
|
+
// default allowlist.
|
|
143
|
+
env: { NODE_ENV: process.env.NODE_ENV ?? "production" }
|
|
144
|
+
});
|
|
145
|
+
return mapSandboxResultToCheckResult(result, t0);
|
|
146
|
+
}
|
|
147
|
+
var CHECK_TIMEOUT_MS, CHECK_STDOUT_CAP, CHECK_NODE_HEAP_MB, WORKER_BASENAME;
|
|
148
|
+
var init_render_check_host = __esm({
|
|
149
|
+
"src/harness/check/runtime-render/render-check-host.ts"() {
|
|
150
|
+
CHECK_TIMEOUT_MS = 3e4;
|
|
151
|
+
CHECK_STDOUT_CAP = 2 * 1024 * 1024;
|
|
152
|
+
CHECK_NODE_HEAP_MB = 512;
|
|
153
|
+
WORKER_BASENAME = "render-check-worker";
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
var installed = false;
|
|
157
|
+
function installProductionActShim() {
|
|
158
|
+
if (installed) return;
|
|
159
|
+
installed = true;
|
|
160
|
+
const require_ = createRequire(import.meta.url);
|
|
161
|
+
let reactCjs;
|
|
162
|
+
try {
|
|
163
|
+
reactCjs = require_("react");
|
|
164
|
+
} catch {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (typeof reactCjs.act === "function") {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const testUtils = require_("react-dom/test-utils");
|
|
172
|
+
const reactDom = require_("react-dom");
|
|
173
|
+
testUtils.act = createFlushSyncAct(reactDom.flushSync);
|
|
174
|
+
} catch {
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function createFlushSyncAct(flushSync) {
|
|
178
|
+
return (callback) => {
|
|
179
|
+
let callbackResult;
|
|
180
|
+
flushSync(() => {
|
|
181
|
+
callbackResult = callback();
|
|
182
|
+
});
|
|
183
|
+
if (isThenable(callbackResult)) {
|
|
184
|
+
const pending = callbackResult;
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
pending.then(() => resolve(), reject);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return new Promise((resolve) => {
|
|
190
|
+
queueMicrotask(resolve);
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function isThenable(value) {
|
|
195
|
+
return typeof value === "object" && value !== null && typeof value.then === "function";
|
|
196
|
+
}
|
|
5
197
|
|
|
6
198
|
// src/harness/check/runtime-render/probe.ts
|
|
7
199
|
function makeInternals() {
|
|
@@ -163,8 +355,8 @@ ${cjsResult.code}
|
|
|
163
355
|
// src/harness/check/runtime-render/load-component.ts
|
|
164
356
|
async function loadComponent(input) {
|
|
165
357
|
const { sourceCode, moduleResolutions = {} } = input;
|
|
166
|
-
const { createRequire } = await import('module');
|
|
167
|
-
const require_ =
|
|
358
|
+
const { createRequire: createRequire3 } = await import('module');
|
|
359
|
+
const require_ = createRequire3(import.meta.url);
|
|
168
360
|
const sandboxRequire = (id) => {
|
|
169
361
|
if (id in moduleResolutions) return moduleResolutions[id];
|
|
170
362
|
if (id === "react/jsx-runtime" || id === "react/jsx-dev-runtime") {
|
|
@@ -482,6 +674,14 @@ function parentLink(node) {
|
|
|
482
674
|
|
|
483
675
|
// src/harness/check/runtime-render/render-check.ts
|
|
484
676
|
async function runRenderCheck(input) {
|
|
677
|
+
const g = hostGlobals();
|
|
678
|
+
if ("window" in g && "document" in g) {
|
|
679
|
+
return runRenderCheckInProcess(input);
|
|
680
|
+
}
|
|
681
|
+
const { runRenderCheckViaWorker: runRenderCheckViaWorker2 } = await Promise.resolve().then(() => (init_render_check_host(), render_check_host_exports));
|
|
682
|
+
return runRenderCheckViaWorker2(input);
|
|
683
|
+
}
|
|
684
|
+
async function runRenderCheckInProcess(input) {
|
|
485
685
|
const t0 = Date.now();
|
|
486
686
|
const issues = [];
|
|
487
687
|
const teardown = await setupHappyDom();
|
|
@@ -537,6 +737,7 @@ async function runRenderCheck(input) {
|
|
|
537
737
|
const probe = createProbe();
|
|
538
738
|
const wireConfig = createProbeWireConfig(probe);
|
|
539
739
|
uninstallSpy = probe.installPostMessageSpy();
|
|
740
|
+
installProductionActShim();
|
|
540
741
|
const { render, cleanup } = await import('@testing-library/react');
|
|
541
742
|
const userEventModule = await import('@testing-library/user-event');
|
|
542
743
|
const userEvent = userEventModule.default ?? userEventModule;
|
|
@@ -692,6 +893,50 @@ async function runRenderCheck(input) {
|
|
|
692
893
|
if (issue) issues.push(issue);
|
|
693
894
|
}
|
|
694
895
|
}
|
|
896
|
+
const declaredProps = input.contract?.propsSpec?.properties;
|
|
897
|
+
if (declaredProps) {
|
|
898
|
+
const optionalFilled = Object.entries(declaredProps).filter(
|
|
899
|
+
([name, entry]) => entry.required !== true && input.mockupProps[name] !== void 0
|
|
900
|
+
).map(([name]) => name);
|
|
901
|
+
if (optionalFilled.length > 0) {
|
|
902
|
+
const requiredOnlyProps = { ...input.mockupProps };
|
|
903
|
+
for (const name of optionalFilled) delete requiredOnlyProps[name];
|
|
904
|
+
const omitBoundaryRef = {};
|
|
905
|
+
class OmitErrorBoundary extends React.Component {
|
|
906
|
+
state = { caught: false };
|
|
907
|
+
static getDerivedStateFromError() {
|
|
908
|
+
return { caught: true };
|
|
909
|
+
}
|
|
910
|
+
componentDidCatch(error) {
|
|
911
|
+
omitBoundaryRef.error ??= error;
|
|
912
|
+
}
|
|
913
|
+
render() {
|
|
914
|
+
if (this.state.caught) return null;
|
|
915
|
+
return this.props.children;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
try {
|
|
919
|
+
render(
|
|
920
|
+
React.createElement(GguiWireProvider, {
|
|
921
|
+
config: wireConfig,
|
|
922
|
+
children: React.createElement(OmitErrorBoundary, {
|
|
923
|
+
children: React.createElement(Component, requiredOnlyProps)
|
|
924
|
+
})
|
|
925
|
+
})
|
|
926
|
+
);
|
|
927
|
+
} catch (e) {
|
|
928
|
+
omitBoundaryRef.error ??= e instanceof Error ? e : new Error(String(e));
|
|
929
|
+
}
|
|
930
|
+
if (omitBoundaryRef.error) {
|
|
931
|
+
issues.push({
|
|
932
|
+
check: "optional-props-omitted",
|
|
933
|
+
outcome: "failed",
|
|
934
|
+
subject: optionalFilled.join(", "),
|
|
935
|
+
reason: `Render crashed when the optional prop(s) ${optionalFilled.map((n) => `'${n}'`).join(", ")} were omitted: ${omitBoundaryRef.error.message}. The contract marks them optional (no \`required: true\`), so a legitimate render may leave them out.`
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
695
940
|
} finally {
|
|
696
941
|
cleanup();
|
|
697
942
|
}
|
|
@@ -1263,6 +1508,15 @@ function synthesizeFromSchema(schema, hint, warnings, depth) {
|
|
|
1263
1508
|
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
1264
1509
|
return synthesizeFromSchema(schema.anyOf[0], hint, warnings, depth + 1);
|
|
1265
1510
|
}
|
|
1511
|
+
if (Array.isArray(schema.type)) {
|
|
1512
|
+
const primary = schema.type.find((t) => t !== "null") ?? "null";
|
|
1513
|
+
return synthesizeFromSchema(
|
|
1514
|
+
{ ...schema, type: primary },
|
|
1515
|
+
hint,
|
|
1516
|
+
warnings,
|
|
1517
|
+
depth + 1
|
|
1518
|
+
);
|
|
1519
|
+
}
|
|
1266
1520
|
switch (schema.type) {
|
|
1267
1521
|
case "string": {
|
|
1268
1522
|
switch (schema.format) {
|
|
@@ -1418,6 +1672,16 @@ function toEvalIssue(issue) {
|
|
|
1418
1672
|
description: `Component crashed at runtime: ${issue.reason}`,
|
|
1419
1673
|
fix: classifyRenderCrashFix(issue.reason)
|
|
1420
1674
|
};
|
|
1675
|
+
case "optional-props-omitted":
|
|
1676
|
+
return {
|
|
1677
|
+
tier: 0,
|
|
1678
|
+
result,
|
|
1679
|
+
category: "crash",
|
|
1680
|
+
subcategory,
|
|
1681
|
+
severity: "critical",
|
|
1682
|
+
description: `Component crashed when optional props were omitted: ${issue.reason}`,
|
|
1683
|
+
fix: `Guard every optional prop before member access (props.${subject.split(",")[0]?.trim() ?? "x"}?.map(...), a default via destructuring, or an early empty-state return). Optional props (no \`required: true\` in the contract) may be absent at runtime even though the harness fills them.`
|
|
1684
|
+
};
|
|
1421
1685
|
case "action-wiring": {
|
|
1422
1686
|
const fix = issue.outcome === "unverified" ? `Source shows the action callback flowing into a non-native or custom-component prop. If wiring is intentional (e.g., Dropdown.onChange, drag-drop), this warn is informational \u2014 manual/browser verification is required to confirm. Otherwise wire to a native onClick={() => ${subject}(payload)} on <button> or design-system <Button>.` : `Wire ${subject}() to a native event prop. Common fix: <Button onClick={() => ${subject}(payload)}>Label</Button>. Source-AST analysis didn't find this wiring in your JSX.`;
|
|
1423
1687
|
return {
|