@vadimcomanescu/nadicode-design-system 2.0.8 → 4.0.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 (137) hide show
  1. package/.agents/skills/seed/SKILL.md +19 -10
  2. package/.agents/skills/seed/contract.md +9 -2
  3. package/.agents/skills/seed/references/blocks.md +5 -5
  4. package/.agents/skills/seed/references/components.md +2 -2
  5. package/.agents/skills/seed/references/nextjs.md +2 -2
  6. package/README.md +3 -3
  7. package/contracts/consumer-intent-map.json +1 -2
  8. package/contracts/release-governance-baseline.json +3 -37
  9. package/dist/catalog/catalog.d.ts +2220 -0
  10. package/dist/catalog/catalog.js +1913 -0
  11. package/dist/catalog/components.d.ts +201 -0
  12. package/dist/catalog/components.js +407 -0
  13. package/dist/catalog/types.d.ts +4 -0
  14. package/dist/catalog/types.js +1 -0
  15. package/dist/chunk-224KPIOG.js +60 -0
  16. package/dist/chunk-25BOZMXA.js +169 -0
  17. package/dist/chunk-32OLQ7FC.js +130 -0
  18. package/dist/chunk-3JJBJ4VR.js +47 -0
  19. package/dist/chunk-3U56FXYC.js +30 -0
  20. package/dist/chunk-4MWKE6F5.js +86 -0
  21. package/dist/chunk-6HGSU24S.js +94 -0
  22. package/dist/chunk-7IADIXDV.js +168 -0
  23. package/dist/chunk-7NS3VFD7.js +86 -0
  24. package/dist/chunk-7XLZCXUL.js +175 -0
  25. package/dist/chunk-ALA6OM7K.js +134 -0
  26. package/dist/chunk-AN5TW4AL.js +50 -0
  27. package/dist/chunk-AWZFQQGN.js +167 -0
  28. package/dist/chunk-BRCBJ3S4.js +42 -0
  29. package/dist/chunk-BRICSLHJ.js +30 -0
  30. package/dist/chunk-BYEHHZZN.js +115 -0
  31. package/dist/chunk-C33GUEDY.js +149 -0
  32. package/dist/chunk-CUDMDYKE.js +150 -0
  33. package/dist/chunk-CVTMWSNS.js +145 -0
  34. package/dist/chunk-DEZXWNYF.js +165 -0
  35. package/dist/chunk-DNJEVMDY.js +40 -0
  36. package/dist/chunk-DNJOBML6.js +66 -0
  37. package/dist/chunk-FTGFOK6T.js +69 -0
  38. package/dist/chunk-HFBJ6L6O.js +104 -0
  39. package/dist/chunk-HPTHS7SX.js +52 -0
  40. package/dist/chunk-KNR3WB5C.js +147 -0
  41. package/dist/chunk-KQ7ZC6EM.js +66 -0
  42. package/dist/chunk-LGW7FVG5.js +83 -0
  43. package/dist/chunk-LP6ZZYOQ.js +36 -0
  44. package/dist/chunk-LV4P7WVM.js +54 -0
  45. package/dist/chunk-MGSGCARB.js +164 -0
  46. package/dist/chunk-N3YFYMNZ.js +73 -0
  47. package/dist/chunk-Q5IYBNA7.js +56 -0
  48. package/dist/chunk-QJCE7NZF.js +85 -0
  49. package/dist/chunk-QW5II6YK.js +96 -0
  50. package/dist/chunk-RMGDDOCD.js +138 -0
  51. package/dist/chunk-RNCX4JIE.js +70 -0
  52. package/dist/chunk-RWCL5OPX.js +112 -0
  53. package/dist/chunk-S5OY2B63.js +28 -0
  54. package/dist/chunk-SIQNG72C.js +257 -0
  55. package/dist/chunk-SP7NIZFP.js +117 -0
  56. package/dist/chunk-SWRJWMGG.js +30 -0
  57. package/dist/chunk-TCQIJ3DO.js +85 -0
  58. package/dist/chunk-TPJ6JJ2F.js +290 -0
  59. package/dist/chunk-TUJZMJXW.js +72 -0
  60. package/dist/chunk-UR43ANYS.js +159 -0
  61. package/dist/chunk-VRGPG2YN.js +79 -0
  62. package/dist/chunk-WSBLCWY7.js +126 -0
  63. package/dist/chunk-XKKFSFYO.js +93 -0
  64. package/dist/chunk-XO7TBM47.js +32 -0
  65. package/dist/chunk-YDYDGG5K.js +132 -0
  66. package/dist/chunk-YMJOUYMT.js +171 -0
  67. package/dist/chunk-Z2WION42.js +32 -0
  68. package/dist/chunk-ZFKSVEYW.js +35 -0
  69. package/dist/components/blocks/AgentProfileGridBlock.js +8 -86
  70. package/dist/components/blocks/AgentRunOverviewBlock.js +8 -147
  71. package/dist/components/blocks/AgentWorkbenchBlock.js +15 -257
  72. package/dist/components/blocks/AudioVisualizerBlock.js +2 -50
  73. package/dist/components/blocks/AuthLayout.js +9 -73
  74. package/dist/components/blocks/BannerBlock.js +8 -66
  75. package/dist/components/blocks/BarChartBlock.js +5 -47
  76. package/dist/components/blocks/ChartBlock.js +7 -54
  77. package/dist/components/blocks/ChartCollectionBlock.js +11 -171
  78. package/dist/components/blocks/ChatLayout.js +12 -126
  79. package/dist/components/blocks/CreateBlock.js +9 -104
  80. package/dist/components/blocks/DataGridBlock.js +9 -117
  81. package/dist/components/blocks/DirectoryBlock.js +12 -85
  82. package/dist/components/blocks/FeatureGridBlock.js +6 -56
  83. package/dist/components/blocks/GalleryBlock.js +6 -69
  84. package/dist/components/blocks/HeroSectionBlock.js +10 -134
  85. package/dist/components/blocks/IntegrationsBlock.js +13 -94
  86. package/dist/components/blocks/InteractiveAreaChartBlock.js +5 -290
  87. package/dist/components/blocks/KanbanDemoBlock.js +8 -145
  88. package/dist/components/blocks/LogoCloud.js +4 -35
  89. package/dist/components/blocks/NavUser.js +5 -85
  90. package/dist/components/blocks/NotFoundBlock.js +8 -32
  91. package/dist/components/blocks/OnboardingBlock.js +7 -66
  92. package/dist/components/blocks/SettingsLayout.js +13 -86
  93. package/dist/components/blocks/SignUpBlock.js +8 -168
  94. package/dist/components/blocks/SolutionShowcaseBlock.js +11 -112
  95. package/dist/components/blocks/StatsBlock.js +6 -60
  96. package/dist/components/blocks/UsageDonutBlock.js +5 -79
  97. package/dist/components/blocks/WizardBlock.js +12 -93
  98. package/dist/components/blocks/user/InviteUserModal.js +10 -132
  99. package/dist/components/page-kits/AccountLockedPageKit.js +3 -40
  100. package/dist/components/page-kits/AgentsChatPageKit.js +11 -159
  101. package/dist/components/page-kits/AnalyticsPageKit.js +12 -150
  102. package/dist/components/page-kits/BlogContentPageKit.js +12 -167
  103. package/dist/components/page-kits/CheckoutPageKit.js +9 -83
  104. package/dist/components/page-kits/CompanySuitePageKit.js +9 -96
  105. package/dist/components/page-kits/DashboardPageKit.js +11 -149
  106. package/dist/components/page-kits/ErrorPageKit.js +5 -52
  107. package/dist/components/page-kits/KanbanBoardPageKit.js +7 -169
  108. package/dist/components/page-kits/LandingPageKit.js +11 -72
  109. package/dist/components/page-kits/LoginPageKit.js +3 -32
  110. package/dist/components/page-kits/OnboardingPageKit.js +6 -115
  111. package/dist/components/page-kits/PricingPageKit.js +12 -138
  112. package/dist/components/page-kits/ProfileSettingsPageKit.js +10 -164
  113. package/dist/components/page-kits/RecoveryPageKit.js +3 -42
  114. package/dist/components/page-kits/ResetPageKit.js +3 -36
  115. package/dist/components/page-kits/ServiceSuitePageKit.js +10 -175
  116. package/dist/components/page-kits/SignupPageKit.js +3 -30
  117. package/dist/components/page-kits/SuccessPageKit.js +4 -30
  118. package/dist/components/page-kits/TeamSettingsPageKit.js +9 -165
  119. package/dist/components/page-kits/TwoFactorPageKit.js +4 -28
  120. package/dist/components/page-kits/VerifyEmailPageKit.js +4 -30
  121. package/dist/components/page-kits/VoiceAgentsPageKit.js +13 -130
  122. package/dist/components/ui/CheckoutForm.js +5 -70
  123. package/eslint-rules/nadicode/config.js +1 -1
  124. package/eslint-rules/nadicode/data/catalog-names.json +93 -0
  125. package/eslint-rules/nadicode/index.js +2 -2
  126. package/eslint-rules/nadicode/rules/__tests__/require-catalog-component.test.js +77 -0
  127. package/eslint-rules/nadicode/rules/require-catalog-component.js +79 -0
  128. package/package.json +18 -25
  129. package/contracts/block-props-schemas.json +0 -2186
  130. package/contracts/component-props-schemas.json +0 -8322
  131. package/contracts/consumer-contract.json +0 -178
  132. package/contracts/page-kit-props-schemas.json +0 -1894
  133. package/contracts/public-surface-registry.json +0 -6162
  134. package/contracts/public-surface-registry.schema.json +0 -227
  135. package/contracts/spec-manifest.json +0 -46
  136. package/dist/catalog.json +0 -5221
  137. package/eslint-rules/nadicode/rules/no-forbidden-page-kit-import.js +0 -99
@@ -1,21 +1,22 @@
1
- import { VoiceAgentCard } from '../../chunk-HWHJ6IRQ.js';
2
- import { AgentConversationBlock } from '../../chunk-4IGBBIYW.js';
1
+ export { VoiceAgentsPageKit } from '../../chunk-32OLQ7FC.js';
2
+ import '../../chunk-HWHJ6IRQ.js';
3
+ import '../../chunk-4IGBBIYW.js';
3
4
  import '../../chunk-4WPZ6T7V.js';
4
5
  import '../../chunk-MDAYDDTC.js';
5
- import { Typography } from '../../chunk-N53OMWW2.js';
6
+ import '../../chunk-N53OMWW2.js';
6
7
  import '../../chunk-OSNTB6RY.js';
7
- import { ScrollArea } from '../../chunk-GLU236NN.js';
8
- import { Heading } from '../../chunk-WI547C47.js';
9
- import { Input } from '../../chunk-AP3XXYAY.js';
8
+ import '../../chunk-GLU236NN.js';
9
+ import '../../chunk-WI547C47.js';
10
+ import '../../chunk-AP3XXYAY.js';
10
11
  import '../../chunk-LIBXYD5Q.js';
11
- import { Card, CardContent, CardHeader, CardTitle } from '../../chunk-AH6YSYYT.js';
12
- import { AudioWaveform } from '../../chunk-5UESKK6S.js';
12
+ import '../../chunk-AH6YSYYT.js';
13
+ import '../../chunk-5UESKK6S.js';
13
14
  import '../../chunk-4S326Z3D.js';
14
- import { Button } from '../../chunk-7KIDDF3I.js';
15
+ import '../../chunk-7KIDDF3I.js';
15
16
  import '../../chunk-PD2YEH3H.js';
16
17
  import '../../chunk-CRY67BIF.js';
17
18
  import '../../chunk-HJC6U46F.js';
18
- import { AgentMessageBubble } from '../../chunk-I66XWYSS.js';
19
+ import '../../chunk-I66XWYSS.js';
19
20
  import '../../chunk-GO35FTNJ.js';
20
21
  import '../../chunk-WUO7OONN.js';
21
22
  import '../../chunk-FLF5AMNO.js';
@@ -125,126 +126,8 @@ import '../../chunk-UHXGBV5N.js';
125
126
  import '../../chunk-UIUMTURU.js';
126
127
  import '../../chunk-PRUXIDBD.js';
127
128
  import '../../chunk-NURPUVUV.js';
128
- import { Badge } from '../../chunk-S4JAHKOP.js';
129
+ import '../../chunk-S4JAHKOP.js';
129
130
  import '../../chunk-TV4RSQH4.js';
130
131
  import '../../chunk-HJBXUXTD.js';
131
132
  import '../../chunk-ASKFAYYR.js';
132
- import { cn } from '../../chunk-QYZT24TS.js';
133
- import { useMemo } from 'react';
134
- import { jsxs, jsx } from 'react/jsx-runtime';
135
-
136
- var EMPTY_MESSAGES = [];
137
- function presenceLabel(state) {
138
- switch (state) {
139
- case "listening":
140
- return "Listening";
141
- case "speaking":
142
- return "Speaking";
143
- case "idle":
144
- return "Idle";
145
- }
146
- }
147
- function VoiceAgentsPageKit({
148
- state = "agent-selection",
149
- agents,
150
- selectedAgentId,
151
- presenceState = "idle",
152
- messages = EMPTY_MESSAGES,
153
- streamingReply,
154
- composerValue = "",
155
- composerPlaceholder = "Send a voice prompt...",
156
- onComposerChange,
157
- onSendMessage,
158
- onSelectAgent,
159
- onBackToSelection,
160
- noAgentsMessage = "No voice agents are available right now.",
161
- disconnectedMessage = "The selected agent is unavailable. Choose another agent.",
162
- className
163
- }) {
164
- const selectedAgent = useMemo(
165
- () => selectedAgentId ? agents.find((agent) => agent.id === selectedAgentId) : null,
166
- [agents, selectedAgentId]
167
- );
168
- function handleSendMessage() {
169
- const payload = composerValue.trim();
170
- if (!payload) {
171
- return;
172
- }
173
- onSendMessage?.(payload);
174
- }
175
- if (state === "agent-selection") {
176
- return /* @__PURE__ */ jsxs("section", { className: cn("space-y-6", className), children: [
177
- /* @__PURE__ */ jsxs("header", { className: "space-y-2", children: [
178
- /* @__PURE__ */ jsx(Heading, { level: 3, children: "Voice agents" }),
179
- /* @__PURE__ */ jsx(Typography, { variant: "muted", children: "Choose an agent to start a voice session." })
180
- ] }),
181
- agents.length === 0 ? /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "py-6", children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-text-secondary", children: noAgentsMessage }) }) }) : /* @__PURE__ */ jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: agents.map((agent) => /* @__PURE__ */ jsx(
182
- VoiceAgentCard,
183
- {
184
- agent,
185
- state: "idle",
186
- selected: false,
187
- onSelect: () => onSelectAgent?.(agent.id),
188
- ariaLabel: `Select ${agent.name}`
189
- },
190
- agent.id
191
- )) })
192
- ] });
193
- }
194
- return /* @__PURE__ */ jsxs("section", { className: cn("space-y-6", className), children: [
195
- /* @__PURE__ */ jsxs("header", { className: "space-y-2", children: [
196
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
197
- /* @__PURE__ */ jsx(Heading, { level: 3, children: "Voice agents" }),
198
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: onBackToSelection, children: "Back to agent selection" })
199
- ] }),
200
- /* @__PURE__ */ jsx(Typography, { variant: "muted", children: selectedAgent ? `Active voice session with ${selectedAgent.name}.` : disconnectedMessage })
201
- ] }),
202
- selectedAgent ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
203
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-medium text-text-primary", children: selectedAgent.name }),
204
- /* @__PURE__ */ jsx(Badge, { variant: "outline", children: presenceLabel(presenceState) }),
205
- /* @__PURE__ */ jsx(AudioWaveform, { active: presenceState === "speaking", bars: 20, size: "sm", variant: "accent" })
206
- ] }) : /* @__PURE__ */ jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 px-4 py-3 text-sm text-destructive", children: disconnectedMessage }),
207
- /* @__PURE__ */ jsxs(Card, { children: [
208
- /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { children: "Conversation" }) }),
209
- /* @__PURE__ */ jsxs(CardContent, { className: "space-y-3", children: [
210
- /* @__PURE__ */ jsxs(ScrollArea, { className: "max-h-96", children: [
211
- /* @__PURE__ */ jsx(
212
- AgentConversationBlock,
213
- {
214
- messages: messages.length > 0 ? messages : [{
215
- id: "voice-empty",
216
- role: "system",
217
- content: "No messages yet. Start speaking to begin the session."
218
- }]
219
- }
220
- ),
221
- !!streamingReply && /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-1", children: [
222
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-medium text-text-primary", children: "Streaming response" }),
223
- /* @__PURE__ */ jsx(
224
- AgentMessageBubble,
225
- {
226
- role: "assistant",
227
- content: streamingReply,
228
- isStreaming: true
229
- }
230
- )
231
- ] })
232
- ] }),
233
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [
234
- /* @__PURE__ */ jsx(
235
- Input,
236
- {
237
- "aria-label": "Voice composer",
238
- value: composerValue,
239
- placeholder: composerPlaceholder,
240
- onChange: (event) => onComposerChange?.(event.target.value)
241
- }
242
- ),
243
- /* @__PURE__ */ jsx(Button, { type: "button", onClick: handleSendMessage, children: "Send voice message" })
244
- ] })
245
- ] })
246
- ] })
247
- ] });
248
- }
249
-
250
- export { VoiceAgentsPageKit };
133
+ import '../../chunk-QYZT24TS.js';
@@ -1,7 +1,8 @@
1
1
  'use client';
2
- import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '../../chunk-AH6YSYYT.js';
3
- import { Alert, AlertTitle, AlertDescription } from '../../chunk-GJUR6HT3.js';
4
- import { Button } from '../../chunk-7KIDDF3I.js';
2
+ export { CheckoutForm } from '../../chunk-RNCX4JIE.js';
3
+ import '../../chunk-AH6YSYYT.js';
4
+ import '../../chunk-GJUR6HT3.js';
5
+ import '../../chunk-7KIDDF3I.js';
5
6
  import '../../chunk-PD2YEH3H.js';
6
7
  import '../../chunk-CRY67BIF.js';
7
8
  import '../../chunk-HJC6U46F.js';
@@ -91,7 +92,7 @@ import '../../chunk-FMH55OKV.js';
91
92
  import '../../chunk-WXVNTJIB.js';
92
93
  import '../../chunk-T7H53CK2.js';
93
94
  import '../../chunk-TBKJ34BB.js';
94
- import { LoaderCircleIcon } from '../../chunk-BRP6D56U.js';
95
+ import '../../chunk-BRP6D56U.js';
95
96
  import '../../chunk-6G3RRWJT.js';
96
97
  import '../../chunk-ZU2GYVAP.js';
97
98
  import '../../chunk-CRZ2JE24.js';
@@ -118,69 +119,3 @@ import '../../chunk-TV4RSQH4.js';
118
119
  import '../../chunk-HJBXUXTD.js';
119
120
  import '../../chunk-ASKFAYYR.js';
120
121
  import '../../chunk-QYZT24TS.js';
121
- import { useState } from 'react';
122
- import { useStripe, useElements, PaymentElement } from '@stripe/react-stripe-js';
123
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
124
-
125
- function CheckoutForm({ amount = 2e3, currency = "usd", returnUrl = "/settings/billing" }) {
126
- const stripe = useStripe();
127
- const elements = useElements();
128
- const [message, setMessage] = useState(null);
129
- const [isLoading, setIsLoading] = useState(false);
130
- const handleSubmit = async (e) => {
131
- e.preventDefault();
132
- if (!stripe || !elements) {
133
- return;
134
- }
135
- setIsLoading(true);
136
- setMessage(null);
137
- const { error } = await stripe.confirmPayment({
138
- elements,
139
- confirmParams: {
140
- // Make sure to change this to your payment completion page
141
- return_url: `${window.location.origin}${returnUrl}`
142
- }
143
- });
144
- if (error.type === "card_error" || error.type === "validation_error") {
145
- setMessage(error.message ?? "An unexpected error occurred.");
146
- } else {
147
- setMessage("An unexpected error occurred.");
148
- }
149
- setIsLoading(false);
150
- };
151
- return /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-md", children: [
152
- /* @__PURE__ */ jsxs(CardHeader, { children: [
153
- /* @__PURE__ */ jsx(CardTitle, { children: "Payment Details" }),
154
- /* @__PURE__ */ jsxs(CardDescription, { children: [
155
- "Complete your purchase of $",
156
- amount / 100,
157
- " ",
158
- currency.toUpperCase(),
159
- "."
160
- ] })
161
- ] }),
162
- /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
163
- /* @__PURE__ */ jsxs(CardContent, { className: "grid gap-6", children: [
164
- /* @__PURE__ */ jsx(PaymentElement, { id: "payment-element", options: { layout: "tabs" } }),
165
- !!message && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
166
- /* @__PURE__ */ jsx(AlertTitle, { children: "Error" }),
167
- /* @__PURE__ */ jsx(AlertDescription, { children: message })
168
- ] })
169
- ] }),
170
- /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(
171
- Button,
172
- {
173
- disabled: isLoading || !stripe || !elements,
174
- className: "w-full",
175
- type: "submit",
176
- children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
177
- /* @__PURE__ */ jsx(LoaderCircleIcon, { size: 16, className: "mr-2 animate-spin" }),
178
- "Processing..."
179
- ] }) : "Pay Now"
180
- }
181
- ) })
182
- ] })
183
- ] });
184
- }
185
-
186
- export { CheckoutForm };
@@ -22,7 +22,6 @@ export const nadicodeRules = {
22
22
  "nadicode/no-falsy-and-render": "error",
23
23
  "nadicode/no-forbidden-bespoke-chat-primitive": "error",
24
24
  "nadicode/no-forbidden-chat-class-usage": "error",
25
- "nadicode/no-forbidden-page-kit-import": "error",
26
25
  "nadicode/no-forbidden-tokens": "error",
27
26
  "nadicode/no-forward-ref": "error",
28
27
  "nadicode/no-hardcoded-inline-color": "error",
@@ -83,6 +82,7 @@ export const nadicodeRules = {
83
82
  "nadicode/no-handcoded-heading": "error",
84
83
  "nadicode/no-handcoded-empty-state": "error",
85
84
  "nadicode/no-handcoded-field": "error",
85
+ "nadicode/require-catalog-component": "error",
86
86
  };
87
87
 
88
88
  function normalizePattern(pattern) {
@@ -0,0 +1,93 @@
1
+ {
2
+ "catalog": [
3
+ "AccountLockedBlock",
4
+ "AccountLockedPageKit",
5
+ "ActivityFeedBlock",
6
+ "AgentConversationBlock",
7
+ "AgentProfileGridBlock",
8
+ "AgentRunOverviewBlock",
9
+ "AgentsChatPageKit",
10
+ "AgentWorkbenchBlock",
11
+ "AnalyticsPageKit",
12
+ "AudioVisualizerBlock",
13
+ "AuthLayout",
14
+ "AuthSuccessBlock",
15
+ "BannerBlock",
16
+ "BarChartBlock",
17
+ "BlogContentPageKit",
18
+ "CallToActionBlock",
19
+ "ChangelogBlock",
20
+ "ChartBlock",
21
+ "ChartCollectionBlock",
22
+ "ChatLayout",
23
+ "CheckoutForm",
24
+ "CheckoutPageKit",
25
+ "CodeBlock",
26
+ "CompanySuitePageKit",
27
+ "ComparisonBlock",
28
+ "ContactBlock",
29
+ "CreateBlock",
30
+ "CrudFormPageKit",
31
+ "CrudListDetailPageKit",
32
+ "DashboardPageKit",
33
+ "DataGridBlock",
34
+ "DirectoryBlock",
35
+ "ErrorPageKit",
36
+ "FAQBlock",
37
+ "FeatureBlock",
38
+ "FeatureGridBlock",
39
+ "FooterBlock",
40
+ "FormWizard",
41
+ "GalleryBlock",
42
+ "HeaderBlock",
43
+ "HeatmapChartBlock",
44
+ "HeroBlock",
45
+ "HeroSectionBlock",
46
+ "IntegrationsBlock",
47
+ "InteractiveAreaChartBlock",
48
+ "InviteUserModal",
49
+ "KanbanBoard",
50
+ "KanbanBoardPageKit",
51
+ "KanbanDemoBlock",
52
+ "LandingPageKit",
53
+ "LoginBlock",
54
+ "LoginPageKit",
55
+ "LogoCloud",
56
+ "MarketingShellPageKit",
57
+ "NavigationShellPageKit",
58
+ "NavUser",
59
+ "NewsletterBlock",
60
+ "NotFoundBlock",
61
+ "OnboardingBlock",
62
+ "OnboardingPageKit",
63
+ "PasswordRecoveryBlock",
64
+ "PricingBlock",
65
+ "PricingPageKit",
66
+ "ProcessFlowBlock",
67
+ "ProfileSettingsPageKit",
68
+ "RecoveryPageKit",
69
+ "ResetPageKit",
70
+ "ResetPasswordBlock",
71
+ "ServiceSuitePageKit",
72
+ "SettingsLayout",
73
+ "SettingsPageKit",
74
+ "SignUpBlock",
75
+ "SignupPageKit",
76
+ "SocialProofBlock",
77
+ "SolutionShowcaseBlock",
78
+ "StatsBlock",
79
+ "StatsMarketingBlock",
80
+ "SuccessPageKit",
81
+ "TeamBlock",
82
+ "TeamSettingsPageKit",
83
+ "TestimonialsBlock",
84
+ "TwoFactorChallengeBlock",
85
+ "TwoFactorPageKit",
86
+ "TwoFactorSetupBlock",
87
+ "UsageDonutBlock",
88
+ "VerifyEmailPageKit",
89
+ "VoiceAgentCard",
90
+ "VoiceAgentsPageKit",
91
+ "WizardBlock"
92
+ ]
93
+ }
@@ -11,7 +11,6 @@ import noExternalUiLibrary from "./rules/no-external-ui-library.js";
11
11
  import noFalsyAndRender from "./rules/no-falsy-and-render.js";
12
12
  import noForbiddenBespokeChatPrimitive from "./rules/no-forbidden-bespoke-chat-primitive.js";
13
13
  import noForbiddenChatClassUsage from "./rules/no-forbidden-chat-class-usage.js";
14
- import noForbiddenPageKitImport from "./rules/no-forbidden-page-kit-import.js";
15
14
  import noForbiddenTokens from "./rules/no-forbidden-tokens.js";
16
15
  import noForwardRef from "./rules/no-forward-ref.js";
17
16
  import noFramerMotionImport from "./rules/no-framer-motion-import.js";
@@ -70,6 +69,7 @@ import noHandcodedBadge from "./rules/no-handcoded-badge.js";
70
69
  import noHandcodedHeading from "./rules/no-handcoded-heading.js";
71
70
  import noHandcodedEmptyState from "./rules/no-handcoded-empty-state.js";
72
71
  import noHandcodedField from "./rules/no-handcoded-field.js";
72
+ import requireCatalogComponent from "./rules/require-catalog-component.js";
73
73
 
74
74
  export { nadicodeRules, createAllowlistOverrides } from "./config.js";
75
75
 
@@ -88,7 +88,6 @@ export const nadicodePlugin = {
88
88
  "no-falsy-and-render": noFalsyAndRender,
89
89
  "no-forbidden-bespoke-chat-primitive": noForbiddenBespokeChatPrimitive,
90
90
  "no-forbidden-chat-class-usage": noForbiddenChatClassUsage,
91
- "no-forbidden-page-kit-import": noForbiddenPageKitImport,
92
91
  "no-forbidden-tokens": noForbiddenTokens,
93
92
  "no-forward-ref": noForwardRef,
94
93
  "no-framer-motion-import": noFramerMotionImport,
@@ -147,5 +146,6 @@ export const nadicodePlugin = {
147
146
  "no-handcoded-heading": noHandcodedHeading,
148
147
  "no-handcoded-empty-state": noHandcodedEmptyState,
149
148
  "no-handcoded-field": noHandcodedField,
149
+ "require-catalog-component": requireCatalogComponent,
150
150
  },
151
151
  };
@@ -0,0 +1,77 @@
1
+ import { RuleTester } from "eslint";
2
+ import { describe } from "vitest";
3
+
4
+ import rule from "../require-catalog-component.js";
5
+
6
+ const tester = new RuleTester({
7
+ languageOptions: {
8
+ ecmaVersion: "latest",
9
+ sourceType: "module",
10
+ },
11
+ });
12
+
13
+ describe("require-catalog-component", () => {
14
+ tester.run("require-catalog-component", rule, {
15
+ valid: [
16
+ // Component that IS in the catalog (catalog array)
17
+ {
18
+ code: "import HeroBlock from '@/components/blocks/HeroBlock';",
19
+ filename: "/repo/src/app/page.tsx",
20
+ },
21
+ // Page-kit that IS in the catalog
22
+ {
23
+ code: "import { LandingPageKit } from '@/components/page-kits/LandingPageKit';",
24
+ filename: "/repo/src/app/page.tsx",
25
+ },
26
+ // Imports from other paths are not checked
27
+ {
28
+ code: "import { Button } from '@/components/ui/Button';",
29
+ filename: "/repo/src/app/page.tsx",
30
+ },
31
+ // Test files are exempt
32
+ {
33
+ code: "import UnknownBlock from '@/components/blocks/UnknownBlock';",
34
+ filename: "/repo/src/components/blocks/__tests__/UnknownBlock.test.tsx",
35
+ },
36
+ {
37
+ code: "import UnknownBlock from '@/components/blocks/UnknownBlock';",
38
+ filename: "/repo/src/components/blocks/UnknownBlock.spec.tsx",
39
+ },
40
+ ],
41
+ invalid: [
42
+ // Component NOT in catalog (blocks)
43
+ {
44
+ code: "import UnknownBlock from '@/components/blocks/UnknownBlock';",
45
+ filename: "/repo/src/app/page.tsx",
46
+ errors: [
47
+ {
48
+ message:
49
+ "Component 'UnknownBlock' is not in the design system catalog. Add it to src/catalog/catalog.ts before use.",
50
+ },
51
+ ],
52
+ },
53
+ // Component NOT in catalog (page-kits)
54
+ {
55
+ code: "import UnknownPageKit from '@/components/page-kits/UnknownPageKit';",
56
+ filename: "/repo/src/app/page.tsx",
57
+ errors: [
58
+ {
59
+ message:
60
+ "Component 'UnknownPageKit' is not in the design system catalog. Add it to src/catalog/catalog.ts before use.",
61
+ },
62
+ ],
63
+ },
64
+ // Named import from unknown block
65
+ {
66
+ code: "import { GhostBlock } from '@/components/blocks/GhostBlock';",
67
+ filename: "/repo/src/app/marketing/page.tsx",
68
+ errors: [
69
+ {
70
+ message:
71
+ "Component 'GhostBlock' is not in the design system catalog. Add it to src/catalog/catalog.ts before use.",
72
+ },
73
+ ],
74
+ },
75
+ ],
76
+ });
77
+ });
@@ -0,0 +1,79 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { getFilename, isTestFile } from "../utils.js";
6
+
7
+ const RULES_DIR = dirname(fileURLToPath(import.meta.url));
8
+ const CATALOG_PATH = resolve(RULES_DIR, "../data/catalog-names.json");
9
+
10
+ const CHECKED_PREFIXES = ["@/components/blocks/", "@/components/page-kits/"];
11
+
12
+ let knownNames = null;
13
+
14
+ function loadKnownNames() {
15
+ if (knownNames) {
16
+ return knownNames;
17
+ }
18
+
19
+ knownNames = new Set();
20
+
21
+ try {
22
+ const data = JSON.parse(readFileSync(CATALOG_PATH, "utf8"));
23
+ for (const name of data.catalog ?? []) {
24
+ knownNames.add(name);
25
+ }
26
+ } catch {
27
+ // Data file missing (first run before generator runs). Rule becomes a no-op.
28
+ }
29
+
30
+ return knownNames;
31
+ }
32
+
33
+ const rule = {
34
+ meta: {
35
+ type: "problem",
36
+ docs: {
37
+ description:
38
+ "Disallow imports of blocks and page-kits that are not registered in the design system catalog.",
39
+ },
40
+ schema: [],
41
+ },
42
+ create(context) {
43
+ const filename = getFilename(context);
44
+ if (isTestFile(filename)) return {};
45
+
46
+ return {
47
+ ImportDeclaration(node) {
48
+ const source = node.source.value;
49
+ if (typeof source !== "string") return;
50
+
51
+ const matched = CHECKED_PREFIXES.find((prefix) =>
52
+ source.startsWith(prefix),
53
+ );
54
+ if (!matched) return;
55
+
56
+ const afterPrefix = source.slice(matched.length);
57
+ // Take only the first path segment so `@/components/blocks/Foo/Bar`
58
+ // resolves to `Foo`, not `Foo/Bar`.
59
+ const name = afterPrefix.split("/")[0];
60
+ if (!name) return;
61
+
62
+ const known = loadKnownNames();
63
+ // Empty set means catalog file is missing; skip silently.
64
+ if (known.size === 0) return;
65
+
66
+ if (!known.has(name)) {
67
+ context.report({
68
+ node,
69
+ message:
70
+ "Component '{{name}}' is not in the design system catalog. Add it to src/catalog/catalog.ts before use.",
71
+ data: { name },
72
+ });
73
+ }
74
+ },
75
+ };
76
+ },
77
+ };
78
+
79
+ export default rule;