@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,144 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
- * `Sun` element under a `RadioGroup`, but not the same icon named through a
4
- * renamed import, nor an option table declared in the same file, and it cannot
5
- * join the icons of two sibling Buttons.
6
- */
7
- import { renderedImports, walk } from "./rendered-import.js";
8
-
9
- const ICON_MODULE = "lucide-react";
10
- const PICKERS = new Set(["RadioGroup", "ToggleGroup", "SegmentedControl", "Switch"]);
11
- const BUTTONS = new Set(["Button", "ButtonLink"]);
12
- const THEME_ICONS = new Map([
13
- ["Sun", "light"],
14
- ["SunIcon", "light"],
15
- ["Moon", "dark"],
16
- ["MoonIcon", "dark"],
17
- ["Monitor", "system"],
18
- ["MonitorIcon", "system"],
19
- ]);
20
- const THEME_VALUES = new Set(["light", "dark"]);
21
-
22
- const keyName = (property) => property.key.name ?? property.key.value;
23
-
24
- function isThemeValue(literal) {
25
- const { parent: container } = literal;
26
- const parent = container.type === "JSXExpressionContainer" ? container.parent : container;
27
- const isItemValue =
28
- (parent.type === "JSXAttribute" && parent.name.name === "value") ||
29
- (parent.type === "Property" && parent.value === literal && keyName(parent) === "value") ||
30
- parent.type === "ArrayExpression" ||
31
- parent.type === "CallExpression";
32
- return isItemValue && THEME_VALUES.has(literal.value);
33
- }
34
-
35
- const isPropertyName = (identifier) => {
36
- const { parent } = identifier;
37
- return (
38
- (parent.type === "Property" && parent.key === identifier && !parent.computed) ||
39
- (parent.type === "MemberExpression" && parent.property === identifier && !parent.computed)
40
- );
41
- };
42
-
43
- export const noHandBuiltThemePicker = {
44
- meta: {
45
- type: "problem",
46
- docs: {
47
- description:
48
- "The appearance is chosen with ThemeModeSwitcher or ThemeModeMenu, never a hand-built picker.",
49
- },
50
- schema: [],
51
- messages: {
52
- "hand-built-theme-picker":
53
- "The appearance is chosen with ThemeModeSwitcher on a settings page or ThemeModeMenu in the account menu; do not rebuild it.",
54
- },
55
- },
56
- create(context) {
57
- const { sourceCode } = context;
58
- const { visitorKeys } = sourceCode;
59
- const importOf = renderedImports(context);
60
-
61
- const iconsByLocalName = new Map();
62
- for (const statement of sourceCode.ast.body) {
63
- if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
64
- if (statement.source.value !== ICON_MODULE) continue;
65
- for (const specifier of statement.specifiers) {
66
- if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type") continue;
67
- const kind = THEME_ICONS.get(specifier.imported.name ?? specifier.imported.value);
68
- if (kind) iconsByLocalName.set(specifier.local.name, kind);
69
- }
70
- }
71
-
72
- const sameFileConstant = (identifier) => {
73
- for (let scope = sourceCode.getScope(identifier); scope; scope = scope.upper) {
74
- const variable = scope.set.get(identifier.name);
75
- if (!variable) continue;
76
- const declarator = variable.defs[0]?.node;
77
- const isConstant =
78
- declarator?.type === "VariableDeclarator" && declarator.parent.kind === "const";
79
- return isConstant ? declarator.init : null;
80
- }
81
- return null;
82
- };
83
-
84
- function collect(root, held, followNames) {
85
- const visit = (child) => {
86
- if (child.type === "JSXOpeningElement") {
87
- const imported = importOf(child.name);
88
- const kind = imported?.source === ICON_MODULE ? THEME_ICONS.get(imported.name) : null;
89
- if (kind) held.icons.add(kind);
90
- } else if (child.type === "Identifier" && !isPropertyName(child)) {
91
- const kind = iconsByLocalName.get(child.name);
92
- if (kind) held.icons.add(kind);
93
- else if (followNames) {
94
- const init = sameFileConstant(child);
95
- if (init) collect(init, held, false);
96
- }
97
- } else if (child.type === "Literal" && isThemeValue(child)) {
98
- held.values.add(child.value);
99
- }
100
- return true;
101
- };
102
- visit(root);
103
- walk(root, visitorKeys, visit);
104
- }
105
-
106
- const holdsAThemeChoice = ({ icons, values }, needsBoth) =>
107
- needsBoth ? icons.size >= 2 || values.size === 2 : icons.size >= 1 || values.size === 2;
108
-
109
- const heldBy = (nodes) => {
110
- const held = { icons: new Set(), values: new Set() };
111
- for (const node of nodes) collect(node, held, true);
112
- return held;
113
- };
114
-
115
- const buttonsIn = (parent) =>
116
- parent.children.flatMap((child) => {
117
- if (child.type === "JSXFragment") return buttonsIn(child);
118
- const isButton =
119
- child.type === "JSXElement" && BUTTONS.has(importOf(child.openingElement.name)?.name);
120
- return isButton ? [child] : [];
121
- });
122
-
123
- const report = (node) => context.report({ node, messageId: "hand-built-theme-picker" });
124
-
125
- return {
126
- JSXElement(node) {
127
- if (PICKERS.has(importOf(node.openingElement.name)?.name)) {
128
- const held = heldBy([node]);
129
- if (holdsAThemeChoice(held, false)) report(node.openingElement);
130
- return;
131
- }
132
- const buttons = buttonsIn(node);
133
- if (buttons.length < 2) return;
134
- if (holdsAThemeChoice(heldBy(buttons), true)) report(node.openingElement);
135
- },
136
- JSXFragment(node) {
137
- if (node.parent.type === "JSXElement" || node.parent.type === "JSXFragment") return;
138
- const buttons = buttonsIn(node);
139
- if (buttons.length < 2) return;
140
- if (holdsAThemeChoice(heldBy(buttons), true)) report(node);
141
- },
142
- };
143
- },
144
- };
@@ -1,41 +0,0 @@
1
- import { findBelow, renderedImports, walk } from "./rendered-import.js";
2
-
3
- const FIELD_LABEL = "FieldLabel";
4
- const FIELD = "Field";
5
- const CONTROLS = new Set(["RadioGroupItem", "Checkbox"]);
6
-
7
- export const noHandBuiltTile = {
8
- meta: {
9
- type: "problem",
10
- docs: {
11
- description:
12
- "A bordered selectable tile is a ChoiceboxGroup, never a FieldLabel around a Field with a radio or a checkbox.",
13
- },
14
- schema: [],
15
- messages: {
16
- "hand-built-tile":
17
- "A bordered selectable tile is a ChoiceboxGroup with ChoiceboxGroupItem. Options without a description are a plain RadioGroup with a Label for each.",
18
- },
19
- },
20
- create(context) {
21
- const importOf = renderedImports(context);
22
- const { visitorKeys } = context.sourceCode;
23
- const isRendered = (name) => (node) =>
24
- node.type === "JSXElement" && name(importOf(node.openingElement.name)?.name);
25
- const isField = isRendered((name) => name === FIELD);
26
- const isControl = isRendered((name) => CONTROLS.has(name));
27
-
28
- return {
29
- JSXElement(node) {
30
- if (importOf(node.openingElement.name)?.name !== FIELD_LABEL) return;
31
- let handBuilt = false;
32
- walk(node, visitorKeys, (child) => {
33
- if (!isField(child) || !findBelow(child, visitorKeys, isControl)) return true;
34
- handBuilt = true;
35
- return "stop";
36
- });
37
- if (handBuilt) context.report({ node: node.openingElement, messageId: "hand-built-tile" });
38
- },
39
- };
40
- },
41
- };
@@ -1,87 +0,0 @@
1
- /*
2
- * Standard tool checked first: `no-restricted-syntax` can match a member named
3
- * `toLocaleDateString` or `new Intl.DateTimeFormat`, but `toLocaleString` is
4
- * also how a number is written, so it is a date only when its options object
5
- * names a date or time part, which a selector cannot read. It reports only a
6
- * date written into markup (inside a JSX expression of the same function): a
7
- * helper that returns a chart label, a period name or an email's text has no
8
- * element to put a RecordTime in.
9
- */
10
- const ALWAYS_DATES = new Set(["toLocaleDateString", "toLocaleTimeString"]);
11
- const DATE_PARTS = new Set([
12
- "dateStyle",
13
- "timeStyle",
14
- "weekday",
15
- "era",
16
- "year",
17
- "month",
18
- "day",
19
- "dayPeriod",
20
- "hour",
21
- "minute",
22
- "second",
23
- "fractionalSecondDigits",
24
- "timeZoneName",
25
- "hour12",
26
- "hourCycle",
27
- ]);
28
-
29
- const isIntlDateTimeFormat = (callee) =>
30
- callee.type === "MemberExpression" &&
31
- !callee.computed &&
32
- callee.object.type === "Identifier" &&
33
- callee.object.name === "Intl" &&
34
- callee.property.name === "DateTimeFormat";
35
-
36
- const namesADatePart = (options) =>
37
- options?.type === "ObjectExpression" &&
38
- options.properties.some(
39
- (property) =>
40
- property.type === "Property" &&
41
- !property.computed &&
42
- property.key.type === "Identifier" &&
43
- DATE_PARTS.has(property.key.name),
44
- );
45
-
46
- const FUNCTIONS = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
47
-
48
- function writtenIntoMarkup(node) {
49
- for (let current = node.parent; current; current = current.parent) {
50
- if (current.type === "JSXExpressionContainer") return true;
51
- if (FUNCTIONS.has(current.type)) return false;
52
- }
53
- return false;
54
- }
55
-
56
- export const noHandFormattedDate = {
57
- meta: {
58
- type: "problem",
59
- docs: {
60
- description: "An app shows a moment through RecordTime, never a hand-formatted date.",
61
- },
62
- schema: [],
63
- messages: {
64
- "hand-formatted-date":
65
- "Show a moment with RecordTime from @nadicodeai/ui/components/record-time: it reads relative within a week, then the date, in the viewer's zone and language, with the exact time in a card. A date formatted by hand drifts from the other screens and shows the server's zone.",
66
- },
67
- },
68
- create(context) {
69
- function check(node) {
70
- if (!writtenIntoMarkup(node)) return;
71
- const { callee } = node;
72
- if (isIntlDateTimeFormat(callee)) {
73
- context.report({ node, messageId: "hand-formatted-date" });
74
- return;
75
- }
76
- if (callee.type !== "MemberExpression" || callee.computed) return;
77
- const name = callee.property.name;
78
- if (
79
- ALWAYS_DATES.has(name) ||
80
- (name === "toLocaleString" && namesADatePart(node.arguments[1]))
81
- ) {
82
- context.report({ node, messageId: "hand-formatted-date" });
83
- }
84
- }
85
- return { CallExpression: check, NewExpression: check };
86
- },
87
- };
@@ -1,67 +0,0 @@
1
- import { openingElementClassUtilities } from "./jsx-class-utility.js";
2
- import { renderedImports } from "./rendered-import.js";
3
-
4
- const AVATAR = "Avatar";
5
- const OVERLAP = /^-(?:space-x-|ml-|ms-|mx-)/;
6
-
7
- const hasOverlapClass = (node, context) =>
8
- openingElementClassUtilities(node, context).some((utility) =>
9
- OVERLAP.test(utility.slice(utility.lastIndexOf(":") + 1).replace(/^!|!$/g, "")),
10
- );
11
-
12
- function returnedElement(callback) {
13
- if (callback?.type !== "ArrowFunctionExpression" && callback?.type !== "FunctionExpression") {
14
- return null;
15
- }
16
- if (callback.body.type !== "BlockStatement") return callback.body;
17
- return callback.body.body.find((statement) => statement.type === "ReturnStatement")?.argument;
18
- }
19
-
20
- export const noHandStackedAvatars = {
21
- meta: {
22
- type: "problem",
23
- docs: {
24
- description:
25
- "Stacked avatars are one AvatarGroup, never sibling Avatars or an overlap written on a parent or an Avatar.",
26
- },
27
- schema: [],
28
- messages: {
29
- "hand-stacked-avatars":
30
- "Stacked avatars are one <AvatarGroup members={...} label={...} />, which owns the overlap, the order, the +N count and the group's label.",
31
- },
32
- },
33
- create(context) {
34
- const importOf = renderedImports(context);
35
- const reported = new WeakSet();
36
- const isAvatar = (node) =>
37
- node?.type === "JSXElement" && importOf(node.openingElement.name)?.name === AVATAR;
38
-
39
- function report(avatar) {
40
- if (reported.has(avatar)) return;
41
- reported.add(avatar);
42
- context.report({ node: avatar.openingElement, messageId: "hand-stacked-avatars" });
43
- }
44
-
45
- return {
46
- JSXElement(node) {
47
- if (isAvatar(node) && hasOverlapClass(node.openingElement, context)) report(node);
48
- const avatars = node.children.filter(isAvatar);
49
- if (avatars.length >= 2) report(avatars[1]);
50
- },
51
- JSXFragment(node) {
52
- const avatars = node.children.filter(isAvatar);
53
- if (avatars.length >= 2) report(avatars[1]);
54
- },
55
- CallExpression(node) {
56
- const { callee, parent } = node;
57
- const isMap =
58
- callee.type === "MemberExpression" && !callee.computed && callee.property.name === "map";
59
- if (!isMap) return;
60
- if (parent.type !== "JSXExpressionContainer" || parent.parent.type !== "JSXElement") return;
61
- if (!hasOverlapClass(parent.parent.openingElement, context)) return;
62
- const returned = returnedElement(node.arguments[0]);
63
- if (isAvatar(returned)) report(returned);
64
- },
65
- };
66
- },
67
- };
@@ -1,60 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` descendant selector
3
- * matches by element name, so it cannot tell the package's Accordion from a
4
- * local component of the same name, a renamed import, or a Disclosure's
5
- * `label` (which is not its content) from its children.
6
- */
7
- import { packageModules, partOf, renderedImports } from "./rendered-import.js";
8
-
9
- const PARTS = [
10
- ["accordion", "Accordion"],
11
- ["accordion", "AccordionContent"],
12
- ["collapsible", "Collapsible"],
13
- ["collapsible", "CollapsibleContent"],
14
- ["disclosure", "Disclosure"],
15
- ];
16
-
17
- const PART_EXPORTERS = new Map(
18
- PARTS.map(([module, part]) => [
19
- part,
20
- [...packageModules(module), `@/components/${module}`].map((from) => ({ from, part })),
21
- ]),
22
- );
23
-
24
- const COLLAPSES = new Set(["Accordion", "Collapsible", "Disclosure"]);
25
- const COLLAPSED_CONTENT = new Set(["AccordionContent", "CollapsibleContent", "Disclosure"]);
26
-
27
- export const noNestedCollapse = {
28
- meta: {
29
- type: "problem",
30
- docs: {
31
- description: "An Accordion, Collapsible or Disclosure never sits in another's content.",
32
- },
33
- schema: [],
34
- messages: {
35
- "nested-collapse":
36
- "A collapse never sits in another: two levels hide too much and break the keyboard path. Make the inner one a section of the page, or flatten the outer.",
37
- },
38
- },
39
- create(context) {
40
- const importOf = renderedImports(context);
41
- const partRendered = (element) =>
42
- partOf(importOf(element.openingElement.name), PART_EXPORTERS, context.filename);
43
-
44
- return {
45
- JSXElement(node) {
46
- if (!COLLAPSES.has(partRendered(node))) return;
47
- let child = node;
48
- for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
49
- const holdsAsContent =
50
- ancestor.type === "JSXElement" && ancestor.children.includes(child);
51
- if (holdsAsContent && COLLAPSED_CONTENT.has(partRendered(ancestor))) {
52
- context.report({ node, messageId: "nested-collapse" });
53
- return;
54
- }
55
- child = ancestor;
56
- }
57
- },
58
- };
59
- },
60
- };
@@ -1,175 +0,0 @@
1
- /*
2
- * A route does not re-declare a component the package already ships.
3
- *
4
- * `@nadicodeai/ui` is the website's component surface. A route that declares
5
- * its own `Footer`, `Card` or `Badge` forks a shipped component into a page,
6
- * where the package's next change does not reach it — and the two are then
7
- * told apart only by their import paths.
8
- *
9
- * The names are DERIVED from the package's build surface at lint time: the
10
- * `types` target of its `exports` map, followed through the barrel's
11
- * `export *` chain. There is no list here to keep in step with the package;
12
- * a component added or removed by the package changes this rule's answer with
13
- * no edit at all.
14
- *
15
- * Standard tool checked first: core `no-redeclare` and
16
- * `no-shadow` answer only inside one scope chain, and `no-restricted-syntax`
17
- * would need every name spelled into a selector — the hand list this derivation
18
- * exists to avoid. Deferred here from #819 for exactly that reason.
19
- */
20
- import { existsSync, readFileSync } from "node:fs";
21
- import { createRequire } from "node:module";
22
- import { dirname, join } from "node:path";
23
- import { fileURLToPath } from "node:url";
24
-
25
- const PACKAGE = "@nadicodeai/ui";
26
-
27
- /*
28
- * TypeScript is loaded when this rule first runs, not when the plugin loads.
29
- * The plugin's bytes are published inside `@nadicodeai/ui`, which does not
30
- * depend on the compiler; a consumer who never enables this rule never asks
31
- * for it, and one who does is already linting TypeScript.
32
- */
33
- let ts;
34
- function typescript() {
35
- ts ??= createRequire(import.meta.url)("typescript");
36
- return ts;
37
- }
38
-
39
- /** The `.d.ts` the package's `exports` map names as its root type surface. */
40
- function typeSurfaceEntry() {
41
- let directory = dirname(fileURLToPath(import.meta.resolve(PACKAGE)));
42
- for (;;) {
43
- const manifestPath = join(directory, "package.json");
44
- let manifest;
45
- try {
46
- manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
47
- } catch {
48
- const parent = dirname(directory);
49
- if (parent === directory) {
50
- throw new Error(`${PACKAGE}: no manifest above its resolved entry point`);
51
- }
52
- directory = parent;
53
- continue;
54
- }
55
- if (manifest.name !== PACKAGE) {
56
- throw new Error(`${manifestPath} is not ${PACKAGE}`);
57
- }
58
- const entry = join(directory, manifest.exports["."].types);
59
- if (!existsSync(entry)) {
60
- throw new Error(
61
- `${PACKAGE} has no built type surface at ${entry}: build the package before linting.`,
62
- );
63
- }
64
- return entry;
65
- }
66
- }
67
-
68
- /*
69
- * A type is not a component. `export type ChartConfig` names a shape a route
70
- * may legitimately declare its own version of, and it cannot be imported as a
71
- * component at all, so the derived set holds values only: type aliases,
72
- * interfaces, and the `type` marker on an export clause are dropped.
73
- */
74
- function isTypeOnlyStatement(ts, statement) {
75
- return (
76
- ts.isTypeAliasDeclaration(statement) ||
77
- ts.isInterfaceDeclaration(statement) ||
78
- statement.isTypeOnly === true
79
- );
80
- }
81
-
82
- function exportedNames(file, names = new Set(), seen = new Set()) {
83
- if (seen.has(file)) return names;
84
- seen.add(file);
85
- const ts = typescript();
86
- const source = ts.createSourceFile(
87
- file,
88
- readFileSync(file, "utf8"),
89
- ts.ScriptTarget.Latest,
90
- false,
91
- ts.ScriptKind.TS,
92
- );
93
- for (const statement of source.statements) {
94
- if (ts.isExportDeclaration(statement)) {
95
- if (statement.moduleSpecifier && !statement.exportClause) {
96
- exportedNames(
97
- join(dirname(file), statement.moduleSpecifier.text.replace(/\.js$/, ".d.ts")),
98
- names,
99
- seen,
100
- );
101
- } else if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
102
- if (isTypeOnlyStatement(ts, statement)) continue;
103
- for (const element of statement.exportClause.elements) {
104
- if (element.isTypeOnly) continue;
105
- names.add(element.name.text);
106
- }
107
- }
108
- continue;
109
- }
110
- const modifiers = ts.canHaveModifiers(statement) ? (ts.getModifiers(statement) ?? []) : [];
111
- if (!modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) continue;
112
- if (isTypeOnlyStatement(ts, statement)) continue;
113
- if (ts.isVariableStatement(statement)) {
114
- for (const declaration of statement.declarationList.declarations) {
115
- if (ts.isIdentifier(declaration.name)) names.add(declaration.name.text);
116
- }
117
- } else if (statement.name && ts.isIdentifier(statement.name)) {
118
- names.add(statement.name.text);
119
- }
120
- }
121
- return names;
122
- }
123
-
124
- let packageExports;
125
-
126
- /** Read once per lint process; the built surface does not move under it. */
127
- function packageExportNames() {
128
- packageExports ??= exportedNames(typeSurfaceEntry());
129
- return packageExports;
130
- }
131
-
132
- const COMPONENT_NAME = /^[A-Z]/;
133
-
134
- function isFunctionLike(node) {
135
- return (
136
- node?.type === "ArrowFunctionExpression" ||
137
- node?.type === "FunctionExpression" ||
138
- node?.type === "ClassExpression" ||
139
- node?.type === "CallExpression"
140
- );
141
- }
142
-
143
- export const noPackageComponentRedefinition = {
144
- meta: {
145
- type: "problem",
146
- docs: {
147
- description: `Disallow declaring a component whose name ${PACKAGE} already exports`,
148
- },
149
- schema: [],
150
- messages: {
151
- redefined: `{{name}} is exported by ${PACKAGE}: import it instead of declaring a second one here.`,
152
- },
153
- },
154
- create(context) {
155
- const report = (node, name) => {
156
- if (!COMPONENT_NAME.test(name) || !packageExportNames().has(name)) return;
157
- context.report({ node, messageId: "redefined", data: { name } });
158
- };
159
-
160
- return {
161
- FunctionDeclaration(node) {
162
- if (node.id) report(node.id, node.id.name);
163
- },
164
- // A class is the third way to write a component; React still renders one.
165
- ClassDeclaration(node) {
166
- if (node.id) report(node.id, node.id.name);
167
- },
168
- VariableDeclarator(node) {
169
- if (node.id.type === "Identifier" && isFunctionLike(node.init)) {
170
- report(node.id, node.id.name);
171
- }
172
- },
173
- };
174
- },
175
- };
@@ -1,47 +0,0 @@
1
- /*
2
- * Standard tool checked first: a `no-restricted-syntax` descendant selector
3
- * (`JSXElement[openingElement.name.name=/Form$/] JSXOpeningElement`) matches by
4
- * element name, so it cannot tell the package's CollectionSearch from a local
5
- * component of the same name, nor a renamed import.
6
- */
7
- import { packageModules, partOf, renderedImports, writtenName } from "./rendered-import.js";
8
-
9
- const COLLECTION_SEARCH = new Map([
10
- [
11
- "CollectionSearch",
12
- packageModules("collection-search").map((from) => ({ from, part: "CollectionSearch" })),
13
- ],
14
- ]);
15
-
16
- const isForm = (element) => {
17
- const name = writtenName(element.openingElement.name);
18
- return name === "form" || name?.endsWith("Form") === true;
19
- };
20
-
21
- export const noSearchInForm = {
22
- meta: {
23
- type: "problem",
24
- docs: {
25
- description: "A collection's search sits in its toolbar, never inside a form that saves.",
26
- },
27
- schema: [],
28
- messages: {
29
- "search-in-form":
30
- "A search box does not belong in a form that saves something; a collection's search sits in its toolbar.",
31
- },
32
- },
33
- create(context) {
34
- const importOf = renderedImports(context);
35
- return {
36
- JSXOpeningElement(node) {
37
- if (partOf(importOf(node.name), COLLECTION_SEARCH, context.filename) === null) return;
38
- for (let ancestor = node.parent.parent; ancestor; ancestor = ancestor.parent) {
39
- if (ancestor.type === "JSXElement" && isForm(ancestor)) {
40
- context.report({ node, messageId: "search-in-form" });
41
- return;
42
- }
43
- }
44
- },
45
- };
46
- },
47
- };