stimeo-ui 0.4.0 → 0.5.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/CHANGELOG.md +68 -0
  2. package/dist/controllers/announcer_controller.js.map +1 -1
  3. package/dist/controllers/aspect_ratio_controller.d.ts +2 -2
  4. package/dist/controllers/aspect_ratio_controller.js +1 -1
  5. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  6. package/dist/controllers/breadcrumb_controller.js +5 -1
  7. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  8. package/dist/controllers/carousel_controller.js +5 -1
  9. package/dist/controllers/carousel_controller.js.map +1 -1
  10. package/dist/controllers/clipboard_controller.js +8 -3
  11. package/dist/controllers/clipboard_controller.js.map +1 -1
  12. package/dist/controllers/collapsible_controller.d.ts +1 -1
  13. package/dist/controllers/collapsible_controller.js +4 -1
  14. package/dist/controllers/collapsible_controller.js.map +1 -1
  15. package/dist/controllers/color_picker_controller.d.ts +2 -2
  16. package/dist/controllers/color_picker_controller.js +6 -2
  17. package/dist/controllers/color_picker_controller.js.map +1 -1
  18. package/dist/controllers/combobox_controller.js.map +1 -1
  19. package/dist/controllers/command_palette_controller.js.map +1 -1
  20. package/dist/controllers/context_menu_controller.d.ts +1 -1
  21. package/dist/controllers/context_menu_controller.js +2 -2
  22. package/dist/controllers/context_menu_controller.js.map +1 -1
  23. package/dist/controllers/countdown_controller.d.ts +5 -3
  24. package/dist/controllers/countdown_controller.js +5 -1
  25. package/dist/controllers/countdown_controller.js.map +1 -1
  26. package/dist/controllers/date_range_picker_controller.js +5 -1
  27. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  28. package/dist/controllers/direct_upload_controller.d.ts +1 -1
  29. package/dist/controllers/direct_upload_controller.js +3 -3
  30. package/dist/controllers/direct_upload_controller.js.map +1 -1
  31. package/dist/controllers/empty_state_controller.d.ts +27 -7
  32. package/dist/controllers/empty_state_controller.js +107 -16
  33. package/dist/controllers/empty_state_controller.js.map +1 -1
  34. package/dist/controllers/flash_controller.d.ts +25 -5
  35. package/dist/controllers/flash_controller.js +161 -21
  36. package/dist/controllers/flash_controller.js.map +1 -1
  37. package/dist/controllers/form_validation_controller.js +8 -2
  38. package/dist/controllers/form_validation_controller.js.map +1 -1
  39. package/dist/controllers/frame_loading_controller.d.ts +19 -2
  40. package/dist/controllers/frame_loading_controller.js +94 -22
  41. package/dist/controllers/frame_loading_controller.js.map +1 -1
  42. package/dist/controllers/highlight_controller.d.ts +8 -4
  43. package/dist/controllers/highlight_controller.js +38 -1
  44. package/dist/controllers/highlight_controller.js.map +1 -1
  45. package/dist/controllers/idle_controller.d.ts +2 -1
  46. package/dist/controllers/idle_controller.js +13 -2
  47. package/dist/controllers/idle_controller.js.map +1 -1
  48. package/dist/controllers/listbox_controller.js.map +1 -1
  49. package/dist/controllers/local_time_controller.js +2 -0
  50. package/dist/controllers/local_time_controller.js.map +1 -1
  51. package/dist/controllers/masonry_controller.d.ts +1 -1
  52. package/dist/controllers/masonry_controller.js +1 -1
  53. package/dist/controllers/masonry_controller.js.map +1 -1
  54. package/dist/controllers/meter_controller.js +3 -1
  55. package/dist/controllers/meter_controller.js.map +1 -1
  56. package/dist/controllers/multi_select_controller.js.map +1 -1
  57. package/dist/controllers/network_status_controller.d.ts +15 -13
  58. package/dist/controllers/network_status_controller.js +1 -3
  59. package/dist/controllers/network_status_controller.js.map +1 -1
  60. package/dist/controllers/number_input_controller.d.ts +8 -0
  61. package/dist/controllers/number_input_controller.js +191 -24
  62. package/dist/controllers/number_input_controller.js.map +1 -1
  63. package/dist/controllers/overflow_menu_controller.js +1 -1
  64. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  65. package/dist/controllers/pagination_controller.js +5 -1
  66. package/dist/controllers/pagination_controller.js.map +1 -1
  67. package/dist/controllers/password_strength_controller.d.ts +1 -1
  68. package/dist/controllers/password_strength_controller.js +1 -1
  69. package/dist/controllers/password_strength_controller.js.map +1 -1
  70. package/dist/controllers/pointer_drag_controller.js +10 -0
  71. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  72. package/dist/controllers/portal_controller.js +10 -0
  73. package/dist/controllers/portal_controller.js.map +1 -1
  74. package/dist/controllers/progress_controller.d.ts +1 -1
  75. package/dist/controllers/progress_controller.js +7 -3
  76. package/dist/controllers/progress_controller.js.map +1 -1
  77. package/dist/controllers/range_slider_controller.d.ts +23 -0
  78. package/dist/controllers/range_slider_controller.js +385 -94
  79. package/dist/controllers/range_slider_controller.js.map +1 -1
  80. package/dist/controllers/rating_controller.js +2 -0
  81. package/dist/controllers/rating_controller.js.map +1 -1
  82. package/dist/controllers/relative_time_controller.js +2 -0
  83. package/dist/controllers/relative_time_controller.js.map +1 -1
  84. package/dist/controllers/scroll_area_controller.d.ts +1 -1
  85. package/dist/controllers/scroll_area_controller.js +1 -1
  86. package/dist/controllers/scroll_area_controller.js.map +1 -1
  87. package/dist/controllers/separator_controller.js +13 -17
  88. package/dist/controllers/separator_controller.js.map +1 -1
  89. package/dist/controllers/skeleton_controller.d.ts +2 -2
  90. package/dist/controllers/skeleton_controller.js +71 -3
  91. package/dist/controllers/skeleton_controller.js.map +1 -1
  92. package/dist/controllers/slider_controller.d.ts +17 -1
  93. package/dist/controllers/slider_controller.js +325 -48
  94. package/dist/controllers/slider_controller.js.map +1 -1
  95. package/dist/controllers/spinner_controller.d.ts +15 -10
  96. package/dist/controllers/spinner_controller.js +18 -3
  97. package/dist/controllers/spinner_controller.js.map +1 -1
  98. package/dist/controllers/step_indicator_controller.d.ts +1 -1
  99. package/dist/controllers/step_indicator_controller.js +3 -1
  100. package/dist/controllers/step_indicator_controller.js.map +1 -1
  101. package/dist/controllers/stepper_controller.js +2 -0
  102. package/dist/controllers/stepper_controller.js.map +1 -1
  103. package/dist/controllers/switch_controller.d.ts +12 -8
  104. package/dist/controllers/switch_controller.js +162 -18
  105. package/dist/controllers/switch_controller.js.map +1 -1
  106. package/dist/controllers/textarea_autosize_controller.js +1 -1
  107. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  108. package/dist/controllers/time_picker_controller.d.ts +3 -0
  109. package/dist/controllers/time_picker_controller.js +6 -3
  110. package/dist/controllers/time_picker_controller.js.map +1 -1
  111. package/dist/controllers/tree_view_controller.d.ts +1 -2
  112. package/dist/controllers/tree_view_controller.js +19 -1
  113. package/dist/controllers/tree_view_controller.js.map +1 -1
  114. package/dist/index.js +1214 -307
  115. package/dist/index.js.map +1 -1
  116. package/dist/inspector/cli.d.ts +76 -1
  117. package/dist/inspector/cli.js +152 -0
  118. package/dist/inspector/cli.js.map +1 -1
  119. package/dist/inspector/cli_bin.js +212 -2
  120. package/dist/inspector/cli_bin.js.map +1 -1
  121. package/dist/inspector/examples.json +17 -17
  122. package/dist/inspector/manifest.json +408 -26
  123. package/package.json +3 -3
@@ -612,6 +612,26 @@ function readEntry(code, start, base) {
612
612
  };
613
613
  }
614
614
  }
615
+ const number = /^[+-]?\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?/.exec(
616
+ code.slice(valueStart)
617
+ )?.[0];
618
+ if (number !== void 0) {
619
+ const end = valueStart + number.length;
620
+ const after = skipTrivia(code, end);
621
+ const whole = after >= code.length || code[after] === "," || code[after] === "}";
622
+ if (whole) {
623
+ return {
624
+ attr: {
625
+ name,
626
+ value: number,
627
+ ...anchor,
628
+ valueStart: base + valueStart,
629
+ valueEnd: base + end
630
+ },
631
+ end
632
+ };
633
+ }
634
+ }
615
635
  return { attr: { name, value: null, ...anchor }, end: skipValue(code, valueStart) };
616
636
  }
617
637
  function readKey(code, start) {
@@ -928,6 +948,7 @@ var DIAGNOSTIC_CODES = [
928
948
  "unknown-controller",
929
949
  "unknown-target",
930
950
  "unknown-value",
951
+ "invalid-value",
931
952
  "unknown-action-controller",
932
953
  "unknown-action-method",
933
954
  "orphan-target",
@@ -936,6 +957,7 @@ var DIAGNOSTIC_CODES = [
936
957
  "missing-aria",
937
958
  "invalid-aria-value",
938
959
  "keyboard-inaccessible",
960
+ "invalid-host",
939
961
  "unresolved-idref",
940
962
  "managed-aria",
941
963
  "composition-mismatch",
@@ -967,6 +989,36 @@ function checkSource(source, manifest) {
967
989
  if (hostAttr && isDynamicValue(hostAttr)) return null;
968
990
  return when.equals.includes(hostAttr ? hostAttr.value.trim() : when.default);
969
991
  };
992
+ const isContentEditable2 = (element) => {
993
+ let current = element;
994
+ while (current && current.tag !== "#root") {
995
+ const attr = current.attrs.find((candidate) => candidate.name === "contenteditable");
996
+ if (attr) {
997
+ if (isDynamicValue(attr)) return null;
998
+ const value = attr.value.trim().toLowerCase();
999
+ if (value === "false") return false;
1000
+ if (value === "" || value === "true" || value === "plaintext-only") return true;
1001
+ }
1002
+ current = current.parent;
1003
+ }
1004
+ return false;
1005
+ };
1006
+ const hostIsAllowed = (element, rule) => {
1007
+ if (element.tag === "button") {
1008
+ if (rule.mode === "non-interactive") return false;
1009
+ const type = element.attrs.find((attr) => attr.name === "type");
1010
+ if (type && isDynamicValue(type)) return null;
1011
+ const effectiveType = type?.value.trim().toLowerCase() || "submit";
1012
+ return (rule.buttonTypes ?? []).includes(effectiveType);
1013
+ }
1014
+ if (ALWAYS_INTERACTIVE_HOSTS.has(element.tag)) return false;
1015
+ if ((element.tag === "a" || element.tag === "area") && hasAttr(element, "href")) return false;
1016
+ if ((element.tag === "audio" || element.tag === "video") && hasAttr(element, "controls")) {
1017
+ return false;
1018
+ }
1019
+ const editable = isContentEditable2(element);
1020
+ return editable === null ? null : !editable;
1021
+ };
970
1022
  const suppressed = (node, code) => {
971
1023
  if (code === "unknown-ignore-code") return false;
972
1024
  let current = node;
@@ -1074,6 +1126,7 @@ function checkSource(source, manifest) {
1074
1126
  focusableRoleTally.set(role, (focusableRoleTally.get(role) ?? 0) + 1);
1075
1127
  }
1076
1128
  }
1129
+ const authoredValueIdentifiers = /* @__PURE__ */ new Set();
1077
1130
  for (const attr of node.attrs) {
1078
1131
  if (attr.name === IGNORE_ATTR) {
1079
1132
  for (const token of attr.value.split(/\s+/).filter((t) => t.length > 0)) {
@@ -1198,6 +1251,7 @@ function checkSource(source, manifest) {
1198
1251
  attr
1199
1252
  );
1200
1253
  } else {
1254
+ authoredValueIdentifiers.add(parsed.identifier);
1201
1255
  const controller = known[parsed.identifier];
1202
1256
  const valid = controller?.values.some((v) => dasherize(v) === parsed.valueToken) ?? false;
1203
1257
  if (!valid) {
@@ -1209,6 +1263,20 @@ function checkSource(source, manifest) {
1209
1263
  attr,
1210
1264
  didYouMean(parsed.valueToken, (controller?.values ?? []).map(dasherize))
1211
1265
  );
1266
+ } else if (!isDynamicValue(attr)) {
1267
+ const constraint = controller?.valueConstraints.find(
1268
+ (candidate) => dasherize(candidate.value) === parsed.valueToken
1269
+ );
1270
+ if (constraint && !valueSatisfiesConstraint(attr.value, constraint)) {
1271
+ report(
1272
+ node,
1273
+ "invalid-value",
1274
+ "error",
1275
+ `Invalid value "${attr.value.trim()}" for "${parsed.identifier}.${parsed.valueToken}". Expected ${describeValueConstraint(constraint)}.`,
1276
+ attr,
1277
+ constraint.suggestion
1278
+ );
1279
+ }
1212
1280
  }
1213
1281
  }
1214
1282
  continue;
@@ -1243,6 +1311,34 @@ function checkSource(source, manifest) {
1243
1311
  }
1244
1312
  }
1245
1313
  }
1314
+ for (const identifier of authoredValueIdentifiers) {
1315
+ const controller = known[identifier];
1316
+ if (!controller) continue;
1317
+ for (const relation of controller.valueRelations) {
1318
+ const leftAttr = node.attrs.find(
1319
+ (attr) => attr.name === `data-${identifier}-${dasherize(relation.left)}-value`
1320
+ );
1321
+ const rightAttr = node.attrs.find(
1322
+ (attr) => attr.name === `data-${identifier}-${dasherize(relation.right)}-value`
1323
+ );
1324
+ const at = rightAttr ?? leftAttr;
1325
+ if (!at || leftAttr && isDynamicValue(leftAttr) || rightAttr && isDynamicValue(rightAttr)) {
1326
+ continue;
1327
+ }
1328
+ const left = leftAttr ? decodeNumericValue(leftAttr.value) : relation.leftDefault;
1329
+ const right = rightAttr ? decodeNumericValue(rightAttr.value) : relation.rightDefault;
1330
+ if (!Number.isFinite(left) || !Number.isFinite(right)) continue;
1331
+ if (relation.operator === "less-than-or-equal" && left <= right) continue;
1332
+ report(
1333
+ node,
1334
+ "invalid-value",
1335
+ "error",
1336
+ `Invalid values for "${identifier}": "${relation.left}" (${left}) must be less than or equal to "${relation.right}" (${right}).`,
1337
+ at,
1338
+ relation.suggestion
1339
+ );
1340
+ }
1341
+ }
1246
1342
  });
1247
1343
  const scopeIsComplete = (container, scope, identifier) => container.origin === "markup" && !targetsUnreadable(container, scope, identifier);
1248
1344
  for (const { node, identifier } of scopes) {
@@ -1320,6 +1416,28 @@ function checkSource(source, manifest) {
1320
1416
  }
1321
1417
  }
1322
1418
  }
1419
+ for (const { node, identifier } of scopes) {
1420
+ const controller = known[identifier];
1421
+ if (!controller) continue;
1422
+ for (const rule of controller.hosts) {
1423
+ const elements = rule.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(rule.target) ?? [];
1424
+ const where = rule.target === "" ? "scope element" : `"${rule.target}" target`;
1425
+ for (const element of elements) {
1426
+ if (!hasReadableMarkup(element)) continue;
1427
+ if (hostIsAllowed(element, rule) !== false) continue;
1428
+ const type = element.attrs.find((attr) => attr.name === "type");
1429
+ const detail = element.tag === "button" ? `the unsupported ${type ? `type="${type.value}"` : 'implicit type="submit"'}` : `an unsupported interactive <${element.tag}>`;
1430
+ report(
1431
+ element,
1432
+ "invalid-host",
1433
+ "error",
1434
+ `The ${where} of "${identifier}" uses ${detail} host.`,
1435
+ type ?? element,
1436
+ rule.suggestion
1437
+ );
1438
+ }
1439
+ }
1440
+ }
1323
1441
  for (const { node, identifier } of scopes) {
1324
1442
  const controller = known[identifier];
1325
1443
  if (!controller) continue;
@@ -1561,6 +1679,23 @@ function checkSource(source, manifest) {
1561
1679
  diagnostics.sort((a, b) => a.line - b.line || a.column - b.column);
1562
1680
  return diagnostics;
1563
1681
  }
1682
+ function valueSatisfiesConstraint(raw, constraint) {
1683
+ const value = decodeNumericValue(raw);
1684
+ if (Number.isNaN(value)) return false;
1685
+ if (constraint.finite && !Number.isFinite(value)) return false;
1686
+ if (constraint.greaterThan !== void 0 && !(value > constraint.greaterThan)) return false;
1687
+ return true;
1688
+ }
1689
+ function decodeNumericValue(raw) {
1690
+ return Number(raw.replace(/_/g, ""));
1691
+ }
1692
+ function describeValueConstraint(constraint) {
1693
+ const parts = [constraint.finite ? "a finite number" : "a number"];
1694
+ if (constraint.greaterThan !== void 0) {
1695
+ parts.push(`greater than ${constraint.greaterThan}`);
1696
+ }
1697
+ return parts.join(" ");
1698
+ }
1564
1699
  var UNRESOLVED_TARGET_HINT = "Part of this scope is rendered by a helper or a non-literal data: hash, so the target may exist at runtime.";
1565
1700
  var UNRESOLVED_HOST_HINT = "An enclosing helper's data: option is not a literal hash, so the host controller may be declared at runtime.";
1566
1701
  function hasReadableMarkup(el) {
@@ -1633,6 +1768,20 @@ function describeCounted(target, attr, values) {
1633
1768
  return `"${target}" targets with ${attr}${constrained}`;
1634
1769
  }
1635
1770
  var NATIVE_FOCUSABLE_TAGS = /* @__PURE__ */ new Set(["button", "select", "textarea", "summary"]);
1771
+ var ALWAYS_INTERACTIVE_HOSTS = /* @__PURE__ */ new Set([
1772
+ "input",
1773
+ "select",
1774
+ "textarea",
1775
+ "label",
1776
+ "summary",
1777
+ "details",
1778
+ "iframe",
1779
+ "object",
1780
+ "embed"
1781
+ ]);
1782
+ function hasAttr(element, name) {
1783
+ return element.attrs.some((attr) => attr.name === name);
1784
+ }
1636
1785
  function isNativelyFocusable(el) {
1637
1786
  if (NATIVE_FOCUSABLE_TAGS.has(el.tag)) return true;
1638
1787
  if (el.tag === "input") {
@@ -1783,12 +1932,15 @@ function isCheckableFile(path) {
1783
1932
  var CONTROLLER_ARRAY_FIELDS = [
1784
1933
  "targets",
1785
1934
  "values",
1935
+ "valueConstraints",
1936
+ "valueRelations",
1786
1937
  "actions",
1787
1938
  "events",
1788
1939
  "requiredTargets",
1789
1940
  "conditionalTargets",
1790
1941
  "a11y",
1791
1942
  "keyboard",
1943
+ "hosts",
1792
1944
  "managedAria",
1793
1945
  "compositions",
1794
1946
  "companions",
@@ -2261,6 +2413,37 @@ var CONTROLLER_CONTRACT_SCHEMA = {
2261
2413
  id: { type: "string", description: 'Controller identifier, e.g. "stimeo--tabs".' },
2262
2414
  targets: STRING_ARRAY,
2263
2415
  values: STRING_ARRAY,
2416
+ valueConstraints: {
2417
+ type: "array",
2418
+ description: "Statically checkable semantic bounds on authored Stimulus Values.",
2419
+ items: {
2420
+ type: "object",
2421
+ properties: {
2422
+ value: { type: "string", description: "Camel-case declared Value name." },
2423
+ type: { type: "string", enum: ["number"] },
2424
+ finite: { type: "boolean" },
2425
+ greaterThan: { type: "number" },
2426
+ suggestion: { type: "string" }
2427
+ },
2428
+ required: ["value", "type", "suggestion"]
2429
+ }
2430
+ },
2431
+ valueRelations: {
2432
+ type: "array",
2433
+ description: "Statically checkable relationships between authored Stimulus Values.",
2434
+ items: {
2435
+ type: "object",
2436
+ properties: {
2437
+ left: { type: "string", description: "Camel-case left Value name." },
2438
+ operator: { type: "string", enum: ["less-than-or-equal"] },
2439
+ right: { type: "string", description: "Camel-case right Value name." },
2440
+ leftDefault: { type: "number" },
2441
+ rightDefault: { type: "number" },
2442
+ suggestion: { type: "string" }
2443
+ },
2444
+ required: ["left", "operator", "right", "leftDefault", "rightDefault", "suggestion"]
2445
+ }
2446
+ },
2264
2447
  actions: STRING_ARRAY,
2265
2448
  events: STRING_ARRAY,
2266
2449
  requiredTargets: {
@@ -2342,6 +2525,30 @@ var CONTROLLER_CONTRACT_SCHEMA = {
2342
2525
  required: ["target", "suggestion"]
2343
2526
  }
2344
2527
  },
2528
+ hosts: {
2529
+ type: "array",
2530
+ description: "Scope or target host-element contracts that prevent the controller from competing with an incompatible native interaction.",
2531
+ items: {
2532
+ type: "object",
2533
+ properties: {
2534
+ target: {
2535
+ type: "string",
2536
+ description: 'Target name; "" means the data-controller element itself.'
2537
+ },
2538
+ mode: {
2539
+ type: "string",
2540
+ enum: ["non-interactive", "non-interactive-or-button"],
2541
+ description: "Supported static host family."
2542
+ },
2543
+ buttonTypes: {
2544
+ ...STRING_ARRAY,
2545
+ description: "Literal button types admitted by non-interactive-or-button."
2546
+ },
2547
+ suggestion: { type: "string" }
2548
+ },
2549
+ required: ["target", "mode", "suggestion"]
2550
+ }
2551
+ },
2345
2552
  managedAria: {
2346
2553
  type: "array",
2347
2554
  description: "ARIA the controller recomputes at runtime; authoring it is a warning.",
@@ -2478,12 +2685,15 @@ var CONTROLLER_CONTRACT_SCHEMA = {
2478
2685
  "id",
2479
2686
  "targets",
2480
2687
  "values",
2688
+ "valueConstraints",
2689
+ "valueRelations",
2481
2690
  "actions",
2482
2691
  "events",
2483
2692
  "requiredTargets",
2484
2693
  "conditionalTargets",
2485
2694
  "a11y",
2486
2695
  "keyboard",
2696
+ "hosts",
2487
2697
  "managedAria",
2488
2698
  "compositions",
2489
2699
  "companions",
@@ -2502,7 +2712,7 @@ var TOOL_DESCRIPTORS = [
2502
2712
  {
2503
2713
  name: "stimeo_check",
2504
2714
  title: "Check Stimeo UI markup",
2505
- description: "Statically check HTML or ERB source that uses Stimeo UI (stimeo--*) controllers \u2014 the headless, accessible Stimulus UI component library for Rails. Returns a CheckReport with diagnostics \u2014 unknown controllers/targets/values/action methods, missing required targets, missing or invalid author-supplied ARIA, keyboard-focusability prerequisites, and misaligned cross-controller composition values \u2014 each with a fix suggestion when available. Call this to validate any Rails view, ERB template, or HTML you write or edit with these components, before finalizing it; ok=true means no errors.",
2715
+ description: "Statically check HTML or ERB source that uses Stimeo UI (stimeo--*) controllers \u2014 the headless, accessible Stimulus UI component library for Rails. Returns a CheckReport with diagnostics \u2014 unknown controllers/targets/values/action methods, missing required targets, missing or invalid author-supplied ARIA, keyboard-focusability prerequisites, incompatible interactive hosts, and misaligned cross-controller composition values \u2014 each with a fix suggestion when available. Call this to validate any Rails view, ERB template, or HTML you write or edit with these components, before finalizing it; ok=true means no errors.",
2506
2716
  annotations: READ_ONLY_ANNOTATIONS,
2507
2717
  inputSchema: {
2508
2718
  type: "object",
@@ -2536,7 +2746,7 @@ var TOOL_DESCRIPTORS = [
2536
2746
  {
2537
2747
  name: "stimeo_controller",
2538
2748
  title: "Get one controller's usage contract",
2539
- description: 'Fetch the full usage contract of one Stimeo UI controller by identifier (e.g. "stimeo--tabs"): targets, values, actions, events, required targets, and the accessibility contract \u2014 author-supplied ARIA requirements, keyboard-focusability prerequisites, the ARIA attributes the controller manages at runtime (which the author must NOT hardcode), and cross-controller composition value alignments. Use it after stimeo_catalog to write correct, accessible Rails ERB or HTML markup for that Stimulus component.',
2749
+ description: 'Fetch the full usage contract of one Stimeo UI controller by identifier (e.g. "stimeo--tabs"): targets, values, actions, events, required targets, and the accessibility contract \u2014 author-supplied ARIA requirements, keyboard-focusability prerequisites, supported host-element shapes, the ARIA attributes the controller manages at runtime (which the author must NOT hardcode), and cross-controller composition value alignments. Use it after stimeo_catalog to write correct, accessible Rails ERB or HTML markup for that Stimulus component.',
2540
2750
  annotations: READ_ONLY_ANNOTATIONS,
2541
2751
  inputSchema: {
2542
2752
  type: "object",