@sntlr/registry-shell 1.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 (134) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/dist/adapter/custom.d.ts +47 -0
  4. package/dist/adapter/custom.js +53 -0
  5. package/dist/adapter/custom.js.map +1 -0
  6. package/dist/adapter/default.d.ts +40 -0
  7. package/dist/adapter/default.js +202 -0
  8. package/dist/adapter/default.js.map +1 -0
  9. package/dist/cli/build.d.ts +1 -0
  10. package/dist/cli/build.js +31 -0
  11. package/dist/cli/build.js.map +1 -0
  12. package/dist/cli/dev.d.ts +1 -0
  13. package/dist/cli/dev.js +26 -0
  14. package/dist/cli/dev.js.map +1 -0
  15. package/dist/cli/index.d.ts +12 -0
  16. package/dist/cli/index.js +49 -0
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/cli/init.d.ts +1 -0
  19. package/dist/cli/init.js +70 -0
  20. package/dist/cli/init.js.map +1 -0
  21. package/dist/cli/shared.d.ts +33 -0
  22. package/dist/cli/shared.js +278 -0
  23. package/dist/cli/shared.js.map +1 -0
  24. package/dist/cli/start.d.ts +1 -0
  25. package/dist/cli/start.js +24 -0
  26. package/dist/cli/start.js.map +1 -0
  27. package/dist/config-loader.d.ts +49 -0
  28. package/dist/config-loader.js +140 -0
  29. package/dist/define-config.d.ts +188 -0
  30. package/dist/define-config.js +21 -0
  31. package/dist/index.d.ts +11 -0
  32. package/dist/index.js +9 -0
  33. package/package.json +124 -0
  34. package/src/adapter/custom.ts +90 -0
  35. package/src/adapter/default.ts +241 -0
  36. package/src/cli/build.ts +38 -0
  37. package/src/cli/dev.ts +38 -0
  38. package/src/cli/index.ts +52 -0
  39. package/src/cli/init.ts +76 -0
  40. package/src/cli/shared.ts +306 -0
  41. package/src/cli/start.ts +28 -0
  42. package/src/config-loader.ts +190 -0
  43. package/src/define-config.ts +206 -0
  44. package/src/index.ts +17 -0
  45. package/src/next-app/app/[...asset]/route.ts +81 -0
  46. package/src/next-app/app/_user-global.css +6 -0
  47. package/src/next-app/app/_user-sources.css +9 -0
  48. package/src/next-app/app/a11y/[name]/route.ts +19 -0
  49. package/src/next-app/app/api/search-index/route.ts +19 -0
  50. package/src/next-app/app/components/[name]/page.tsx +61 -0
  51. package/src/next-app/app/components/layout.tsx +18 -0
  52. package/src/next-app/app/docs/[slug]/page.tsx +53 -0
  53. package/src/next-app/app/docs/layout.tsx +18 -0
  54. package/src/next-app/app/globals.css +329 -0
  55. package/src/next-app/app/layout.tsx +102 -0
  56. package/src/next-app/app/page.tsx +9 -0
  57. package/src/next-app/app/preview-snapshot/[name]/page.tsx +20 -0
  58. package/src/next-app/app/preview-snapshot/layout.tsx +17 -0
  59. package/src/next-app/app/props/[name]/route.ts +19 -0
  60. package/src/next-app/app/r/[name]/route.ts +14 -0
  61. package/src/next-app/app/tests/[name]/route.ts +19 -0
  62. package/src/next-app/components/a11y-info.tsx +287 -0
  63. package/src/next-app/components/a11y-provider.tsx +39 -0
  64. package/src/next-app/components/component-breadcrumb.tsx +55 -0
  65. package/src/next-app/components/component-icon.tsx +140 -0
  66. package/src/next-app/components/component-preview.tsx +13 -0
  67. package/src/next-app/components/component-tabs.tsx +209 -0
  68. package/src/next-app/components/docs-toc.tsx +86 -0
  69. package/src/next-app/components/global-mobile-sidebar.tsx +35 -0
  70. package/src/next-app/components/header.tsx +188 -0
  71. package/src/next-app/components/heading-anchor.tsx +52 -0
  72. package/src/next-app/components/homepage-demo.tsx +180 -0
  73. package/src/next-app/components/locale-toggle.tsx +35 -0
  74. package/src/next-app/components/localized-mdx-client.tsx +14 -0
  75. package/src/next-app/components/localized-mdx.tsx +27 -0
  76. package/src/next-app/components/mobile-sidebar.tsx +22 -0
  77. package/src/next-app/components/nav-data-provider.tsx +37 -0
  78. package/src/next-app/components/navigation-progress.tsx +62 -0
  79. package/src/next-app/components/preview-canvas.tsx +368 -0
  80. package/src/next-app/components/preview-controls.tsx +94 -0
  81. package/src/next-app/components/preview-layout.tsx +218 -0
  82. package/src/next-app/components/props-table.tsx +134 -0
  83. package/src/next-app/components/resizable-preview.tsx +101 -0
  84. package/src/next-app/components/search.tsx +177 -0
  85. package/src/next-app/components/settings-modal.tsx +98 -0
  86. package/src/next-app/components/shell-ui/accordion.tsx +70 -0
  87. package/src/next-app/components/shell-ui/backdrop.tsx +29 -0
  88. package/src/next-app/components/shell-ui/badge.tsx +55 -0
  89. package/src/next-app/components/shell-ui/breadcrumb.tsx +120 -0
  90. package/src/next-app/components/shell-ui/button.tsx +64 -0
  91. package/src/next-app/components/shell-ui/card.tsx +127 -0
  92. package/src/next-app/components/shell-ui/checkbox.tsx +33 -0
  93. package/src/next-app/components/shell-ui/dialog.tsx +171 -0
  94. package/src/next-app/components/shell-ui/empty-state.tsx +66 -0
  95. package/src/next-app/components/shell-ui/input.tsx +27 -0
  96. package/src/next-app/components/shell-ui/kbd.tsx +30 -0
  97. package/src/next-app/components/shell-ui/label.tsx +25 -0
  98. package/src/next-app/components/shell-ui/select.tsx +204 -0
  99. package/src/next-app/components/shell-ui/separator.tsx +32 -0
  100. package/src/next-app/components/shell-ui/skeleton.tsx +18 -0
  101. package/src/next-app/components/shell-ui/table.tsx +124 -0
  102. package/src/next-app/components/shell-ui/tabs.tsx +102 -0
  103. package/src/next-app/components/shell-ui/toggle.tsx +56 -0
  104. package/src/next-app/components/sidebar-layout.tsx +37 -0
  105. package/src/next-app/components/sidebar-provider.tsx +75 -0
  106. package/src/next-app/components/sidebar.tsx +222 -0
  107. package/src/next-app/components/snapshot-preview.tsx +28 -0
  108. package/src/next-app/components/test-info.tsx +155 -0
  109. package/src/next-app/components/theme-provider.tsx +16 -0
  110. package/src/next-app/components/theme-toggle.tsx +21 -0
  111. package/src/next-app/components/translated-text.tsx +8 -0
  112. package/src/next-app/fallback/homepage.tsx +112 -0
  113. package/src/next-app/fallback/previews.ts +17 -0
  114. package/src/next-app/hooks/use-active-section.ts +23 -0
  115. package/src/next-app/hooks/use-controls.ts +72 -0
  116. package/src/next-app/hooks/use-mobile.ts +19 -0
  117. package/src/next-app/lib/branding.ts +52 -0
  118. package/src/next-app/lib/components-nav.ts +8 -0
  119. package/src/next-app/lib/docs.ts +16 -0
  120. package/src/next-app/lib/github.ts +38 -0
  121. package/src/next-app/lib/i18n.tsx +630 -0
  122. package/src/next-app/lib/locales.ts +17 -0
  123. package/src/next-app/lib/preview-loader.ts +7 -0
  124. package/src/next-app/lib/registry-adapter.ts +199 -0
  125. package/src/next-app/lib/utils.ts +6 -0
  126. package/src/next-app/next-env.d.ts +6 -0
  127. package/src/next-app/next.config.ts +101 -0
  128. package/src/next-app/postcss.config.mjs +7 -0
  129. package/src/next-app/public/favicon.ico +0 -0
  130. package/src/next-app/public/favicon_dark.svg +3 -0
  131. package/src/next-app/public/favicon_light.svg +3 -0
  132. package/src/next-app/registry.config.ts +50 -0
  133. package/src/next-app/tsconfig.json +29 -0
  134. package/src/next-app/user-aliases.d.ts +17 -0
@@ -0,0 +1,329 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @plugin "@tailwindcss/typography";
4
+
5
+ /* Tailwind content scan for the user's registry files. Generated by the CLI
6
+ * from the `paths` declared in sntlr.ui-shell.config.ts — regenerated every
7
+ * `ui-shell dev` / `build` boot. Do not edit _user-sources.css by hand. */
8
+ @import "./_user-sources.css";
9
+
10
+ @custom-variant dark (&:is(.dark *));
11
+
12
+ @theme inline {
13
+ --color-background: var(--background);
14
+ --color-foreground: var(--foreground);
15
+ --default-font-family: var(--font-sans);
16
+ --default-mono-font-family: var(--font-mono);
17
+ --color-sidebar-ring: var(--sidebar-ring);
18
+ --color-sidebar-border: var(--sidebar-border);
19
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
20
+ --color-sidebar-accent: var(--sidebar-accent);
21
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
22
+ --color-sidebar-primary: var(--sidebar-primary);
23
+ --color-sidebar-foreground: var(--sidebar-foreground);
24
+ --color-sidebar: var(--sidebar);
25
+ --color-chart-5: var(--chart-5);
26
+ --color-chart-4: var(--chart-4);
27
+ --color-chart-3: var(--chart-3);
28
+ --color-chart-2: var(--chart-2);
29
+ --color-chart-1: var(--chart-1);
30
+ --color-ring: var(--ring);
31
+ --color-input: var(--input);
32
+ --color-border: var(--border);
33
+ --color-destructive: var(--destructive);
34
+ --color-accent-foreground: var(--accent-foreground);
35
+ --color-accent: var(--accent);
36
+ --color-muted-foreground: var(--muted-foreground);
37
+ --color-muted: var(--muted);
38
+ --color-secondary-foreground: var(--secondary-foreground);
39
+ --color-secondary: var(--secondary);
40
+ --color-primary-foreground: var(--primary-foreground);
41
+ --color-primary: var(--primary);
42
+ --color-popover-foreground: var(--popover-foreground);
43
+ --color-popover: var(--popover);
44
+ --color-card-foreground: var(--card-foreground);
45
+ --color-card: var(--card);
46
+ --radius-sm: calc(var(--radius) - 4px);
47
+ --radius-md: calc(var(--radius) - 2px);
48
+ --radius-lg: var(--radius);
49
+ --radius-xl: calc(var(--radius) + 4px);
50
+ }
51
+
52
+ :root {
53
+ --radius: 0.625rem;
54
+ --background: oklch(1 0 0);
55
+ --foreground: oklch(0.145 0 0);
56
+ --card: oklch(1 0 0);
57
+ --card-foreground: oklch(0.145 0 0);
58
+ --popover: oklch(1 0 0);
59
+ --popover-foreground: oklch(0.145 0 0);
60
+ --primary: oklch(0.426 0.094 161.619);
61
+ --primary-foreground: oklch(0.985 0 0);
62
+ --secondary: oklch(0.97 0 0);
63
+ --secondary-foreground: oklch(0.205 0 0);
64
+ --muted: oklch(0.97 0 0);
65
+ --muted-foreground: oklch(0.525 0 0);
66
+ --accent: oklch(0.97 0 0);
67
+ --accent-foreground: oklch(0.205 0 0);
68
+ --destructive: oklch(0.577 0.245 27.325);
69
+ --border: oklch(0.922 0 0);
70
+ --input: oklch(0.922 0 0);
71
+ --ring: oklch(0.708 0 0);
72
+ --chart-1: oklch(0.646 0.222 41.116);
73
+ --chart-2: oklch(0.6 0.118 184.714);
74
+ --chart-3: oklch(0.398 0.07 227.392);
75
+ --chart-4: oklch(0.828 0.189 84.429);
76
+ --chart-5: oklch(0.769 0.188 70.08);
77
+ --sidebar: oklch(0.985 0 0);
78
+ --sidebar-foreground: oklch(0.145 0 0);
79
+ --sidebar-primary: oklch(0.426 0.094 161.619);
80
+ --sidebar-primary-foreground: oklch(0.985 0 0);
81
+ --sidebar-accent: oklch(0.97 0 0);
82
+ --sidebar-accent-foreground: oklch(0.205 0 0);
83
+ --sidebar-border: oklch(0.922 0 0);
84
+ --sidebar-ring: oklch(0.708 0 0);
85
+ }
86
+
87
+ .dark {
88
+ --background: oklch(0.145 0 0);
89
+ --foreground: oklch(0.985 0 0);
90
+ --card: oklch(0.145 0 0);
91
+ --card-foreground: oklch(0.985 0 0);
92
+ --popover: oklch(0.145 0 0);
93
+ --popover-foreground: oklch(0.985 0 0);
94
+ --primary: oklch(0.796 0.125 168.082);
95
+ --primary-foreground: oklch(0.145 0 0);
96
+ --secondary: oklch(0.269 0 0);
97
+ --secondary-foreground: oklch(0.985 0 0);
98
+ --muted: oklch(0.269 0 0);
99
+ --muted-foreground: oklch(0.708 0 0);
100
+ --accent: oklch(0.269 0 0);
101
+ --accent-foreground: oklch(0.985 0 0);
102
+ --destructive: oklch(0.577 0.245 27.325);
103
+ --border: oklch(0.269 0 0);
104
+ --input: oklch(0.269 0 0);
105
+ --ring: oklch(0.439 0 0);
106
+ --chart-1: oklch(0.488 0.243 264.376);
107
+ --chart-2: oklch(0.696 0.17 162.48);
108
+ --chart-3: oklch(0.769 0.188 70.08);
109
+ --chart-4: oklch(0.627 0.265 303.9);
110
+ --chart-5: oklch(0.645 0.246 16.439);
111
+ --sidebar: oklch(0.205 0 0);
112
+ --sidebar-foreground: oklch(0.985 0 0);
113
+ --sidebar-primary: oklch(0.796 0.125 168.082);
114
+ --sidebar-primary-foreground: oklch(0.145 0 0);
115
+ --sidebar-accent: oklch(0.269 0 0);
116
+ --sidebar-accent-foreground: oklch(0.985 0 0);
117
+ --sidebar-border: oklch(0.269 0 0);
118
+ --sidebar-ring: oklch(0.439 0 0);
119
+ }
120
+
121
+ @layer base {
122
+ * {
123
+ @apply border-border;
124
+ }
125
+ html, body {
126
+ scrollbar-gutter: stable;
127
+ }
128
+ body {
129
+ @apply bg-background text-foreground font-sans;
130
+ }
131
+ /* Prevent Radix scroll-lock from causing layout shift */
132
+ body[data-scroll-locked] {
133
+ margin-right: 0 !important;
134
+ scrollbar-gutter: stable;
135
+ }
136
+ }
137
+
138
+ /* Minimal scrollbars */
139
+ * {
140
+ scrollbar-width: thin;
141
+ scrollbar-color: var(--border) transparent;
142
+ }
143
+
144
+ *::-webkit-scrollbar {
145
+ width: 6px;
146
+ height: 6px;
147
+ }
148
+
149
+ *::-webkit-scrollbar-track {
150
+ background: transparent;
151
+ }
152
+
153
+ *::-webkit-scrollbar-thumb {
154
+ background: var(--border);
155
+ border-radius: 3px;
156
+ }
157
+
158
+ *::-webkit-scrollbar-thumb:hover {
159
+ background: var(--muted-foreground);
160
+ }
161
+
162
+ .prose pre {
163
+ @apply bg-muted text-foreground rounded-lg;
164
+ }
165
+
166
+ .prose code::before,
167
+ .prose code::after {
168
+ content: none;
169
+ }
170
+
171
+ .prose :not(pre) > code {
172
+ @apply bg-muted px-1.5 py-0.5 rounded text-xs font-medium;
173
+ }
174
+
175
+ .prose a {
176
+ @apply underline underline-offset-2 decoration-border hover:decoration-foreground transition-colors;
177
+ }
178
+
179
+ .prose table {
180
+ @apply w-full text-sm;
181
+ }
182
+
183
+ .prose th {
184
+ @apply text-left font-medium px-3 py-2 border-b border-border;
185
+ }
186
+
187
+ .prose td {
188
+ @apply px-3 py-2 border-b border-border;
189
+ }
190
+
191
+ @media (prefers-reduced-motion: reduce) {
192
+ *, *::before, *::after {
193
+ animation-duration: 0.01ms !important;
194
+ animation-iteration-count: 1 !important;
195
+ transition-duration: 0.01ms !important;
196
+ }
197
+ }
198
+
199
+ /* Chart line draw-in animation */
200
+ @keyframes chart-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
201
+ /* Pair with `pathLength="1"` on the path so the animation is length-agnostic. */
202
+ .chart-line { stroke-dasharray: 1; animation: chart-draw 1.5s ease-out forwards; }
203
+
204
+ /* Bento chart entry animations — line draws, area fades in, dots pop sequentially */
205
+ @keyframes chart-area-fade { from { opacity: 0; } to { opacity: 1; } }
206
+ @keyframes chart-dot-pop {
207
+ 0% { opacity: 0; transform: scale(0); }
208
+ 60% { opacity: 1; transform: scale(1.4); }
209
+ 100% { opacity: 1; transform: scale(1); }
210
+ }
211
+ .chart-area-fade { animation: chart-area-fade 1.4s ease-out 0.4s both; }
212
+ .chart-dot-pop { transform-box: fill-box; transform-origin: center; animation: chart-dot-pop 0.5s ease-out both; }
213
+
214
+ /* Typing cursor blink */
215
+ @keyframes cursor-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
216
+ .typing-cursor { animation: cursor-blink 0.8s step-end infinite; }
217
+
218
+ /* Marquee */
219
+ @keyframes marquee-left { from { transform: translateX(0); } to { transform: translateX(-50%); } }
220
+ @keyframes marquee-right { from { transform: translateX(-50%); } to { transform: translateX(0); } }
221
+ .marquee-track { animation: marquee-left 30s linear infinite; }
222
+ .marquee-track-reverse { animation: marquee-right 30s linear infinite; }
223
+ .marquee-container:hover .marquee-track,
224
+ .marquee-container:hover .marquee-track-reverse { animation-play-state: paused; }
225
+
226
+ /* Bounce chevron */
227
+ @keyframes bounce-gentle { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }
228
+ .bounce-gentle { animation: bounce-gentle 2s ease-in-out infinite; }
229
+
230
+ /* Hero bento card entrance — pre-animation hidden state.
231
+ * The actual animation is driven by the Web Animations API in HeroSection
232
+ * (with a module-level guard) so it's guaranteed to fire exactly once per
233
+ * page load — even under React strict-mode double-mounts or hydration
234
+ * recovery. Cells are kept invisible via this class until the JS effect
235
+ * runs and replaces it with the WAAPI-driven keyframes. */
236
+ .bento-cell-pending { opacity: 0; }
237
+
238
+ /* Prototyping section — file flying along the conveyor */
239
+ @keyframes proto-fly {
240
+ 0% { left: 0%; opacity: 0; transform: translate(-50%, -50%) scale(0.85); }
241
+ 10% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
242
+ 85% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
243
+ 100% { left: 100%; opacity: 0; transform: translate(-50%, -50%) scale(0.85); }
244
+ }
245
+ .proto-fly { animation: proto-fly 2.2s ease-in-out forwards; }
246
+
247
+ /* Prototyping section — checkmark pop-in for the test list */
248
+ @keyframes proto-check-pop {
249
+ 0% { opacity: 0; transform: scale(0.4); }
250
+ 60% { opacity: 1; transform: scale(1.25); }
251
+ 100% { opacity: 1; transform: scale(1); }
252
+ }
253
+ .proto-check-pop { animation: proto-check-pop 0.45s ease-out both; }
254
+
255
+ /* Prototyping section — neural network node pulse */
256
+ @keyframes proto-node-pulse {
257
+ 0%, 100% { opacity: 0.55; r: 3.5; }
258
+ 50% { opacity: 1; r: 5; }
259
+ }
260
+ .proto-node-pulse { animation: proto-node-pulse 1.6s ease-in-out infinite; }
261
+
262
+ /* Prototyping section — synapse trail (used by stroke-dasharray fired links) */
263
+ @keyframes proto-synapse {
264
+ 0% { stroke-dashoffset: 1; opacity: 0; }
265
+ 20% { opacity: 0.9; }
266
+ 100% { stroke-dashoffset: 0; opacity: 0; }
267
+ }
268
+ .proto-synapse { stroke-dasharray: 1; animation: proto-synapse 1.4s ease-out forwards; }
269
+
270
+ /* Data-table row flash highlights — appear (primary), update (orange), delete (red→fade out) */
271
+ @keyframes row-flash-appear {
272
+ 0% { background-color: color-mix(in oklch, var(--color-primary) 28%, transparent); }
273
+ 100% { background-color: transparent; }
274
+ }
275
+ @keyframes row-flash-update {
276
+ 0% { background-color: oklch(0.75 0.18 65 / 0.32); }
277
+ 100% { background-color: transparent; }
278
+ }
279
+ @keyframes row-flash-delete {
280
+ 0% { background-color: oklch(0.6 0.22 25 / 0.45); opacity: 1; }
281
+ 70% { background-color: oklch(0.6 0.22 25 / 0.45); opacity: 1; }
282
+ 100% { background-color: oklch(0.6 0.22 25 / 0.45); opacity: 0; }
283
+ }
284
+ .row-flash-appear { animation: row-flash-appear 3s ease-out forwards; }
285
+ .row-flash-update { animation: row-flash-update 3s ease-out forwards; }
286
+ .row-flash-delete { animation: row-flash-delete 1.4s ease-out forwards; pointer-events: none; }
287
+
288
+ /* Row presence avatar — fades+scales in (and stays visible until removed by JS) */
289
+ @keyframes row-presence-pop {
290
+ 0% { opacity: 0; transform: translateY(-50%) scale(0.6); }
291
+ 60% { opacity: 1; transform: translateY(-50%) scale(1.1); }
292
+ 100% { opacity: 1; transform: translateY(-50%) scale(1); }
293
+ }
294
+ .row-presence-pop { animation: row-presence-pop 0.35s ease-out forwards; }
295
+
296
+ /* Gradient text */
297
+ .text-gradient-primary {
298
+ color: transparent;
299
+ background-image: linear-gradient(135deg, var(--color-primary), oklch(0.6 0.15 180));
300
+ -webkit-background-clip: text;
301
+ background-clip: text;
302
+ }
303
+
304
+ /* Skip navigation link */
305
+ .skip-nav {
306
+ @apply sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[100] focus:px-4 focus:py-2 focus:rounded-md focus:bg-background focus:text-foreground focus:border focus:border-border focus:shadow-lg;
307
+ }
308
+
309
+ /* High contrast mode */
310
+ @media (forced-colors: active) {
311
+ * { border-color: CanvasText !important; }
312
+ button, [role="button"] { border: 1px solid ButtonText !important; }
313
+ }
314
+
315
+ /* Text scaling safety */
316
+ html { font-size: clamp(14px, 1rem, 20px); }
317
+
318
+ @media print {
319
+ header, aside, .no-print { display: none !important; }
320
+ main { width: 100% !important; margin: 0 !important; }
321
+ pre { white-space: pre-wrap !important; word-break: break-word !important; }
322
+ * { box-shadow: none !important; }
323
+ }
324
+
325
+ /* User's optional global CSS — `paths.globalCss` in their config.
326
+ * Generated by the CLI. Imported LAST so user `:root`/`.dark` token
327
+ * overrides cascade-win over the shell's defaults above. Always exists
328
+ * (no-op when unconfigured) so this import never 404s. */
329
+ @import "./_user-global.css";
@@ -0,0 +1,102 @@
1
+ import type { Metadata } from "next"
2
+ import { Space_Grotesk, JetBrains_Mono } from "next/font/google"
3
+ import { ThemeProvider } from "@shell/components/theme-provider"
4
+ import { I18nProvider } from "@shell/lib/i18n"
5
+ import { SidebarProvider } from "@shell/components/sidebar-provider"
6
+ import { Header } from "@shell/components/header"
7
+ import { A11yProvider } from "@shell/components/a11y-provider"
8
+ import { NavigationProgress } from "@shell/components/navigation-progress"
9
+ import { NavDataProvider } from "@shell/components/nav-data-provider"
10
+ import { GlobalMobileSidebar } from "@shell/components/global-mobile-sidebar"
11
+ import { getAllDocs } from "@shell/lib/docs"
12
+ import { getAllComponents } from "@shell/lib/components-nav"
13
+ import { getGithubStars } from "@shell/lib/github"
14
+ import { branding } from "@shell/lib/branding"
15
+ import { registry } from "@shell/registry.config"
16
+ import { getShellDefaultLocale, getShellLocales } from "@shell/lib/locales"
17
+ import "./globals.css"
18
+
19
+ const shellLocales = getShellLocales()
20
+ const shellDefaultLocale = getShellDefaultLocale() || "en"
21
+
22
+ const spaceGrotesk = Space_Grotesk({
23
+ variable: "--font-sans",
24
+ subsets: ["latin"],
25
+ })
26
+
27
+ const spaceMono = JetBrains_Mono({
28
+ variable: "--font-mono",
29
+ weight: ["400", "700"],
30
+ subsets: ["latin"],
31
+ })
32
+
33
+ const description = branding.description || `${branding.siteName} component registry.`
34
+
35
+ export const metadata: Metadata = {
36
+ title: branding.siteName,
37
+ description,
38
+ metadataBase: branding.siteUrl ? new URL(branding.siteUrl) : undefined,
39
+ openGraph: {
40
+ title: branding.siteName,
41
+ description,
42
+ siteName: branding.siteName,
43
+ url: branding.siteUrl || undefined,
44
+ images: branding.ogImage ? [{ url: branding.ogImage }] : undefined,
45
+ type: "website",
46
+ },
47
+ twitter: {
48
+ card: branding.ogImage ? "summary_large_image" : "summary",
49
+ title: branding.siteName,
50
+ description,
51
+ site: branding.twitterHandle ? `@${branding.twitterHandle}` : undefined,
52
+ images: branding.ogImage ? [branding.ogImage] : undefined,
53
+ },
54
+ icons: {
55
+ icon: [
56
+ { url: branding.faviconDark, type: "image/svg+xml", media: "(prefers-color-scheme: light)" },
57
+ { url: branding.faviconLight, type: "image/svg+xml", media: "(prefers-color-scheme: dark)" },
58
+ { url: branding.faviconIco, sizes: "48x48" },
59
+ ],
60
+ },
61
+ }
62
+
63
+ export default async function RootLayout({
64
+ children,
65
+ }: Readonly<{
66
+ children: React.ReactNode
67
+ }>) {
68
+ const docs = getAllDocs()
69
+ const components = getAllComponents()
70
+ // Server-side, revalidates hourly. Returns null on failure → header shows
71
+ // the GitHub icon without a count.
72
+ const githubStars = await getGithubStars()
73
+
74
+ return (
75
+ <html lang="en" suppressHydrationWarning>
76
+ <body
77
+ className={`${spaceGrotesk.variable} ${spaceMono.variable} antialiased`}
78
+ >
79
+ <ThemeProvider>
80
+ <I18nProvider
81
+ extraTranslations={registry?.extraTranslations}
82
+ defaultLocale={shellDefaultLocale}
83
+ availableLocales={shellLocales}
84
+ >
85
+ <NavDataProvider docs={docs} components={components}>
86
+ <SidebarProvider>
87
+ <NavigationProgress />
88
+ <A11yProvider />
89
+ <Header githubStars={githubStars} />
90
+ {/* Mobile-only sidebar mounted here so the hamburger menu works
91
+ on every page (including the homepage). Per-section layouts
92
+ still mount their own desktop-only Sidebar via SidebarLayout. */}
93
+ <GlobalMobileSidebar docs={docs} components={components} />
94
+ {children}
95
+ </SidebarProvider>
96
+ </NavDataProvider>
97
+ </I18nProvider>
98
+ </ThemeProvider>
99
+ </body>
100
+ </html>
101
+ )
102
+ }
@@ -0,0 +1,9 @@
1
+ import { getAllDocs } from "@shell/lib/docs"
2
+ import HomePage from "@user/homepage"
3
+
4
+ export default function Home() {
5
+ const docs = getAllDocs()
6
+ const firstDocSlug = docs[0]?.slug
7
+
8
+ return <HomePage firstDocSlug={firstDocSlug} />
9
+ }
@@ -0,0 +1,20 @@
1
+ import { notFound } from "next/navigation"
2
+ import { getAllComponents } from "@shell/lib/components-nav"
3
+ import { SnapshotPreview } from "@shell/components/snapshot-preview"
4
+
5
+ export function generateStaticParams() {
6
+ return getAllComponents().map((comp) => ({ name: comp.name }))
7
+ }
8
+
9
+ export default async function SnapshotPage({
10
+ params,
11
+ }: {
12
+ params: Promise<{ name: string }>
13
+ }) {
14
+ const { name } = await params
15
+ const comp = getAllComponents().find((c) => c.name === name)
16
+
17
+ if (!comp) notFound()
18
+
19
+ return <SnapshotPreview name={name} />
20
+ }
@@ -0,0 +1,17 @@
1
+ import "@shell/app/globals.css"
2
+
3
+ /**
4
+ * Minimal layout for Playwright visual snapshots.
5
+ * No header, sidebar, or chrome — just the component.
6
+ */
7
+ export default function SnapshotLayout({
8
+ children,
9
+ }: {
10
+ children: React.ReactNode
11
+ }) {
12
+ return (
13
+ <div className="p-4 bg-background text-foreground font-sans">
14
+ {children}
15
+ </div>
16
+ )
17
+ }
@@ -0,0 +1,19 @@
1
+ import { NextRequest, NextResponse } from "next/server"
2
+ import { registry } from "@shell/registry.config"
3
+
4
+ /**
5
+ * Serves the user registry's `public/props/{name}.json` files via the
6
+ * adapter, so the props table inside the Docs tab can fetch them through a
7
+ * stable URL even though the files live in the user's project.
8
+ */
9
+ export async function GET(
10
+ _request: NextRequest,
11
+ { params }: { params: Promise<{ name: string }> },
12
+ ) {
13
+ const { name } = await params
14
+ const data = registry?.getPropsData ? await registry.getPropsData(name) : null
15
+ if (!data) {
16
+ return NextResponse.json({ error: "Props data not found" }, { status: 404 })
17
+ }
18
+ return NextResponse.json(data)
19
+ }
@@ -0,0 +1,14 @@
1
+ import { NextRequest, NextResponse } from "next/server"
2
+ import { registry } from "@shell/registry.config"
3
+
4
+ export async function GET(
5
+ _request: NextRequest,
6
+ { params }: { params: Promise<{ name: string }> },
7
+ ) {
8
+ const { name } = await params
9
+ const data = registry ? await registry.getRegistryItem(name) : null
10
+ if (!data) {
11
+ return NextResponse.json({ error: "Registry item not found" }, { status: 404 })
12
+ }
13
+ return NextResponse.json(data)
14
+ }
@@ -0,0 +1,19 @@
1
+ import { NextRequest, NextResponse } from "next/server"
2
+ import { registry } from "@shell/registry.config"
3
+
4
+ /**
5
+ * Serves the user registry's `public/tests/{name}.json` files via the
6
+ * adapter, so the Tests tab can fetch them through standard URLs even though
7
+ * the files live in the user's project rather than the shell's `public/`.
8
+ */
9
+ export async function GET(
10
+ _request: NextRequest,
11
+ { params }: { params: Promise<{ name: string }> },
12
+ ) {
13
+ const { name } = await params
14
+ const data = registry?.getTestData ? await registry.getTestData(name) : null
15
+ if (!data) {
16
+ return NextResponse.json({ error: "Test data not found" }, { status: 404 })
17
+ }
18
+ return NextResponse.json(data)
19
+ }