@ggui-ai/ui-gen 0.15.0 → 0.17.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 +24 -4
- package/dist/adapters/generation-dispatch.js +5188 -3920
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +33 -26
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +22 -13
- package/dist/advanced/index.js +4889 -3561
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +8 -12
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.d.ts +19 -1
- package/dist/boilerplate.js +671 -254
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +17 -3
- package/dist/check/index.js +34 -25
- package/dist/check/index.js.map +1 -1
- package/dist/compose.js +1 -1
- package/dist/compose.js.map +1 -1
- package/dist/create-ui-generator-C8Ki9Wz1.d.ts +189 -0
- package/dist/design-mode-DLufhrNp.d.ts +61 -0
- package/dist/{dispatch-BuZfTh9r.d.ts → dispatch-D2htzi9h.d.ts} +4 -1
- package/dist/evaluation/axis-checks/index.d.ts +4 -3
- package/dist/evaluation/axis-checks/index.js +300 -14
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +2 -1
- package/dist/evaluation/axis-checks/registry.js +253 -1
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +297 -5
- package/dist/evaluation/index.js +4515 -120
- package/dist/evaluation/index.js.map +1 -1
- package/dist/fragments/index.js +1 -1
- package/dist/fragments/index.js.map +1 -1
- package/dist/{generate-BTfvELgl.d.ts → generate-9IeNT4ZY.d.ts} +10 -1
- package/dist/harness/check/runtime-render/index.d.ts +3 -1
- package/dist/harness/check/runtime-render/index.js +8 -12
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/index.d.ts +3 -1
- package/dist/harness/index.js +913 -441
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/llm-trace-sink.d.ts +3 -3
- package/dist/harness/llm-trace-sink.js.map +1 -1
- package/dist/harness/prompts.d.ts +2 -1
- package/dist/harness/prompts.js +6 -3
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +17 -5
- package/dist/harness/result-types.js.map +1 -1
- package/dist/harness/runtime.d.ts +28 -3
- package/dist/harness/runtime.js +702 -274
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +39 -1
- package/dist/harness/types-public.js +913 -441
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +2 -1
- package/dist/index.d.ts +32 -6
- package/dist/index.js +4899 -3560
- package/dist/index.js.map +1 -1
- package/dist/patch.d.ts +3 -1
- package/dist/{prompts-BVhtH3M1.d.ts → prompts-YU9piOgs.d.ts} +21 -45
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/styling-profile-CxfvV48g.d.ts +35 -0
- package/dist/tools/get-primitives-ts.d.ts +1 -1
- package/dist/tools/get-primitives-ts.js +1 -1
- package/dist/tools/get-primitives-ts.js.map +1 -1
- package/dist/{types-public-CkNWEov9.d.ts → types-public-DVSssdc-.d.ts} +85 -5
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +4 -4
- package/dist/validation/index.js.map +1 -1
- package/dist/workflows.d.ts +3 -1
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +62 -4
- package/src/adapters/provider-router.ts +7 -6
- package/src/adapters/tools.ts +5 -5
- package/src/advanced/generator.ts +11 -10
- package/src/advanced/index.ts +5 -4
- package/src/boilerplate/free-design-prompt.ts +336 -0
- package/src/boilerplate/generate.ts +10 -1
- package/src/boilerplate/hard-sections.ts +257 -0
- package/src/boilerplate/render.ts +32 -8
- package/src/boilerplate/styling-profile.ts +166 -0
- package/src/boilerplate/system-prompt.ts +101 -237
- package/src/boilerplate/templates/base-free.tsx.tmpl +24 -0
- package/src/check/run-tier0.ts +82 -20
- package/src/coding-agent/tools.ts +8 -2
- package/src/contract-context.ts +43 -14
- package/src/create-harness.ts +15 -0
- package/src/create-ui-generator.ts +112 -27
- package/src/design-mode.ts +192 -0
- package/src/design-system-docs.ts +24 -17
- package/src/effort.ts +48 -0
- package/src/evaluation/axis-checks/checks/grid.ts +148 -0
- package/src/evaluation/axis-checks/checks/universal.ts +244 -0
- package/src/evaluation/axis-checks/dispatch.ts +82 -13
- package/src/evaluation/axis-checks/extras.ts +5 -0
- package/src/evaluation/axis-checks/helpers.ts +12 -0
- package/src/evaluation/axis-checks/registry.ts +2 -0
- package/src/evaluation/cost-tracker.ts +2 -3
- package/src/evaluation/design-identity.ts +96 -0
- package/src/evaluation/index.ts +63 -2
- package/src/evaluation/llm-evaluator.ts +89 -21
- package/src/evaluation/theme-css.ts +14 -0
- package/src/evaluation/types-public.ts +137 -8
- package/src/evaluation/visual-evaluator.ts +759 -54
- package/src/fragments/render.ts +1 -1
- package/src/harness/check/runtime-render/prepare-mockup.ts +18 -11
- package/src/harness/coding/dupe-break.ts +0 -0
- package/src/harness/coding/init-session.ts +2 -0
- package/src/harness/coding/run-coding-turn.ts +9 -14
- package/src/harness/coding/run-eval-round.ts +46 -5
- package/src/harness/llm-trace-sink.ts +3 -3
- package/src/harness/result-types.ts +15 -3
- package/src/harness/runtime.ts +28 -0
- package/src/harness/types-public.ts +38 -0
- package/src/index.ts +28 -0
- package/src/internal/extract-call-signatures.ts +2 -2
- package/src/pin-fixtures.ts +98 -0
- package/src/providers/bedrock.ts +4 -4
- package/src/run-check.ts +11 -10
- package/src/tools/get-primitives-ts.ts +1 -1
- package/src/tools.ts +2 -1
- package/src/validation/allowed-imports.ts +8 -2
- package/src/validation/primitives.ts +1 -1
- package/dist/create-ui-generator-D6TKuv9V.d.ts +0 -106
package/dist/check/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataContract, JsonObject, PropsSpec, ActionSpec, StreamSpec } from '@ggui-ai/protocol';
|
|
2
|
-
import {
|
|
2
|
+
import { D as DesignMode } from '../design-mode-DLufhrNp.js';
|
|
3
|
+
import { b as EvalResult, E as EvalIssue } from '../types-public-DVSssdc-.js';
|
|
3
4
|
export { j as jsonSchemaTypeToTs } from '../json-schema-ts-CbB78b3n.js';
|
|
4
5
|
import '../axes-CzLEMDeB.js';
|
|
5
6
|
import '../types-BOvHNG7K.js';
|
|
@@ -223,10 +224,23 @@ declare function runTier0Checks(sourceCode: string, contract?: DataContract, bui
|
|
|
223
224
|
* resolves against the real declaration (strict option/return
|
|
224
225
|
* narrowing). Standard-library-only callers omit it.
|
|
225
226
|
*/
|
|
226
|
-
gadgetTypes?: Readonly<Record<string, string
|
|
227
|
+
gadgetTypes?: Readonly<Record<string, string>>,
|
|
228
|
+
/**
|
|
229
|
+
* Which triad produced the source. `constrained` (default) runs every
|
|
230
|
+
* leg exactly as before. `free` gates OFF the design-VOCABULARY legs —
|
|
231
|
+
* `raw-spacing`, `raw-pixels`, `numeric-spacing-prop`, the
|
|
232
|
+
* `stack-row-*` prop checks and `clickable-wrapper` — because raw
|
|
233
|
+
* elements, inline CSS and literal spacing are first-class there. It
|
|
234
|
+
* KEEPS every contract leg: `hex-color`, `hardcoded-color-fn`,
|
|
235
|
+
* `named-color`, `token-fallback`, `off-manifest-token`,
|
|
236
|
+
* `asset-color-pair` (color stays on the closed token manifest),
|
|
237
|
+
* imports, security, Props / default-export, wire_* and gadget checks,
|
|
238
|
+
* and the double-wired-action detector (retargeted at raw elements).
|
|
239
|
+
*/
|
|
240
|
+
designMode?: DesignMode): Promise<EvalIssue[]>;
|
|
227
241
|
/**
|
|
228
242
|
* Run tier 0 checks and return a full EvalResult with pass list.
|
|
229
243
|
*/
|
|
230
|
-
declare function runTier0(sourceCode: string, contract?: DataContract): Promise<EvalResult>;
|
|
244
|
+
declare function runTier0(sourceCode: string, contract?: DataContract, designMode?: DesignMode): Promise<EvalResult>;
|
|
231
245
|
|
|
232
246
|
export { type ContractIssue, type ReactLintDiagnostic, type TypeCheckDiagnostic, type TypeCheckResult, type WireCallSite, type WireImportReport, type WireImportSite, type WireKind, type WirePreservationReport, checkWireImports, checkWirePreservation, collectExpectedWires, extractPropsInterface, extractWireCallSites, extractWireImports, inferPropsSpecFromSampleData, lintReactHooks, propsSpecToTypeScript, runTier0, runTier0Checks, typecheck, validateActionSpecConformance, validateAllContracts, validatePropsAgainstSchema, validateStreamSpecConformance };
|
package/dist/check/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import ts4 from 'typescript';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
|
+
import { FORBIDDEN_IMPORT_SPECIFIERS } from '@ggui-ai/design/rendering';
|
|
5
6
|
import { Linter } from 'eslint';
|
|
6
7
|
import { listContractGadgets, HOOK_NAME_RE } from '@ggui-ai/protocol';
|
|
7
8
|
import { consumedTokenManifest } from '@ggui-ai/design/themes';
|
|
@@ -395,8 +396,6 @@ function inferJsonSchema(value) {
|
|
|
395
396
|
}
|
|
396
397
|
return { type: "string" };
|
|
397
398
|
}
|
|
398
|
-
|
|
399
|
-
// src/validation/allowed-imports.ts
|
|
400
399
|
var ALLOWED_IMPORT_BASES = [
|
|
401
400
|
"react",
|
|
402
401
|
"react-dom",
|
|
@@ -405,13 +404,14 @@ var ALLOWED_IMPORT_BASES = [
|
|
|
405
404
|
"@ggui-ai/gadgets"
|
|
406
405
|
];
|
|
407
406
|
function isAllowedImport(specifier, gadgetPackages) {
|
|
407
|
+
if (FORBIDDEN_IMPORT_SPECIFIERS.includes(specifier)) return false;
|
|
408
408
|
for (const base of ALLOWED_IMPORT_BASES) {
|
|
409
409
|
if (specifier === base || specifier.startsWith(`${base}/`)) return true;
|
|
410
410
|
}
|
|
411
411
|
return gadgetPackages?.has(specifier) ?? false;
|
|
412
412
|
}
|
|
413
413
|
function describeAllowedImports() {
|
|
414
|
-
return "react, @ggui-ai/design, @ggui-ai/wire, @ggui-ai/gadgets, or a gadget package declared on the contract";
|
|
414
|
+
return "react, @ggui-ai/design, @ggui-ai/wire, @ggui-ai/gadgets, or a gadget package declared on the contract \u2014 never @ggui-ai/wire/internal (the writer side of the wire; components read useRender().isConnected)";
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
// src/check/type-checker.ts
|
|
@@ -877,6 +877,9 @@ function generateReactFix(ruleId, message) {
|
|
|
877
877
|
return "Fix the React violation.";
|
|
878
878
|
}
|
|
879
879
|
|
|
880
|
+
// src/design-mode.ts
|
|
881
|
+
var DEFAULT_DESIGN_MODE = "constrained";
|
|
882
|
+
|
|
880
883
|
// src/evaluation/types-public.ts
|
|
881
884
|
function priorityForIssue(category) {
|
|
882
885
|
if (category === "interactivity" || category === "accessibility" || category === "layout" || category === "loading" || category === "visual") {
|
|
@@ -904,10 +907,11 @@ function stripComments(code) {
|
|
|
904
907
|
}
|
|
905
908
|
return out;
|
|
906
909
|
}
|
|
907
|
-
function detectCertainDoubleWiredActions(sourceCode, actionBindings) {
|
|
910
|
+
function detectCertainDoubleWiredActions(sourceCode, actionBindings, designMode = DEFAULT_DESIGN_MODE) {
|
|
908
911
|
const issues = [];
|
|
909
912
|
if (actionBindings.length === 0) return issues;
|
|
910
913
|
const bindings = new Set(actionBindings);
|
|
914
|
+
const anyElementIsInteractive = designMode === "free";
|
|
911
915
|
const TRAIT_HOST_TAGS = /* @__PURE__ */ new Set(["Card", "Box", "Stack", "Row"]);
|
|
912
916
|
const TRAITS_THAT_FIRE = /* @__PURE__ */ new Set(["Clickable", "Pressable"]);
|
|
913
917
|
const INTERACTIVE_DESCENDANTS = /* @__PURE__ */ new Set([
|
|
@@ -943,7 +947,9 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings) {
|
|
|
943
947
|
}
|
|
944
948
|
function isTraitHostWithFiringAs(node) {
|
|
945
949
|
const tag = tagNameText(node);
|
|
946
|
-
if (tag === void 0
|
|
950
|
+
if (tag === void 0) return false;
|
|
951
|
+
if (anyElementIsInteractive) return true;
|
|
952
|
+
if (!TRAIT_HOST_TAGS.has(tag)) return false;
|
|
947
953
|
const asExpr = attrExpression(node, "as");
|
|
948
954
|
return asExpr !== void 0 && ts4.isIdentifier(asExpr) && TRAITS_THAT_FIRE.has(asExpr.text);
|
|
949
955
|
}
|
|
@@ -972,7 +978,7 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings) {
|
|
|
972
978
|
const opening = ts4.isJsxElement(node) ? node.openingElement : ts4.isJsxSelfClosingElement(node) ? node : void 0;
|
|
973
979
|
if (opening !== void 0) {
|
|
974
980
|
const tag = tagNameText(opening);
|
|
975
|
-
if (tag !== void 0 && INTERACTIVE_DESCENDANTS.has(tag)) {
|
|
981
|
+
if (tag !== void 0 && (anyElementIsInteractive || INTERACTIVE_DESCENDANTS.has(tag))) {
|
|
976
982
|
for (const attrName of HANDLER_ATTRS) {
|
|
977
983
|
const callee = extractCalleeName(attrExpression(opening, attrName));
|
|
978
984
|
if (callee === expectedBinding) {
|
|
@@ -998,14 +1004,15 @@ function detectCertainDoubleWiredActions(sourceCode, actionBindings) {
|
|
|
998
1004
|
if (match !== void 0) {
|
|
999
1005
|
const outerTag = tagNameText(node.openingElement);
|
|
1000
1006
|
const outerLine = sf.getLineAndCharacterOfPosition(node.openingElement.getStart()).line + 1;
|
|
1007
|
+
const outerLabel = anyElementIsInteractive ? `<${outerTag}>` : `<${outerTag} as={...}>`;
|
|
1001
1008
|
issues.push({
|
|
1002
1009
|
tier: 0,
|
|
1003
1010
|
result: "fail",
|
|
1004
1011
|
category: "interactivity",
|
|
1005
1012
|
subcategory: "double-wired-action:certain",
|
|
1006
1013
|
severity: "critical",
|
|
1007
|
-
description: `Nested-interactive double-wire: outer
|
|
1008
|
-
fix: `Pick ONE surface for the gesture: either drop \`as={...}\` + onClick on the outer <${outerTag}> and let the inner <${match.tag}> own the gesture, OR remove the inner <${match.tag}> and let the outer <${outerTag} as={...}> own it. Don't wire both to the same useAction binding.`,
|
|
1014
|
+
description: `Nested-interactive double-wire: outer ${outerLabel} at line ${outerLine} and inner <${match.tag}> at line ${match.line} both dispatch the same useAction binding '${outerCallee}'. One user click on the inner control fires its handler AND bubbles to the outer handler \u2014 '${outerCallee}' dispatches TWICE, the action runs back-to-back, and a toggle-style action silently reverts the user's change.`,
|
|
1015
|
+
fix: anyElementIsInteractive ? `Pick ONE gesture surface for '${outerCallee}': either remove the onClick from the outer <${outerTag}> and let the inner <${match.tag}> own the gesture, OR drop the inner <${match.tag}>'s handler and let the outer <${outerTag}> own it. Never wire both to the same useAction binding \u2014 stopPropagation is not a fix.` : `Pick ONE surface for the gesture: either drop \`as={...}\` + onClick on the outer <${outerTag}> and let the inner <${match.tag}> own the gesture, OR remove the inner <${match.tag}> and let the outer <${outerTag} as={...}> own it. Don't wire both to the same useAction binding.`,
|
|
1009
1016
|
line: outerLine
|
|
1010
1017
|
});
|
|
1011
1018
|
}
|
|
@@ -1042,9 +1049,10 @@ function isGadgetExportImported(source, pkg, exportName) {
|
|
|
1042
1049
|
}
|
|
1043
1050
|
return false;
|
|
1044
1051
|
}
|
|
1045
|
-
async function runTier0Checks(sourceCode, contract, buildErrors, gadgetTypes) {
|
|
1052
|
+
async function runTier0Checks(sourceCode, contract, buildErrors, gadgetTypes, designMode = DEFAULT_DESIGN_MODE) {
|
|
1046
1053
|
const issues = [];
|
|
1047
1054
|
const lines = sourceCode.split("\n");
|
|
1055
|
+
const enforceDesignVocabulary = designMode === "constrained";
|
|
1048
1056
|
if (buildErrors && buildErrors.length > 0) {
|
|
1049
1057
|
const errorDetail = buildErrors.map((e) => {
|
|
1050
1058
|
const match = e.match(/<stdin>:(\d+):\d+: ERROR: (.+)/);
|
|
@@ -1054,6 +1062,7 @@ async function runTier0Checks(sourceCode, contract, buildErrors, gadgetTypes) {
|
|
|
1054
1062
|
tier: 0,
|
|
1055
1063
|
result: "fail",
|
|
1056
1064
|
category: "compile",
|
|
1065
|
+
subcategory: "build-error",
|
|
1057
1066
|
severity: "critical",
|
|
1058
1067
|
description: `Component failed to compile:
|
|
1059
1068
|
${errorDetail}`,
|
|
@@ -1126,6 +1135,7 @@ ${errorDetail}`,
|
|
|
1126
1135
|
tier: 0,
|
|
1127
1136
|
result: "fail",
|
|
1128
1137
|
category: "imports",
|
|
1138
|
+
subcategory: "disallowed-package",
|
|
1129
1139
|
severity: "critical",
|
|
1130
1140
|
description: `Import from "${pkg}" is not allowed`,
|
|
1131
1141
|
fix: `Only import from: ${describeAllowedImports()}`,
|
|
@@ -1173,7 +1183,7 @@ ${errorDetail}`,
|
|
|
1173
1183
|
subcategory: "hardcoded-color-fn",
|
|
1174
1184
|
severity: "critical",
|
|
1175
1185
|
description: `Hardcoded ${colorFnMatch[1]}() breaks theme switching \u2014 use design tokens.`,
|
|
1176
|
-
fix: "Replace with a primitive variant OR a
|
|
1186
|
+
fix: "Replace with a primitive variant OR a layering role: var(--ggui-color-container), var(--ggui-color-onContainer), var(--ggui-color-outline), etc.",
|
|
1177
1187
|
line: lineNum
|
|
1178
1188
|
});
|
|
1179
1189
|
}
|
|
@@ -1190,13 +1200,13 @@ ${errorDetail}`,
|
|
|
1190
1200
|
subcategory: "off-manifest-token",
|
|
1191
1201
|
severity: "critical",
|
|
1192
1202
|
description: `Token "${name}" is not in the design system's token manifest \u2014 no theme defines it, so it renders unset.`,
|
|
1193
|
-
fix: `Choose a token that exists: the closed vocabulary is the manifest the design-system docs list (colors: var(--ggui-color-primary-500), var(--ggui-color-
|
|
1203
|
+
fix: `Choose a token that exists: the closed vocabulary is the manifest the design-system docs list (colors: var(--ggui-color-primary-500), var(--ggui-color-onContainer), \u2026; spacing: var(--ggui-spacing-4); type: var(--ggui-font-size-sm)). Never invent a name and never add a literal fallback.`,
|
|
1194
1204
|
line: lineNum
|
|
1195
1205
|
});
|
|
1196
1206
|
}
|
|
1197
|
-
const rawSpacingMatch = line.match(
|
|
1207
|
+
const rawSpacingMatch = enforceDesignVocabulary ? line.match(
|
|
1198
1208
|
/\b(gap|padding|paddingX|paddingY|margin|radius)\s*=\s*["'][\d.]+(?:px|rem|em)["']/
|
|
1199
|
-
);
|
|
1209
|
+
) : null;
|
|
1200
1210
|
if (rawSpacingMatch) {
|
|
1201
1211
|
issues.push({
|
|
1202
1212
|
tier: 0,
|
|
@@ -1290,12 +1300,12 @@ ${errorDetail}`,
|
|
|
1290
1300
|
subcategory: "named-color",
|
|
1291
1301
|
severity: "critical",
|
|
1292
1302
|
description: `Hardcoded CSS named color "${named}" breaks theme switching \u2014 use design tokens.`,
|
|
1293
|
-
fix: `Replace with a typed slot (Text tone="muted" / Box surface="accent" / Badge variant="success") OR a semantic token: var(--ggui-color-
|
|
1303
|
+
fix: `Replace with a typed slot (Text tone="muted" / Box surface="accent" / Badge variant="success") OR a semantic token: var(--ggui-color-onSunken), var(--ggui-color-primary-500), etc. The keyword "${named}" maps to a fixed RGB; the operator's theme has no effect on it.`,
|
|
1294
1304
|
line: lineNum
|
|
1295
1305
|
});
|
|
1296
1306
|
}
|
|
1297
1307
|
}
|
|
1298
|
-
const pxMatch = line.match(/(?:padding|margin|gap|borderRadius)\s*:\s*['"]?\d+px/);
|
|
1308
|
+
const pxMatch = enforceDesignVocabulary ? line.match(/(?:padding|margin|gap|borderRadius)\s*:\s*['"]?\d+px/) : null;
|
|
1299
1309
|
if (pxMatch && !line.includes("var(--ggui-")) {
|
|
1300
1310
|
issues.push({
|
|
1301
1311
|
tier: 0,
|
|
@@ -1303,11 +1313,11 @@ ${errorDetail}`,
|
|
|
1303
1313
|
category: "tokens",
|
|
1304
1314
|
subcategory: "raw-pixels",
|
|
1305
1315
|
description: "Raw pixel value in spacing \u2014 must use design tokens",
|
|
1306
|
-
fix:
|
|
1316
|
+
fix: 'Replace with a bare var(--ggui-spacing-*) token \u2014 no literal fallback (the runtime injects every token) \u2014 or the scale name on the primitive prop (gap="md", padding="lg")',
|
|
1307
1317
|
line: lineNum
|
|
1308
1318
|
});
|
|
1309
1319
|
}
|
|
1310
|
-
const numericSpacingMatch = line.match(/\b(padding|paddingX|paddingY|gap|margin)\s*=\s*\{?\s*(\d+)\s*\}?/);
|
|
1320
|
+
const numericSpacingMatch = enforceDesignVocabulary ? line.match(/\b(padding|paddingX|paddingY|gap|margin)\s*=\s*\{?\s*(\d+)\s*\}?/) : null;
|
|
1311
1321
|
if (numericSpacingMatch && !line.includes("var(--ggui-")) {
|
|
1312
1322
|
issues.push({
|
|
1313
1323
|
tier: 0,
|
|
@@ -1321,7 +1331,7 @@ ${errorDetail}`,
|
|
|
1321
1331
|
}
|
|
1322
1332
|
}
|
|
1323
1333
|
const clickablePattern = /<(?:Card|Box|Stack|Row)\s[^>]*onClick/;
|
|
1324
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1334
|
+
for (let i = 0; enforceDesignVocabulary && i < lines.length; i++) {
|
|
1325
1335
|
if (clickablePattern.test(lines[i]) && !lines[i].includes("as={Clickable}") && !lines[i].includes("as={Pressable}")) {
|
|
1326
1336
|
issues.push({
|
|
1327
1337
|
tier: 0,
|
|
@@ -1334,9 +1344,8 @@ ${errorDetail}`,
|
|
|
1334
1344
|
});
|
|
1335
1345
|
}
|
|
1336
1346
|
}
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1339
|
-
].map((m) => m[1]).filter((name) => typeof name === "string");
|
|
1347
|
+
const bindingRe = designMode === "free" ? /(?:const|let)\s+(\w+)\s*=\s*useAction\s*(?:<[^>]*>)?\s*\(/g : /(?:const|let)\s+(\w+)\s*=\s*useAction\s*\(/g;
|
|
1348
|
+
const actionBindings = [...sourceCode.matchAll(bindingRe)].map((m) => m[1]).filter((name) => typeof name === "string");
|
|
1340
1349
|
for (const binding of actionBindings) {
|
|
1341
1350
|
const callSites = (sourceCode.match(new RegExp(`\\b${binding}\\s*\\(`, "g")) ?? []).length;
|
|
1342
1351
|
if (callSites >= 2) {
|
|
@@ -1352,7 +1361,7 @@ ${errorDetail}`,
|
|
|
1352
1361
|
}
|
|
1353
1362
|
if (actionBindings.length > 0) {
|
|
1354
1363
|
issues.push(
|
|
1355
|
-
...detectCertainDoubleWiredActions(sourceCode, actionBindings)
|
|
1364
|
+
...detectCertainDoubleWiredActions(sourceCode, actionBindings, designMode)
|
|
1356
1365
|
);
|
|
1357
1366
|
}
|
|
1358
1367
|
if (/\buseRender\s*\(/.test(sourceCode)) {
|
|
@@ -1397,7 +1406,7 @@ ${errorDetail}`,
|
|
|
1397
1406
|
const ALIGN_VALUES = /* @__PURE__ */ new Set(["start", "center", "end", "stretch"]);
|
|
1398
1407
|
const JUSTIFY_VALUES = /* @__PURE__ */ new Set(["start", "center", "end", "between", "around", "evenly"]);
|
|
1399
1408
|
const tagRegex = /<(Stack|Row)\b([^>]*)>/gs;
|
|
1400
|
-
for (const tag of sourceCode.matchAll(tagRegex)) {
|
|
1409
|
+
for (const tag of enforceDesignVocabulary ? sourceCode.matchAll(tagRegex) : []) {
|
|
1401
1410
|
const tagName = tag[1];
|
|
1402
1411
|
const attrs = tag[2];
|
|
1403
1412
|
const tagLine = sourceCode.slice(0, tag.index).split("\n").length;
|
|
@@ -1683,8 +1692,8 @@ Import \`${exportName}\` ONLY from '${use.package}' \u2014 that is the package i
|
|
|
1683
1692
|
}
|
|
1684
1693
|
return issues;
|
|
1685
1694
|
}
|
|
1686
|
-
async function runTier0(sourceCode, contract) {
|
|
1687
|
-
const issues = await runTier0Checks(sourceCode, contract);
|
|
1695
|
+
async function runTier0(sourceCode, contract, designMode = DEFAULT_DESIGN_MODE) {
|
|
1696
|
+
const issues = await runTier0Checks(sourceCode, contract, void 0, void 0, designMode);
|
|
1688
1697
|
const pass = [];
|
|
1689
1698
|
const failedCategories = new Set(issues.map((i) => i.category));
|
|
1690
1699
|
const tier0Categories = ["compile", "security", "contract", "types", "imports", "tokens"];
|