bitboss-ui 3.0.0-beta.57 → 3.0.0-beta.59

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 (232) hide show
  1. package/bin/bitboss-ui-mcp.mjs +118 -31
  2. package/bin/bitboss-ui.mjs +43 -9
  3. package/dist/ai/BbBadgeButton.md +3 -3
  4. package/dist/ai/BbBaseButton.md +3 -3
  5. package/dist/ai/BbCheckbox.md +6 -6
  6. package/dist/ai/BbCheckboxGroup.md +6 -6
  7. package/dist/ai/BbColorInput.md +8 -8
  8. package/dist/ai/BbDatePickerInput.md +8 -8
  9. package/dist/ai/BbDialog.md +177 -26
  10. package/dist/ai/BbDropdown.md +2 -2
  11. package/dist/ai/BbDropdownButton.md +148 -98
  12. package/dist/ai/BbDropzone.md +5 -5
  13. package/dist/ai/BbForm.md +82 -11
  14. package/dist/ai/BbNumberInput.md +8 -8
  15. package/dist/ai/BbOffCanvas.md +33 -15
  16. package/dist/ai/BbRadio.md +4 -4
  17. package/dist/ai/BbRadioGroup.md +6 -6
  18. package/dist/ai/BbRating.md +7 -7
  19. package/dist/ai/BbSelect.md +104 -24
  20. package/dist/ai/BbSelectPopover.md +6 -6
  21. package/dist/ai/BbSlider.md +6 -6
  22. package/dist/ai/BbSwitch.md +6 -6
  23. package/dist/ai/BbSwitchGroup.md +6 -6
  24. package/dist/ai/BbTable.md +43 -31
  25. package/dist/ai/BbTag.md +8 -8
  26. package/dist/ai/BbTextInput.md +8 -8
  27. package/dist/ai/BbTextarea.md +8 -8
  28. package/dist/ai/BbTimePickerInput.md +6 -6
  29. package/dist/ai/BbTree.md +1 -1
  30. package/dist/ai/CommonField.md +5 -5
  31. package/dist/ai/CommonFieldInput.md +1 -1
  32. package/dist/ai/FlatListBox.md +1 -0
  33. package/dist/ai/GroupedListBox.md +1 -0
  34. package/dist/ai/ListBox.md +1 -0
  35. package/dist/ai/changelog.json +2052 -5
  36. package/dist/ai/components.json +88 -85
  37. package/dist/ai/composables/useBbDialogContext.md +26 -19
  38. package/dist/ai/composables/useBbFormContext.md +8 -10
  39. package/dist/ai/composables/useBbOffCanvasContext.md +4 -2
  40. package/dist/ai/guides/agent-contract.md +1 -1
  41. package/dist/ai/guides/ai-router.md +2 -1
  42. package/dist/ai/guides/coherence-playbook.md +3 -2
  43. package/dist/ai/guides/eslint-rules.md +2 -2
  44. package/dist/ai/guides/fetch-items-playbook.md +3 -2
  45. package/dist/ai/guides/icons-policy.md +6 -7
  46. package/dist/ai/guides/migration/components/bb-dropdown-button.md +32 -9
  47. package/dist/ai/guides/migration/components/bb-select.md +1 -1
  48. package/dist/ai/guides/migration/v2-to-v3.md +59 -3
  49. package/dist/ai/guides/migration/v3-beta-tooling.md +36 -9
  50. package/dist/ai/helpers/inertia/manifest.json +1 -1
  51. package/dist/ai/index.md +2 -2
  52. package/dist/ai/manifest/common.json +119 -0
  53. package/dist/ai/manifest/components/BbAccordion.json +9 -10
  54. package/dist/ai/manifest/components/BbAlert.json +14 -15
  55. package/dist/ai/manifest/components/BbAsterisk.json +1 -2
  56. package/dist/ai/manifest/components/BbAvatar.json +8 -9
  57. package/dist/ai/manifest/components/BbBadge.json +10 -11
  58. package/dist/ai/manifest/components/BbBadgeButton.json +41 -42
  59. package/dist/ai/manifest/components/BbBaseButton.json +43 -44
  60. package/dist/ai/manifest/components/BbBaseCheckboxIcon.json +9 -10
  61. package/dist/ai/manifest/components/BbBaseRadioIcon.json +8 -9
  62. package/dist/ai/manifest/components/BbBaseSwitchIcon.json +9 -10
  63. package/dist/ai/manifest/components/BbBreadcrumbs.json +10 -11
  64. package/dist/ai/manifest/components/BbButton.json +52 -53
  65. package/dist/ai/manifest/components/BbCalendar.json +29 -30
  66. package/dist/ai/manifest/components/BbCheckbox.json +42 -43
  67. package/dist/ai/manifest/components/BbCheckboxGroup.json +61 -62
  68. package/dist/ai/manifest/components/BbCollapsible.json +6 -7
  69. package/dist/ai/manifest/components/BbColorInput.json +67 -68
  70. package/dist/ai/manifest/components/BbColorPalette.json +22 -23
  71. package/dist/ai/manifest/components/BbConfirm.json +1 -2
  72. package/dist/ai/manifest/components/BbDatePicker.json +42 -43
  73. package/dist/ai/manifest/components/BbDatePickerInput.json +74 -75
  74. package/dist/ai/manifest/components/BbDialog.json +32 -32
  75. package/dist/ai/manifest/components/BbDropdown.json +33 -34
  76. package/dist/ai/manifest/components/BbDropdownButton.json +72 -33
  77. package/dist/ai/manifest/components/BbDropdownGroup.json +4 -4
  78. package/dist/ai/manifest/components/BbDropzone.json +21 -22
  79. package/dist/ai/manifest/components/BbForm.json +10 -9
  80. package/dist/ai/manifest/components/BbHoverCard.json +16 -17
  81. package/dist/ai/manifest/components/BbIcon.json +4 -5
  82. package/dist/ai/manifest/components/BbIndicator.json +8 -9
  83. package/dist/ai/manifest/components/BbNumberInput.json +66 -67
  84. package/dist/ai/manifest/components/BbOffCanvas.json +32 -32
  85. package/dist/ai/manifest/components/BbPagination.json +14 -15
  86. package/dist/ai/manifest/components/BbPopover.json +21 -22
  87. package/dist/ai/manifest/components/BbProgress.json +6 -7
  88. package/dist/ai/manifest/components/BbPtScope.json +1 -1
  89. package/dist/ai/manifest/components/BbRadio.json +37 -38
  90. package/dist/ai/manifest/components/BbRadioGroup.json +62 -63
  91. package/dist/ai/manifest/components/BbRating.json +40 -41
  92. package/dist/ai/manifest/components/BbSelect.json +79 -78
  93. package/dist/ai/manifest/components/BbSelectPopover.json +51 -52
  94. package/dist/ai/manifest/components/BbSelectPopover.tree.json +4 -0
  95. package/dist/ai/manifest/components/BbSlider.json +40 -41
  96. package/dist/ai/manifest/components/BbSmoothHeight.json +7 -8
  97. package/dist/ai/manifest/components/BbSpinner.json +3 -4
  98. package/dist/ai/manifest/components/BbSwitch.json +42 -43
  99. package/dist/ai/manifest/components/BbSwitchGroup.json +62 -63
  100. package/dist/ai/manifest/components/BbTable.json +68 -69
  101. package/dist/ai/manifest/components/BbTabs.json +27 -28
  102. package/dist/ai/manifest/components/BbTabsList.json +2 -3
  103. package/dist/ai/manifest/components/BbTabsPanels.json +1 -2
  104. package/dist/ai/manifest/components/BbTabsRoot.json +26 -26
  105. package/dist/ai/manifest/components/BbTag.json +48 -49
  106. package/dist/ai/manifest/components/BbTextInput.json +64 -65
  107. package/dist/ai/manifest/components/BbTextarea.json +59 -60
  108. package/dist/ai/manifest/components/BbTimePicker.json +26 -27
  109. package/dist/ai/manifest/components/BbTimePickerInput.json +56 -57
  110. package/dist/ai/manifest/components/BbToast.json +1 -2
  111. package/dist/ai/manifest/components/BbTooltip.json +16 -17
  112. package/dist/ai/manifest/components/BbTree.json +4 -5
  113. package/dist/ai/manifest/components/ChipsBox.json +3 -3
  114. package/dist/ai/manifest/components/ClearableButton.json +1 -1
  115. package/dist/ai/manifest/components/CommaBox.json +1 -1
  116. package/dist/ai/manifest/components/CommonField.json +32 -32
  117. package/dist/ai/manifest/components/CommonFieldInput.json +15 -15
  118. package/dist/ai/manifest/components/CommonPopover.json +17 -17
  119. package/dist/ai/manifest/components/CommonTimeSelector.json +9 -9
  120. package/dist/ai/manifest/components/FlatListBox.json +15 -14
  121. package/dist/ai/manifest/components/GroupedListBox.json +16 -15
  122. package/dist/ai/manifest/components/ListBox.json +18 -17
  123. package/dist/ai/manifest/components/OptionsContainer.json +16 -16
  124. package/dist/ai/manifest/index.json +89 -85
  125. package/dist/ai/manifest/meta.json +43 -2
  126. package/dist/ai/manifest/types.advanced.json +10 -10
  127. package/dist/ai/manifest/types.api.json +52 -49
  128. package/dist/ai/recipes/inertia/approvals-inbox.md +6 -0
  129. package/dist/ai/recipes/inertia/inline-edit-workspace.md +6 -0
  130. package/dist/ai/recipes/inertia/onboarding.md +6 -0
  131. package/dist/ai/recipes/inertia/ownership-atlas.md +6 -0
  132. package/dist/ai/recipes/inertia/records-workspace.md +6 -0
  133. package/dist/ai/recipes/inertia/settings-workspace.md +6 -0
  134. package/dist/ai/recipes/inertia/upload-center.md +6 -0
  135. package/dist/ai/source/BbBaseButton.md +9 -11
  136. package/dist/ai/source/BbButton.md +15 -4
  137. package/dist/ai/source/BbCheckbox.md +12 -12
  138. package/dist/ai/source/BbCheckboxGroup.md +12 -12
  139. package/dist/ai/source/BbColorInput.md +20 -21
  140. package/dist/ai/source/BbDatePickerInput.md +20 -21
  141. package/dist/ai/source/BbDialog.md +77 -40
  142. package/dist/ai/source/BbDropdownButton.md +103 -112
  143. package/dist/ai/source/BbDropzone.md +11 -12
  144. package/dist/ai/source/BbForm.md +77 -27
  145. package/dist/ai/source/BbNumberInput.md +20 -21
  146. package/dist/ai/source/BbOffCanvas.md +67 -32
  147. package/dist/ai/source/BbRadio.md +12 -12
  148. package/dist/ai/source/BbRadioGroup.md +12 -12
  149. package/dist/ai/source/BbRating.md +16 -16
  150. package/dist/ai/source/BbSelect.md +323 -282
  151. package/dist/ai/source/BbSelectPopover.md +946 -48
  152. package/dist/ai/source/BbSlider.md +12 -12
  153. package/dist/ai/source/BbTable.md +66 -78
  154. package/dist/ai/source/BbTag.md +20 -21
  155. package/dist/ai/source/BbTextInput.md +20 -21
  156. package/dist/ai/source/BbTextarea.md +20 -21
  157. package/dist/ai/source/BbTimePickerInput.md +14 -15
  158. package/dist/ai/source/BbTree.md +4 -5
  159. package/dist/ai/source/CommonField.md +18 -18
  160. package/dist/ai/source/CommonFieldInput.md +4 -4
  161. package/dist/ai/source/FlatListBox.md +14 -0
  162. package/dist/ai/source/GroupedListBox.md +14 -0
  163. package/dist/ai/source/ListBox.md +8 -0
  164. package/dist/components/BbBaseButton/types.d.ts +9 -11
  165. package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +1 -1
  166. package/dist/components/BbButton/types.d.ts +6 -0
  167. package/dist/components/BbCheckbox/types.d.ts +12 -12
  168. package/dist/components/BbCheckboxGroup/types.d.ts +12 -12
  169. package/dist/components/BbColorInput/types.d.ts +20 -21
  170. package/dist/components/BbDatePickerInput/types.d.ts +20 -21
  171. package/dist/components/BbDialog/BbDialog.vue.d.ts +2 -2
  172. package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +192 -182
  173. package/dist/components/BbDialog/types.d.ts +42 -34
  174. package/dist/components/BbDropdownButton/BbDropdownButton.vue_vue_type_script_setup_true_lang.js +143 -100
  175. package/dist/components/BbDropdownButton/types.d.ts +25 -15
  176. package/dist/components/BbDropzone/types.d.ts +11 -12
  177. package/dist/components/BbForm/BbForm.vue.d.ts +17 -17
  178. package/dist/components/BbForm/BbForm.vue_vue_type_script_setup_true_lang.js +195 -188
  179. package/dist/components/BbForm/formSchema.d.ts +9 -0
  180. package/dist/components/BbHost/BbHostOpening.js +1 -1
  181. package/dist/components/BbNumberInput/types.d.ts +20 -21
  182. package/dist/components/BbOffCanvas/BbOffCanvas.vue.d.ts +2 -2
  183. package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +242 -234
  184. package/dist/components/BbOffCanvas/types.d.ts +36 -28
  185. package/dist/components/BbRadio/types.d.ts +12 -12
  186. package/dist/components/BbRadioGroup/types.d.ts +12 -12
  187. package/dist/components/BbRating/types.d.ts +16 -16
  188. package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +291 -283
  189. package/dist/components/BbSelect/types.d.ts +43 -31
  190. package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +773 -487
  191. package/dist/components/BbSelectPopover/knownItems.d.ts +60 -0
  192. package/dist/components/BbSelectPopover/knownItems.js +26 -0
  193. package/dist/components/BbSelectPopover/types.d.ts +16 -18
  194. package/dist/components/BbSlider/types.d.ts +12 -12
  195. package/dist/components/BbTable/types.d.ts +66 -78
  196. package/dist/components/BbTag/types.d.ts +20 -21
  197. package/dist/components/BbTextInput/types.d.ts +20 -21
  198. package/dist/components/BbTextarea/types.d.ts +20 -21
  199. package/dist/components/BbTimePickerInput/types.d.ts +14 -15
  200. package/dist/components/BbTree/types.d.ts +4 -5
  201. package/dist/components/CommonField/types.d.ts +18 -18
  202. package/dist/components/CommonFieldInput/types.d.ts +4 -4
  203. package/dist/components/FlatListBox/FlatListBox.vue_vue_type_script_setup_true_lang.js +73 -68
  204. package/dist/components/FlatListBox/types.d.ts +6 -0
  205. package/dist/components/GroupedListBox/GroupedListBox.vue_vue_type_script_setup_true_lang.js +35 -30
  206. package/dist/components/GroupedListBox/types.d.ts +6 -0
  207. package/dist/components/ListBox/ListBox.vue_vue_type_script_setup_true_lang.js +5 -0
  208. package/dist/components/ListBox/types.d.ts +6 -0
  209. package/dist/composables/formSubmitLock.d.ts +6 -1
  210. package/dist/composables/hostOpenings.d.ts +10 -4
  211. package/dist/composables/hostOpenings.js +1 -1
  212. package/dist/composables/overlayRegistry.d.ts +22 -9
  213. package/dist/composables/overlayRegistry.js +175 -132
  214. package/dist/composables/useBbDialogContext.d.ts +29 -11
  215. package/dist/composables/useBbDialogContext.js +17 -8
  216. package/dist/composables/useBbFormContext.d.ts +12 -7
  217. package/dist/composables/useBbFormContext.js +7 -6
  218. package/dist/composables/useFormBinding.d.ts +7 -9
  219. package/dist/composables/useOnDemandOpener.d.ts +6 -13
  220. package/dist/composables/useOnDemandOpener.js +47 -22
  221. package/dist/directives/overlayTrigger.d.ts +4 -2
  222. package/dist/directives/overlayTrigger.js +1 -0
  223. package/dist/index.d.ts +3 -3
  224. package/dist/llms-full.txt +943 -401
  225. package/dist/llms-medium.txt +3 -2
  226. package/dist/llms.txt +2 -2
  227. package/dist/styles.css +2 -2
  228. package/dist/utils/versionCheck.js +1 -1
  229. package/llms.txt +2 -2
  230. package/package.json +1 -1
  231. package/scripts/lib/agent-manifest.mjs +231 -18
  232. package/scripts/lib/validate-bb-markup.mjs +53 -9
@@ -1161,6 +1161,19 @@ function getComponent({
1161
1161
  const type = (manifest.types ?? []).find((t) => t.name === name);
1162
1162
  if (type) {
1163
1163
  const use = type.usedBy?.[0];
1164
+ // Q74.1: a component's own Props type is that component's props.
1165
+ const propsOf =
1166
+ type.component && name === `${type.component}Props`
1167
+ ? type.component
1168
+ : null;
1169
+ // Q74.2: `fields` stand in for a declaration that adds nothing.
1170
+ const shape = type.definition
1171
+ ? '`definition` above is the type itself'
1172
+ : type.fields
1173
+ ? '`fields` above are its shape'
1174
+ : propsOf
1175
+ ? `Its fields are ${possessive(propsOf)} props: get_component('${propsOf}')`
1176
+ : 'It is described above';
1164
1177
  return {
1165
1178
  found: true,
1166
1179
  kind: 'type',
@@ -1179,9 +1192,10 @@ function getComponent({
1179
1192
  ...(type.fields ? { fields: type.fields } : {}),
1180
1193
  // A union of private members: each member's fields (Q51.21).
1181
1194
  ...(type.members ? { members: type.members } : {}),
1195
+ ...(propsOf ? { propsOf } : {}),
1182
1196
  import: `import type { ${type.replacedBy ?? name} } from 'bitboss-ui';`,
1183
1197
  hint: use
1184
- ? `A type, not a component: it types ${possessive(use.component)} \`${use.name}\` ${use.surface}. \`definition\` above is the type itself; call get_component('${use.component}') to see it in use.`
1198
+ ? `A type, not a component: it types ${possessive(use.component)} \`${use.name}\` ${use.surface}. ${shape}; call get_component('${use.component}') to see it in use.`
1185
1199
  : 'A type, not a component — import it for the value you pass, never write it as a tag.',
1186
1200
  };
1187
1201
  }
@@ -2124,7 +2138,11 @@ function docIndex() {
2124
2138
  ? `accepted values ${type.values.join(' | ')}`
2125
2139
  : type.fields
2126
2140
  ? `object shape, fields ${type.fields.join('; ')}`
2127
- : String(type.definition ?? '')) +
2141
+ : type.definition
2142
+ ? String(type.definition)
2143
+ : type.component && type.name === `${type.component}Props`
2144
+ ? `the props of ${type.component}`
2145
+ : '') +
2128
2146
  `. import type { ${type.name} } from 'bitboss-ui'.`,
2129
2147
  read: { tool: 'get_component', args: { name: type.name } },
2130
2148
  });
@@ -2556,6 +2574,66 @@ function changeLine(row, focus) {
2556
2574
  return `${row.id} ${row.summary ?? firstSentence(row.description, 160)}`;
2557
2575
  }
2558
2576
 
2577
+ /**
2578
+ * One page of `[release, line]` pairs, grouped by release: it ends at
2579
+ * CHANGES_PAGE lines or CHANGES_PAGE_CHARS of them, so its size holds however
2580
+ * long the lines grow; the rest is a cursor away.
2581
+ */
2582
+ function pageLines(lines, cursor) {
2583
+ const start = Math.max(0, Number(cursor ?? 0) || 0);
2584
+ let end = start;
2585
+ for (let used = 0; end < lines.length && end - start < CHANGES_PAGE; end++) {
2586
+ used += lines[end][1].length + 3;
2587
+ if (used > CHANGES_PAGE_CHARS && end > start) break;
2588
+ }
2589
+ const byRelease = {};
2590
+ for (const [release, line] of lines.slice(start, end))
2591
+ (byRelease[release] ??= []).push(line);
2592
+ return {
2593
+ byRelease,
2594
+ more:
2595
+ end < lines.length
2596
+ ? { nextCursor: String(end), remaining: lines.length - end }
2597
+ : {},
2598
+ };
2599
+ }
2600
+
2601
+ /**
2602
+ * Q74.4: what the releases in range ADDED, as `[release, "area · what"]`,
2603
+ * oldest first; `unreleased` only when `until` is open. A component keeps
2604
+ * the lines of its own area (BbDialog → `dialog`) and those whose commits
2605
+ * touched its folder (a family scope: `inputs`).
2606
+ */
2607
+ function addedPairs(doc, { since, until, component }) {
2608
+ const area = component
2609
+ ? String(component).replace(/^Bb/, '').toLowerCase()
2610
+ : null;
2611
+ return Object.entries(doc.added ?? {})
2612
+ .filter(([at]) => {
2613
+ if (at === 'unreleased') return until == null;
2614
+ if (since != null && compareVersions(at, String(since)) <= 0)
2615
+ return false;
2616
+ if (until != null && compareVersions(at, String(until)) > 0) return false;
2617
+ return true;
2618
+ })
2619
+ .flatMap(([at, entries]) =>
2620
+ entries
2621
+ .map((entry) =>
2622
+ typeof entry === 'string' ? { text: entry, components: [] } : entry
2623
+ )
2624
+ .filter(
2625
+ ({ text, components }) =>
2626
+ !area ||
2627
+ text.split(' · ')[0] === area ||
2628
+ components.includes(String(component))
2629
+ )
2630
+ .map(({ text }) => [at, text])
2631
+ );
2632
+ }
2633
+
2634
+ const ADDED_HINT =
2635
+ 'Fixes are not tracked. What the range added, one line per feature: list_changes({ since, kind: "added" }).';
2636
+
2559
2637
  function changeLines(rows, cursor, withClassMap = false, focus) {
2560
2638
  const lines = [];
2561
2639
  const classRenames = {};
@@ -2576,25 +2654,12 @@ function changeLines(rows, cursor, withClassMap = false, focus) {
2576
2654
  ? -1
2577
2655
  : compareVersions(a, b)
2578
2656
  );
2657
+ const page = pageLines(lines, cursor);
2658
+ // The class map rides on the first page only.
2579
2659
  const start = Math.max(0, Number(cursor ?? 0) || 0);
2580
- // A page ends at CHANGES_PAGE lines or CHANGES_PAGE_CHARS of them, so its
2581
- // size holds however long the summaries grow; the rest is a cursor away.
2582
- let end = start;
2583
- for (let used = 0; end < lines.length && end - start < CHANGES_PAGE; end++) {
2584
- used += lines[end][1].length + 3;
2585
- if (used > CHANGES_PAGE_CHARS && end > start) break;
2586
- }
2587
- const byRelease = {};
2588
- for (const [release, line] of lines.slice(start, end))
2589
- (byRelease[release] ??= []).push(line);
2590
2660
  return {
2591
- changes: byRelease,
2592
- ...(end < lines.length
2593
- ? {
2594
- nextCursor: String(end),
2595
- remaining: lines.length - end,
2596
- }
2597
- : {}),
2661
+ changes: page.byRelease,
2662
+ ...page.more,
2598
2663
  ...(Object.keys(classRenames).length && start === 0 && !withClassMap
2599
2664
  ? {
2600
2665
  classRenames: `${Object.keys(classRenames).length} .bb-* class renames (the beta.36 sweep): the map with surface: 'class'`,
@@ -2692,6 +2757,24 @@ function listChanges({
2692
2757
  }
2693
2758
  }
2694
2759
 
2760
+ if (kind === 'added') {
2761
+ const pairs = addedPairs(doc, { since, until, component });
2762
+ const page = pageLines(pairs, cursor);
2763
+ return {
2764
+ version: doc.version,
2765
+ since: since ?? null,
2766
+ until: until ?? doc.version,
2767
+ ...(component ? { filteredBy: { component } } : {}),
2768
+ count: pairs.length,
2769
+ added: page.byRelease,
2770
+ ...page.more,
2771
+ hint:
2772
+ pairs.length === 0
2773
+ ? 'Nothing added in that range (fixes are not tracked).'
2774
+ : 'One "area · what" line per feature, grouped by release, from its feat commit. Breaking changes: the same call without kind. Next page: cursor: nextCursor.',
2775
+ };
2776
+ }
2777
+
2695
2778
  const rows = detail === 'rows';
2696
2779
  const filtered = (doc.breaking ?? []).filter(
2697
2780
  (row) =>
@@ -2751,7 +2834,8 @@ function listChanges({
2751
2834
  Object.fromEntries(Object.entries(bucket).filter(([, n]) => n > 0)),
2752
2835
  ])
2753
2836
  ),
2754
- hint: 'Counts only (a missing count is 0). What a bump breaks: list_changes({ since: "<your version>" }), one line per change; from v2: since: "2". One component: { component: "BbSelect" }. Full rows: detail: "rows".',
2837
+ added: addedPairs(doc, {}).length,
2838
+ hint: 'Counts only (a missing count is 0). What a bump breaks: list_changes({ since: "<your version>" }), one line per change; from v2: since: "2". One component: { component: "BbSelect" }. Full rows: detail: "rows". `added`: features, kind: "added".',
2755
2839
  };
2756
2840
  }
2757
2841
 
@@ -2808,10 +2892,11 @@ function listChanges({
2808
2892
  count: inRange.length,
2809
2893
  ...changeLines(inRange, cursor, surface === 'class', component),
2810
2894
  guide: doc.guide,
2895
+ added: addedPairs(doc, { since, until, component }).length,
2811
2896
  hint:
2812
2897
  inRange.length === 0
2813
- ? 'No breaking changes recorded in that range — features and fixes are deliberately not tracked here, so an empty result means nothing BREAKS, not that nothing changed.'
2814
- : 'One line per change, grouped by the release that shipped it (id, components, old → new, or the break in one sentence). Whole rows: detail: "rows". The `guide` page explains each. Next page: cursor: nextCursor.',
2898
+ ? `No breaking changes recorded in that range: nothing BREAKS. ${ADDED_HINT}`
2899
+ : `One line per change, grouped by the release that shipped it (id, components, old → new, or the break in one sentence). Whole rows: detail: "rows". The \`guide\` page explains each. Next page: cursor: nextCursor. \`added\`: features, kind: "added".`,
2815
2900
  };
2816
2901
  }
2817
2902
 
@@ -2824,7 +2909,7 @@ function listChanges({
2824
2909
  guide: doc.guide,
2825
2910
  hint:
2826
2911
  inRange.length === 0
2827
- ? 'No breaking changes recorded in that range — features and fixes are deliberately not tracked here, so an empty result means nothing BREAKS, not that nothing changed.'
2912
+ ? `No breaking changes recorded in that range: nothing BREAKS. ${ADDED_HINT}`
2828
2913
  : `Each entry names the component and the new spelling; \`guide\` is the migration page with the full explanation. ${ROW_FIELDS}`,
2829
2914
  };
2830
2915
  }
@@ -2865,13 +2950,13 @@ const platformSchema = z.enum(RECIPE_PLATFORMS);
2865
2950
  * way to know which tool answers which question, and read whole guides.
2866
2951
  */
2867
2952
  const SERVER_INSTRUCTIONS = [
2868
- 'bitboss-ui: a Vue 3 component library. Answer from these tools, never from memory — agents that guess invent props.',
2953
+ 'bitboss-ui: a Vue 3 component library. Answer from these tools, never from memory: agents that guess invent props.',
2869
2954
  "- Tailwind v4 app: set cssLayers: TAILWIND_V4_LAYERS ('bitboss-ui/vite') at setup, or utilities lose.",
2870
- '- A topic or "how do I…" question (dark mode, cascade layers, SSR, a plugin option, a CLI command): search_docs, then open the section it points to.',
2871
- '- Which component: search_components (empty query = the whole catalog). One component: get_component({ name }), one line per member; members: [names] for whole rows, sections for prose.',
2872
- '- A type in a props table (BbTableColumn, ButtonVariantType): get_component({ name }) returns its definition and values.',
2955
+ '- A topic or "how do I…" (dark mode, cascade layers, SSR, a plugin option, a CLI command): search_docs, then open the section it names.',
2956
+ '- Which component: search_components (empty query = whole catalog). One component: get_component({ name }), one line per member; members: [names] for whole rows, sections for prose.',
2957
+ '- A type in a props table (BbTableColumn, ButtonVariantType): get_component({ name }) gives its definition and values.',
2873
2958
  '- After writing markup: validate. Before custom CSS: list_tokens.',
2874
- '- An old name (v2 or earlier-beta prop, slot, item field, pt key, class) that stopped working: search_docs(the old name). What a bump breaks: list_changes({ since: <your version> }), one line per change.',
2959
+ '- An old name (v2 or earlier-beta prop, slot, item field, pt key, class) that stopped working: search_docs(old name). What a bump breaks: list_changes({ since: <your version> }), a line per change.',
2875
2960
  "- pt in <script> (a const, a wrapper's prop): type it <Name>Props['pt']; Pt* types are not exported.",
2876
2961
  '- Composables: get_composable. Whole pages: list_recipes, get_recipe (prose + files). Long docs answer with an outline: pass sections.',
2877
2962
  ].join('\n');
@@ -3018,7 +3103,7 @@ function buildServer() {
3018
3103
  'list_changes',
3019
3104
  {
3020
3105
  description:
3021
- 'Breaking changes between bitboss-ui releases — what breaks if you bump. Call with since=<the version you are on>: one line per change, grouped by release. No arguments: counts per release. Narrow with component / surface / kind; detail: "rows" for the full records. Only BREAKING changes are tracked; features and fixes are not.',
3106
+ 'Breaking changes between bitboss-ui releases — what breaks if you bump. Call with since=<the version you are on>: one line per change, grouped by release. No arguments: counts per release. Narrow with component / surface / kind; detail: "rows" for the full records. kind: "added" lists what the range added (features); fixes are not tracked.',
3022
3107
  inputSchema: {
3023
3108
  since: z
3024
3109
  .string()
@@ -3053,9 +3138,11 @@ function buildServer() {
3053
3138
  'Only renames of this surface (`item` = a field of an `items` entry, e.g. a dropdown row `key` → `value`).'
3054
3139
  ),
3055
3140
  kind: z
3056
- .enum(['rename', 'behaviour'])
3141
+ .enum(['rename', 'behaviour', 'added'])
3057
3142
  .optional()
3058
- .describe('Only renames, or only behaviour changes.'),
3143
+ .describe(
3144
+ "Only renames, or only behaviour changes; 'added': what the range added (features), one line each."
3145
+ ),
3059
3146
  detail: z
3060
3147
  .enum(['lines', 'rows'])
3061
3148
  .optional()
@@ -40,8 +40,8 @@
40
40
  * add Copy Inertia helpers into the app as files it owns
41
41
  * (scripts/lib/helpers-add.mjs); `helpers list|diff|update`
42
42
  * reports on them and refreshes the ones the app has not edited.
43
- * changelog Breaking changes between two releases — `--since <version>`
44
- * answers "what breaks if I bump", without MCP. Reads the
43
+ * changelog Breaking changes between two releases, then what they added —
44
+ * `--since <version>` answers "what breaks if I bump", without MCP. Reads the
45
45
  * installed `dist/ai/changelog.json`.
46
46
  * mcp Start a stdio MCP server exposing the dist/ai knowledge base
47
47
  * (search_docs, search_components, get_component, get_composable,
@@ -167,7 +167,7 @@ function coreRules(version) {
167
167
  1. **Start at the AI Router**: \`${ROUTER}\` — which file to open next (do not dump the full catalogue into context).
168
168
  2. **Obey the agent contract**: \`${CONTRACT}\`.
169
169
  3. **Prefer \`Bb*\` consumer components**; never use entries marked "Reference Only - Internal" unless the user explicitly asks for internals. (\`BbBaseButton\` and the \`BbBase*Icon\` glyphs are consumer API; the \`Common*\` building blocks are not.)
170
- 4. **Never invent prop/event/slot names** — open \`${KNOWLEDGE_BASE}/<ComponentName>.md\` (Usage & Guidelines first) or its API file \`${KNOWLEDGE_BASE}/manifest/components/<ComponentName>.json\` (one member per line; the list is \`${KNOWLEDGE_BASE}/manifest/index.json\`). Never read \`components.json\` whole: it is the 1.2 MB file the tools use.
170
+ 4. **Never invent prop/event/slot names** — open \`${KNOWLEDGE_BASE}/<ComponentName>.md\` (Usage & Guidelines first) or its API file \`${KNOWLEDGE_BASE}/manifest/components/<ComponentName>.json\` (one member per line; a row marked \`common: true\` is whole in \`${KNOWLEDGE_BASE}/manifest/common.json\`; the list is \`${KNOWLEDGE_BASE}/manifest/index.json\`). Never read \`components.json\` whole: it is the 1.2 MB file the tools use.
171
171
  5. **Icons**: discover names first; never invent them (\`${KNOWLEDGE_BASE}/guides/icons-policy.md\`).
172
172
  6. **Item-driven components**: declarative \`items\` (an object row reads its own \`label\` and \`value\`; \`item-text\` / \`item-value\` only for other field names, never to restate those two), \`dependencies\`, \`enforce-coherence\` — no manual watchers for coherence.
173
173
  7. **Whole pages**: one recipe under \`${KNOWLEDGE_BASE}/recipes/{vue|nuxt|inertia}/\` (see the router table).
@@ -780,7 +780,7 @@ function printFindingLegend(findings) {
780
780
  */
781
781
  /**
782
782
  * `changelog [--since <v>] [--until <v>] [--json]` — what breaks between two
783
- * releases, for anyone not driving the MCP server (a human at a terminal, CI,
783
+ * releases, then what they added (Q74.4: one line per feature), for anyone not driving the MCP server (a human at a terminal, CI,
784
784
  * or an agent whose harness has no MCP). Same data and same range rules as the
785
785
  * `list_changes` tool; this is the transport-free path to it.
786
786
  */
@@ -823,6 +823,22 @@ function changelogCommand({ since, until, json: jsonMode }) {
823
823
  if (until != null && compareReleaseVersions(at, until) > 0) return false;
824
824
  return true;
825
825
  });
826
+ // Q74.4: what the range ADDED (feat commits, one line each), oldest first.
827
+ const inRange = (at) => {
828
+ if (!ranged) return true;
829
+ if (at === 'unreleased') return until == null;
830
+ if (since != null && compareReleaseVersions(at, since) <= 0) return false;
831
+ if (until != null && compareReleaseVersions(at, until) > 0) return false;
832
+ return true;
833
+ };
834
+ const added = Object.entries(doc.added ?? {})
835
+ .filter(([at]) => inRange(at))
836
+ .flatMap(([at, entries]) =>
837
+ entries.map((entry) => ({
838
+ at,
839
+ line: typeof entry === 'string' ? entry : entry.text,
840
+ }))
841
+ );
826
842
 
827
843
  if (jsonMode) {
828
844
  console.log(
@@ -833,6 +849,7 @@ function changelogCommand({ since, until, json: jsonMode }) {
833
849
  until: until ?? doc.version,
834
850
  count: rows.length,
835
851
  breaking: rows,
852
+ added: added.map(({ at, line }) => `[${at}] ${line}`),
836
853
  guide: doc.guide,
837
854
  },
838
855
  null,
@@ -845,13 +862,26 @@ function changelogCommand({ since, until, json: jsonMode }) {
845
862
  const range = ranged
846
863
  ? `${since ?? 'the start of v3'} → ${until ?? doc.version}`
847
864
  : `the whole v2 → v3 upgrade`;
865
+ const printAdded = () => {
866
+ if (!added.length) return;
867
+ // Compact by default: the whole v3 line is hundreds of lines.
868
+ if (!ranged) {
869
+ console.log(
870
+ `\n${added.length} addition(s) over ${range}: list one hop with --since <your version>.`
871
+ );
872
+ return;
873
+ }
874
+ console.log(`\n${added.length} addition(s), ${range}:\n`);
875
+ for (const { at, line } of added) console.log(` [${at}] ${line}`);
876
+ };
848
877
  if (rows.length === 0) {
849
878
  // "Nothing breaks" and "I could not tell you" must not look alike.
850
879
  console.log(
851
880
  `[bitboss-ui] changelog: no BREAKING changes in ${range}.\n` +
852
- ' Features and fixes are deliberately not tracked here, so this\n' +
853
- ' means nothing breaks — not that nothing changed.'
881
+ ' Fixes are not tracked here, so this means nothing\n' +
882
+ ' breaks — not that nothing changed.'
854
883
  );
884
+ printAdded();
855
885
  return;
856
886
  }
857
887
  console.log(
@@ -874,6 +904,7 @@ function changelogCommand({ since, until, json: jsonMode }) {
874
904
  }
875
905
  if (entry.guide) console.log(` ${entry.guide}`);
876
906
  }
907
+ printAdded();
877
908
  console.log(`\nFull migration guide: ${doc.guide}`);
878
909
  }
879
910
 
@@ -1651,7 +1682,8 @@ Commands:
1651
1682
  \`bitboss-ui/validated\`, retired: everything is in
1652
1683
  \`bitboss-ui\`; \`--fix\` rewrites it in .vue files),
1653
1684
  \`unregistered-rule\` (a string or object rule no
1654
- \`defineRule\` in the project registers; a
1685
+ \`defineRule\` in the project registers (in a .md
1686
+ page, also one the page's own fences show); a
1655
1687
  registration it cannot read is named, never skipped:
1656
1688
  list such names under \`registeredRules\` in
1657
1689
  bitboss-ui.check.json),
@@ -1721,8 +1753,10 @@ Commands:
1721
1753
  Breaking changes between two releases — "what breaks if I
1722
1754
  bump". \`--since\` is the version you are ON, and is
1723
1755
  exclusive. With no range, every break in the v2 → v3
1724
- upgrade. Only BREAKING changes are tracked, so an empty
1725
- result means nothing breaks, not that nothing changed.
1756
+ upgrade. Then what the range added, one line per feature
1757
+ (with no range, only their count). Fixes are not tracked,
1758
+ so no breaking changes means nothing breaks, not that
1759
+ nothing changed.
1726
1760
  npx bitboss-ui changelog --since 3.0.0-beta.20
1727
1761
  Pair it with \`check --manifest\` to turn the list into the
1728
1762
  files in YOUR app that break.
@@ -20,11 +20,11 @@
20
20
  | `cacheTags` | `string \| string[] \| undefined` | | | Inertia: tags to file this visit's prefetch cache under, so a later request can invalidate the whole tagged group. |
21
21
  | `component` | `string \| undefined` | | | Inertia: the page component this visit resolves to. Rarely set by hand — the server normally decides it. |
22
22
  | `data` | `object \| undefined` | | | Request payload forwarded to Inertia when navigating via `href` in an Inertia-enabled app. Ignored when not using Inertia. |
23
- | `disabled` | `boolean \| undefined` | `false` | | Disables user interaction. - When rendering as a native button, sets the `disabled` attribute. - When rendering as a link (anchor/Inertia), removes `href`, adds `aria-disabled="true"` and `tabindex="-1"`, and prevents navigation: the link stays announced as a link but leaves the tab order. |
23
+ | `disabled` | `boolean \| undefined` | `false` | | Disables user interaction. A native button gets the `disabled` attribute; a link (anchor/Inertia) loses `href`, gets `aria-disabled="true"` and `tabindex="-1"`, and never navigates: still announced as a link, but out of the tab order. |
24
24
  | `download` | `string \| boolean \| undefined` | | | Marks an `href` link as a download: a plain `<a download>` with a native navigation (never an Inertia or router visit), so `blob:` and `data:` files work too. A string sets the suggested filename. |
25
25
  | `exactActiveClass` | `string \| undefined` | `'router-link-exact-active'` | | CSS class applied when the component renders as a link and the target of the link and the url matches exactly. Ported for Inertia compatibility. |
26
26
  | `except` | `string[] \| undefined` | | | Inertia: the inverse of `only` — properties to EXCLUDE from a partial reload. Pass one or the other, not both. |
27
- | `external` | `boolean \| undefined` | `false` | | Forces an `href` link to render as a plain `<a>` (native navigation), bypassing Inertia/router interception — the same intent as Nuxt's `NuxtLink` `external`. Use for links outside the SPA. One of the native-anchor signals alongside `target` and `download`. |
27
+ | `external` | `boolean \| undefined` | `false` | | Forces an `href` link to render as a plain `<a>` (native navigation), bypassing Inertia/router interception, as Nuxt's `NuxtLink` `external` does. For links outside the SPA; a native-anchor signal alongside `target` and `download`. |
28
28
  | `headers` | `object \| undefined` | | | Additional HTTP headers forwarded to Inertia when navigating via `href` in an Inertia-enabled app. |
29
29
  | `href` | `string \| undefined` | | | Hyperlink reference used when rendering as an anchor (or as an Inertia link in Inertia-enabled apps). If provided and not disabled, the component renders as an anchor/Inertia link. |
30
30
  | `instant` | `boolean \| undefined` | `false` | | Inertia: navigate optimistically on click and reconcile when the response lands, instead of waiting for the round trip. |
@@ -48,7 +48,7 @@
48
48
  | `queryStringArrayFormat` | `"brackets" \| "indices" \| undefined` | `'brackets'` | | Format to use when serializing array values into the query string for Inertia requests. |
49
49
  | `rel` | `string \| undefined` | | | Relationship between the current document and the linked resource. Useful for security when opening new tabs (e.g. `noopener noreferrer`). |
50
50
  | `replace` | `boolean \| undefined` | `false` | | Uses history replacement instead of push navigation. - With Vue Router (`to`), calls `router.replace`. - With Inertia (`href`), performs a replace visit. |
51
- | `tag` | `'button'` \| `'div'` \| `'span'` \| `'label'` | `'button'` | | Element for the plain (non-link) button: `'button'` (default), `'div'` / `'span'` (a clickable box inside another button or link) or `'label'` (a file-upload trigger). Ignored with `href` / `to`. Off `button`, native `type` / `disabled` drop (disabled stays a class). |
51
+ | `tag` | `'button'` \| `'div'` \| `'span'` \| `'label'` | `'button'` | | Element of the plain (non-link) button: `'button'` (default), `'div'` / `'span'` (a clickable box inside a button or link) or `'label'` (a file-upload trigger). Ignored with `href` / `to`. Off `button`, native `type` / `disabled` drop (disabled stays a class). |
52
52
  | `target` | `string \| undefined` | | | Target browsing context for anchor/Inertia links (e.g. `_self`, `_blank`). Ignored when rendering as a native button. |
53
53
  | `to` | `string \| wt \| bt \| undefined` | | | Route location to navigate to. When provided (and not disabled), the component renders as a Vue Router link. |
54
54
  | `type` | `"button" \| "submit" \| "reset" \| undefined` | `'button'` | | Native `type` attribute used when rendering as a button (e.g. `button`, `submit`, `reset`). |
@@ -547,11 +547,11 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
547
547
  | `cacheTags` | `string \| string[] \| undefined` | | | Inertia: tags to file this visit's prefetch cache under, so a later request can invalidate the whole tagged group. |
548
548
  | `component` | `string \| undefined` | | | Inertia: the page component this visit resolves to. Rarely set by hand — the server normally decides it. |
549
549
  | `data` | `object \| undefined` | | | Request payload forwarded to Inertia when navigating via `href` in an Inertia-enabled app. Ignored when not using Inertia. |
550
- | `disabled` | `boolean \| undefined` | `false` | | Disables user interaction. - When rendering as a native button, sets the `disabled` attribute. - When rendering as a link (anchor/Inertia), removes `href`, adds `aria-disabled="true"` and `tabindex="-1"`, and prevents navigation: the link stays announced as a link but leaves the tab order. |
550
+ | `disabled` | `boolean \| undefined` | `false` | | Disables user interaction. A native button gets the `disabled` attribute; a link (anchor/Inertia) loses `href`, gets `aria-disabled="true"` and `tabindex="-1"`, and never navigates: still announced as a link, but out of the tab order. |
551
551
  | `download` | `string \| boolean \| undefined` | | | Marks an `href` link as a download: a plain `<a download>` with a native navigation (never an Inertia or router visit), so `blob:` and `data:` files work too. A string sets the suggested filename. |
552
552
  | `exactActiveClass` | `string \| undefined` | `'router-link-exact-active'` | | CSS class applied when the component renders as a link and the target of the link and the url matches exactly. Ported for Inertia compatibility. |
553
553
  | `except` | `string[] \| undefined` | | | Inertia: the inverse of `only` — properties to EXCLUDE from a partial reload. Pass one or the other, not both. |
554
- | `external` | `boolean \| undefined` | `false` | | Forces an `href` link to render as a plain `<a>` (native navigation), bypassing Inertia/router interception — the same intent as Nuxt's `NuxtLink` `external`. Use for links outside the SPA. One of the native-anchor signals alongside `target` and `download`. |
554
+ | `external` | `boolean \| undefined` | `false` | | Forces an `href` link to render as a plain `<a>` (native navigation), bypassing Inertia/router interception, as Nuxt's `NuxtLink` `external` does. For links outside the SPA; a native-anchor signal alongside `target` and `download`. |
555
555
  | `headers` | `object \| undefined` | | | Additional HTTP headers forwarded to Inertia when navigating via `href` in an Inertia-enabled app. |
556
556
  | `href` | `string \| undefined` | | | Hyperlink reference used when rendering as an anchor (or as an Inertia link in Inertia-enabled apps). If provided and not disabled, the component renders as an anchor/Inertia link. |
557
557
  | `instant` | `boolean \| undefined` | `false` | | Inertia: navigate optimistically on click and reconcile when the response lands, instead of waiting for the round trip. |
@@ -575,7 +575,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
575
575
  | `queryStringArrayFormat` | `"brackets" \| "indices" \| undefined` | `'brackets'` | | Format to use when serializing array values into the query string for Inertia requests. |
576
576
  | `rel` | `string \| undefined` | | | Relationship between the current document and the linked resource. Useful for security when opening new tabs (e.g. `noopener noreferrer`). |
577
577
  | `replace` | `boolean \| undefined` | `false` | | Uses history replacement instead of push navigation. - With Vue Router (`to`), calls `router.replace`. - With Inertia (`href`), performs a replace visit. |
578
- | `tag` | `'button'` \| `'div'` \| `'span'` \| `'label'` | `'button'` | | Element for the plain (non-link) button: `'button'` (default), `'div'` / `'span'` (a clickable box inside another button or link) or `'label'` (a file-upload trigger). Ignored with `href` / `to`. Off `button`, native `type` / `disabled` drop (disabled stays a class). |
578
+ | `tag` | `'button'` \| `'div'` \| `'span'` \| `'label'` | `'button'` | | Element of the plain (non-link) button: `'button'` (default), `'div'` / `'span'` (a clickable box inside a button or link) or `'label'` (a file-upload trigger). Ignored with `href` / `to`. Off `button`, native `type` / `disabled` drop (disabled stays a class). |
579
579
  | `target` | `string \| undefined` | | | Target browsing context for anchor/Inertia links (e.g. `_self`, `_blank`). Ignored when rendering as a native button. |
580
580
  | `text` | `string \| undefined` | | | Fallback text content rendered when no default slot is provided. |
581
581
  | `to` | `string \| wt \| bt \| undefined` | | | Route location to navigate to. When provided (and not disabled), the component renders as a Vue Router link. |
@@ -712,9 +712,9 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
712
712
  | `autofocus` | `Booleanish \| undefined` | | | Focuses the field when it mounts (a dialog already open included), unless the user is typing in another field. |
713
713
  | `checked` | `boolean \| undefined` | `undefined` | | Defines the input as checked. |
714
714
  | `compact` | `boolean \| undefined` | `false` | | Compact density. Swaps `--bb-label-spacing-y` for `--bb-label-spacing-y-compact`, so tune a compact field's label gap with the `-compact` token, not the base one. The same meaning as on the checkbox, radio and switch groups. |
715
- | `description` | `string \| undefined` | | | Descriptive text displayed below the label and above the input. Unlike the hint it is always visible, and it is linked to the input via `aria-describedby` (after any `errors` / `warnings`, before the `hint`). |
715
+ | `description` | `string \| undefined` | | | Text between the label and the input. Unlike the hint it is always visible; it joins the input's `aria-describedby` after any `errors` / `warnings`, before the `hint`. |
716
716
  | `disabled` | `boolean \| undefined` | `false` | | Disables the component. |
717
- | `errors` | `string \| string[] \| undefined` | | | Error messages under the field, a string or string array. They render in an `aria-live="polite"` region and are referenced first from the control's `aria-describedby`, so they are re-read when it regains focus. |
717
+ | `errors` | `string \| string[] \| undefined` | | | Error messages under the field, a string or array, in an `aria-live="polite"` region and first in the control's `aria-describedby`, so they are re-read when it regains focus. |
718
718
  | `falseValue` | `any` | | | Value of the input when unchecked. It handles any kind of serializable object. |
719
719
  | `formless` | `boolean \| undefined` | `false` | | Keep this control out of the enclosing `BbForm` entirely: not submitted, not dirty, not reset, not validated. For a control that is not form data (a filter, a "show advanced" switch). |
720
720
  | `hasErrors` | `boolean \| undefined` | `false` | | Define if the component should be in an error state. It usually attaches a CSS class for styling purposes. |
@@ -728,7 +728,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
728
728
  | `label` | `string` | | yes | Text content of the label of the element. |
729
729
  | `labelAlign` | `FieldCrossAlign \| undefined` | | | Vertical position of the label block in a side-by-side row; not `labelPosition`, the horizontal one. Omitted, the label's first line meets the control's. |
730
730
  | `labelPosition` | `"left" \| "center" \| "right" \| undefined` | | | Sets the text alignment of the label. |
731
- | `layout` | `Responsive<FieldLayout> \| undefined` | `"auto"` | | How the label and the input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'`, `'input-fill'`, a ratio like `'xx xxxxx'`, or a per-breakpoint map such as `{ default: 'vertical', md: 'xx xxxxx' }`. |
731
+ | `layout` | `Responsive<FieldLayout> \| undefined` | `"auto"` | | How label and input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'`, `'input-fill'`, a ratio like `'xx xxxxx'`, or a per-breakpoint map (`{ default: 'vertical', md: 'xx xxxxx' }`). |
732
732
  | `modelValue` | `any` | | | Used by v-model. Can be any serializable type. |
733
733
  | `name` | `string \| undefined` | | | Defines the name of the input. |
734
734
  | `persistentHint` | `boolean \| undefined` | `false` | | Keeps the hint displayed. |
@@ -738,9 +738,9 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
738
738
  | `rules` | `RuleExpression<any>` | | | vee-validate rules for this field. Only inside a `BbForm`. |
739
739
  | `submitWhenFalse` | `boolean \| undefined` | `false` | | Will submit "falseValue" if the input is not checked. Otherwise "trueValue" will be submitted. |
740
740
  | `trueValue` | `any` | | | Value of the input when checked. It handles any kind of serializable object. |
741
- | `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; the field's `name` never shows. Set it when the label is not a good noun for a sentence (an icon-only label, a question) or is hidden. Only inside a `BbForm`. |
742
- | `validationMode` | `ValidationMode \| undefined` | | | When this field validates as the user works: `aggressive` (every change), `balanced` (on leave, then live while it shows an error), `lazy` (on leave), `submit-only` or `submit-live`. Unset: the `BbForm`'s `validation-mode`, then `formControls.validationMode` (`'balanced'`). Only in a `BbForm`. |
743
- | `warnings` | `string \| string[] \| undefined` | | | Warning messages under the field, a string or string array: amber, in their own `aria-live` region, and in the control's `aria-describedby` after the errors. A non-empty list implies the warning state; errors win when both are set. |
741
+ | `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; never the `name`. Set it when the label is hidden or reads badly in a sentence (icon-only, a question). `BbForm` only. |
742
+ | `validationMode` | `ValidationMode \| undefined` | | | When the field validates: `aggressive` (each change), `balanced` (on leave, then live while in error), `lazy` (on leave), `submit-only`, `submit-live`. Unset: the `BbForm`'s `validation-mode`, then `formControls.validationMode` (`'balanced'`). `BbForm` only. |
743
+ | `warnings` | `string \| string[] \| undefined` | | | Warning messages under the field, a string or array: amber, in their own `aria-live` region and in the control's `aria-describedby` after the errors. Non-empty turns on the warning state; errors win when both are set. |
744
744
 
745
745
  ## Passthrough parts and states
746
746
 
@@ -987,11 +987,11 @@ for a preset that fits any row type (never `<any>`), not an imported
987
987
  | `compact` | `boolean \| undefined` | `false` | | Compact density. Swaps `--bb-label-spacing-y` for `--bb-label-spacing-y-compact`, so tune a compact group's label gap with the `-compact` token, not the base one. |
988
988
  | `dependencies` | `unknown[] \| undefined` | | | Defines an array of dependencies that will trigger actions in the component upon change. |
989
989
  | `depsDebounceTime` | `number \| undefined` | | | Timeout used to debounce response to changes to dependencies. |
990
- | `description` | `string \| undefined` | | | Descriptive text displayed below the label and above the input. Unlike the hint it is always visible, and it is linked to the input via `aria-describedby` (after any `errors` / `warnings`, before the `hint`). |
990
+ | `description` | `string \| undefined` | | | Text between the label and the input. Unlike the hint it is always visible; it joins the input's `aria-describedby` after any `errors` / `warnings`, before the `hint`. |
991
991
  | `disabled` | `boolean \| undefined` | `false` | | Disables the component. |
992
992
  | `emptyText` | `string \| undefined` | | | String displayed when there are no items to display. |
993
993
  | `enforceCoherence` | `boolean \| undefined` | `false` | | After every applied, successful load (an empty one included), drops the model values the options no longer contain (a single value resets to `null`). A failed load never prunes. |
994
- | `errors` | `string \| string[] \| undefined` | | | Error messages under the field, a string or string array. They render in an `aria-live="polite"` region and are referenced first from the control's `aria-describedby`, so they are re-read when it regains focus. |
994
+ | `errors` | `string \| string[] \| undefined` | | | Error messages under the field, a string or array, in an `aria-live="polite"` region and first in the control's `aria-describedby`, so they are re-read when it regains focus. |
995
995
  | `failedText` | `string \| undefined` | `the localized `common.failedText` ("Couldn't load items")` | | String displayed in the empty state while the latest load FAILED (the `items` provider threw): the twin of `emptyText` for that case. |
996
996
  | `formless` | `boolean \| undefined` | `false` | | Keep this control out of the enclosing `BbForm` entirely: not submitted, not dirty, not reset, not validated. For a control that is not form data (a filter, a "show advanced" switch). |
997
997
  | `hasErrors` | `boolean \| undefined` | `false` | | Define if the component should be in an error state. It usually attaches a CSS class for styling purposes. |
@@ -1011,7 +1011,7 @@ for a preset that fits any row type (never `<any>`), not an imported
1011
1011
  | `itemText` | `ItemAccessor<T, string> \| undefined` | `own `label`, else `text`, else `JSON.stringify(item)`` | | Defines a path that returns a property of the object to use as text or a function that returns a string. Unset: own `label`, else `text` (`{ label, value }` needs no accessor). |
1012
1012
  | `itemValue` | `ItemAccessor<T> \| undefined` | `own `value`, else the whole item` | | Defines a path that returns a property of the object to use as value or a function that returns any value. Unset: own `value`, even `null`. |
1013
1013
  | `labelAlign` | `'top'` \| `'center'` \| `'bottom'` | | | Vertical position of the label block in a side-by-side row; not `labelPosition`, the horizontal one. Omitted, the label's first line meets the control's. |
1014
- | `layout` | `Responsive<FieldLayout> \| undefined` | | | How the label and the input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'`, `'input-fill'`, a ratio like `'xx xxxxx'`, or a per-breakpoint map such as `{ default: 'vertical', md: 'xx xxxxx' }`. |
1014
+ | `layout` | `Responsive<FieldLayout> \| undefined` | | | How label and input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'`, `'input-fill'`, a ratio like `'xx xxxxx'`, or a per-breakpoint map (`{ default: 'vertical', md: 'xx xxxxx' }`). |
1015
1015
  | `legend` | `string` | | yes | Text content of the legend. |
1016
1016
  | `legendMode` | `"outside" \| "inside" \| undefined` | | | Legend rendering mode. `'outside'` renders the legend above the fieldset in the normal flow; `'inside'` overlays the legend inside the fieldset border. Floating mode is not supported for option groups (checkbox, radio or switch) — there is no single field for the legend to float against. |
1017
1017
  | `legendPosition` | `"left" \| "center" \| "right" \| undefined` | | | Sets the text alignment of the label. |
@@ -1027,9 +1027,9 @@ for a preset that fits any row type (never `<any>`), not an imported
1027
1027
  | `reverse` | `boolean \| undefined` | `false` | | Reverses the layout. Applicable in every direction the order of the label and the input is swapped. |
1028
1028
  | `rules` | `RuleExpression<any>` | | | vee-validate rules for this field. Only inside a `BbForm`. |
1029
1029
  | `selectable` | `boolean \| ((item: T) => boolean) \| undefined` | `true` | | Defines whether options are selectable. Can be a global boolean that affects all options or a function that accepts an item and returns a boolean that only affects that item. Non-selectable options are rendered disabled. |
1030
- | `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; the field's `name` never shows. Set it when the label is not a good noun for a sentence (an icon-only label, a question) or is hidden. Only inside a `BbForm`. |
1031
- | `validationMode` | `'aggressive'` \| `'balanced'` \| `'lazy'` \| `'submit-only'` \| `'submit-live'` | | | When this field validates as the user works: `aggressive` (every change), `balanced` (on leave, then live while it shows an error), `lazy` (on leave), `submit-only` or `submit-live`. Unset: the `BbForm`'s `validation-mode`, then `formControls.validationMode` (`'balanced'`). Only in a `BbForm`. |
1032
- | `warnings` | `string \| string[] \| undefined` | | | Warning messages under the field, a string or string array: amber, in their own `aria-live` region, and in the control's `aria-describedby` after the errors. A non-empty list implies the warning state; errors win when both are set. |
1030
+ | `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; never the `name`. Set it when the label is hidden or reads badly in a sentence (icon-only, a question). `BbForm` only. |
1031
+ | `validationMode` | `'aggressive'` \| `'balanced'` \| `'lazy'` \| `'submit-only'` \| `'submit-live'` | | | When the field validates: `aggressive` (each change), `balanced` (on leave, then live while in error), `lazy` (on leave), `submit-only`, `submit-live`. Unset: the `BbForm`'s `validation-mode`, then `formControls.validationMode` (`'balanced'`). `BbForm` only. |
1032
+ | `warnings` | `string \| string[] \| undefined` | | | Warning messages under the field, a string or array: amber, in their own `aria-live` region and in the control's `aria-describedby` after the errors. Non-empty turns on the warning state; errors win when both are set. |
1033
1033
 
1034
1034
  ## Passthrough parts and states
1035
1035
 
@@ -689,10 +689,10 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
689
689
  | `autocomplete` | `string \| undefined` | `"off"` | | Guides the browser as to the type of information expected in the field. |
690
690
  | `autofocus` | `Booleanish \| undefined` | | | Focuses the field when it mounts (a dialog already open included), unless the user is typing in another field. |
691
691
  | `clearable` | `boolean \| undefined` | `false` | | Displays a clear button when the input has a value and is being interacted with. |
692
- | `compact` | `boolean \| undefined` | `false` | | Compact density. Swaps `--bb-input-h`, `--bb-input-py` and `--bb-label-spacing-y` for their `-compact` twins on the field, so a `--bb-input-h` you set on the component is ignored while `compact` is on: size a compact field with `--bb-input-h-compact` (and `--bb-input-py-compact`) instead. |
693
- | `description` | `string \| undefined` | | | Descriptive text displayed below the label and above the input. Unlike the hint it is always visible, and it is linked to the input via `aria-describedby` (after any `errors` / `warnings`, before the `hint`). |
692
+ | `compact` | `boolean \| undefined` | `false` | | Compact density: the field uses the `-compact` twins of `--bb-input-h`, `--bb-input-py` and `--bb-label-spacing-y`, so a `--bb-input-h` set on the component is ignored while it is on. Size a compact field with `--bb-input-h-compact` (and `--bb-input-py-compact`). |
693
+ | `description` | `string \| undefined` | | | Text between the label and the input. Unlike the hint it is always visible; it joins the input's `aria-describedby` after any `errors` / `warnings`, before the `hint`. |
694
694
  | `disabled` | `boolean \| undefined` | `false` | | Disables the component. |
695
- | `errors` | `string \| string[] \| undefined` | | | Error messages under the field, a string or string array. They render in an `aria-live="polite"` region and are referenced first from the control's `aria-describedby`, so they are re-read when it regains focus. |
695
+ | `errors` | `string \| string[] \| undefined` | | | Error messages under the field, a string or array, in an `aria-live="polite"` region and first in the control's `aria-describedby`, so they are re-read when it regains focus. |
696
696
  | `eyeDropper` | `boolean \| undefined` | `false` | | Show an eyedropper button to sample any color on screen. Uses the native EyeDropper API — hidden automatically on unsupported browsers. |
697
697
  | `formless` | `boolean \| undefined` | `false` | | Keep this control out of the enclosing `BbForm` entirely: not submitted, not dirty, not reset, not validated. For a control that is not form data (a filter, a "show advanced" switch). |
698
698
  | `hasErrors` | `boolean \| undefined` | `false` | | Define if the component should be in an error state. It usually attaches a CSS class for styling purposes. |
@@ -705,7 +705,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
705
705
  | `labelAlign` | `FieldCrossAlign \| undefined` | | | Vertical position of the label block in a side-by-side row; not `labelPosition`, the horizontal one. Omitted, the label's first line meets the control's. |
706
706
  | `labelMode` | `"outside" \| "floating" \| "inside" \| undefined` | | | Label rendering mode: `outside` places the label above the input, `floating` overlays it on the field and shrinks it when a value is set, `inside` keeps it inside the field. |
707
707
  | `labelPosition` | `"left" \| "center" \| "right" \| undefined` | | | Sets the text alignment of the label. |
708
- | `layout` | `Responsive<FieldLayout> \| undefined` | | | How the label and the input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'`, `'input-fill'`, a ratio like `'xx xxxxx'`, or a per-breakpoint map such as `{ default: 'vertical', md: 'xx xxxxx' }`. |
708
+ | `layout` | `Responsive<FieldLayout> \| undefined` | | | How label and input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'`, `'input-fill'`, a ratio like `'xx xxxxx'`, or a per-breakpoint map (`{ default: 'vertical', md: 'xx xxxxx' }`). |
709
709
  | `loading` | `boolean \| undefined` | `false` | | Sets the component in a loading state, usually triggering some visual styles. |
710
710
  | `modelValue` | `string \| null \| undefined` | `null` | | Used by v-model. |
711
711
  | `name` | `string \| undefined` | | | Defines the name of the input. |
@@ -718,10 +718,10 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
718
718
  | `reverse` | `boolean \| undefined` | `false` | | Reverses the layout. Applicable in every direction the order of the label and the input is swapped. |
719
719
  | `rules` | `RuleExpression<string \| null>` | | | vee-validate rules for this field. Only inside a `BbForm`. |
720
720
  | `swatches` | `boolean \| string[][] \| undefined` | | | Controls swatches display in the picker: - `true` — show the built-in Material-palette swatches - `string[][]` — show custom swatches (each inner array = a column of shades) - `false` / omitted — no swatches section |
721
- | `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; the field's `name` never shows. Set it when the label is not a good noun for a sentence (an icon-only label, a question) or is hidden. Only inside a `BbForm`. |
722
- | `validationMode` | `ValidationMode \| undefined` | | | When this field validates as the user works: `aggressive` (every change), `balanced` (on leave, then live while it shows an error), `lazy` (on leave), `submit-only` or `submit-live`. Unset: the `BbForm`'s `validation-mode`, then `formControls.validationMode` (`'balanced'`). Only in a `BbForm`. |
723
- | `variant` | `InputVariantType \| undefined` | `'outline'` | | Visual variant of the field box, with the `BbButton` names and tokens. Colours only, so a form never reflows; `'ghost'` has no border in any state, and `'none'` takes no look at all (you own hover, focus and the states). Register more with the plugin's `inputVariants`. |
724
- | `warnings` | `string \| string[] \| undefined` | | | Warning messages under the field, a string or string array: amber, in their own `aria-live` region, and in the control's `aria-describedby` after the errors. A non-empty list implies the warning state; errors win when both are set. |
721
+ | `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; never the `name`. Set it when the label is hidden or reads badly in a sentence (icon-only, a question). `BbForm` only. |
722
+ | `validationMode` | `ValidationMode \| undefined` | | | When the field validates: `aggressive` (each change), `balanced` (on leave, then live while in error), `lazy` (on leave), `submit-only`, `submit-live`. Unset: the `BbForm`'s `validation-mode`, then `formControls.validationMode` (`'balanced'`). `BbForm` only. |
723
+ | `variant` | `InputVariantType \| undefined` | `'outline'` | | The field box's look, with `BbButton`'s names and tokens. Colours only, so a form never reflows; `'ghost'` has no border in any state, `'none'` no look at all (you own hover, focus and the states). Register more with the plugin's `inputVariants`. |
724
+ | `warnings` | `string \| string[] \| undefined` | | | Warning messages under the field, a string or array: amber, in their own `aria-live` region and in the control's `aria-describedby` after the errors. Non-empty turns on the warning state; errors win when both are set. |
725
725
 
726
726
  ## Passthrough parts and states
727
727
 
@@ -885,12 +885,12 @@ label>')` matches nothing; query each segment by its own label instead. See
885
885
  | `autocomplete` | `string \| undefined` | `"off"` | | Guides the browser as to the type of information expected in the field. |
886
886
  | `autofocus` | `Booleanish \| undefined` | | | Focuses the field when it mounts (a dialog already open included), unless the user is typing in another field. |
887
887
  | `clearable` | `boolean \| undefined` | `false` | | Displays a clear button when the input has a value and is being interacted with. |
888
- | `compact` | `boolean \| undefined` | `false` | | Compact density. Swaps `--bb-input-h`, `--bb-input-py` and `--bb-label-spacing-y` for their `-compact` twins on the field, so a `--bb-input-h` you set on the component is ignored while `compact` is on: size a compact field with `--bb-input-h-compact` (and `--bb-input-py-compact`) instead. |
889
- | `description` | `string \| undefined` | | | Descriptive text displayed below the label and above the input. Unlike the hint it is always visible, and it is linked to the input via `aria-describedby` (after any `errors` / `warnings`, before the `hint`). |
888
+ | `compact` | `boolean \| undefined` | `false` | | Compact density: the field uses the `-compact` twins of `--bb-input-h`, `--bb-input-py` and `--bb-label-spacing-y`, so a `--bb-input-h` set on the component is ignored while it is on. Size a compact field with `--bb-input-h-compact` (and `--bb-input-py-compact`). |
889
+ | `description` | `string \| undefined` | | | Text between the label and the input. Unlike the hint it is always visible; it joins the input's `aria-describedby` after any `errors` / `warnings`, before the `hint`. |
890
890
  | `disableCalendar` | `boolean \| "mobile" \| "desktop" \| undefined` | `false` | | Disables the calendar: hides the trigger button and prevents the popover from opening. Use `'desktop'` to disable it on desktop only (keeping the calendar for mobile, complementing `disableWriting: 'mobile'`) or `'mobile'` to disable it on mobile only. |
891
891
  | `disabled` | `boolean \| undefined` | `false` | | Disables the component. |
892
892
  | `disableWriting` | `boolean \| "mobile" \| "desktop" \| undefined` | `false` | | Disables typing into the input. Use `'mobile'` to disable typing only on mobile, `'desktop'` to disable typing only on desktop. |
893
- | `errors` | `string \| string[] \| undefined` | | | Error messages under the field, a string or string array. They render in an `aria-live="polite"` region and are referenced first from the control's `aria-describedby`, so they are re-read when it regains focus. |
893
+ | `errors` | `string \| string[] \| undefined` | | | Error messages under the field, a string or array, in an `aria-live="polite"` region and first in the control's `aria-describedby`, so they are re-read when it regains focus. |
894
894
  | `firstDayOfWeek` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | `the locale's week start` | | First day of the week (0 = Sunday … 6 = Saturday). Omitted, the active locale's week start: Monday for `it`, `de`, `fr`, `en-gb`…, Sunday for bare `en` (US) and `pt` (Brazil). |
895
895
  | `floating` | `boolean \| undefined` | `false` | | If true the date will have a format YYYY-MM-DD instead of the default ISO string. |
896
896
  | `formless` | `boolean \| undefined` | `false` | | Keep this control out of the enclosing `BbForm` entirely: not submitted, not dirty, not reset, not validated. For a control that is not form data (a filter, a "show advanced" switch). |
@@ -904,7 +904,7 @@ label>')` matches nothing; query each segment by its own label instead. See
904
904
  | `labelAlign` | `'top'` \| `'center'` \| `'bottom'` | | | Vertical position of the label block in a side-by-side row; not `labelPosition`, the horizontal one. Omitted, the label's first line meets the control's. |
905
905
  | `labelMode` | `"outside" \| "floating" \| "inside" \| undefined` | | | Label rendering mode. |
906
906
  | `labelPosition` | `"left" \| "center" \| "right" \| undefined` | | | Sets the text alignment of the label. |
907
- | `layout` | `Responsive<FieldLayout> \| undefined` | | | How the label and the input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'`, `'input-fill'`, a ratio like `'xx xxxxx'`, or a per-breakpoint map such as `{ default: 'vertical', md: 'xx xxxxx' }`. |
907
+ | `layout` | `Responsive<FieldLayout> \| undefined` | | | How label and input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'`, `'input-fill'`, a ratio like `'xx xxxxx'`, or a per-breakpoint map (`{ default: 'vertical', md: 'xx xxxxx' }`). |
908
908
  | `loading` | `boolean \| undefined` | `false` | | Sets the component in a loading state, usually triggering some visual styles. |
909
909
  | `max` | `string \| undefined` | | | Maximum selectable date in `YYYY-MM-DD` format — or, with `type="datetime"`, `YYYY-MM-DDTHH:mm` to tighten the boundary day to a minute. Values outside both patterns throw during setup to surface configuration mistakes. |
910
910
  | `min` | `string \| undefined` | | | Minimum selectable date in `YYYY-MM-DD` format — or, with `type="datetime"`, `YYYY-MM-DDTHH:mm` to tighten the boundary day to a minute. Values outside both patterns throw during setup to surface configuration mistakes. |
@@ -926,10 +926,10 @@ label>')` matches nothing; query each segment by its own label instead. See
926
926
  | `transitionDuration` | `number \| undefined` | | | Transition duration, in milliseconds, for the popover appearance. |
927
927
  | `type` | `'date'` \| `'month'` \| `'year'` \| `'datetime'` | `"date"` | | What the field edits, and so its segments and emitted shape: `date` (`DD/MM/YYYY`), `datetime` (adds the time segments and rail), `month` (`MM/YYYY`, emits `YYYY-MM`) or `year` (`YYYY`). `month` / `year` force `floating`, ignore `utc`, and leave the time props inert. |
928
928
  | `utc` | `boolean \| undefined` | `false` | | Emits UTC ISO strings (`…Z`) instead of the local offset (`…+01:00`). Emission only: incoming values parse the same either way, and the field always displays local time. Ignored with `floating`. |
929
- | `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; the field's `name` never shows. Set it when the label is not a good noun for a sentence (an icon-only label, a question) or is hidden. Only inside a `BbForm`. |
930
- | `validationMode` | `'aggressive'` \| `'balanced'` \| `'lazy'` \| `'submit-only'` \| `'submit-live'` | | | When this field validates as the user works: `aggressive` (every change), `balanced` (on leave, then live while it shows an error), `lazy` (on leave), `submit-only` or `submit-live`. Unset: the `BbForm`'s `validation-mode`, then `formControls.validationMode` (`'balanced'`). Only in a `BbForm`. |
931
- | `variant` | `'outline'` \| `'secondary'` \| `'none'` \| `'ghost'` | `'outline'` | | Visual variant of the field box, with the `BbButton` names and tokens. Colours only, so a form never reflows; `'ghost'` has no border in any state, and `'none'` takes no look at all (you own hover, focus and the states). Register more with the plugin's `inputVariants`. |
932
- | `warnings` | `string \| string[] \| undefined` | | | Warning messages under the field, a string or string array: amber, in their own `aria-live` region, and in the control's `aria-describedby` after the errors. A non-empty list implies the warning state; errors win when both are set. |
929
+ | `validationLabel` | `string \| undefined` | | | The field's name in validation messages (`{field}`): "Email is not valid". Defaults to the `label` (or `legend`), then "This field"; never the `name`. Set it when the label is hidden or reads badly in a sentence (icon-only, a question). `BbForm` only. |
930
+ | `validationMode` | `'aggressive'` \| `'balanced'` \| `'lazy'` \| `'submit-only'` \| `'submit-live'` | | | When the field validates: `aggressive` (each change), `balanced` (on leave, then live while in error), `lazy` (on leave), `submit-only`, `submit-live`. Unset: the `BbForm`'s `validation-mode`, then `formControls.validationMode` (`'balanced'`). `BbForm` only. |
931
+ | `variant` | `'outline'` \| `'secondary'` \| `'none'` \| `'ghost'` | `'outline'` | | The field box's look, with `BbButton`'s names and tokens. Colours only, so a form never reflows; `'ghost'` has no border in any state, `'none'` no look at all (you own hover, focus and the states). Register more with the plugin's `inputVariants`. |
932
+ | `warnings` | `string \| string[] \| undefined` | | | Warning messages under the field, a string or array: amber, in their own `aria-live` region and in the control's `aria-describedby` after the errors. Non-empty turns on the warning state; errors win when both are set. |
933
933
 
934
934
  ## Passthrough parts and states
935
935