@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.
Files changed (123) hide show
  1. package/dist/adapters/generation-dispatch.d.ts +24 -4
  2. package/dist/adapters/generation-dispatch.js +5188 -3920
  3. package/dist/adapters/generation-dispatch.js.map +1 -1
  4. package/dist/adapters/index.js +33 -26
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/advanced/index.d.ts +22 -13
  7. package/dist/advanced/index.js +4889 -3561
  8. package/dist/advanced/index.js.map +1 -1
  9. package/dist/blueprint-validator.js +8 -12
  10. package/dist/blueprint-validator.js.map +1 -1
  11. package/dist/boilerplate.d.ts +19 -1
  12. package/dist/boilerplate.js +671 -254
  13. package/dist/boilerplate.js.map +1 -1
  14. package/dist/check/index.d.ts +17 -3
  15. package/dist/check/index.js +34 -25
  16. package/dist/check/index.js.map +1 -1
  17. package/dist/compose.js +1 -1
  18. package/dist/compose.js.map +1 -1
  19. package/dist/create-ui-generator-C8Ki9Wz1.d.ts +189 -0
  20. package/dist/design-mode-DLufhrNp.d.ts +61 -0
  21. package/dist/{dispatch-BuZfTh9r.d.ts → dispatch-D2htzi9h.d.ts} +4 -1
  22. package/dist/evaluation/axis-checks/index.d.ts +4 -3
  23. package/dist/evaluation/axis-checks/index.js +300 -14
  24. package/dist/evaluation/axis-checks/index.js.map +1 -1
  25. package/dist/evaluation/axis-checks/registry.d.ts +2 -1
  26. package/dist/evaluation/axis-checks/registry.js +253 -1
  27. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  28. package/dist/evaluation/index.d.ts +297 -5
  29. package/dist/evaluation/index.js +4515 -120
  30. package/dist/evaluation/index.js.map +1 -1
  31. package/dist/fragments/index.js +1 -1
  32. package/dist/fragments/index.js.map +1 -1
  33. package/dist/{generate-BTfvELgl.d.ts → generate-9IeNT4ZY.d.ts} +10 -1
  34. package/dist/harness/check/runtime-render/index.d.ts +3 -1
  35. package/dist/harness/check/runtime-render/index.js +8 -12
  36. package/dist/harness/check/runtime-render/index.js.map +1 -1
  37. package/dist/harness/index.d.ts +3 -1
  38. package/dist/harness/index.js +913 -441
  39. package/dist/harness/index.js.map +1 -1
  40. package/dist/harness/llm-trace-sink.d.ts +3 -3
  41. package/dist/harness/llm-trace-sink.js.map +1 -1
  42. package/dist/harness/prompts.d.ts +2 -1
  43. package/dist/harness/prompts.js +6 -3
  44. package/dist/harness/prompts.js.map +1 -1
  45. package/dist/harness/result-types.d.ts +17 -5
  46. package/dist/harness/result-types.js.map +1 -1
  47. package/dist/harness/runtime.d.ts +28 -3
  48. package/dist/harness/runtime.js +702 -274
  49. package/dist/harness/runtime.js.map +1 -1
  50. package/dist/harness/types-public.d.ts +39 -1
  51. package/dist/harness/types-public.js +913 -441
  52. package/dist/harness/types-public.js.map +1 -1
  53. package/dist/harness/validator-trace-sink.d.ts +2 -1
  54. package/dist/index.d.ts +32 -6
  55. package/dist/index.js +4899 -3560
  56. package/dist/index.js.map +1 -1
  57. package/dist/patch.d.ts +3 -1
  58. package/dist/{prompts-BVhtH3M1.d.ts → prompts-YU9piOgs.d.ts} +21 -45
  59. package/dist/providers/index.d.ts +2 -2
  60. package/dist/providers/index.js.map +1 -1
  61. package/dist/styling-profile-CxfvV48g.d.ts +35 -0
  62. package/dist/tools/get-primitives-ts.d.ts +1 -1
  63. package/dist/tools/get-primitives-ts.js +1 -1
  64. package/dist/tools/get-primitives-ts.js.map +1 -1
  65. package/dist/{types-public-CkNWEov9.d.ts → types-public-DVSssdc-.d.ts} +85 -5
  66. package/dist/validation/index.d.ts +1 -1
  67. package/dist/validation/index.js +4 -4
  68. package/dist/validation/index.js.map +1 -1
  69. package/dist/workflows.d.ts +3 -1
  70. package/package.json +12 -12
  71. package/src/adapters/generation-dispatch.ts +62 -4
  72. package/src/adapters/provider-router.ts +7 -6
  73. package/src/adapters/tools.ts +5 -5
  74. package/src/advanced/generator.ts +11 -10
  75. package/src/advanced/index.ts +5 -4
  76. package/src/boilerplate/free-design-prompt.ts +336 -0
  77. package/src/boilerplate/generate.ts +10 -1
  78. package/src/boilerplate/hard-sections.ts +257 -0
  79. package/src/boilerplate/render.ts +32 -8
  80. package/src/boilerplate/styling-profile.ts +166 -0
  81. package/src/boilerplate/system-prompt.ts +101 -237
  82. package/src/boilerplate/templates/base-free.tsx.tmpl +24 -0
  83. package/src/check/run-tier0.ts +82 -20
  84. package/src/coding-agent/tools.ts +8 -2
  85. package/src/contract-context.ts +43 -14
  86. package/src/create-harness.ts +15 -0
  87. package/src/create-ui-generator.ts +112 -27
  88. package/src/design-mode.ts +192 -0
  89. package/src/design-system-docs.ts +24 -17
  90. package/src/effort.ts +48 -0
  91. package/src/evaluation/axis-checks/checks/grid.ts +148 -0
  92. package/src/evaluation/axis-checks/checks/universal.ts +244 -0
  93. package/src/evaluation/axis-checks/dispatch.ts +82 -13
  94. package/src/evaluation/axis-checks/extras.ts +5 -0
  95. package/src/evaluation/axis-checks/helpers.ts +12 -0
  96. package/src/evaluation/axis-checks/registry.ts +2 -0
  97. package/src/evaluation/cost-tracker.ts +2 -3
  98. package/src/evaluation/design-identity.ts +96 -0
  99. package/src/evaluation/index.ts +63 -2
  100. package/src/evaluation/llm-evaluator.ts +89 -21
  101. package/src/evaluation/theme-css.ts +14 -0
  102. package/src/evaluation/types-public.ts +137 -8
  103. package/src/evaluation/visual-evaluator.ts +759 -54
  104. package/src/fragments/render.ts +1 -1
  105. package/src/harness/check/runtime-render/prepare-mockup.ts +18 -11
  106. package/src/harness/coding/dupe-break.ts +0 -0
  107. package/src/harness/coding/init-session.ts +2 -0
  108. package/src/harness/coding/run-coding-turn.ts +9 -14
  109. package/src/harness/coding/run-eval-round.ts +46 -5
  110. package/src/harness/llm-trace-sink.ts +3 -3
  111. package/src/harness/result-types.ts +15 -3
  112. package/src/harness/runtime.ts +28 -0
  113. package/src/harness/types-public.ts +38 -0
  114. package/src/index.ts +28 -0
  115. package/src/internal/extract-call-signatures.ts +2 -2
  116. package/src/pin-fixtures.ts +98 -0
  117. package/src/providers/bedrock.ts +4 -4
  118. package/src/run-check.ts +11 -10
  119. package/src/tools/get-primitives-ts.ts +1 -1
  120. package/src/tools.ts +2 -1
  121. package/src/validation/allowed-imports.ts +8 -2
  122. package/src/validation/primitives.ts +1 -1
  123. package/dist/create-ui-generator-D6TKuv9V.d.ts +0 -106
@@ -1,5 +1,6 @@
1
1
  import { DataContract, JsonObject, PropsSpec, ActionSpec, StreamSpec } from '@ggui-ai/protocol';
2
- import { b as EvalResult, E as EvalIssue } from '../types-public-CkNWEov9.js';
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>>): Promise<EvalIssue[]>;
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 };
@@ -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 || !TRAIT_HOST_TAGS.has(tag)) return false;
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 <${outerTag} as={...}> 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.`,
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 semantic token: var(--ggui-color-surface), var(--ggui-color-onSurface), var(--ggui-color-outline), etc.",
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-onSurface), \u2026; spacing: var(--ggui-spacing-4); type: var(--ggui-font-size-sm)). Never invent a name and never add a literal fallback.`,
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-onSurfaceVariant), var(--ggui-color-primary-500), etc. The keyword "${named}" maps to a fixed RGB; the operator's theme has no effect on it.`,
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: "Replace with var(--ggui-spacing-*, fallback)",
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 actionBindings = [
1338
- ...sourceCode.matchAll(/(?:const|let)\s+(\w+)\s*=\s*useAction\s*\(/g)
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"];