@devalok/shilp-sutra 0.37.1 → 0.38.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 (63) hide show
  1. package/MIGRATION.md +67 -0
  2. package/dist/_chunks/tiptap.js +977 -968
  3. package/dist/_chunks/tiptap.js.map +1 -1
  4. package/dist/composed/index.d.ts +0 -2
  5. package/dist/composed/index.d.ts.map +1 -1
  6. package/dist/composed/index.js +5 -6
  7. package/dist/hooks/index.d.ts +2 -2
  8. package/dist/hooks/index.d.ts.map +1 -1
  9. package/dist/hooks/index.js +0 -1
  10. package/dist/ui/alert.d.ts +1 -2
  11. package/dist/ui/alert.d.ts.map +1 -1
  12. package/dist/ui/alert.js +1 -27
  13. package/dist/ui/alert.js.map +1 -1
  14. package/dist/ui/banner.d.ts +3 -5
  15. package/dist/ui/banner.d.ts.map +1 -1
  16. package/dist/ui/banner.js +13 -13
  17. package/dist/ui/banner.js.map +1 -1
  18. package/dist/ui/index.d.ts +1 -1
  19. package/dist/ui/index.d.ts.map +1 -1
  20. package/dist/ui/index.js +2 -2
  21. package/dist/ui/index.js.map +1 -1
  22. package/dist/ui/input.d.ts +1 -9
  23. package/dist/ui/input.d.ts.map +1 -1
  24. package/dist/ui/input.js +26 -27
  25. package/dist/ui/input.js.map +1 -1
  26. package/dist/ui/segmented-control.d.ts +1 -1
  27. package/dist/ui/segmented-control.d.ts.map +1 -1
  28. package/dist/ui/segmented-control.js +2 -4
  29. package/dist/ui/segmented-control.js.map +1 -1
  30. package/docs/components/_header.md +1 -1
  31. package/docs/components/ui/alert.md +4 -1
  32. package/docs/components/ui/banner.md +3 -1
  33. package/docs/components/ui/input.md +4 -2
  34. package/docs/components/ui/segmented-control.md +13 -6
  35. package/docs/recipes/customize-brand.md +216 -0
  36. package/docs/recipes/index.md +51 -0
  37. package/docs/recipes/install-astro.md +178 -0
  38. package/docs/recipes/install-next-app-router.md +230 -0
  39. package/docs/recipes/install-next-pages.md +123 -0
  40. package/docs/recipes/install-remix.md +171 -0
  41. package/docs/recipes/install-tanstack-start.md +143 -0
  42. package/docs/recipes/install-vite.md +170 -0
  43. package/docs/recipes/server-components.md +209 -0
  44. package/docs/recipes/troubleshoot.md +217 -0
  45. package/llms-full.txt +26 -53
  46. package/llms.txt +33 -16
  47. package/package.json +45 -35
  48. package/dist/composed/responsive-overlay.d.ts +0 -23
  49. package/dist/composed/responsive-overlay.d.ts.map +0 -1
  50. package/dist/composed/responsive-overlay.js +0 -40
  51. package/dist/composed/responsive-overlay.js.map +0 -1
  52. package/dist/hooks/use-toast.d.ts +0 -17
  53. package/dist/hooks/use-toast.d.ts.map +0 -1
  54. package/dist/hooks/use-toast.js +0 -3
  55. package/dist/tailwind/index.cjs +0 -41
  56. package/dist/tailwind/index.d.ts +0 -2
  57. package/dist/tailwind/index.d.ts.map +0 -1
  58. package/dist/tailwind/index.js +0 -2
  59. package/dist/tailwind/preset.d.ts +0 -25
  60. package/dist/tailwind/preset.d.ts.map +0 -1
  61. package/dist/tailwind/preset.js +0 -17
  62. package/dist/tailwind/preset.js.map +0 -1
  63. package/docs/components/composed/responsive-overlay.md +0 -41
@@ -0,0 +1,143 @@
1
+ # Install: TanStack Start
2
+
3
+ > Setup recipe for adding `@devalok/shilp-sutra` to a TanStack Start project (the React full-stack framework built on Vinxi/Vite).
4
+
5
+ ## 1. Detect
6
+
7
+ You are in this recipe if:
8
+
9
+ - `package.json` lists `"@tanstack/start"` and `"@tanstack/react-router"`
10
+ - `app.config.{ts,js}` (Vinxi) exists at the project root
11
+ - `app/router.tsx` and `app/routes/__root.tsx` exist
12
+
13
+ ## 2. Install
14
+
15
+ ```bash
16
+ pnpm add @devalok/shilp-sutra framer-motion
17
+ pnpm add -D tailwindcss@^4 @tailwindcss/vite
18
+ ```
19
+
20
+ Optional:
21
+
22
+ ```bash
23
+ pnpm add sonner # only if rendering <Toaster />
24
+ ```
25
+
26
+ ## 3. Wire Tailwind 4 in `app.config.ts`
27
+
28
+ ```ts
29
+ import { defineConfig } from "@tanstack/start/config";
30
+ import tailwindcss from "@tailwindcss/vite";
31
+
32
+ export default defineConfig({
33
+ vite: {
34
+ plugins: [tailwindcss()],
35
+ },
36
+ });
37
+ ```
38
+
39
+ ## 4. Wire tokens
40
+
41
+ Create `app/styles/globals.css`:
42
+
43
+ ```css
44
+ @import "tailwindcss";
45
+ @import "@devalok/shilp-sutra/css";
46
+ ```
47
+
48
+ Import it from `app/routes/__root.tsx`:
49
+
50
+ ```tsx
51
+ import "../styles/globals.css";
52
+ ```
53
+
54
+ If you prefer asset-URL imports the way Remix does it, use `import css from "../styles/globals.css?url"` and add `<link rel="stylesheet" href={css} />` to the `<head>` returned by `__root.tsx`.
55
+
56
+ ## 5. Theme toggle
57
+
58
+ Create `public/theme-bootstrap.js` (a static asset served verbatim):
59
+
60
+ ```js
61
+ (function () {
62
+ try {
63
+ var stored = localStorage.getItem("theme");
64
+ var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
65
+ if (stored === "dark" || (!stored && prefersDark)) {
66
+ document.documentElement.classList.add("dark");
67
+ }
68
+ } catch (e) {}
69
+ })();
70
+ ```
71
+
72
+ Reference it from `app/routes/__root.tsx`:
73
+
74
+ ```tsx
75
+ import { createRootRoute, Outlet } from "@tanstack/react-router";
76
+
77
+ export const Route = createRootRoute({
78
+ component: () => (
79
+ <html lang="en" suppressHydrationWarning>
80
+ <head>
81
+ <script src="/theme-bootstrap.js" />
82
+ </head>
83
+ <body>
84
+ <Outlet />
85
+ </body>
86
+ </html>
87
+ ),
88
+ });
89
+ ```
90
+
91
+ For runtime toggling, use the `useColorMode` hook — see [install-vite.md § 5](./install-vite.md#5-theme-toggle-no-next-themes-here).
92
+
93
+ ## 6. Toaster (optional)
94
+
95
+ Mount in `__root.tsx` next to `<Outlet />`:
96
+
97
+ ```tsx
98
+ import { Toaster } from "@devalok/shilp-sutra/ui/toaster";
99
+
100
+ // inside <body>
101
+ <>
102
+ <Outlet />
103
+ <Toaster />
104
+ </>
105
+ ```
106
+
107
+ ## 7. Verify
108
+
109
+ Create or replace `app/routes/index.tsx`:
110
+
111
+ ```tsx
112
+ import { createFileRoute } from "@tanstack/react-router";
113
+ import { Button } from "@devalok/shilp-sutra/ui/button";
114
+ import { Stack } from "@devalok/shilp-sutra/ui/stack";
115
+ import { Text } from "@devalok/shilp-sutra/ui/text";
116
+
117
+ export const Route = createFileRoute("/")({
118
+ component: () => (
119
+ <Stack className="p-ds-08" gap="ds-04">
120
+ <Text variant="heading-2xl">Hello, Shilp Sutra</Text>
121
+ <Stack direction="row" gap="ds-03">
122
+ <Button>Primary</Button>
123
+ <Button variant="soft">Soft</Button>
124
+ </Stack>
125
+ </Stack>
126
+ ),
127
+ });
128
+ ```
129
+
130
+ Run `pnpm dev` and open the URL.
131
+
132
+ ## 8. TanStack Start specifics
133
+
134
+ - **Server functions** (`createServerFn`) — do not import shilp-sutra components inside server functions; they run server-only.
135
+ - **Streaming SSR** is the default. All shilp-sutra components SSR cleanly because they have no client-only side effects at module top-level.
136
+ - **`framer-motion` SSR** — animations gracefully degrade on the initial render.
137
+ - **CSP.** The static `theme-bootstrap.js` asset complies with strict CSP (no inline scripts required).
138
+
139
+ ## 9. What NOT to do
140
+
141
+ - ❌ Add `tailwind.config.{ts,js}` — Tailwind 4 is CSS-first.
142
+ - ❌ Mount `<Toaster />` inside route components — it should live once at the `__root`.
143
+ - ❌ Mix `@tailwindcss/postcss` and `@tailwindcss/vite` — pick one (Vite plugin recommended for TanStack Start).
@@ -0,0 +1,170 @@
1
+ # Install: Vite + React
2
+
3
+ > Setup recipe for adding `@devalok/shilp-sutra` to a Vite + React SPA.
4
+
5
+ ## 1. Detect
6
+
7
+ You are in this recipe if:
8
+
9
+ - `vite.config.{ts,js,mjs}` exists at the project root
10
+ - `package.json` lists `"vite"` and `"react"`
11
+ - `src/main.{tsx,jsx}` is the entry that calls `createRoot(...).render(<App />)`
12
+
13
+ This recipe also covers Vite + React + React Router (any version) — the design system is router-agnostic.
14
+
15
+ If you are using Remix (which now runs on Vite), use [install-remix.md](./install-remix.md). If you are using TanStack Start, use [install-tanstack-start.md](./install-tanstack-start.md).
16
+
17
+ ## 2. Install dependencies
18
+
19
+ ```bash
20
+ # pnpm
21
+ pnpm add @devalok/shilp-sutra framer-motion
22
+ pnpm add -D tailwindcss@^4 @tailwindcss/vite
23
+
24
+ # npm
25
+ npm install @devalok/shilp-sutra framer-motion
26
+ npm install -D tailwindcss@^4 @tailwindcss/vite
27
+
28
+ # yarn
29
+ yarn add @devalok/shilp-sutra framer-motion
30
+ yarn add -D tailwindcss@^4 @tailwindcss/vite
31
+
32
+ # bun
33
+ bun add @devalok/shilp-sutra framer-motion
34
+ bun add -d tailwindcss@^4 @tailwindcss/vite
35
+ ```
36
+
37
+ Add only if rendering `<Toaster />`:
38
+
39
+ ```bash
40
+ pnpm add sonner
41
+ ```
42
+
43
+ ## 3. Wire Tailwind 4 in `vite.config.ts`
44
+
45
+ Use the official Tailwind 4 Vite plugin (faster than PostCSS for Vite):
46
+
47
+ ```ts
48
+ import { defineConfig } from "vite";
49
+ import react from "@vitejs/plugin-react";
50
+ import tailwindcss from "@tailwindcss/vite";
51
+
52
+ export default defineConfig({
53
+ plugins: [react(), tailwindcss()],
54
+ });
55
+ ```
56
+
57
+ If the project already uses PostCSS for other reasons, you can use `@tailwindcss/postcss` instead — see [install-next-app-router.md § 3](./install-next-app-router.md#3-configure-postcss). Stick with one approach; do not load both.
58
+
59
+ ## 4. Wire tokens in the global CSS
60
+
61
+ Common CSS entry paths in priority order:
62
+
63
+ - `src/index.css`
64
+ - `src/main.css`
65
+ - `src/styles/globals.css`
66
+ - `src/App.css`
67
+
68
+ If none exists, create `src/index.css`. Set or merge:
69
+
70
+ ```css
71
+ @import "tailwindcss";
72
+ @import "@devalok/shilp-sutra/css";
73
+ ```
74
+
75
+ Import it once from the entry (`src/main.tsx`):
76
+
77
+ ```tsx
78
+ import "./index.css";
79
+ ```
80
+
81
+ ## 5. Theme toggle (no `next-themes` here)
82
+
83
+ Vite has no built-in theme provider. Use the design system's `useColorMode` hook with a small bootstrap script to avoid a flash of wrong theme.
84
+
85
+ Add the bootstrap to `index.html` (in `<head>`, before any stylesheet):
86
+
87
+ ```html
88
+ <script>
89
+ (function () {
90
+ try {
91
+ var stored = localStorage.getItem("theme");
92
+ var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
93
+ var dark = stored === "dark" || (!stored && prefersDark);
94
+ if (dark) document.documentElement.classList.add("dark");
95
+ } catch (e) {}
96
+ })();
97
+ </script>
98
+ ```
99
+
100
+ Wire the runtime hook from anywhere in the app (e.g., a header button):
101
+
102
+ ```tsx
103
+ import { useColorMode } from "@devalok/shilp-sutra/hooks/use-color-mode";
104
+
105
+ export function ThemeToggle() {
106
+ const { mode, toggle } = useColorMode();
107
+ return (
108
+ <button onClick={toggle} aria-label="Toggle theme">
109
+ {mode === "dark" ? "☀" : "☾"}
110
+ </button>
111
+ );
112
+ }
113
+ ```
114
+
115
+ ## 6. Mount Toaster (optional)
116
+
117
+ If you installed `sonner`, mount the Toaster once near the app root:
118
+
119
+ ```tsx
120
+ // src/main.tsx
121
+ import { StrictMode } from "react";
122
+ import { createRoot } from "react-dom/client";
123
+ import { Toaster } from "@devalok/shilp-sutra/ui/toaster";
124
+ import { App } from "./App";
125
+ import "./index.css";
126
+
127
+ createRoot(document.getElementById("root")!).render(
128
+ <StrictMode>
129
+ <App />
130
+ <Toaster />
131
+ </StrictMode>,
132
+ );
133
+ ```
134
+
135
+ ## 7. Verify
136
+
137
+ Replace `src/App.tsx`:
138
+
139
+ ```tsx
140
+ import { Button } from "@devalok/shilp-sutra/ui/button";
141
+ import { Stack } from "@devalok/shilp-sutra/ui/stack";
142
+ import { Text } from "@devalok/shilp-sutra/ui/text";
143
+
144
+ export function App() {
145
+ return (
146
+ <Stack className="p-ds-08" gap="ds-04">
147
+ <Text variant="heading-2xl">Hello, Shilp Sutra</Text>
148
+ <Stack direction="row" gap="ds-03">
149
+ <Button>Primary</Button>
150
+ <Button variant="soft">Soft</Button>
151
+ </Stack>
152
+ </Stack>
153
+ );
154
+ }
155
+ ```
156
+
157
+ Run `pnpm dev` and open the printed Vite URL. Expected output matches [Next App Router § 7](./install-next-app-router.md#7-verify-the-install).
158
+
159
+ ## 8. Gotchas (Vite-specific)
160
+
161
+ - **`@tailwindcss/vite` and `@tailwindcss/postcss` together.** Pick one — running both causes utilities to be processed twice and CSS bloat.
162
+ - **CSS import paths in HMR.** Vite is strict about case-sensitivity even on macOS. `@devalok/shilp-sutra/CSS` will not resolve; use the lowercase `/css`.
163
+ - **Multiple framer-motion copies.** Same fix as Next — see [install-next-app-router.md § 8](./install-next-app-router.md#8-common-gotchas).
164
+ - **Spacing utilities.** Use `p-ds-04` (not `p-4`). Tailwind composite text utilities: `text-heading-xl`, `text-body-md`, `text-code`.
165
+
166
+ ## 9. What NOT to do
167
+
168
+ - ❌ Add `@devalok/shilp-sutra/tailwind` — the export was removed in 0.38.
169
+ - ❌ Add a `tailwind.config.{ts,js}` with `presets: [shilpSutra]` — JS preset removed.
170
+ - ❌ Try to use `next-themes` here — it requires Next. Use the bootstrap script + `useColorMode` shown in § 5.
@@ -0,0 +1,209 @@
1
+ # Server vs Client Components
2
+
3
+ Reference for using `@devalok/shilp-sutra` correctly inside React Server Components (Next.js App Router, TanStack Start, Remix v3+, Astro, future RSC frameworks).
4
+
5
+ ## TL;DR
6
+
7
+ - **Always use per-component imports** (`@devalok/shilp-sutra/ui/button`, NOT `@devalok/shilp-sutra/ui`) — barrels drag in client-only siblings and trip the RSC barrel-import error.
8
+ - **Importing a client component into a Server Component is fine.** The client component renders as a client island at that point. The Server Component shell stays server-rendered.
9
+ - **What actually breaks RSC**, in order of frequency: (a) calling a client hook in a server file; (b) passing functions or class instances as props from server → client; (c) reading `window`/`document` in a server module's top-level body.
10
+ - The matrix below tells you which components have NO `"use client"` (so they SSR without hydration cost) vs. which become client islands (still importable, just hydrate).
11
+
12
+ ## RSC-safety matrix
13
+
14
+ The list is generated from the `// @server-safe` source annotation. Components without the annotation get `"use client"` injected at build time and ship as client islands.
15
+
16
+ ### `@devalok/shilp-sutra/ui`
17
+
18
+ | Server-safe (renders without hydration) | Everything else |
19
+ |---|---|
20
+ | `Text` | All other UI primitives become client islands when rendered |
21
+ | `Stack` | |
22
+ | `Container` | |
23
+ | `Skeleton` | |
24
+ | `Table` (and child components: `TableHeader`, `TableRow`, `TableCell`) | |
25
+ | `Code` | |
26
+ | `VisuallyHidden` | |
27
+
28
+ ```tsx
29
+ // ✅ Server Component — Text/Stack/Container/etc. SSR with zero hydration cost
30
+ import { Text } from "@devalok/shilp-sutra/ui/text";
31
+ import { Stack } from "@devalok/shilp-sutra/ui/stack";
32
+
33
+ export default function ServerPage() {
34
+ return (
35
+ <Stack className="p-ds-08">
36
+ <Text variant="heading-2xl">Server-rendered</Text>
37
+ </Stack>
38
+ );
39
+ }
40
+ ```
41
+
42
+ ```tsx
43
+ // ✅ Also fine — Button is a client component, but importing one into a Server
44
+ // Component just creates a client island at that boundary. No wrapper needed.
45
+ import { Button } from "@devalok/shilp-sutra/ui/button";
46
+
47
+ export default function ServerPage() {
48
+ return <Button>Click</Button>;
49
+ }
50
+ ```
51
+
52
+ ```tsx
53
+ // ❌ NOT fine — passing a function as a prop from server → client.
54
+ // Functions are not serializable across the RSC boundary.
55
+ import { Button } from "@devalok/shilp-sutra/ui/button";
56
+
57
+ export default function ServerPage() {
58
+ return <Button onClick={() => console.log("hi")}>Click</Button>;
59
+ // ^^^^^^^ — define handler inside a "use client" wrapper instead
60
+ }
61
+ ```
62
+
63
+ When you need an event handler, define it inside a client component:
64
+
65
+ ```tsx
66
+ // app/components/MyButton.tsx
67
+ "use client";
68
+ import { Button } from "@devalok/shilp-sutra/ui/button";
69
+
70
+ export function MyButton() {
71
+ return <Button onClick={() => console.log("hi")}>Click</Button>;
72
+ }
73
+ ```
74
+
75
+ ### `@devalok/shilp-sutra/composed`
76
+
77
+ | Server-safe (renders without hydration) | Everything else |
78
+ |---|---|
79
+ | `ContentCard` | All other composed components become client islands when rendered |
80
+ | `PageHeader` | |
81
+ | `LoadingSkeleton` | |
82
+ | `PageSkeletons` | |
83
+
84
+ ### `@devalok/shilp-sutra/shell`
85
+
86
+ All shell components are client islands. They manage state (open/close, active route, viewport detection) and require hydration.
87
+
88
+ ### `@devalok/shilp-sutra/ai`
89
+
90
+ All AI components are client islands. They manage conversation state, streaming responses, and command bar focus.
91
+
92
+ ### `@devalok/shilp-sutra/hooks`
93
+
94
+ All hooks (`useColorMode`, `useIsMobile`, etc.) are client-only by definition (React hooks).
95
+
96
+ ### `@devalok/shilp-sutra/utils`, `/ui/lib/utils`, `/ui/lib/motion`, `/ui/lib/date-utils`
97
+
98
+ Pure functions. Server-safe.
99
+
100
+ ## Why barrel imports break RSC
101
+
102
+ The barrel re-exports every component in a layer. When a Server Component imports anything from `@devalok/shilp-sutra/ui`, the bundler walks the barrel and pulls in code from sibling components. If any sibling has `"use client"`, the bundler complains:
103
+
104
+ ```
105
+ You're importing a component that needs useState.
106
+ It only works in a Client Component.
107
+ ```
108
+
109
+ Per-component imports avoid this — the bundler only walks the imported file's module graph.
110
+
111
+ This is also why **per-component imports tree-shake better** even in non-RSC frameworks. They're the recommended pattern everywhere.
112
+
113
+ ## "use client" propagation
114
+
115
+ A component is server-safe only if:
116
+
117
+ 1. It has no `"use client"` directive at the top of its source file
118
+ 2. None of its imports have `"use client"` either (transitively)
119
+
120
+ We mark sources `// @server-safe` and a build-time check verifies the import graph stays clean. The `"use client"` directive is injected automatically during the build for components without that annotation.
121
+
122
+ If you fork the source and remove `"use client"` from a component that uses `useState`, the build will still inject it back unless you also remove the React-hook usage. Don't fight the system — wrap in a client component instead.
123
+
124
+ ## Streaming and Suspense
125
+
126
+ All shilp-sutra components SSR cleanly. They have no client-only side effects at module top-level — no `window.*`, `document.*`, or `localStorage.*` access during render. Side effects happen in `useEffect`, which runs after hydration.
127
+
128
+ Use `<Suspense>` boundaries normally:
129
+
130
+ ```tsx
131
+ import { Suspense } from "react";
132
+ import { LoadingSkeleton } from "@devalok/shilp-sutra/composed/loading-skeleton";
133
+
134
+ export default function Page() {
135
+ return (
136
+ <Suspense fallback={<LoadingSkeleton variant="page" />}>
137
+ <ClientHeavyComponent />
138
+ </Suspense>
139
+ );
140
+ }
141
+ ```
142
+
143
+ Both `<Suspense>` and `<LoadingSkeleton>` are server-safe.
144
+
145
+ ## Common RSC mistakes
146
+
147
+ ### Mistake 1: Calling a client hook in a Server Component
148
+
149
+ ```tsx
150
+ // ❌ — useColorMode is a React hook; hooks only run in client components
151
+ import { useColorMode } from "@devalok/shilp-sutra/hooks/use-color-mode";
152
+
153
+ export default function ServerPage() {
154
+ const { mode } = useColorMode(); // breaks
155
+ return <div>{mode}</div>;
156
+ }
157
+ ```
158
+
159
+ Fix: use the hook inside a `"use client"` component.
160
+
161
+ ### Mistake 2: Reading `window.*` in a server module's top level
162
+
163
+ ```tsx
164
+ // ❌ — window is undefined on the server
165
+ const w = window.innerWidth;
166
+
167
+ export default function ServerPage() {
168
+ return <p>{w}px</p>;
169
+ }
170
+ ```
171
+
172
+ Fix: read inside a `useEffect` of a client component, or use `useIsMobile` in a client wrapper.
173
+
174
+ ### Mistake 3: Passing functions as props from a Server Component to a client island
175
+
176
+ ```tsx
177
+ // ❌ — function props are not serializable across the RSC boundary
178
+ import { Button } from "@devalok/shilp-sutra/ui/button";
179
+
180
+ export default function ServerPage() {
181
+ return <Button onClick={() => console.log("hi")}>Click</Button>;
182
+ }
183
+ ```
184
+
185
+ Fix: define the handler inside the client island, or use a Server Action and pass the action reference (Server Actions ARE serializable).
186
+
187
+ ### Mistake 4: Barrel-importing into a Server Component
188
+
189
+ ```tsx
190
+ // ❌ — pulls every UI primitive's module graph into the server file,
191
+ // and any sibling with "use client" trips the bundler error.
192
+ import { Text } from "@devalok/shilp-sutra/ui";
193
+ ```
194
+
195
+ Fix: per-component import.
196
+
197
+ ```tsx
198
+ // ✅
199
+ import { Text } from "@devalok/shilp-sutra/ui/text";
200
+ ```
201
+
202
+ ## When in doubt
203
+
204
+ If you're unsure whether a component is server-safe, look at the source:
205
+
206
+ - `packages/core/src/ui/<name>.tsx` — top of file. The `// @server-safe` annotation marks components that ship without `"use client"`.
207
+ - `packages/core/src/composed/<name>/index.tsx` — same.
208
+
209
+ The matrix in this file is the authoritative public list. If you find a discrepancy, file an issue.