@nadicodeai/ui 12.1.165 → 12.1.166

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 (363) hide show
  1. package/AGENTS.md +8 -9
  2. package/README.md +2 -17
  3. package/dist/components/accordion.d.ts.map +1 -1
  4. package/dist/components/accordion.js +2 -1
  5. package/dist/components/account-menu.d.ts.map +1 -1
  6. package/dist/components/account-menu.js +5 -3
  7. package/dist/components/activity-calendar.d.ts.map +1 -1
  8. package/dist/components/activity-calendar.js +3 -2
  9. package/dist/components/agent-card.js +1 -1
  10. package/dist/components/agent-orb.d.ts.map +1 -1
  11. package/dist/components/agent-orb.js +1 -0
  12. package/dist/components/agent-profile.d.ts.map +1 -1
  13. package/dist/components/agent-profile.js +7 -4
  14. package/dist/components/alert-dialog.d.ts +1 -1
  15. package/dist/components/alert-dialog.d.ts.map +1 -1
  16. package/dist/components/alert-dialog.js +3 -3
  17. package/dist/components/app-mark.js +1 -1
  18. package/dist/components/avatar.js +8 -8
  19. package/dist/components/badge.d.ts +11 -16
  20. package/dist/components/badge.d.ts.map +1 -1
  21. package/dist/components/badge.js +7 -43
  22. package/dist/components/breadcrumb.d.ts.map +1 -1
  23. package/dist/components/breadcrumb.js +3 -2
  24. package/dist/components/button.d.ts +5 -21
  25. package/dist/components/button.d.ts.map +1 -1
  26. package/dist/components/button.js +6 -76
  27. package/dist/components/calendar.d.ts +1 -1
  28. package/dist/components/calendar.d.ts.map +1 -1
  29. package/dist/components/calendar.js +3 -3
  30. package/dist/components/card-row.js +1 -1
  31. package/dist/components/card.d.ts +4 -1
  32. package/dist/components/card.d.ts.map +1 -1
  33. package/dist/components/card.js +3 -2
  34. package/dist/components/catalog-drawer.d.ts.map +1 -1
  35. package/dist/components/catalog-drawer.js +3 -2
  36. package/dist/components/chart-axis.d.ts.map +1 -1
  37. package/dist/components/chart-axis.js +0 -2
  38. package/dist/components/chart-bar-horizontal.d.ts.map +1 -1
  39. package/dist/components/chart-bar-horizontal.js +16 -14
  40. package/dist/components/chart-card.d.ts.map +1 -1
  41. package/dist/components/chart-card.js +0 -1
  42. package/dist/components/chart.d.ts.map +1 -1
  43. package/dist/components/chart.js +18 -32
  44. package/dist/components/checkbox.d.ts +3 -2
  45. package/dist/components/checkbox.d.ts.map +1 -1
  46. package/dist/components/checkbox.js +4 -3
  47. package/dist/components/choicebox.d.ts +5 -5
  48. package/dist/components/choicebox.d.ts.map +1 -1
  49. package/dist/components/choicebox.js +6 -5
  50. package/dist/components/clearable-input.d.ts.map +1 -1
  51. package/dist/components/clearable-input.js +2 -2
  52. package/dist/components/code-block.d.ts.map +1 -1
  53. package/dist/components/code-block.js +4 -2
  54. package/dist/components/collapsible.js +1 -1
  55. package/dist/components/collection-filters.d.ts.map +1 -1
  56. package/dist/components/collection-filters.js +1 -0
  57. package/dist/components/collection-page.js +1 -1
  58. package/dist/components/collection-sort.d.ts.map +1 -1
  59. package/dist/components/collection-sort.js +1 -4
  60. package/dist/components/collection-toolbar.js +1 -1
  61. package/dist/components/combobox.js +4 -4
  62. package/dist/components/command.d.ts.map +1 -1
  63. package/dist/components/command.js +1 -1
  64. package/dist/components/confirm-dialog.js +2 -2
  65. package/dist/components/context-menu.d.ts +1 -1
  66. package/dist/components/context-menu.d.ts.map +1 -1
  67. package/dist/components/copy-button.js +1 -1
  68. package/dist/components/data-table.d.ts +11 -3
  69. package/dist/components/data-table.d.ts.map +1 -1
  70. package/dist/components/data-table.js +12 -4
  71. package/dist/components/dialog.d.ts +1 -1
  72. package/dist/components/dialog.d.ts.map +1 -1
  73. package/dist/components/dialog.js +2 -2
  74. package/dist/components/drawer.d.ts +4 -4
  75. package/dist/components/drawer.d.ts.map +1 -1
  76. package/dist/components/drawer.js +14 -15
  77. package/dist/components/dropdown-menu.d.ts +1 -1
  78. package/dist/components/dropdown-menu.d.ts.map +1 -1
  79. package/dist/components/dropdown-menu.js +9 -5
  80. package/dist/components/empty-art.js +1 -1
  81. package/dist/components/empty-state.js +2 -2
  82. package/dist/components/entry-scene.d.ts.map +1 -1
  83. package/dist/components/entry-scene.js +2 -3
  84. package/dist/components/field.js +3 -3
  85. package/dist/components/file-diff.d.ts.map +1 -1
  86. package/dist/components/file-diff.js +5 -2
  87. package/dist/components/footer.d.ts +1 -1
  88. package/dist/components/footer.d.ts.map +1 -1
  89. package/dist/components/footer.js +3 -3
  90. package/dist/components/form-dialog.d.ts.map +1 -1
  91. package/dist/components/form-dialog.js +2 -1
  92. package/dist/components/form-drawer.d.ts +7 -9
  93. package/dist/components/form-drawer.d.ts.map +1 -1
  94. package/dist/components/form-drawer.js +5 -5
  95. package/dist/components/form.d.ts +26 -0
  96. package/dist/components/form.d.ts.map +1 -0
  97. package/dist/components/form.js +29 -0
  98. package/dist/components/gauge.js +2 -2
  99. package/dist/components/heatmap/calendar-heatmap.d.ts.map +1 -1
  100. package/dist/components/heatmap/calendar-heatmap.js +15 -13
  101. package/dist/components/hive-scene.d.ts.map +1 -1
  102. package/dist/components/hive-scene.js +3 -1
  103. package/dist/components/hover-card.d.ts +1 -1
  104. package/dist/components/hover-card.d.ts.map +1 -1
  105. package/dist/components/hover-card.js +1 -1
  106. package/dist/components/input-group.d.ts +5 -6
  107. package/dist/components/input-group.d.ts.map +1 -1
  108. package/dist/components/input-group.js +8 -11
  109. package/dist/components/input-otp.d.ts.map +1 -1
  110. package/dist/components/input-otp.js +2 -2
  111. package/dist/components/item.d.ts +1 -1
  112. package/dist/components/item.js +2 -2
  113. package/dist/components/kbd.d.ts +6 -9
  114. package/dist/components/kbd.d.ts.map +1 -1
  115. package/dist/components/kbd.js +7 -3
  116. package/dist/components/message-notice.js +1 -1
  117. package/dist/components/middle-truncate.d.ts.map +1 -1
  118. package/dist/components/middle-truncate.js +1 -0
  119. package/dist/components/model-identity.d.ts +4 -4
  120. package/dist/components/model-identity.d.ts.map +1 -1
  121. package/dist/components/model-identity.js +2 -2
  122. package/dist/components/model-mark.d.ts.map +1 -1
  123. package/dist/components/model-mark.js +4 -2
  124. package/dist/components/multi-select.js +2 -2
  125. package/dist/components/native-select.d.ts.map +1 -1
  126. package/dist/components/native-select.js +2 -6
  127. package/dist/components/notice-banner.js +1 -1
  128. package/dist/components/object-page.js +2 -2
  129. package/dist/components/object-workspace.d.ts.map +1 -1
  130. package/dist/components/object-workspace.js +1 -3
  131. package/dist/components/overview-page.d.ts +1 -1
  132. package/dist/components/overview-page.d.ts.map +1 -1
  133. package/dist/components/overview-page.js +2 -1
  134. package/dist/components/page-actions.d.ts.map +1 -1
  135. package/dist/components/page-actions.js +9 -4
  136. package/dist/components/pairing-code.js +1 -1
  137. package/dist/components/pending-label.d.ts.map +1 -1
  138. package/dist/components/pending-label.js +0 -1
  139. package/dist/components/popover.d.ts +1 -1
  140. package/dist/components/popover.d.ts.map +1 -1
  141. package/dist/components/portal-sidebar-frame.d.ts.map +1 -1
  142. package/dist/components/portal-sidebar-frame.js +1 -1
  143. package/dist/components/product-message.d.ts.map +1 -1
  144. package/dist/components/product-message.js +1 -0
  145. package/dist/components/progress-checklist.d.ts.map +1 -1
  146. package/dist/components/progress-checklist.js +2 -2
  147. package/dist/components/progress.d.ts.map +1 -1
  148. package/dist/components/progress.js +5 -4
  149. package/dist/components/radio-group.js +2 -2
  150. package/dist/components/reading-column.js +1 -1
  151. package/dist/components/record-identity.d.ts.map +1 -1
  152. package/dist/components/record-identity.js +2 -5
  153. package/dist/components/record-list.d.ts.map +1 -1
  154. package/dist/components/record-list.js +4 -3
  155. package/dist/components/record-sheet.js +1 -1
  156. package/dist/components/record-time.d.ts.map +1 -1
  157. package/dist/components/record-time.js +2 -1
  158. package/dist/components/resizable.d.ts.map +1 -1
  159. package/dist/components/resizable.js +1 -1
  160. package/dist/components/row-edit-form.d.ts +2 -1
  161. package/dist/components/row-edit-form.d.ts.map +1 -1
  162. package/dist/components/row-edit-form.js +2 -1
  163. package/dist/components/run-timeline.js +1 -1
  164. package/dist/components/scope-switcher.js +1 -1
  165. package/dist/components/segmented-control.js +1 -1
  166. package/dist/components/select.js +1 -1
  167. package/dist/components/settings-facts.js +1 -1
  168. package/dist/components/settings-section.js +1 -1
  169. package/dist/components/sheet.d.ts +1 -1
  170. package/dist/components/sheet.d.ts.map +1 -1
  171. package/dist/components/sheet.js +1 -1
  172. package/dist/components/sidebar.d.ts +2 -2
  173. package/dist/components/sidebar.d.ts.map +1 -1
  174. package/dist/components/sidebar.js +16 -18
  175. package/dist/components/site-menu.d.ts +28 -0
  176. package/dist/components/site-menu.d.ts.map +1 -0
  177. package/dist/components/site-menu.js +23 -0
  178. package/dist/components/slider.d.ts.map +1 -1
  179. package/dist/components/slider.js +2 -1
  180. package/dist/components/snippet.d.ts +2 -0
  181. package/dist/components/snippet.d.ts.map +1 -1
  182. package/dist/components/snippet.js +13 -6
  183. package/dist/components/sonner.d.ts.map +1 -1
  184. package/dist/components/sonner.js +3 -4
  185. package/dist/components/status-badge.d.ts +1 -1
  186. package/dist/components/status-badge.d.ts.map +1 -1
  187. package/dist/components/switch.js +1 -1
  188. package/dist/components/table-card.js +1 -1
  189. package/dist/components/table.js +4 -4
  190. package/dist/components/tabs-nav.d.ts +52 -0
  191. package/dist/components/tabs-nav.d.ts.map +1 -0
  192. package/dist/components/tabs-nav.js +95 -0
  193. package/dist/components/tabs.d.ts +2 -56
  194. package/dist/components/tabs.d.ts.map +1 -1
  195. package/dist/components/tabs.js +2 -91
  196. package/dist/components/text-link.d.ts +1 -2
  197. package/dist/components/text-link.d.ts.map +1 -1
  198. package/dist/components/text-link.js +1 -2
  199. package/dist/components/textarea.d.ts +5 -2
  200. package/dist/components/textarea.d.ts.map +1 -1
  201. package/dist/components/textarea.js +12 -4
  202. package/dist/components/toast.d.ts +2 -0
  203. package/dist/components/toast.d.ts.map +1 -1
  204. package/dist/components/toast.js +4 -0
  205. package/dist/components/toggle-group.d.ts +3 -1
  206. package/dist/components/toggle-group.d.ts.map +1 -1
  207. package/dist/components/toggle-group.js +3 -6
  208. package/dist/components/toggle.js +1 -1
  209. package/dist/components/tooltip.d.ts +12 -12
  210. package/dist/components/tooltip.d.ts.map +1 -1
  211. package/dist/components/tooltip.js +9 -9
  212. package/dist/components/trend-badge.js +2 -2
  213. package/dist/components/update-card.js +1 -1
  214. package/dist/components/usage-meter.d.ts +1 -1
  215. package/dist/components/usage-meter.d.ts.map +1 -1
  216. package/dist/components/usage-meter.js +9 -7
  217. package/dist/components/waiting-line.js +1 -1
  218. package/dist/index.d.ts +3 -7
  219. package/dist/index.d.ts.map +1 -1
  220. package/dist/index.js +3 -7
  221. package/dist/internal/agent-orb-motion.d.ts +4 -1
  222. package/dist/internal/agent-orb-motion.d.ts.map +1 -1
  223. package/dist/internal/agent-orb-motion.js +9 -7
  224. package/dist/internal/agent-orb.d.ts +4 -1
  225. package/dist/internal/agent-orb.d.ts.map +1 -1
  226. package/dist/internal/agent-orb.js +7 -1
  227. package/dist/internal/assistant-ui/attachment.d.ts.map +1 -1
  228. package/dist/internal/assistant-ui/attachment.js +15 -15
  229. package/dist/internal/assistant-ui/follow-up-suggestions.js +1 -1
  230. package/dist/internal/assistant-ui/markdown-text.js +26 -25
  231. package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -1
  232. package/dist/internal/assistant-ui/reasoning.js +13 -16
  233. package/dist/internal/assistant-ui/thread.d.ts.map +1 -1
  234. package/dist/internal/assistant-ui/thread.js +22 -16
  235. package/dist/internal/assistant-ui/tool-fallback.d.ts.map +1 -1
  236. package/dist/internal/assistant-ui/tool-fallback.js +29 -21
  237. package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -1
  238. package/dist/internal/assistant-ui/tool-group.js +6 -7
  239. package/dist/internal/assistant-ui/tooltip-icon-button.js +1 -1
  240. package/dist/internal/badge-frame.d.ts +15 -0
  241. package/dist/internal/badge-frame.d.ts.map +1 -0
  242. package/dist/internal/badge-frame.js +44 -0
  243. package/dist/internal/button-classes.d.ts +20 -0
  244. package/dist/internal/button-classes.d.ts.map +1 -0
  245. package/dist/internal/button-classes.js +74 -0
  246. package/dist/internal/catalog-list.d.ts.map +1 -1
  247. package/dist/internal/catalog-list.js +8 -4
  248. package/dist/internal/collection-rail.js +3 -3
  249. package/dist/internal/form-drawer-frame.d.ts +10 -0
  250. package/dist/internal/form-drawer-frame.d.ts.map +1 -0
  251. package/dist/internal/form-drawer-frame.js +9 -0
  252. package/dist/internal/form-submit.d.ts +2 -1
  253. package/dist/internal/form-submit.d.ts.map +1 -1
  254. package/dist/internal/settings-save.d.ts.map +1 -1
  255. package/dist/internal/settings-save.js +4 -2
  256. package/dist/internal/status-mark.d.ts.map +1 -1
  257. package/dist/internal/status-mark.js +4 -1
  258. package/dist/internal/tab-row.d.ts +10 -0
  259. package/dist/internal/tab-row.d.ts.map +1 -0
  260. package/dist/internal/tab-row.js +2 -0
  261. package/dist/internal/table-compact-line.d.ts +1 -1
  262. package/dist/internal/table-compact-line.d.ts.map +1 -1
  263. package/dist/internal/table-compact-line.js +1 -1
  264. package/dist/internal/tooltip-content-frame.d.ts +14 -0
  265. package/dist/internal/tooltip-content-frame.d.ts.map +1 -0
  266. package/dist/internal/tooltip-content-frame.js +11 -0
  267. package/dist/lib/css-variables.d.ts +8 -0
  268. package/dist/lib/css-variables.d.ts.map +1 -0
  269. package/dist/lib/css-variables.js +4 -0
  270. package/dist/lib/merge-tokens.generated.js +1 -1
  271. package/dist/lib/use-copy-to-clipboard.d.ts.map +1 -1
  272. package/dist/lib/use-copy-to-clipboard.js +1 -2
  273. package/dist/showcase/samples.js +1 -1
  274. package/dist/showcase/specimens/choices.d.ts.map +1 -1
  275. package/dist/showcase/specimens/choices.js +3 -2
  276. package/dist/showcase/specimens/compositions.d.ts.map +1 -1
  277. package/dist/showcase/specimens/compositions.js +7 -5
  278. package/dist/showcase/specimens/data.d.ts.map +1 -1
  279. package/dist/showcase/specimens/data.js +1 -1
  280. package/dist/showcase/specimens/feedback.d.ts.map +1 -1
  281. package/dist/showcase/specimens/feedback.js +1 -2
  282. package/dist/showcase/specimens/index.d.ts +2 -0
  283. package/dist/showcase/specimens/index.d.ts.map +1 -1
  284. package/dist/showcase/specimens/inputs.d.ts +1 -0
  285. package/dist/showcase/specimens/inputs.d.ts.map +1 -1
  286. package/dist/showcase/specimens/inputs.js +5 -2
  287. package/dist/showcase/specimens/menus.d.ts +1 -0
  288. package/dist/showcase/specimens/menus.d.ts.map +1 -1
  289. package/dist/showcase/specimens/menus.js +7 -2
  290. package/dist/showcase/specimens/navigation.d.ts.map +1 -1
  291. package/dist/showcase/specimens/navigation.js +6 -5
  292. package/dist/showcase/specimens/status.d.ts.map +1 -1
  293. package/dist/showcase/specimens/status.js +4 -4
  294. package/dist/showcase/usage.generated.d.ts +1 -1
  295. package/dist/showcase/usage.generated.d.ts.map +1 -1
  296. package/dist/showcase/usage.generated.js +38 -16
  297. package/dist/standalone/index.js +1 -1
  298. package/dist/standalone/orb-page.js +10 -8
  299. package/dist/styles/components.css +1 -1
  300. package/package.json +32 -33
  301. package/dist/eslint/index.js +0 -224
  302. package/dist/eslint/rules/badge-is-static-label.js +0 -93
  303. package/dist/eslint/rules/button-icon-slot.js +0 -102
  304. package/dist/eslint/rules/button-link-for-navigation.js +0 -146
  305. package/dist/eslint/rules/button-no-aria-label-with-text.js +0 -75
  306. package/dist/eslint/rules/button-type-roles-only-in-buttons.js +0 -83
  307. package/dist/eslint/rules/choice-control-has-a-name.js +0 -98
  308. package/dist/eslint/rules/choice-group-in-fieldset.js +0 -86
  309. package/dist/eslint/rules/command-shortcut-single-owner.js +0 -118
  310. package/dist/eslint/rules/confirm-dialog-words.js +0 -63
  311. package/dist/eslint/rules/data-table-one-unsized-column.js +0 -96
  312. package/dist/eslint/rules/destructive-action-placement.js +0 -69
  313. package/dist/eslint/rules/disabled-choice-says-why.js +0 -71
  314. package/dist/eslint/rules/disabled-control-appearance.js +0 -75
  315. package/dist/eslint/rules/ellipsis-opens-a-dialog.js +0 -110
  316. package/dist/eslint/rules/empty-state-owner.js +0 -145
  317. package/dist/eslint/rules/input-group-owners.js +0 -155
  318. package/dist/eslint/rules/jsx-class-utility.js +0 -200
  319. package/dist/eslint/rules/kbd-children-no-modifier-text.js +0 -128
  320. package/dist/eslint/rules/literal-text.js +0 -41
  321. package/dist/eslint/rules/menu-owner.js +0 -75
  322. package/dist/eslint/rules/middle-truncate-placement.js +0 -69
  323. package/dist/eslint/rules/no-app-feature-component.js +0 -100
  324. package/dist/eslint/rules/no-bare-button-label.js +0 -55
  325. package/dist/eslint/rules/no-choice-control-wrapper.js +0 -97
  326. package/dist/eslint/rules/no-comment.js +0 -77
  327. package/dist/eslint/rules/no-control-in-read-only-surface.js +0 -106
  328. package/dist/eslint/rules/no-decorative-icon.js +0 -164
  329. package/dist/eslint/rules/no-deprecated-zod-api.js +0 -215
  330. package/dist/eslint/rules/no-global-patch.js +0 -115
  331. package/dist/eslint/rules/no-hand-built-busy.js +0 -104
  332. package/dist/eslint/rules/no-hand-built-theme-picker.js +0 -144
  333. package/dist/eslint/rules/no-hand-built-tile.js +0 -41
  334. package/dist/eslint/rules/no-hand-formatted-date.js +0 -87
  335. package/dist/eslint/rules/no-hand-stacked-avatars.js +0 -67
  336. package/dist/eslint/rules/no-nested-collapse.js +0 -60
  337. package/dist/eslint/rules/no-package-component-redefinition.js +0 -175
  338. package/dist/eslint/rules/no-search-in-form.js +0 -47
  339. package/dist/eslint/rules/no-shadcn-appearance-override.js +0 -305
  340. package/dist/eslint/rules/no-shell-prompt-in-snippet.js +0 -74
  341. package/dist/eslint/rules/no-stock-error-words.js +0 -56
  342. package/dist/eslint/rules/no-subject-derived-expectation.js +0 -183
  343. package/dist/eslint/rules/no-tooltip-around-labelled-field.js +0 -72
  344. package/dist/eslint/rules/no-weight-class-on-strong-in-type-role.js +0 -57
  345. package/dist/eslint/rules/no-write-in-subscription.js +0 -140
  346. package/dist/eslint/rules/one-submenu-level.js +0 -43
  347. package/dist/eslint/rules/one-toast-per-flow.js +0 -91
  348. package/dist/eslint/rules/option-count.js +0 -167
  349. package/dist/eslint/rules/page-command-link-not-render.js +0 -43
  350. package/dist/eslint/rules/page-composition.js +0 -232
  351. package/dist/eslint/rules/progress-needs-name.js +0 -57
  352. package/dist/eslint/rules/rendered-import.js +0 -121
  353. package/dist/eslint/rules/server-action-discipline.js +0 -233
  354. package/dist/eslint/rules/settings-limit-in-footer.js +0 -94
  355. package/dist/eslint/rules/skeleton-in-region.js +0 -80
  356. package/dist/eslint/rules/sortable-heading-uses-sort.js +0 -79
  357. package/dist/eslint/rules/table-cell-inset-owner.js +0 -107
  358. package/dist/eslint/rules/tabs-selection-is-controlled.js +0 -47
  359. package/dist/eslint/rules/test-files-from-one-definition.js +0 -67
  360. package/dist/eslint/rules/tooltip-only-arrow.js +0 -38
  361. package/dist/eslint/rules/tooltip-trigger-accessible-name.js +0 -114
  362. package/dist/eslint/rules/workspace-declares-imports.js +0 -163
  363. package/dist/eslint/strict-design-system.js +0 -250
@@ -1,94 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` selector can find a
3
- * `maxLength` inside a `SettingsSection`, but not follow a renamed import to the
4
- * component, nor read whether the section's `save` or `apply` object names a hint.
5
- */
6
- import { packageModules, partOf, renderedImports } from "./rendered-import.js";
7
-
8
- const SECTION = new Map([
9
- [
10
- "SettingsSection",
11
- packageModules("settings-section").map((from) => ({ from, part: "SettingsSection" })),
12
- ],
13
- ]);
14
-
15
- const attributeNamed = (openingElement, name) =>
16
- openingElement.attributes.find(
17
- (attribute) => attribute.type === "JSXAttribute" && attribute.name.name === name,
18
- );
19
-
20
- /** true: the section shows a footer line; false: it shows none; null: the source does not say. */
21
- function footerLine(openingElement) {
22
- if (openingElement.attributes.some((attribute) => attribute.type === "JSXSpreadAttribute"))
23
- return null;
24
- if (
25
- attributeNamed(openingElement, "footerHint") ||
26
- attributeNamed(openingElement, "lockedReason")
27
- )
28
- return true;
29
- const commit = attributeNamed(openingElement, "save") ?? attributeNamed(openingElement, "apply");
30
- if (!commit) return false;
31
- const value = commit.value?.type === "JSXExpressionContainer" ? commit.value.expression : null;
32
- if (value?.type !== "ObjectExpression") return null;
33
- if (value.properties.some((property) => property.type === "SpreadElement")) return null;
34
- return value.properties.some(
35
- (property) =>
36
- property.type === "Property" && (property.key.name ?? property.key.value) === "hint",
37
- );
38
- }
39
-
40
- function limitsIn(element, isSection) {
41
- const found = [];
42
- const visit = (node) => {
43
- if (node.type === "JSXElement") {
44
- if (node !== element && isSection(node.openingElement)) return;
45
- const limit = attributeNamed(node.openingElement, "maxLength");
46
- if (limit) found.push(limit);
47
- node.children.forEach(visit);
48
- } else if (node.type === "JSXFragment") {
49
- node.children.forEach(visit);
50
- } else if (node.type === "JSXExpressionContainer") {
51
- visitExpression(node.expression);
52
- }
53
- };
54
- const visitExpression = (expression) => {
55
- if (!expression) return;
56
- if (expression.type === "JSXElement" || expression.type === "JSXFragment") visit(expression);
57
- else if (expression.type === "LogicalExpression") visitExpression(expression.right);
58
- else if (expression.type === "ConditionalExpression") {
59
- visitExpression(expression.consequent);
60
- visitExpression(expression.alternate);
61
- }
62
- };
63
- element.children.forEach(visit);
64
- return found;
65
- }
66
-
67
- export const settingsLimitInFooter = {
68
- meta: {
69
- type: "problem",
70
- docs: {
71
- description:
72
- "A SettingsSection whose field takes a maxLength says the limit in its footer line, as Vercel's settings cards do.",
73
- },
74
- schema: [],
75
- messages: {
76
- "limit-not-said":
77
- "Say this limit in the card's footer line (`save.hint`, `apply.hint` or `footerHint`), worded by the product's catalogue, such as \"Please use 48 characters at maximum.\"",
78
- },
79
- },
80
- create(context) {
81
- const importOf = renderedImports(context);
82
- const isSection = (openingElement) =>
83
- partOf(importOf(openingElement.name), SECTION, context.filename) === "SettingsSection";
84
-
85
- return {
86
- JSXElement(node) {
87
- if (!isSection(node.openingElement)) return;
88
- if (footerLine(node.openingElement) !== false) return;
89
- for (const limit of limitsIn(node, isSection))
90
- context.report({ node: limit, messageId: "limit-not-said" });
91
- },
92
- };
93
- },
94
- };
@@ -1,80 +0,0 @@
1
- /*
2
- * Standard tool checked first: `no-restricted-syntax` selects a JSX element by
3
- * name and ancestors, but not the one the import renders under another name,
4
- * nor "this file or this function is a loading view", which depends on the
5
- * file name and the enclosing function's name together.
6
- */
7
- import { renderedImports } from "./rendered-import.js";
8
-
9
- const SKELETON = "Skeleton";
10
- const REGION = "SkeletonRegion";
11
- const PLACEHOLDER_FILE = /(?:^|\/)(?:loading|[^/]+-skeleton)\.[jt]sx$/;
12
- const PLACEHOLDER_FUNCTION = /Skeleton$/;
13
- const FUNCTIONS = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
14
-
15
- const hasContent = (child) => {
16
- if (child.type === "JSXText") return child.value.trim() !== "";
17
- if (child.type === "JSXExpressionContainer")
18
- return child.expression.type !== "JSXEmptyExpression";
19
- return true;
20
- };
21
-
22
- const marksBusy = (openingElement) =>
23
- openingElement.attributes.some(
24
- (attribute) => attribute.type === "JSXAttribute" && attribute.name.name === "aria-busy",
25
- );
26
-
27
- function functionName(fn) {
28
- if (fn.id) return fn.id.name;
29
- const owner = fn.parent;
30
- return owner.type === "VariableDeclarator" && owner.id.type === "Identifier"
31
- ? owner.id.name
32
- : null;
33
- }
34
-
35
- export const skeletonInRegion = {
36
- meta: {
37
- type: "problem",
38
- docs: {
39
- description:
40
- "Placeholders of a loading view sit in a SkeletonRegion, or under an element marked aria-busy.",
41
- },
42
- schema: [],
43
- messages: {
44
- "placeholder-outside-region":
45
- "Wrap these placeholders in <SkeletonRegion busy> from @nadicodeai/ui/components/skeleton so the loading view is aria-busy; a component that renders both the placeholders and the data passes busy={isPending}.",
46
- },
47
- },
48
- create(context) {
49
- const filename = context.filename.replaceAll("\\", "/");
50
- const loadingFile = PLACEHOLDER_FILE.test(filename);
51
- const importOf = renderedImports(context);
52
-
53
- function loadingView(element) {
54
- if (loadingFile) return true;
55
- for (let current = element.parent; current; current = current.parent) {
56
- if (!FUNCTIONS.has(current.type)) continue;
57
- if (PLACEHOLDER_FUNCTION.test(functionName(current) ?? "")) return true;
58
- }
59
- return false;
60
- }
61
-
62
- function inABusyRegion(element) {
63
- for (let current = element.parent; current; current = current.parent) {
64
- if (current.type !== "JSXElement") continue;
65
- if (importOf(current.openingElement.name)?.name === REGION) return true;
66
- if (marksBusy(current.openingElement)) return true;
67
- }
68
- return false;
69
- }
70
-
71
- return {
72
- JSXElement(node) {
73
- if (importOf(node.openingElement.name)?.name !== SKELETON) return;
74
- if (node.children.some(hasContent)) return;
75
- if (!loadingView(node) || inABusyRegion(node)) return;
76
- context.report({ node: node.openingElement, messageId: "placeholder-outside-region" });
77
- },
78
- };
79
- },
80
- };
@@ -1,79 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` selector cannot follow
3
- * a renamed `TableHead` or `Button` import, nor tell a column definition's
4
- * `header` from any other property of that name.
5
- */
6
- import { packageModules, partOf, renderedImports, walk } from "./rendered-import.js";
7
-
8
- const exporters = (module, part) =>
9
- new Map([
10
- [part, [...packageModules(module), `@/components/${module}`].map((from) => ({ from, part }))],
11
- ]);
12
-
13
- const TABLE_HEAD = exporters("table", "TableHead");
14
- const BUTTON = exporters("button", "Button");
15
-
16
- const FUNCTIONS = new Set(["FunctionExpression", "ArrowFunctionExpression"]);
17
- const COLUMN_KEYS = new Set(["accessorKey", "accessorFn", "id", "cell"]);
18
-
19
- const keyName = (property) => {
20
- if (property.computed) return null;
21
- return property.key.name ?? property.key.value ?? null;
22
- };
23
-
24
- const isColumnHeader = (property) =>
25
- property.type === "Property" &&
26
- keyName(property) === "header" &&
27
- FUNCTIONS.has(property.value.type) &&
28
- property.parent.properties.some(
29
- (sibling) => sibling.type === "Property" && COLUMN_KEYS.has(keyName(sibling)),
30
- );
31
-
32
- export const sortableHeadingUsesSort = {
33
- meta: {
34
- type: "problem",
35
- docs: {
36
- description: "A sortable heading is TableHead's sort prop, not a button drawn inside it.",
37
- },
38
- schema: [],
39
- messages: {
40
- "drawn-sort-button":
41
- "A sortable heading is TableHead's sort prop (order, next, onSort), not a button you draw. The sort prop gives the heading aria-sort and names the next sort.",
42
- },
43
- },
44
- create(context) {
45
- const importOf = renderedImports(context);
46
- const { visitorKeys } = context.sourceCode;
47
-
48
- const drawsAControl = (opening) =>
49
- opening.attributes.some(
50
- (attribute) => attribute.type === "JSXAttribute" && attribute.name.name === "onClick",
51
- ) || partOf(importOf(opening.name), BUTTON, context.filename) === "Button";
52
-
53
- const reported = new Set();
54
-
55
- function reportControlsBelow(root, skipped) {
56
- walk(root, visitorKeys, (child) => {
57
- if (child === skipped) return false;
58
- if (child.type === "JSXOpeningElement" && !reported.has(child) && drawsAControl(child)) {
59
- reported.add(child);
60
- context.report({ node: child, messageId: "drawn-sort-button" });
61
- }
62
- return true;
63
- });
64
- }
65
-
66
- return {
67
- JSXElement(node) {
68
- const { openingElement } = node;
69
- if (partOf(importOf(openingElement.name), TABLE_HEAD, context.filename) !== "TableHead") {
70
- return;
71
- }
72
- reportControlsBelow(node, openingElement);
73
- },
74
- Property(node) {
75
- if (isColumnHeader(node)) reportControlsBelow(node.value, null);
76
- },
77
- };
78
- },
79
- };
@@ -1,107 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
- * padding class written as a literal on `<TableCell>`, but not one composed
4
- * through `cn(...)` or a conditional, not which JSX name is the table's part
5
- * once the import is renamed, and not which object literal is a column
6
- * definition rather than any other object with a `className`.
7
- */
8
- import { PACKAGE_BARREL, partOf, renderedImports, walk } from "./rendered-import.js";
9
-
10
- const CELL_PARTS = new Set(["TableHead", "TableCell"]);
11
-
12
- const TABLE_PARTS = new Map(
13
- [...CELL_PARTS].map((name) => [
14
- name,
15
- ["@nadicodeai/ui/components/table", PACKAGE_BARREL, "@/components/table"].map((from) => ({
16
- from,
17
- part: name,
18
- })),
19
- ]),
20
- );
21
-
22
- const COLUMN_KEYS = new Set(["accessorKey", "accessorFn", "header", "cell"]);
23
- const SIDEWAYS_PADDING = /^-?(?:px|ps|pe|pl|pr)-(?<step>.+?)!?$/;
24
-
25
- const keyName = (property) => {
26
- if (property.computed) return null;
27
- return property.key.type === "Identifier" ? property.key.name : property.key.value;
28
- };
29
-
30
- const propertyNamed = (objectExpression, name) =>
31
- objectExpression.properties.find(
32
- (property) => property.type === "Property" && keyName(property) === name,
33
- );
34
-
35
- function withoutVariants(utility) {
36
- let depth = 0;
37
- let start = 0;
38
- for (let index = 0; index < utility.length; index += 1) {
39
- const character = utility[index];
40
- if (character === "[") depth += 1;
41
- else if (character === "]") depth -= 1;
42
- else if (character === ":" && depth === 0) start = index + 1;
43
- }
44
- return utility.slice(start).replace(/^!/, "");
45
- }
46
-
47
- const insetsSideways = (utility) => {
48
- const step = SIDEWAYS_PADDING.exec(withoutVariants(utility))?.groups.step;
49
- return step !== undefined && step !== "0";
50
- };
51
-
52
- export const tableCellInsetOwner = {
53
- meta: {
54
- type: "problem",
55
- docs: {
56
- description: "The table insets its cells sideways; a cell or a column never sets it.",
57
- },
58
- schema: [],
59
- messages: {
60
- "cell-inset":
61
- "Table cells are inset 8 px sideways at every width by the table. Remove the class; set the width with the column's `size`.",
62
- },
63
- },
64
- create(context) {
65
- const importOf = renderedImports(context);
66
- const { visitorKeys } = context.sourceCode;
67
-
68
- function checkClasses(value) {
69
- const fragments = [];
70
- const read = (node) => {
71
- // oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
72
- if (node.type === "Literal" && typeof node.value === "string") fragments.push(node.value);
73
- if (node.type === "TemplateElement") fragments.push(node.value.cooked ?? node.value.raw);
74
- };
75
- read(value);
76
- walk(value, visitorKeys, read);
77
- const refused = fragments
78
- .flatMap((fragment) => fragment.split(/\s+/).filter(Boolean))
79
- .some(insetsSideways);
80
- if (refused) context.report({ node: value, messageId: "cell-inset" });
81
- }
82
-
83
- return {
84
- JSXOpeningElement(node) {
85
- if (!CELL_PARTS.has(partOf(importOf(node.name), TABLE_PARTS, context.filename) ?? "")) {
86
- return;
87
- }
88
- for (const attribute of node.attributes) {
89
- if (attribute.type === "JSXAttribute" && attribute.name.name === "className") {
90
- if (attribute.value) checkClasses(attribute.value);
91
- }
92
- }
93
- },
94
- ObjectExpression(node) {
95
- const isColumn = node.properties.some(
96
- (property) => property.type === "Property" && COLUMN_KEYS.has(keyName(property)),
97
- );
98
- if (!isColumn) return;
99
- const own = propertyNamed(node, "className");
100
- if (own) checkClasses(own.value);
101
- const meta = propertyNamed(node, "meta")?.value;
102
- const metaOwn = meta?.type === "ObjectExpression" && propertyNamed(meta, "className");
103
- if (metaOwn) checkClasses(metaOwn.value);
104
- },
105
- };
106
- },
107
- };
@@ -1,47 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
- * `JSXAttribute[name.name='defaultValue']`, but not that the element is the
4
- * package's Tabs rather than TabsNav or a local component, nor that a missing
5
- * `value` is covered by a spread. Those need the import binding and the
6
- * attribute list read together.
7
- */
8
- import { packageModules, partOf, renderedImports } from "./rendered-import.js";
9
-
10
- const TABS = new Map([["Tabs", packageModules("tabs").map((from) => ({ from, part: "Tabs" }))]]);
11
-
12
- const hasAttribute = (openingElement, attributeName) =>
13
- openingElement.attributes.some(
14
- (attribute) => attribute.type === "JSXAttribute" && attribute.name.name === attributeName,
15
- );
16
-
17
- const hasSpread = (openingElement) =>
18
- openingElement.attributes.some((attribute) => attribute.type === "JSXSpreadAttribute");
19
-
20
- export const tabsSelectionIsControlled = {
21
- meta: {
22
- type: "problem",
23
- docs: {
24
- description: "Tabs is controlled by a `value` read from the address, never `defaultValue`.",
25
- },
26
- schema: [],
27
- messages: {
28
- "chosen-tab-in-address":
29
- "The chosen tab lives in the address: control Tabs with `value` read from the route or ?tab=, so a reload, Back and a shared link restore it.",
30
- },
31
- },
32
- create(context) {
33
- const importOf = renderedImports(context);
34
- return {
35
- JSXElement(node) {
36
- const { openingElement } = node;
37
- if (!partOf(importOf(openingElement.name), TABS, context.filename)) return;
38
- const uncontrolled =
39
- hasAttribute(openingElement, "defaultValue") ||
40
- (!hasAttribute(openingElement, "value") && !hasSpread(openingElement));
41
- if (uncontrolled) {
42
- context.report({ node: openingElement, messageId: "chosen-tab-in-address" });
43
- }
44
- },
45
- };
46
- },
47
- };
@@ -1,67 +0,0 @@
1
- const EVERY_TEST =
2
- /(?:^|\/)\*\.test(?:-\*)?\.|(?:^|\/)tests\/\*\*(?:\/\*[^/]*)?$|(?:^|\/)\*\.e2e\.ts$/;
3
-
4
- const textOf = (node) => {
5
- // oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
6
- if (node.type === "Literal" && typeof node.value === "string") return node.value;
7
- if (node.type === "TemplateLiteral")
8
- return node.quasis.map((quasi) => quasi.value.cooked).join("*");
9
- return null;
10
- };
11
-
12
- const variableNamed = (scope, name) => {
13
- for (let current = scope; current; current = current.upper) {
14
- const variable = current.set.get(name);
15
- if (variable) return variable;
16
- }
17
- return undefined;
18
- };
19
-
20
- const keyName = (property) =>
21
- property.key.type === "Identifier" ? property.key.name : textOf(property.key);
22
-
23
- export const testFilesFromOneDefinition = {
24
- meta: {
25
- type: "problem",
26
- docs: {
27
- description:
28
- "A config block skips tests with TEST_FILES from @nadicodeai/test-support/test-files, never a glob of its own.",
29
- },
30
- schema: [],
31
- messages: {
32
- handWritten:
33
- "Skip tests with TEST_FILES from @nadicodeai/test-support/test-files: a test glob written here misses the next kind of test.",
34
- },
35
- },
36
- create(context) {
37
- const constantOf = (node) => {
38
- if (node.type !== "Identifier") return null;
39
- const definition = variableNamed(context.sourceCode.getScope(node), node.name)?.defs[0];
40
- return definition?.type === "Variable" ? definition.node.init : null;
41
- };
42
- const checkGlobs = (node) => {
43
- if (node.type === "Identifier") {
44
- const init = constantOf(node);
45
- if (init?.type === "ArrayExpression") checkGlobs(init);
46
- return;
47
- }
48
- if (node.type !== "ArrayExpression") return;
49
- for (const element of node.elements) {
50
- if (!element) continue;
51
- if (element.type === "SpreadElement") {
52
- checkGlobs(element.argument);
53
- continue;
54
- }
55
- const glob = textOf(constantOf(element) ?? element);
56
- if (glob !== null && EVERY_TEST.test(glob)) {
57
- context.report({ node: element, messageId: "handWritten" });
58
- }
59
- }
60
- };
61
- return {
62
- Property(node) {
63
- if (keyName(node) === "ignores") checkGlobs(node.value);
64
- },
65
- };
66
- },
67
- };
@@ -1,38 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
- * member element named `Arrow`, but not that its object is an import from
4
- * `@base-ui/react`, so it would also refuse a product's own `Chart.Arrow`.
5
- */
6
- const BASE_UI = /^@base-ui\/react(\/|$)/;
7
-
8
- const rootOf = (name) => (name.type === "JSXMemberExpression" ? rootOf(name.object) : name);
9
-
10
- export const tooltipOnlyArrow = {
11
- meta: {
12
- type: "problem",
13
- docs: {
14
- description: "Only a tooltip has a pointer: a popup part named Arrow belongs in tooltip.tsx.",
15
- },
16
- schema: [],
17
- messages: {
18
- "popup-arrow": "Only a tooltip has a pointer. Remove the arrow from this popup.",
19
- },
20
- },
21
- create(context) {
22
- const baseUiBindings = new Set();
23
- return {
24
- ImportDeclaration(node) {
25
- if (node.importKind === "type" || !BASE_UI.test(node.source.value)) return;
26
- for (const specifier of node.specifiers) {
27
- if (specifier.importKind !== "type") baseUiBindings.add(specifier.local.name);
28
- }
29
- },
30
- JSXOpeningElement(node) {
31
- const { name } = node;
32
- if (name.type !== "JSXMemberExpression" || name.property.name !== "Arrow") return;
33
- if (baseUiBindings.has(rootOf(name).name))
34
- context.report({ node, messageId: "popup-arrow" });
35
- },
36
- };
37
- },
38
- };
@@ -1,114 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
- * `TooltipTrigger` with no `aria-label`, but not that the package's trigger is
4
- * meant rather than a local one, that its children draw no text, nor that the
5
- * name may sit on the element passed as `render`. Those need the import
6
- * binding, the children and the `render` element read together.
7
- */
8
- import { PACKAGE_BARREL, partOf, renderedImports, staticAttribute } from "./rendered-import.js";
9
-
10
- const TRIGGERS = new Map(
11
- [
12
- ["TooltipTrigger", "tooltip"],
13
- ["PopoverTrigger", "popover"],
14
- ["HoverCardTrigger", "hover-card"],
15
- ].map(([name, module]) => [
16
- name,
17
- [`@nadicodeai/ui/components/${module}`, PACKAGE_BARREL, `@/components/${module}`].map(
18
- (from) => ({ from, part: name }),
19
- ),
20
- ]),
21
- );
22
-
23
- const BUTTONS = new Map(
24
- ["Button", "ButtonLink"].map((name) => [
25
- name,
26
- ["@nadicodeai/ui/components/button", PACKAGE_BARREL].map((from) => ({ from, part: name })),
27
- ]),
28
- );
29
-
30
- const NAMING_ATTRIBUTES = new Set(["aria-label", "aria-labelledby"]);
31
-
32
- const hasAttribute = (openingElement, names) =>
33
- openingElement.attributes.some(
34
- (attribute) => attribute.type === "JSXAttribute" && names.has(attribute.name.name),
35
- );
36
-
37
- const hasSpread = (openingElement) =>
38
- openingElement.attributes.some((attribute) => attribute.type === "JSXSpreadAttribute");
39
-
40
- function staticText(expression) {
41
- if (expression.type === "Literal") {
42
- // oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
43
- return typeof expression.value === "string" ? expression.value : null;
44
- }
45
- if (expression.type === "TemplateLiteral" && expression.expressions.length === 0) {
46
- return expression.quasis[0].value.cooked;
47
- }
48
- return null;
49
- }
50
-
51
- function rendersText(child) {
52
- if (child.type === "JSXText") return child.value.trim() !== "";
53
- if (child.type === "JSXElement" || child.type === "JSXFragment") {
54
- return child.children.some(rendersText);
55
- }
56
- if (child.type !== "JSXExpressionContainer") return false;
57
- const { expression } = child;
58
- if (expression.type === "JSXEmptyExpression") return false;
59
- const text = staticText(expression);
60
- if (text !== null) return text.trim() !== "";
61
- return expression.type !== "Literal";
62
- }
63
-
64
- const isElement = (child) => child.type === "JSXElement" || child.type === "JSXFragment";
65
-
66
- const isIconOnly = (children) => children.some(isElement) && !children.some(rendersText);
67
-
68
- const renderElement = (openingElement) => {
69
- const attribute = openingElement.attributes.find(
70
- (candidate) => candidate.type === "JSXAttribute" && candidate.name.name === "render",
71
- );
72
- const expression = attribute?.value?.expression;
73
- return expression?.type === "JSXElement" ? expression : null;
74
- };
75
-
76
- const isIconButton = (importOf, filename, element) =>
77
- Boolean(partOf(importOf(element.openingElement.name), BUTTONS, filename)) &&
78
- String(staticAttribute(element.openingElement, "size") ?? "").startsWith("icon");
79
-
80
- export const tooltipTriggerAccessibleName = {
81
- meta: {
82
- type: "problem",
83
- docs: {
84
- description:
85
- "A trigger whose children are only icons is named by an aria-label: the tooltip describes the control, it does not name it.",
86
- },
87
- schema: [],
88
- messages: {
89
- "icon-only-trigger":
90
- "An icon-only trigger needs an aria-label that names the action. The tooltip describes the control; it does not name it.",
91
- },
92
- },
93
- create(context) {
94
- const importOf = renderedImports(context);
95
- return {
96
- JSXElement(node) {
97
- const { openingElement } = node;
98
- if (!partOf(importOf(openingElement.name), TRIGGERS, context.filename)) return;
99
- if (hasSpread(openingElement) || hasAttribute(openingElement, NAMING_ATTRIBUTES)) return;
100
- const rendered = renderElement(openingElement);
101
- if (rendered) {
102
- if (isIconButton(importOf, context.filename, rendered)) return;
103
- if (hasSpread(rendered.openingElement)) return;
104
- if (hasAttribute(rendered.openingElement, NAMING_ATTRIBUTES)) return;
105
- }
106
- const drawsItself = node.children.some(isElement) || node.children.some(rendersText);
107
- const children = drawsItself ? node.children : (rendered?.children ?? []);
108
- if (isIconOnly(children)) {
109
- context.report({ node: openingElement, messageId: "icon-only-trigger" });
110
- }
111
- },
112
- };
113
- },
114
- };