@gntik-ai/blocks 0.0.0-stage → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/dist/index.d.ts +3494 -0
  2. package/dist/index.js +14911 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +62 -3
  5. package/src/ai/AssistantCardGrid/AssistantCardGrid.test.tsx +32 -0
  6. package/src/ai/AssistantCardGrid/AssistantCardGrid.tsx +98 -0
  7. package/src/ai/AssistantCardGrid/block.meta.ts +10 -0
  8. package/src/ai/AssistantCardGrid/fixtures.ts +72 -0
  9. package/src/ai/EvaluationScorecard/EvaluationScorecard.test.tsx +39 -0
  10. package/src/ai/EvaluationScorecard/EvaluationScorecard.tsx +233 -0
  11. package/src/ai/EvaluationScorecard/block.meta.ts +10 -0
  12. package/src/ai/EvaluationScorecard/fixtures.ts +79 -0
  13. package/src/ai/ModelKeysList/ModelKeysList.test.tsx +44 -0
  14. package/src/ai/ModelKeysList/ModelKeysList.tsx +203 -0
  15. package/src/ai/ModelKeysList/block.meta.ts +10 -0
  16. package/src/ai/ModelKeysList/fixtures.ts +44 -0
  17. package/src/ai/PromptEditor/PromptEditor.test.tsx +97 -0
  18. package/src/ai/PromptEditor/PromptEditor.tsx +202 -0
  19. package/src/ai/PromptEditor/block.meta.ts +10 -0
  20. package/src/ai/PromptEditor/examples/controlled-variables.tsx +23 -0
  21. package/src/ai/PromptEditor/fixtures.ts +12 -0
  22. package/src/ai/TokenCostCard/TokenCostCard.test.tsx +36 -0
  23. package/src/ai/TokenCostCard/TokenCostCard.tsx +130 -0
  24. package/src/ai/TokenCostCard/block.meta.ts +9 -0
  25. package/src/ai/TokenCostCard/fixtures.ts +18 -0
  26. package/src/ai/TraceWaterfall/TraceWaterfall.test.tsx +119 -0
  27. package/src/ai/TraceWaterfall/TraceWaterfall.tsx +317 -0
  28. package/src/ai/TraceWaterfall/block.meta.ts +10 -0
  29. package/src/ai/TraceWaterfall/examples/no-details.tsx +6 -0
  30. package/src/ai/TraceWaterfall/examples/running.tsx +16 -0
  31. package/src/ai/TraceWaterfall/fixtures.ts +133 -0
  32. package/src/ai/TraceWaterfall/span-details.tsx +51 -0
  33. package/src/ai/index.ts +13 -0
  34. package/src/auth/ForgotPasswordForm/ForgotPasswordForm.test.tsx +209 -0
  35. package/src/auth/ForgotPasswordForm/ForgotPasswordForm.tsx +211 -0
  36. package/src/auth/ForgotPasswordForm/block.meta.ts +10 -0
  37. package/src/auth/ForgotPasswordForm/examples/UsernameOrEmail.tsx +24 -0
  38. package/src/auth/MfaChallenge/MfaChallenge.test.tsx +35 -0
  39. package/src/auth/MfaChallenge/MfaChallenge.tsx +170 -0
  40. package/src/auth/MfaChallenge/block.meta.ts +9 -0
  41. package/src/auth/ResetPasswordForm/ResetPasswordForm.test.tsx +27 -0
  42. package/src/auth/ResetPasswordForm/ResetPasswordForm.tsx +100 -0
  43. package/src/auth/ResetPasswordForm/block.meta.ts +10 -0
  44. package/src/auth/SignInForm/SignInForm.test.tsx +222 -0
  45. package/src/auth/SignInForm/SignInForm.tsx +226 -0
  46. package/src/auth/SignInForm/block.meta.ts +22 -0
  47. package/src/auth/SignInForm/examples/Username.tsx +26 -0
  48. package/src/auth/SignUpForm/SignUpForm.test.tsx +33 -0
  49. package/src/auth/SignUpForm/SignUpForm.tsx +154 -0
  50. package/src/auth/SignUpForm/block.meta.ts +9 -0
  51. package/src/auth/SsoButtons/SsoButtons.test.tsx +23 -0
  52. package/src/auth/SsoButtons/SsoButtons.tsx +52 -0
  53. package/src/auth/SsoButtons/block.meta.ts +9 -0
  54. package/src/auth/SsoButtons/fixtures.ts +15 -0
  55. package/src/auth/index.ts +15 -0
  56. package/src/auth/shared.tsx +113 -0
  57. package/src/billing/CostBreakdown/CostBreakdown.test.tsx +24 -0
  58. package/src/billing/CostBreakdown/CostBreakdown.tsx +95 -0
  59. package/src/billing/CostBreakdown/block.meta.ts +10 -0
  60. package/src/billing/CostBreakdown/fixtures.ts +9 -0
  61. package/src/billing/InvoiceTable/InvoiceTable.test.tsx +29 -0
  62. package/src/billing/InvoiceTable/InvoiceTable.tsx +130 -0
  63. package/src/billing/InvoiceTable/block.meta.ts +10 -0
  64. package/src/billing/InvoiceTable/fixtures.ts +38 -0
  65. package/src/billing/PaymentMethodCard/PaymentMethodCard.test.tsx +28 -0
  66. package/src/billing/PaymentMethodCard/PaymentMethodCard.tsx +103 -0
  67. package/src/billing/PaymentMethodCard/block.meta.ts +10 -0
  68. package/src/billing/PaymentMethodCard/fixtures.ts +12 -0
  69. package/src/billing/PlanCard/PlanCard.baseline.test.tsx +26 -0
  70. package/src/billing/PlanCard/PlanCard.summary.test.tsx +227 -0
  71. package/src/billing/PlanCard/PlanCard.test.tsx +27 -0
  72. package/src/billing/PlanCard/PlanCard.tsx +140 -0
  73. package/src/billing/PlanCard/PlanSummary.tsx +101 -0
  74. package/src/billing/PlanCard/README.md +23 -0
  75. package/src/billing/PlanCard/__snapshots__/PlanCard.baseline.test.tsx.snap +5 -0
  76. package/src/billing/PlanCard/block.meta.ts +60 -0
  77. package/src/billing/PlanCard/examples/SummaryActionsOnly.tsx +12 -0
  78. package/src/billing/PlanCard/examples/SummaryFeaturesOnly.tsx +14 -0
  79. package/src/billing/PlanCard/examples/SummaryFull.tsx +28 -0
  80. package/src/billing/PlanCard/examples/SummaryMinimal.tsx +5 -0
  81. package/src/billing/PlanCard/examples/SummaryPriceOnly.tsx +15 -0
  82. package/src/billing/PlanCard/examples/SummaryRenewalOnly.tsx +14 -0
  83. package/src/billing/PlanCard/examples/SummaryUnassigned.tsx +5 -0
  84. package/src/billing/PlanCard/examples/SummaryUnassignedNoLabel.tsx +5 -0
  85. package/src/billing/PlanCard/examples/SummaryZeroPrice.tsx +15 -0
  86. package/src/billing/PlanCard/fixtures.ts +19 -0
  87. package/src/billing/PlanDefinitionCard/PlanDefinitionCard.test.tsx +111 -0
  88. package/src/billing/PlanDefinitionCard/PlanDefinitionCard.tsx +86 -0
  89. package/src/billing/PlanDefinitionCard/README.md +29 -0
  90. package/src/billing/PlanDefinitionCard/block.meta.ts +46 -0
  91. package/src/billing/PlanDefinitionCard/examples/Compact.tsx +23 -0
  92. package/src/billing/PlanDefinitionCard/examples/Full.tsx +23 -0
  93. package/src/billing/PlanDefinitionCard/fixtures.ts +12 -0
  94. package/src/billing/QuotaMeters/QuotaMeters.test.tsx +23 -0
  95. package/src/billing/QuotaMeters/QuotaMeters.tsx +92 -0
  96. package/src/billing/QuotaMeters/block.meta.ts +10 -0
  97. package/src/billing/QuotaMeters/fixtures.ts +9 -0
  98. package/src/billing/SpendVsBudget/SpendVsBudget.test.tsx +24 -0
  99. package/src/billing/SpendVsBudget/SpendVsBudget.tsx +105 -0
  100. package/src/billing/SpendVsBudget/block.meta.ts +10 -0
  101. package/src/billing/SpendVsBudget/fixtures.ts +18 -0
  102. package/src/billing/format.ts +42 -0
  103. package/src/billing/index.ts +21 -0
  104. package/src/builders/CodePanel/CodePanel.test.tsx +280 -0
  105. package/src/builders/CodePanel/CodePanel.tsx +239 -0
  106. package/src/builders/CodePanel/block.meta.ts +10 -0
  107. package/src/builders/CodePanel/examples/controlled.tsx +15 -0
  108. package/src/builders/CodePanel/examples/diagnostics.tsx +40 -0
  109. package/src/builders/CodePanel/fixtures.ts +95 -0
  110. package/src/builders/CodePanel/marker-store.ts +90 -0
  111. package/src/builders/CodePanel/markers.ts +62 -0
  112. package/src/builders/FlowBuilder/FlowBuilder.test.tsx +480 -0
  113. package/src/builders/FlowBuilder/FlowBuilder.tsx +343 -0
  114. package/src/builders/FlowBuilder/block.meta.ts +20 -0
  115. package/src/builders/FlowBuilder/connections.ts +67 -0
  116. package/src/builders/FlowBuilder/decorated-nodes.ts +80 -0
  117. package/src/builders/FlowBuilder/examples/extensions.tsx +120 -0
  118. package/src/builders/FlowBuilder/extensions.ts +37 -0
  119. package/src/builders/FlowBuilder/fixtures.ts +80 -0
  120. package/src/builders/FlowBuilder/parts.tsx +196 -0
  121. package/src/builders/FlowBuilder/problems.tsx +40 -0
  122. package/src/builders/LogViewer/LogViewer.test.tsx +97 -0
  123. package/src/builders/LogViewer/LogViewer.tsx +253 -0
  124. package/src/builders/LogViewer/block.meta.ts +10 -0
  125. package/src/builders/LogViewer/fixtures.ts +49 -0
  126. package/src/builders/index.ts +15 -0
  127. package/src/data-display/ActivityFeed/ActivityFeed.test.tsx +62 -0
  128. package/src/data-display/ActivityFeed/ActivityFeed.tsx +118 -0
  129. package/src/data-display/ActivityFeed/block.meta.ts +10 -0
  130. package/src/data-display/ActivityFeed/fixtures.ts +29 -0
  131. package/src/data-display/ChartCard/ChartCard.ranges.test.tsx +282 -0
  132. package/src/data-display/ChartCard/ChartCard.test.tsx +54 -0
  133. package/src/data-display/ChartCard/ChartCard.tsx +218 -0
  134. package/src/data-display/ChartCard/block.meta.ts +10 -0
  135. package/src/data-display/ChartCard/examples/custom-ranges.tsx +37 -0
  136. package/src/data-display/ChartCard/examples/heatmap.tsx +20 -0
  137. package/src/data-display/ChartCard/examples/toolbar-owned-range.tsx +34 -0
  138. package/src/data-display/ChartCard/fixtures.ts +82 -0
  139. package/src/data-display/CommentThread/CommentThread.test.tsx +29 -0
  140. package/src/data-display/CommentThread/CommentThread.tsx +120 -0
  141. package/src/data-display/CommentThread/block.meta.ts +10 -0
  142. package/src/data-display/CommentThread/fixtures.ts +28 -0
  143. package/src/data-display/DescriptionListCard/DescriptionListCard.test.tsx +28 -0
  144. package/src/data-display/DescriptionListCard/DescriptionListCard.tsx +120 -0
  145. package/src/data-display/DescriptionListCard/block.meta.ts +9 -0
  146. package/src/data-display/DescriptionListCard/fixtures.ts +12 -0
  147. package/src/data-display/KpiRow/KpiRow.test.tsx +29 -0
  148. package/src/data-display/KpiRow/KpiRow.tsx +101 -0
  149. package/src/data-display/KpiRow/block.meta.ts +10 -0
  150. package/src/data-display/KpiRow/fixtures.ts +34 -0
  151. package/src/data-display/ResourceCardGrid/ResourceCardGrid.test.tsx +22 -0
  152. package/src/data-display/ResourceCardGrid/ResourceCardGrid.tsx +98 -0
  153. package/src/data-display/ResourceCardGrid/block.meta.ts +10 -0
  154. package/src/data-display/ResourceCardGrid/fixtures.ts +72 -0
  155. package/src/data-display/StatCard/StatCard.test.tsx +30 -0
  156. package/src/data-display/StatCard/StatCard.tsx +138 -0
  157. package/src/data-display/StatCard/block.meta.ts +10 -0
  158. package/src/data-display/StatCard/fixtures.ts +26 -0
  159. package/src/data-display/StatusTimeline/StatusTimeline.test.tsx +153 -0
  160. package/src/data-display/StatusTimeline/StatusTimeline.tsx +114 -0
  161. package/src/data-display/StatusTimeline/block.meta.ts +10 -0
  162. package/src/data-display/StatusTimeline/examples/Empty.tsx +6 -0
  163. package/src/data-display/StatusTimeline/fixtures.ts +62 -0
  164. package/src/data-display/index.ts +34 -0
  165. package/src/data-display/shared/TrendDelta.tsx +60 -0
  166. package/src/feedback/ConfirmDestructive/ConfirmDestructive.test.tsx +47 -0
  167. package/src/feedback/ConfirmDestructive/ConfirmDestructive.tsx +145 -0
  168. package/src/feedback/ConfirmDestructive/block.meta.ts +10 -0
  169. package/src/feedback/EmptyStates/EmptyStates.test.tsx +48 -0
  170. package/src/feedback/EmptyStates/EmptyStates.tsx +191 -0
  171. package/src/feedback/EmptyStates/block.meta.ts +10 -0
  172. package/src/feedback/EmptyStates/fixtures.ts +6 -0
  173. package/src/feedback/ErrorPanel/ErrorPanel.test.tsx +37 -0
  174. package/src/feedback/ErrorPanel/ErrorPanel.tsx +146 -0
  175. package/src/feedback/ErrorPanel/block.meta.ts +10 -0
  176. package/src/feedback/InlineCallout/InlineCallout.test.tsx +33 -0
  177. package/src/feedback/InlineCallout/InlineCallout.tsx +81 -0
  178. package/src/feedback/InlineCallout/block.meta.ts +9 -0
  179. package/src/feedback/Skeletons/Skeletons.test.tsx +25 -0
  180. package/src/feedback/Skeletons/Skeletons.tsx +142 -0
  181. package/src/feedback/Skeletons/block.meta.ts +10 -0
  182. package/src/feedback/SystemBanner/SystemBanner.test.tsx +29 -0
  183. package/src/feedback/SystemBanner/SystemBanner.tsx +105 -0
  184. package/src/feedback/SystemBanner/block.meta.ts +9 -0
  185. package/src/feedback/index.ts +14 -0
  186. package/src/feedback/types.ts +13 -0
  187. package/src/forms/CreateApiKeyDialog/CreateApiKeyDialog.test.tsx +36 -0
  188. package/src/forms/CreateApiKeyDialog/CreateApiKeyDialog.tsx +197 -0
  189. package/src/forms/CreateApiKeyDialog/block.meta.ts +10 -0
  190. package/src/forms/CreateApiKeyDialog/fixtures.ts +31 -0
  191. package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.test.tsx +339 -0
  192. package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.tsx +193 -0
  193. package/src/forms/CredentialDisclosureDialog/block.meta.ts +10 -0
  194. package/src/forms/CredentialDisclosureDialog/examples/Fresh.tsx +32 -0
  195. package/src/forms/CredentialDisclosureDialog/examples/ResponseGating.tsx +69 -0
  196. package/src/forms/CredentialDisclosureDialog/examples/Stored.tsx +32 -0
  197. package/src/forms/CredentialDisclosureDialog/fixtures.ts +14 -0
  198. package/src/forms/DangerZone/ConfirmDestructiveDialog.tsx +101 -0
  199. package/src/forms/DangerZone/DangerZone.test.tsx +28 -0
  200. package/src/forms/DangerZone/DangerZone.tsx +86 -0
  201. package/src/forms/DangerZone/block.meta.ts +10 -0
  202. package/src/forms/DangerZone/fixtures.ts +36 -0
  203. package/src/forms/DateRangeFilter/DateRangeFilter.test.tsx +26 -0
  204. package/src/forms/DateRangeFilter/DateRangeFilter.tsx +83 -0
  205. package/src/forms/DateRangeFilter/block.meta.ts +9 -0
  206. package/src/forms/DialogWizard/DialogWizard.test.tsx +95 -0
  207. package/src/forms/DialogWizard/DialogWizard.tsx +39 -0
  208. package/src/forms/DialogWizard/block.meta.ts +10 -0
  209. package/src/forms/DialogWizard/examples/CreateResource.tsx +65 -0
  210. package/src/forms/DialogWizard/fixtures.ts +20 -0
  211. package/src/forms/FileUploadPanel/FileUploadPanel.test.tsx +43 -0
  212. package/src/forms/FileUploadPanel/FileUploadPanel.tsx +244 -0
  213. package/src/forms/FileUploadPanel/block.meta.ts +10 -0
  214. package/src/forms/FileUploadPanel/fixtures.ts +66 -0
  215. package/src/forms/FormSection/FormSection.test.tsx +34 -0
  216. package/src/forms/FormSection/FormSection.tsx +77 -0
  217. package/src/forms/FormSection/block.meta.ts +9 -0
  218. package/src/forms/InviteMembersDialog/InviteMembersDialog.test.tsx +37 -0
  219. package/src/forms/InviteMembersDialog/InviteMembersDialog.tsx +183 -0
  220. package/src/forms/InviteMembersDialog/block.meta.ts +10 -0
  221. package/src/forms/InviteMembersDialog/fixtures.ts +14 -0
  222. package/src/forms/SettingsRow/SettingsRow.test.tsx +50 -0
  223. package/src/forms/SettingsRow/SettingsRow.tsx +74 -0
  224. package/src/forms/SettingsRow/block.meta.ts +9 -0
  225. package/src/forms/ValidationSummary/ValidationSummary.test.tsx +46 -0
  226. package/src/forms/ValidationSummary/ValidationSummary.tsx +75 -0
  227. package/src/forms/ValidationSummary/block.meta.ts +9 -0
  228. package/src/forms/ValidationSummary/fixtures.ts +13 -0
  229. package/src/forms/index.ts +27 -0
  230. package/src/index.ts +14 -0
  231. package/src/marketing/ComparisonTable/ComparisonTable.test.tsx +69 -0
  232. package/src/marketing/ComparisonTable/ComparisonTable.tsx +245 -0
  233. package/src/marketing/ComparisonTable/block.meta.ts +10 -0
  234. package/src/marketing/ComparisonTable/examples/options.tsx +48 -0
  235. package/src/marketing/ComparisonTable/fixtures.ts +83 -0
  236. package/src/marketing/Faq/Faq.test.tsx +25 -0
  237. package/src/marketing/Faq/Faq.tsx +68 -0
  238. package/src/marketing/Faq/block.meta.ts +10 -0
  239. package/src/marketing/Faq/fixtures.ts +32 -0
  240. package/src/marketing/FeatureGrid/FeatureGrid.test.tsx +19 -0
  241. package/src/marketing/FeatureGrid/FeatureGrid.tsx +79 -0
  242. package/src/marketing/FeatureGrid/block.meta.ts +10 -0
  243. package/src/marketing/FeatureGrid/fixtures.ts +36 -0
  244. package/src/marketing/Hero/Hero.test.tsx +33 -0
  245. package/src/marketing/Hero/Hero.tsx +107 -0
  246. package/src/marketing/Hero/block.meta.ts +10 -0
  247. package/src/marketing/PricingTable/PricingTable.test.tsx +92 -0
  248. package/src/marketing/PricingTable/PricingTable.tsx +323 -0
  249. package/src/marketing/PricingTable/block.meta.ts +10 -0
  250. package/src/marketing/PricingTable/fixtures.ts +34 -0
  251. package/src/marketing/actions.tsx +40 -0
  252. package/src/marketing/index.ts +15 -0
  253. package/src/meta.ts +23 -0
  254. package/src/page-chrome/BulkActionBar/BulkActionBar.test.tsx +34 -0
  255. package/src/page-chrome/BulkActionBar/BulkActionBar.tsx +76 -0
  256. package/src/page-chrome/BulkActionBar/block.meta.ts +9 -0
  257. package/src/page-chrome/PageHeader/PageHeader.test.tsx +68 -0
  258. package/src/page-chrome/PageHeader/PageHeader.tsx +152 -0
  259. package/src/page-chrome/PageHeader/block.meta.ts +10 -0
  260. package/src/page-chrome/PageHeader/fixtures.ts +29 -0
  261. package/src/page-chrome/PageToolbar/PageToolbar.test.tsx +38 -0
  262. package/src/page-chrome/PageToolbar/PageToolbar.tsx +138 -0
  263. package/src/page-chrome/PageToolbar/block.meta.ts +10 -0
  264. package/src/page-chrome/PageToolbar/fixtures.ts +8 -0
  265. package/src/page-chrome/SectionHeader/SectionHeader.test.tsx +28 -0
  266. package/src/page-chrome/SectionHeader/SectionHeader.tsx +73 -0
  267. package/src/page-chrome/SectionHeader/block.meta.ts +9 -0
  268. package/src/page-chrome/StickyActionBar/StickyActionBar.test.tsx +29 -0
  269. package/src/page-chrome/StickyActionBar/StickyActionBar.tsx +66 -0
  270. package/src/page-chrome/StickyActionBar/block.meta.ts +9 -0
  271. package/src/page-chrome/index.ts +13 -0
  272. package/src/page-chrome/types.ts +25 -0
  273. package/src/scheduling/AgendaList/AgendaList.test.tsx +28 -0
  274. package/src/scheduling/AgendaList/AgendaList.tsx +165 -0
  275. package/src/scheduling/AgendaList/block.meta.ts +10 -0
  276. package/src/scheduling/MonthCalendar/MonthCalendar.test.tsx +32 -0
  277. package/src/scheduling/MonthCalendar/MonthCalendar.tsx +223 -0
  278. package/src/scheduling/MonthCalendar/block.meta.ts +10 -0
  279. package/src/scheduling/TimeSlotPicker/TimeSlotPicker.test.tsx +26 -0
  280. package/src/scheduling/TimeSlotPicker/TimeSlotPicker.tsx +115 -0
  281. package/src/scheduling/TimeSlotPicker/block.meta.ts +10 -0
  282. package/src/scheduling/TimeSlotPicker/fixtures.ts +10 -0
  283. package/src/scheduling/WeekCalendar/WeekCalendar.test.tsx +27 -0
  284. package/src/scheduling/WeekCalendar/WeekCalendar.tsx +274 -0
  285. package/src/scheduling/WeekCalendar/block.meta.ts +10 -0
  286. package/src/scheduling/index.ts +11 -0
  287. package/src/scheduling/shared/CalendarHeader.tsx +54 -0
  288. package/src/scheduling/shared/EventTile.tsx +21 -0
  289. package/src/scheduling/shared/dates.ts +98 -0
  290. package/src/scheduling/shared/events.ts +90 -0
  291. package/src/scheduling/shared/fixtures.ts +54 -0
  292. package/src/shell/AppSidebar/AppSidebar.test.tsx +38 -0
  293. package/src/shell/AppSidebar/AppSidebar.tsx +226 -0
  294. package/src/shell/AppSidebar/block.meta.ts +10 -0
  295. package/src/shell/AppSidebar/fixtures.ts +58 -0
  296. package/src/shell/AppTopbar/AppTopbar.test.tsx +41 -0
  297. package/src/shell/AppTopbar/AppTopbar.tsx +82 -0
  298. package/src/shell/AppTopbar/block.meta.ts +19 -0
  299. package/src/shell/AppTopbar/fixtures.ts +22 -0
  300. package/src/shell/EnvironmentBadge/EnvironmentBadge.test.tsx +22 -0
  301. package/src/shell/EnvironmentBadge/EnvironmentBadge.tsx +55 -0
  302. package/src/shell/EnvironmentBadge/block.meta.ts +10 -0
  303. package/src/shell/GlobalSearch/GlobalSearch.test.tsx +28 -0
  304. package/src/shell/GlobalSearch/GlobalSearch.tsx +80 -0
  305. package/src/shell/GlobalSearch/block.meta.ts +10 -0
  306. package/src/shell/GlobalSearch/fixtures.ts +64 -0
  307. package/src/shell/index.ts +11 -0
  308. package/src/styles.css +2 -0
  309. package/src/tables/DataTable/DataTable.edit-views.test.tsx +249 -0
  310. package/src/tables/DataTable/DataTable.layout.test.tsx +188 -0
  311. package/src/tables/DataTable/DataTable.single-select.test.tsx +501 -0
  312. package/src/tables/DataTable/DataTable.test.tsx +268 -0
  313. package/src/tables/DataTable/DataTable.tsx +520 -0
  314. package/src/tables/DataTable/block.meta.ts +27 -0
  315. package/src/tables/DataTable/column-menu.tsx +109 -0
  316. package/src/tables/DataTable/column-sizing.ts +166 -0
  317. package/src/tables/DataTable/data-table-body.tsx +194 -0
  318. package/src/tables/DataTable/data-table-props.ts +156 -0
  319. package/src/tables/DataTable/data-table-row.tsx +220 -0
  320. package/src/tables/DataTable/data-table-utils.ts +195 -0
  321. package/src/tables/DataTable/density-toggle.tsx +30 -0
  322. package/src/tables/DataTable/editable-cell.tsx +274 -0
  323. package/src/tables/DataTable/examples/editable.tsx +71 -0
  324. package/src/tables/DataTable/examples/grouped.tsx +18 -0
  325. package/src/tables/DataTable/examples/resizable-pinned.tsx +20 -0
  326. package/src/tables/DataTable/examples/saved-views.tsx +61 -0
  327. package/src/tables/DataTable/examples/single-selection.tsx +55 -0
  328. package/src/tables/DataTable/examples/virtualized.tsx +20 -0
  329. package/src/tables/DataTable/fixtures.ts +119 -0
  330. package/src/tables/DataTable/header-cell.tsx +86 -0
  331. package/src/tables/DataTable/resize-handle.tsx +88 -0
  332. package/src/tables/DataTable/single-selection.tsx +149 -0
  333. package/src/tables/DataTable/use-table-views.ts +79 -0
  334. package/src/tables/DataTable/use-virtual-rows.ts +83 -0
  335. package/src/tables/DataTable/views.tsx +186 -0
  336. package/src/tables/FilterBar/FilterBar.test.tsx +43 -0
  337. package/src/tables/FilterBar/FilterBar.tsx +199 -0
  338. package/src/tables/FilterBar/block.meta.ts +10 -0
  339. package/src/tables/FilterBar/fixtures.ts +32 -0
  340. package/src/tables/InlineEditRow/InlineEditRow.test.tsx +41 -0
  341. package/src/tables/InlineEditRow/InlineEditRow.tsx +215 -0
  342. package/src/tables/InlineEditRow/block.meta.ts +10 -0
  343. package/src/tables/InlineEditRow/fixtures.ts +28 -0
  344. package/src/tables/PaginationFooter/PaginationFooter.test.tsx +29 -0
  345. package/src/tables/PaginationFooter/PaginationFooter.tsx +94 -0
  346. package/src/tables/PaginationFooter/block.meta.ts +10 -0
  347. package/src/tables/SubordinateList/SubordinateList.test.tsx +351 -0
  348. package/src/tables/SubordinateList/SubordinateList.tsx +140 -0
  349. package/src/tables/SubordinateList/block.meta.ts +10 -0
  350. package/src/tables/SubordinateList/examples/ResourceForm.tsx +129 -0
  351. package/src/tables/SubordinateList/examples/empty.tsx +5 -0
  352. package/src/tables/SubordinateList/examples/error.tsx +5 -0
  353. package/src/tables/SubordinateList/examples/loading.tsx +5 -0
  354. package/src/tables/TruncatedCollectionFooter/TruncatedCollectionFooter.test.tsx +159 -0
  355. package/src/tables/TruncatedCollectionFooter/TruncatedCollectionFooter.tsx +60 -0
  356. package/src/tables/TruncatedCollectionFooter/block.meta.ts +10 -0
  357. package/src/tables/TruncatedCollectionFooter/examples/exact.tsx +6 -0
  358. package/src/tables/TruncatedCollectionFooter/examples/has-more.tsx +12 -0
  359. package/src/tables/TruncatedCollectionFooter/fixtures.ts +2 -0
  360. package/src/tables/index.ts +45 -0
  361. package/src/team/MembersTable/MembersTable.test.tsx +29 -0
  362. package/src/team/MembersTable/MembersTable.tsx +141 -0
  363. package/src/team/MembersTable/block.meta.ts +9 -0
  364. package/src/team/MembersTable/fixtures.ts +19 -0
  365. package/src/team/NotificationMatrix/NotificationMatrix.test.tsx +36 -0
  366. package/src/team/NotificationMatrix/NotificationMatrix.tsx +144 -0
  367. package/src/team/NotificationMatrix/block.meta.ts +9 -0
  368. package/src/team/NotificationMatrix/fixtures.ts +43 -0
  369. package/src/team/PendingInvitations/PendingInvitations.test.tsx +30 -0
  370. package/src/team/PendingInvitations/PendingInvitations.tsx +132 -0
  371. package/src/team/PendingInvitations/block.meta.ts +9 -0
  372. package/src/team/PendingInvitations/fixtures.ts +39 -0
  373. package/src/team/ProfileHeader/ProfileHeader.test.tsx +29 -0
  374. package/src/team/ProfileHeader/ProfileHeader.tsx +106 -0
  375. package/src/team/ProfileHeader/block.meta.ts +10 -0
  376. package/src/team/ProfileHeader/fixtures.ts +28 -0
  377. package/src/team/SessionsDevices/SessionsDevices.test.tsx +30 -0
  378. package/src/team/SessionsDevices/SessionsDevices.tsx +126 -0
  379. package/src/team/SessionsDevices/block.meta.ts +10 -0
  380. package/src/team/SessionsDevices/fixtures.ts +58 -0
  381. package/src/team/index.ts +15 -0
  382. package/src/test/a11y.ts +18 -0
  383. package/src/test/heading-levels.test.tsx +69 -0
  384. package/src/test/setup.ts +36 -0
  385. package/README.md +0 -3
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "@gntik-ai/blocks",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.5.0",
4
+ "description": "gntik-ui blocks: page chrome, data display, tables, forms, feedback, billing, team and builder sections composed from @gntik-ai/ui.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": [
8
+ "*.css"
9
+ ],
10
+ "files": [
11
+ "dist",
12
+ "src"
13
+ ],
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./package.json": "./package.json",
22
+ "./styles.css": "./src/styles.css"
23
+ },
24
+ "publishConfig": {
25
+ "registry": "https://registry.npmjs.org/",
26
+ "access": "public"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/gntik-ai/gntik-ui.git",
31
+ "directory": "packages/blocks"
32
+ },
33
+ "peerDependencies": {
34
+ "react": "^19.0.0",
35
+ "react-dom": "^19.0.0"
36
+ },
37
+ "dependencies": {
38
+ "@gntik-ai/charts": "0.5.0",
39
+ "@gntik-ai/editor": "0.5.0",
40
+ "@gntik-ai/flow": "0.5.0",
41
+ "@gntik-ai/icons": "0.5.0",
42
+ "@gntik-ai/tokens": "0.2.1",
43
+ "@gntik-ai/ui": "0.5.0",
44
+ "lucide-react": "^1.48.0"
45
+ },
46
+ "devDependencies": {
47
+ "@testing-library/jest-dom": "^7.0.1",
48
+ "@testing-library/react": "^16.3.3",
49
+ "@testing-library/user-event": "^14.6.7",
50
+ "@types/react": "^19.3.0",
51
+ "@types/react-dom": "^19.3.0",
52
+ "axe-core": "^4.13.0",
53
+ "jsdom": "^30.1.1",
54
+ "react": "^19.3.0",
55
+ "react-dom": "^19.3.0",
56
+ "tsdown": "^0.23.0",
57
+ "typescript": "~6.0.3",
58
+ "vitest": "^5.0.2"
59
+ },
60
+ "scripts": {
61
+ "build": "tsdown",
62
+ "typecheck": "tsc -p tsconfig.json --noEmit",
63
+ "test": "vitest run"
64
+ }
6
65
  }
@@ -0,0 +1,32 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { AssistantCardGrid } from './AssistantCardGrid';
5
+
6
+ describe('AssistantCardGrid', () => {
7
+ it('renders one card per assistant with status and runtime', async () => {
8
+ render(<AssistantCardGrid onCreate={() => {}} />);
9
+ expect(screen.getByRole('heading', { name: 'Assistants' })).toBeInTheDocument();
10
+ expect(screen.getAllByRole('listitem')).toHaveLength(6);
11
+ expect(screen.getByText('6 total · 2 active')).toBeInTheDocument();
12
+ expect(screen.getAllByText('Degraded')).toHaveLength(1);
13
+ expect(screen.getByText('reasoning-small · self-hosted')).toBeInTheDocument();
14
+ await expectNoAxeViolations();
15
+ });
16
+
17
+ it('calls onSelect and onCreate', async () => {
18
+ const user = userEvent.setup();
19
+ const onSelect = vi.fn();
20
+ const onCreate = vi.fn();
21
+ render(<AssistantCardGrid onSelect={onSelect} onCreate={onCreate} />);
22
+ await user.click(screen.getByRole('button', { name: 'Invoice checker' }));
23
+ expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: 'a3' }));
24
+ await user.click(screen.getByRole('button', { name: 'New assistant' }));
25
+ expect(onCreate).toHaveBeenCalledTimes(1);
26
+ });
27
+
28
+ it('shows an empty state', () => {
29
+ render(<AssistantCardGrid assistants={[]} />);
30
+ expect(screen.getByText('No assistants yet')).toBeInTheDocument();
31
+ });
32
+ });
@@ -0,0 +1,98 @@
1
+ import { useId } from 'react';
2
+ import { Bot, Plus } from '@gntik-ai/icons';
3
+ import { Button, ClickableCard, EmptyState, StatusTag, cn, type StatusDefinition } from '@gntik-ai/ui';
4
+ import { assistants as defaultAssistants, type Assistant, type AssistantStatus } from './fixtures';
5
+
6
+ export type { Assistant, AssistantStatus } from './fixtures';
7
+
8
+ export const ASSISTANT_STATUSES: Record<AssistantStatus, StatusDefinition> = {
9
+ active: { label: 'Active', tone: 'success' },
10
+ paused: { label: 'Paused', tone: 'neutral' },
11
+ draft: { label: 'Draft', tone: 'neutral' },
12
+ degraded: { label: 'Degraded', tone: 'warning' },
13
+ failed: { label: 'Failed', tone: 'destructive' },
14
+ };
15
+
16
+ export interface AssistantCardGridProps {
17
+ assistants?: Assistant[];
18
+ title?: string;
19
+ /** Called when a card is activated (cards with `href` also navigate). */
20
+ onSelect?: (assistant: Assistant) => void;
21
+ /** Shows a "New assistant" button. */
22
+ onCreate?: () => void;
23
+ /** Heading level of the section title (default h2); card names use the next level. */
24
+ titleAs?: 'h2' | 'h3';
25
+ className?: string;
26
+ }
27
+
28
+ /** Grid of assistants as clickable cards (1 → 2 → 3 columns). */
29
+ export function AssistantCardGrid({
30
+ assistants = defaultAssistants,
31
+ title = 'Assistants',
32
+ onSelect,
33
+ onCreate,
34
+ titleAs: TitleTag = 'h2',
35
+ className,
36
+ }: AssistantCardGridProps) {
37
+ const titleId = useId();
38
+ const active = assistants.filter((a) => a.status === 'active').length;
39
+ return (
40
+ <section aria-labelledby={titleId} className={cn('flex flex-col gap-4', className)}>
41
+ <div className="flex flex-wrap items-end justify-between gap-3">
42
+ <div>
43
+ <TitleTag id={titleId} className="text-[15px] font-semibold tracking-tight text-foreground">
44
+ {title}
45
+ </TitleTag>
46
+ <p className="text-[12.5px] text-muted-foreground">
47
+ {assistants.length} total · {active} active
48
+ </p>
49
+ </div>
50
+ {onCreate && (
51
+ <Button size="sm" icon={Plus} onClick={onCreate}>
52
+ New assistant
53
+ </Button>
54
+ )}
55
+ </div>
56
+ {assistants.length === 0 ? (
57
+ <EmptyState
58
+ icon={Bot}
59
+ title="No assistants yet"
60
+ titleAs={TitleTag === 'h2' ? 'h3' : 'h4'}
61
+ description="Create an assistant to automate a task with a model."
62
+ primaryAction={
63
+ onCreate ? (
64
+ <Button size="sm" icon={Plus} onClick={onCreate}>
65
+ New assistant
66
+ </Button>
67
+ ) : undefined
68
+ }
69
+ />
70
+ ) : (
71
+ <ul className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
72
+ {assistants.map((a) => (
73
+ <li key={a.id} className="flex">
74
+ <ClickableCard
75
+ className="w-full"
76
+ titleAs={TitleTag === 'h2' ? 'h3' : 'h4'}
77
+ href={a.href}
78
+ onClick={() => onSelect?.(a)}
79
+ icon={<Bot aria-hidden />}
80
+ title={a.name}
81
+ description={a.description}
82
+ meta={
83
+ <span className="flex flex-wrap items-center gap-x-2 gap-y-1.5">
84
+ <StatusTag status={a.status} statuses={ASSISTANT_STATUSES} />
85
+ <span className="font-mono text-[11px] text-muted-foreground">
86
+ {a.model} · {a.runtime}
87
+ </span>
88
+ {a.updated && <span className="text-[11px] text-muted-foreground">Updated {a.updated}</span>}
89
+ </span>
90
+ }
91
+ />
92
+ </li>
93
+ ))}
94
+ </ul>
95
+ )}
96
+ </section>
97
+ );
98
+ }
@@ -0,0 +1,10 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Assistant card grid',
5
+ family: 'ai',
6
+ status: 'beta',
7
+ description:
8
+ 'Responsive grid of clickable assistant cards: name, description, model and runtime, status and last update, with a create action.',
9
+ uses: ['ClickableCard', 'StatusTag', 'Button', 'EmptyState'],
10
+ };
@@ -0,0 +1,72 @@
1
+ export type AssistantStatus = 'active' | 'paused' | 'draft' | 'degraded' | 'failed';
2
+
3
+ export interface Assistant {
4
+ id: string;
5
+ name: string;
6
+ description: string;
7
+ /** Model identifier, e.g. "general-large". */
8
+ model: string;
9
+ /** Where it runs, e.g. "openai-compatible" or "self-hosted". */
10
+ runtime: string;
11
+ status: AssistantStatus;
12
+ /** Relative update time, e.g. "2h ago". */
13
+ updated?: string;
14
+ /** Optional link to the assistant's page. */
15
+ href?: string;
16
+ }
17
+
18
+ export const assistants: Assistant[] = [
19
+ {
20
+ id: 'a1',
21
+ name: 'Support triage',
22
+ description: 'Classifies incoming tickets and drafts first replies.',
23
+ model: 'general-large',
24
+ runtime: 'openai-compatible',
25
+ status: 'active',
26
+ updated: '2h ago',
27
+ },
28
+ {
29
+ id: 'a2',
30
+ name: 'Release notes writer',
31
+ description: 'Summarises merged changes into customer-facing notes.',
32
+ model: 'general-medium',
33
+ runtime: 'Provider A',
34
+ status: 'active',
35
+ updated: 'yesterday',
36
+ },
37
+ {
38
+ id: 'a3',
39
+ name: 'Invoice checker',
40
+ description: 'Flags invoices whose totals do not match the usage report.',
41
+ model: 'reasoning-small',
42
+ runtime: 'self-hosted',
43
+ status: 'degraded',
44
+ updated: '3d ago',
45
+ },
46
+ {
47
+ id: 'a4',
48
+ name: 'Onboarding guide',
49
+ description: 'Answers setup questions from the product documentation.',
50
+ model: 'general-small',
51
+ runtime: 'self-hosted',
52
+ status: 'paused',
53
+ updated: '1w ago',
54
+ },
55
+ {
56
+ id: 'a5',
57
+ name: 'Data cleanup',
58
+ description: 'Normalises imported records before they reach the warehouse.',
59
+ model: 'general-medium',
60
+ runtime: 'openai-compatible',
61
+ status: 'draft',
62
+ },
63
+ {
64
+ id: 'a6',
65
+ name: 'Incident summariser',
66
+ description: 'Builds a timeline from alerts and chat during an incident.',
67
+ model: 'general-large',
68
+ runtime: 'Provider A',
69
+ status: 'failed',
70
+ updated: '5h ago',
71
+ },
72
+ ];
@@ -0,0 +1,39 @@
1
+ import { render, screen, within } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { EvaluationScorecard } from './EvaluationScorecard';
5
+
6
+ const bodyRows = () => within(screen.getByRole('table')).getAllByRole('row').slice(1);
7
+
8
+ describe('EvaluationScorecard', () => {
9
+ it('renders datasets × metrics with pass rates, deltas and flagged regressions', async () => {
10
+ render(<EvaluationScorecard />);
11
+ expect(bodyRows()).toHaveLength(5);
12
+ expect(screen.getByRole('columnheader', { name: /p95 latency/ })).toBeInTheDocument();
13
+ const billing = bodyRows().find((r) => r.textContent?.includes('billing-questions'))!;
14
+ expect(within(billing).getByText('2 regressions')).toBeInTheDocument();
15
+ expect(within(billing).getByText('80.8%')).toBeInTheDocument();
16
+ expect(within(billing).getAllByText(/\(regression\)/)).toHaveLength(2);
17
+ expect(within(billing).getByText(/\(below threshold\)/)).toBeInTheDocument();
18
+ const docs = bodyRows().find((r) => r.textContent?.includes('product-docs'))!;
19
+ expect(within(docs).getByText('1 regression')).toBeInTheDocument();
20
+ await expectNoAxeViolations();
21
+ });
22
+
23
+ it('filters to regressed datasets only', async () => {
24
+ const user = userEvent.setup();
25
+ render(<EvaluationScorecard />);
26
+ await user.click(screen.getByRole('button', { name: /Regressions/ }));
27
+ expect(bodyRows().map((r) => within(r).getAllByRole('cell')[0]?.textContent)).toEqual([
28
+ expect.stringContaining('billing-questions'),
29
+ expect.stringContaining('product-docs'),
30
+ ]);
31
+ });
32
+
33
+ it('shows an empty row when nothing regressed', async () => {
34
+ const user = userEvent.setup();
35
+ render(<EvaluationScorecard rows={[]} />);
36
+ await user.click(screen.getByRole('button', { name: /Regressions/ }));
37
+ expect(screen.getByText('No regressions against baseline.')).toBeInTheDocument();
38
+ });
39
+ });
@@ -0,0 +1,233 @@
1
+ import { useId, useState } from 'react';
2
+ import { ArrowDown, ArrowUp, Minus } from '@gntik-ai/icons';
3
+ import {
4
+ Badge,
5
+ Table,
6
+ TableBody,
7
+ TableCell,
8
+ TableHead,
9
+ TableHeader,
10
+ TableRow,
11
+ Toggle,
12
+ ToggleGroup,
13
+ cn,
14
+ } from '@gntik-ai/ui';
15
+ import { evalMetrics, evalRows, type EvalMetric, type EvalRow } from './fixtures';
16
+
17
+ export type { EvalMetric, EvalRow } from './fixtures';
18
+
19
+ export interface EvaluationScorecardProps {
20
+ metrics?: EvalMetric[];
21
+ rows?: EvalRow[];
22
+ title?: string;
23
+ /** Label of the run compared against, e.g. "baseline · v12". */
24
+ baselineLabel?: string;
25
+ /** Pass-rate drop (ratio) that counts as a regression. */
26
+ passRateTolerance?: number;
27
+ /** Heading level of the title, to fit the page outline (default h3). */
28
+ titleAs?: 'h2' | 'h3' | 'h4';
29
+ className?: string;
30
+ }
31
+
32
+ type Trend = 'better' | 'worse' | 'flat';
33
+
34
+ function formatValue(value: number, format: EvalMetric['format'] = 'percent') {
35
+ if (format === 'percent') return `${(value * 100).toFixed(1)}%`;
36
+ if (format === 'ms') return `${Math.round(value)}ms`;
37
+ return value.toFixed(2);
38
+ }
39
+
40
+ function formatDelta(delta: number, format: EvalMetric['format'] = 'percent') {
41
+ const sign = delta > 0 ? '+' : delta < 0 ? '−' : '±';
42
+ const abs = Math.abs(delta);
43
+ if (format === 'percent') return `${sign}${(abs * 100).toFixed(1)} pt`;
44
+ if (format === 'ms') return `${sign}${Math.round(abs)}ms`;
45
+ return `${sign}${abs.toFixed(2)}`;
46
+ }
47
+
48
+ function trendOf(delta: number, higherIsBetter: boolean, tolerance: number): Trend {
49
+ const signed = higherIsBetter ? delta : -delta;
50
+ if (signed < -tolerance) return 'worse';
51
+ if (signed > tolerance) return 'better';
52
+ return 'flat';
53
+ }
54
+
55
+ const TREND: Record<Trend, { className: string; sr: string }> = {
56
+ better: { className: 'text-success-text', sr: 'improved' },
57
+ worse: { className: 'text-destructive-text', sr: 'regression' },
58
+ flat: { className: 'text-muted-foreground', sr: 'unchanged' },
59
+ };
60
+
61
+ function Delta({
62
+ delta,
63
+ trend,
64
+ format,
65
+ increase,
66
+ }: {
67
+ delta: number;
68
+ trend: Trend;
69
+ format: EvalMetric['format'];
70
+ increase: boolean;
71
+ }) {
72
+ const t = TREND[trend];
73
+ const TrendIcon = trend === 'flat' ? Minus : increase ? ArrowUp : ArrowDown;
74
+ return (
75
+ <span className={cn('inline-flex items-center gap-0.5 font-mono text-[10.5px]', t.className)}>
76
+ <TrendIcon size={11} aria-hidden />
77
+ {formatDelta(delta, format)}
78
+ <span className="sr-only"> ({t.sr})</span>
79
+ </span>
80
+ );
81
+ }
82
+
83
+ function analyse(row: EvalRow, metrics: EvalMetric[], passRateTolerance: number) {
84
+ const passRate = row.samples > 0 ? row.passed / row.samples : 0;
85
+ const basePassRate = row.samples > 0 ? row.baselinePassed / row.samples : 0;
86
+ const passTrend = trendOf(passRate - basePassRate, true, passRateTolerance);
87
+ const cells = metrics.map((m) => {
88
+ const value = row.scores[m.id];
89
+ const base = row.baseline[m.id];
90
+ const higher = m.higherIsBetter ?? true;
91
+ const delta = value !== undefined && base !== undefined ? value - base : 0;
92
+ const trend = value !== undefined && base !== undefined ? trendOf(delta, higher, m.tolerance ?? 0) : 'flat';
93
+ const passes = value === undefined ? undefined : higher ? value >= m.threshold : value <= m.threshold;
94
+ return { metric: m, value, delta, trend, passes };
95
+ });
96
+ const regressions = cells.filter((c) => c.trend === 'worse').length + (passTrend === 'worse' ? 1 : 0);
97
+ return { passRate, basePassRate, passTrend, cells, regressions };
98
+ }
99
+
100
+ /** Evaluation results by dataset and metric, compared with a baseline run. */
101
+ export function EvaluationScorecard({
102
+ metrics = evalMetrics,
103
+ rows = evalRows,
104
+ title = 'Evaluation results',
105
+ baselineLabel = 'baseline',
106
+ passRateTolerance = 0.02,
107
+ titleAs: TitleTag = 'h3',
108
+ className,
109
+ }: EvaluationScorecardProps) {
110
+ const titleId = useId();
111
+ const [filter, setFilter] = useState<'all' | 'regressions'>('all');
112
+ const analysed = rows.map((row) => ({ row, ...analyse(row, metrics, passRateTolerance) }));
113
+ const regressed = analysed.filter((a) => a.regressions > 0);
114
+ const shown = filter === 'regressions' ? regressed : analysed;
115
+ const samples = rows.reduce((s, r) => s + r.samples, 0);
116
+ const overall = samples > 0 ? rows.reduce((s, r) => s + r.passed, 0) / samples : 0;
117
+ const overallBase = samples > 0 ? rows.reduce((s, r) => s + r.baselinePassed, 0) / samples : 0;
118
+ const overallTrend = trendOf(overall - overallBase, true, passRateTolerance / 2);
119
+
120
+ return (
121
+ <section
122
+ aria-labelledby={titleId}
123
+ className={cn('overflow-hidden rounded-lg border border-border bg-card shadow-sm', className)}
124
+ >
125
+ <header className="flex flex-col gap-3 border-b border-border px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
126
+ <div>
127
+ <TitleTag id={titleId} className="text-[13.5px] font-semibold text-foreground">
128
+ {title}
129
+ </TitleTag>
130
+ <p className="mt-0.5 flex flex-wrap items-center gap-x-2 text-[12px] text-muted-foreground">
131
+ <span>
132
+ Pass rate <span className="font-mono font-semibold text-foreground">{formatValue(overall)}</span>
133
+ </span>
134
+ <Delta
135
+ delta={overall - overallBase}
136
+ trend={overallTrend}
137
+ format="percent"
138
+ increase={overall >= overallBase}
139
+ />
140
+ <span>
141
+ vs {baselineLabel} · {samples.toLocaleString('en-US')} samples
142
+ </span>
143
+ </p>
144
+ </div>
145
+ <ToggleGroup
146
+ aria-label="Show datasets"
147
+ size="sm"
148
+ value={[filter]}
149
+ onValueChange={(v) => v[0] && setFilter(v[0] as 'all' | 'regressions')}
150
+ >
151
+ <Toggle value="all">
152
+ All <span className="font-mono text-[10.5px] opacity-70">{rows.length}</span>
153
+ </Toggle>
154
+ <Toggle value="regressions">
155
+ Regressions <span className="font-mono text-[10.5px] opacity-70">{regressed.length}</span>
156
+ </Toggle>
157
+ </ToggleGroup>
158
+ </header>
159
+ <Table density="compact" containerLabel={title}>
160
+ <TableHeader>
161
+ <TableRow>
162
+ <TableHead>Dataset</TableHead>
163
+ <TableHead align="right">Pass rate</TableHead>
164
+ {metrics.map((m) => (
165
+ <TableHead key={m.id} align="right">
166
+ {m.label}
167
+ <span className="block font-mono text-[10px] font-normal text-muted-foreground">
168
+ {(m.higherIsBetter ?? true) ? '≥' : '≤'} {formatValue(m.threshold, m.format)}
169
+ </span>
170
+ </TableHead>
171
+ ))}
172
+ </TableRow>
173
+ </TableHeader>
174
+ <TableBody>
175
+ {shown.length === 0 ? (
176
+ <TableRow>
177
+ <TableCell colSpan={metrics.length + 2} className="py-6 text-center text-muted-foreground">
178
+ No regressions against {baselineLabel}.
179
+ </TableCell>
180
+ </TableRow>
181
+ ) : (
182
+ shown.map(({ row, passRate, basePassRate, passTrend, cells, regressions }) => (
183
+ <TableRow key={row.id} data-regressed={regressions > 0 || undefined}>
184
+ <TableCell>
185
+ <div className="flex flex-wrap items-center gap-2">
186
+ <span className="font-mono text-[12.5px] text-foreground">{row.dataset}</span>
187
+ {regressions > 0 && (
188
+ <Badge tone="destructive" size="sm">
189
+ {regressions} {regressions === 1 ? 'regression' : 'regressions'}
190
+ </Badge>
191
+ )}
192
+ </div>
193
+ <span className="text-[11px] text-muted-foreground">
194
+ {row.passed}/{row.samples} passed
195
+ </span>
196
+ </TableCell>
197
+ <TableCell align="right">
198
+ <span className="block font-mono text-[12.5px] text-foreground">{formatValue(passRate)}</span>
199
+ <Delta
200
+ delta={passRate - basePassRate}
201
+ trend={passTrend}
202
+ format="percent"
203
+ increase={passRate >= basePassRate}
204
+ />
205
+ </TableCell>
206
+ {cells.map(({ metric, value, delta, trend, passes }) => (
207
+ <TableCell key={metric.id} align="right">
208
+ {value === undefined ? (
209
+ <span className="text-muted-foreground">—</span>
210
+ ) : (
211
+ <>
212
+ <span
213
+ className={cn(
214
+ 'block font-mono text-[12.5px]',
215
+ passes ? 'text-foreground' : 'text-warning-text',
216
+ )}
217
+ >
218
+ {formatValue(value, metric.format)}
219
+ {!passes && <span className="sr-only"> (below threshold)</span>}
220
+ </span>
221
+ <Delta delta={delta} trend={trend} format={metric.format} increase={delta >= 0} />
222
+ </>
223
+ )}
224
+ </TableCell>
225
+ ))}
226
+ </TableRow>
227
+ ))
228
+ )}
229
+ </TableBody>
230
+ </Table>
231
+ </section>
232
+ );
233
+ }
@@ -0,0 +1,10 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Evaluation scorecard',
5
+ family: 'ai',
6
+ status: 'beta',
7
+ description:
8
+ 'Evaluation run as a datasets × metrics table: pass rate, scores against thresholds, deltas vs the baseline and flagged regressions, with a regressions-only filter.',
9
+ uses: ['Table', 'Badge', 'ToggleGroup', 'Toggle'],
10
+ };
@@ -0,0 +1,79 @@
1
+ export interface EvalMetric {
2
+ id: string;
3
+ label: string;
4
+ /** Minimum (or maximum, when lower is better) value that passes. */
5
+ threshold: number;
6
+ /** Defaults to true; false for costs and latencies. */
7
+ higherIsBetter?: boolean;
8
+ /** How values are shown: ratio as a percentage, or milliseconds. */
9
+ format?: 'percent' | 'ms' | 'number';
10
+ /** Change vs baseline that counts as a regression (same unit as the values). */
11
+ tolerance?: number;
12
+ }
13
+
14
+ export interface EvalRow {
15
+ id: string;
16
+ dataset: string;
17
+ samples: number;
18
+ /** Samples that passed every check in this run. */
19
+ passed: number;
20
+ /** Samples that passed in the baseline run. */
21
+ baselinePassed: number;
22
+ scores: Record<string, number>;
23
+ baseline: Record<string, number>;
24
+ }
25
+
26
+ export const evalMetrics: EvalMetric[] = [
27
+ { id: 'accuracy', label: 'Accuracy', threshold: 0.85, format: 'percent', tolerance: 0.02 },
28
+ { id: 'faithfulness', label: 'Faithfulness', threshold: 0.8, format: 'percent', tolerance: 0.02 },
29
+ { id: 'relevance', label: 'Relevance', threshold: 0.8, format: 'percent', tolerance: 0.02 },
30
+ { id: 'latency', label: 'p95 latency', threshold: 2000, higherIsBetter: false, format: 'ms', tolerance: 150 },
31
+ ];
32
+
33
+ export const evalRows: EvalRow[] = [
34
+ {
35
+ id: 'r1',
36
+ dataset: 'support-faq',
37
+ samples: 240,
38
+ passed: 221,
39
+ baselinePassed: 214,
40
+ scores: { accuracy: 0.93, faithfulness: 0.91, relevance: 0.89, latency: 1420 },
41
+ baseline: { accuracy: 0.9, faithfulness: 0.9, relevance: 0.88, latency: 1510 },
42
+ },
43
+ {
44
+ id: 'r2',
45
+ dataset: 'billing-questions',
46
+ samples: 120,
47
+ passed: 97,
48
+ baselinePassed: 106,
49
+ scores: { accuracy: 0.84, faithfulness: 0.86, relevance: 0.82, latency: 1650 },
50
+ baseline: { accuracy: 0.89, faithfulness: 0.87, relevance: 0.83, latency: 1600 },
51
+ },
52
+ {
53
+ id: 'r3',
54
+ dataset: 'product-docs',
55
+ samples: 310,
56
+ passed: 284,
57
+ baselinePassed: 281,
58
+ scores: { accuracy: 0.92, faithfulness: 0.88, relevance: 0.9, latency: 1880 },
59
+ baseline: { accuracy: 0.91, faithfulness: 0.88, relevance: 0.9, latency: 1700 },
60
+ },
61
+ {
62
+ id: 'r4',
63
+ dataset: 'edge-cases',
64
+ samples: 80,
65
+ passed: 58,
66
+ baselinePassed: 55,
67
+ scores: { accuracy: 0.76, faithfulness: 0.79, relevance: 0.81, latency: 2210 },
68
+ baseline: { accuracy: 0.73, faithfulness: 0.78, relevance: 0.8, latency: 2250 },
69
+ },
70
+ {
71
+ id: 'r5',
72
+ dataset: 'multilingual',
73
+ samples: 150,
74
+ passed: 132,
75
+ baselinePassed: 131,
76
+ scores: { accuracy: 0.88, faithfulness: 0.85, relevance: 0.86, latency: 1540 },
77
+ baseline: { accuracy: 0.88, faithfulness: 0.86, relevance: 0.85, latency: 1530 },
78
+ },
79
+ ];