@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,180 @@
1
+ "use client"
2
+
3
+ import { Button } from "@shell/components/shell-ui/button"
4
+ import { Badge } from "@shell/components/shell-ui/badge"
5
+ import { Input } from "@shell/components/shell-ui/input"
6
+ import { Checkbox } from "@shell/components/shell-ui/checkbox"
7
+ import { Label } from "@shell/components/shell-ui/label"
8
+ import { Skeleton } from "@shell/components/shell-ui/skeleton"
9
+ import { Separator } from "@shell/components/shell-ui/separator"
10
+ import { Toggle } from "@shell/components/shell-ui/toggle"
11
+ import {
12
+ Card,
13
+ CardHeader,
14
+ CardTitle,
15
+ CardDescription,
16
+ CardContent,
17
+ } from "@shell/components/shell-ui/card"
18
+ import {
19
+ Bold,
20
+ Italic,
21
+ Underline,
22
+ Star,
23
+ Heart,
24
+ Zap,
25
+ Check,
26
+ } from "lucide-react"
27
+
28
+ function DemoCard({ children }: { children: React.ReactNode }) {
29
+ return (
30
+ <div className="rounded-lg border border-border bg-card p-4 shadow-sm w-56 shrink-0">
31
+ {children}
32
+ </div>
33
+ )
34
+ }
35
+
36
+ const leftColumn = [
37
+ <DemoCard key="buttons">
38
+ <p className="text-xs font-medium text-muted-foreground mb-2">Buttons</p>
39
+ <div className="flex flex-wrap gap-2">
40
+ <Button size="sm">Primary</Button>
41
+ <Button size="sm" variant="outline">Outline</Button>
42
+ <Button size="sm" variant="ghost">Ghost</Button>
43
+ </div>
44
+ </DemoCard>,
45
+
46
+ <DemoCard key="badges">
47
+ <p className="text-xs font-medium text-muted-foreground mb-2">Badges</p>
48
+ <div className="flex flex-wrap gap-2">
49
+ <Badge>Default</Badge>
50
+ <Badge variant="secondary">Secondary</Badge>
51
+ <Badge variant="destructive">Error</Badge>
52
+ <Badge variant="outline">Outline</Badge>
53
+ </div>
54
+ </DemoCard>,
55
+
56
+ <DemoCard key="input">
57
+ <p className="text-xs font-medium text-muted-foreground mb-2">Input</p>
58
+ <Input placeholder="Type something..." className="h-8 text-xs" />
59
+ </DemoCard>,
60
+
61
+ <DemoCard key="skeleton">
62
+ <p className="text-xs font-medium text-muted-foreground mb-2">Skeleton</p>
63
+ <div className="flex items-center gap-3">
64
+ <Skeleton className="h-8 w-8 rounded-full" />
65
+ <div className="space-y-1.5 flex-1">
66
+ <Skeleton className="h-3 w-full" />
67
+ <Skeleton className="h-3 w-3/4" />
68
+ </div>
69
+ </div>
70
+ </DemoCard>,
71
+
72
+ <DemoCard key="card">
73
+ <Card className="border-0 shadow-none p-0 gap-2">
74
+ <CardHeader className="p-0">
75
+ <CardTitle className="text-sm">Card Title</CardTitle>
76
+ <CardDescription className="text-xs">A sample card</CardDescription>
77
+ </CardHeader>
78
+ <CardContent className="p-0">
79
+ <p className="text-xs text-muted-foreground">Card content goes here.</p>
80
+ </CardContent>
81
+ </Card>
82
+ </DemoCard>,
83
+ ]
84
+
85
+ const rightColumn = [
86
+ <DemoCard key="checkbox">
87
+ <p className="text-xs font-medium text-muted-foreground mb-2">Checkbox</p>
88
+ <div className="space-y-2">
89
+ <div className="flex items-center gap-2">
90
+ <Checkbox id="h-1" defaultChecked />
91
+ <Label htmlFor="h-1" className="text-xs">Completed</Label>
92
+ </div>
93
+ <div className="flex items-center gap-2">
94
+ <Checkbox id="h-2" />
95
+ <Label htmlFor="h-2" className="text-xs">In progress</Label>
96
+ </div>
97
+ </div>
98
+ </DemoCard>,
99
+
100
+ <DemoCard key="toggle">
101
+ <p className="text-xs font-medium text-muted-foreground mb-2">Toggle</p>
102
+ <div className="flex gap-1">
103
+ <Toggle size="sm" aria-label="Bold"><Bold className="size-3.5" /></Toggle>
104
+ <Toggle size="sm" aria-label="Italic"><Italic className="size-3.5" /></Toggle>
105
+ <Toggle size="sm" aria-label="Underline"><Underline className="size-3.5" /></Toggle>
106
+ </div>
107
+ </DemoCard>,
108
+
109
+ <DemoCard key="separator">
110
+ <p className="text-xs font-medium text-muted-foreground mb-2">Separator</p>
111
+ <div className="space-y-2">
112
+ <p className="text-xs">Above</p>
113
+ <Separator />
114
+ <p className="text-xs">Below</p>
115
+ </div>
116
+ </DemoCard>,
117
+
118
+ <DemoCard key="icons">
119
+ <p className="text-xs font-medium text-muted-foreground mb-2">Icons</p>
120
+ <div className="flex gap-3">
121
+ <Star className="size-5 text-amber-500" />
122
+ <Heart className="size-5 text-red-500" />
123
+ <Zap className="size-5 text-blue-500" />
124
+ <Check className="size-5 text-green-500" />
125
+ </div>
126
+ </DemoCard>,
127
+
128
+ <DemoCard key="buttons2">
129
+ <p className="text-xs font-medium text-muted-foreground mb-2">Sizes</p>
130
+ <div className="flex items-center gap-2">
131
+ <Button size="xs">XS</Button>
132
+ <Button size="sm">SM</Button>
133
+ <Button size="default">MD</Button>
134
+ </div>
135
+ </DemoCard>,
136
+ ]
137
+
138
+ export function HomepageDemo() {
139
+ // Duplicate items for seamless infinite scroll
140
+ const left = [...leftColumn, ...leftColumn]
141
+ const right = [...rightColumn, ...rightColumn]
142
+
143
+ return (
144
+ <div className="absolute inset-0 overflow-hidden pointer-events-none opacity-30 hidden md:flex">
145
+ {/* Left column — scrolling up */}
146
+ <div
147
+ className="scroll-up absolute left-[8%] top-0 flex flex-col gap-4 -rotate-12"
148
+ style={{ animation: "scroll-up 30s linear infinite" }}
149
+ >
150
+ {left.map((item, i) => (
151
+ <div key={i}>{item}</div>
152
+ ))}
153
+ </div>
154
+
155
+ {/* Right column — scrolling down */}
156
+ <div
157
+ className="scroll-down absolute right-[8%] top-0 flex flex-col gap-4 rotate-12"
158
+ style={{ animation: "scroll-down 25s linear infinite" }}
159
+ >
160
+ {right.map((item, i) => (
161
+ <div key={i}>{item}</div>
162
+ ))}
163
+ </div>
164
+
165
+ <style jsx>{`
166
+ @keyframes scroll-up {
167
+ 0% { transform: rotate(-12deg) translateY(0); }
168
+ 100% { transform: rotate(-12deg) translateY(-50%); }
169
+ }
170
+ @keyframes scroll-down {
171
+ 0% { transform: rotate(12deg) translateY(-50%); }
172
+ 100% { transform: rotate(12deg) translateY(0); }
173
+ }
174
+ @media (prefers-reduced-motion: reduce) {
175
+ .scroll-up, .scroll-down { animation: none !important; }
176
+ }
177
+ `}</style>
178
+ </div>
179
+ )
180
+ }
@@ -0,0 +1,35 @@
1
+ "use client"
2
+
3
+ import { useLocale } from "@shell/lib/i18n"
4
+ import { Button } from "@shell/components/shell-ui/button"
5
+ import { getShellLocales } from "@shell/lib/locales"
6
+
7
+ const LOCALES = getShellLocales()
8
+
9
+ /**
10
+ * Cycles the UI locale through the configured set. Hidden entirely when the
11
+ * shell is in single-locale mode (the toggle would be a no-op).
12
+ */
13
+ export function LocaleToggle() {
14
+ const { locale, setLocale } = useLocale()
15
+
16
+ if (LOCALES.length < 2) return null
17
+
18
+ const next = () => {
19
+ const i = LOCALES.indexOf(locale)
20
+ const nextIdx = (i + 1) % LOCALES.length
21
+ setLocale(LOCALES[nextIdx])
22
+ }
23
+
24
+ return (
25
+ <Button
26
+ variant="ghost"
27
+ size="icon"
28
+ onClick={next}
29
+ aria-label="Cycle language"
30
+ className="text-xs font-semibold"
31
+ >
32
+ {locale.toUpperCase()}
33
+ </Button>
34
+ )
35
+ }
@@ -0,0 +1,14 @@
1
+ "use client"
2
+
3
+ import type { ReactNode } from "react"
4
+ import { useLocale } from "@shell/lib/i18n"
5
+
6
+ /**
7
+ * Client wrapper that picks one of the pre-rendered locale trees produced by
8
+ * the server-side `LocalizedMdx`. The trees are React nodes, not strings, so
9
+ * locale switching is a tree swap with no MDX recompilation.
10
+ */
11
+ export function LocalizedMdxClient({ rendered }: { rendered: Record<string, ReactNode> }) {
12
+ const { locale } = useLocale()
13
+ return <>{rendered[locale] ?? rendered.en ?? Object.values(rendered)[0] ?? null}</>
14
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Server component: pre-renders each locale's MDX into a React tree using
3
+ * `next-mdx-remote/rsc`, then hands the keyed map to a client wrapper that
4
+ * picks the active locale. Lets us keep client-side locale switching without
5
+ * the "MDXRemote is an async Client Component" error that fires when /rsc
6
+ * runs inside a "use client" boundary.
7
+ */
8
+ import { MDXRemote } from "next-mdx-remote/rsc"
9
+ import remarkGfm from "remark-gfm"
10
+ import { mdxHeadings } from "@shell/components/heading-anchor"
11
+ import { LocalizedMdxClient } from "@shell/components/localized-mdx-client"
12
+
13
+ const mdxOptions = {
14
+ mdxOptions: {
15
+ remarkPlugins: [remarkGfm],
16
+ },
17
+ }
18
+
19
+ export function LocalizedMdx({ locales }: { locales: Record<string, string> }) {
20
+ const rendered: Record<string, React.ReactNode> = {}
21
+ for (const [loc, source] of Object.entries(locales)) {
22
+ rendered[loc] = (
23
+ <MDXRemote source={source} options={mdxOptions} components={mdxHeadings} />
24
+ )
25
+ }
26
+ return <LocalizedMdxClient rendered={rendered} />
27
+ }
@@ -0,0 +1,22 @@
1
+ "use client"
2
+
3
+ import { Sidebar } from "@shell/components/sidebar"
4
+ import { useMobileSidebar } from "@shell/components/sidebar-provider"
5
+ import type { DocMeta } from "@shell/lib/docs"
6
+ import type { ComponentMeta } from "@shell/lib/components-nav"
7
+
8
+ export function MobileSidebar({
9
+ docs,
10
+ components,
11
+ }: {
12
+ docs: DocMeta[]
13
+ components: ComponentMeta[]
14
+ }) {
15
+ const { open, close } = useMobileSidebar()
16
+
17
+ return (
18
+ <div className="md:hidden">
19
+ <Sidebar docs={docs} components={components} open={open} onClose={close} />
20
+ </div>
21
+ )
22
+ }
@@ -0,0 +1,37 @@
1
+ "use client"
2
+
3
+ import { createContext, useContext, type ReactNode } from "react"
4
+ import type { DocMeta } from "@shell/lib/docs"
5
+ import type { ComponentMeta } from "@shell/lib/components-nav"
6
+
7
+ interface NavData {
8
+ docs: DocMeta[]
9
+ components: ComponentMeta[]
10
+ }
11
+
12
+ const NavDataContext = createContext<NavData | null>(null)
13
+
14
+ /**
15
+ * Provides the full navigation data (docs + components + blocks) to any
16
+ * descendant client component. Used by the Header to render section tabs
17
+ * without having to load the data itself.
18
+ */
19
+ export function NavDataProvider({
20
+ docs,
21
+ components,
22
+ children,
23
+ }: {
24
+ docs: DocMeta[]
25
+ components: ComponentMeta[]
26
+ children: ReactNode
27
+ }) {
28
+ return (
29
+ <NavDataContext.Provider value={{ docs, components }}>
30
+ {children}
31
+ </NavDataContext.Provider>
32
+ )
33
+ }
34
+
35
+ export function useNavData(): NavData | null {
36
+ return useContext(NavDataContext)
37
+ }
@@ -0,0 +1,62 @@
1
+ "use client"
2
+
3
+ import { useEffect, useRef, useSyncExternalStore } from "react"
4
+ import { usePathname } from "next/navigation"
5
+
6
+ // Simple external store for navigation state
7
+ let listeners: Array<() => void> = []
8
+ let navProgress = { loading: false, progress: 0 }
9
+
10
+ function setNav(updates: Partial<typeof navProgress>) {
11
+ navProgress = { ...navProgress, ...updates }
12
+ listeners.forEach((l) => l())
13
+ }
14
+
15
+ function subscribe(listener: () => void) {
16
+ listeners.push(listener)
17
+ return () => { listeners = listeners.filter((l) => l !== listener) }
18
+ }
19
+
20
+ function getSnapshot() { return navProgress }
21
+
22
+ export function NavigationProgress() {
23
+ const pathname = usePathname()
24
+ const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
25
+ const prevPathname = useRef(pathname)
26
+
27
+ useEffect(() => {
28
+ if (prevPathname.current !== pathname) {
29
+ prevPathname.current = pathname
30
+ setNav({ progress: 100 })
31
+ const timer = setTimeout(() => setNav({ loading: false, progress: 0 }), 200)
32
+ return () => clearTimeout(timer)
33
+ }
34
+ }, [pathname])
35
+
36
+ // Intercept link clicks to show progress
37
+ useEffect(() => {
38
+ function handleClick(e: MouseEvent) {
39
+ const link = (e.target as HTMLElement).closest("a")
40
+ if (!link) return
41
+ const href = link.getAttribute("href")
42
+ if (!href || href.startsWith("#") || href.startsWith("http") || href.startsWith("mailto:")) return
43
+ if (href === pathname) return
44
+ setNav({ loading: true, progress: 30 })
45
+ setTimeout(() => setNav({ progress: 70 }), 100)
46
+ }
47
+ document.addEventListener("click", handleClick)
48
+ return () => document.removeEventListener("click", handleClick)
49
+ }, [pathname])
50
+
51
+ if (!state.loading && state.progress === 0) return null
52
+
53
+ return (
54
+ <div
55
+ className="fixed top-0 left-0 z-[100] h-0.5 bg-primary transition-all duration-300 ease-out"
56
+ style={{
57
+ width: `${state.progress}%`,
58
+ opacity: state.progress >= 100 ? 0 : 1,
59
+ }}
60
+ />
61
+ )
62
+ }