handoff-app 1.2.2-7 → 2.0.0-1

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 (351) hide show
  1. package/Changelog.md +37 -15
  2. package/README.md +372 -118
  3. package/dist/app-builder/build.d.ts +41 -2
  4. package/dist/app-builder/build.js +455 -18
  5. package/dist/app-builder/client-config.d.ts +15 -2
  6. package/dist/app-builder/client-config.js +43 -4
  7. package/dist/app-builder/config-diff/snapshot.d.ts +1 -6
  8. package/dist/app-builder/config-diff/snapshot.js +2 -9
  9. package/dist/app-builder/index.d.ts +1 -1
  10. package/dist/app-builder/index.js +3 -1
  11. package/dist/app-builder/nav-shell.d.ts +23 -0
  12. package/dist/app-builder/nav-shell.js +80 -0
  13. package/dist/app-builder/paths.js +25 -1
  14. package/dist/app-builder/static-export.d.ts +17 -0
  15. package/dist/app-builder/static-export.js +206 -0
  16. package/dist/app-builder/vercel-output.d.ts +39 -0
  17. package/dist/app-builder/vercel-output.js +259 -0
  18. package/dist/app-builder/watchers/utils.d.ts +2 -0
  19. package/dist/app-builder/watchers.d.ts +14 -4
  20. package/dist/app-builder/watchers.js +123 -28
  21. package/dist/app-builder/websocket.js +1 -1
  22. package/dist/artifacts/index.d.ts +2 -0
  23. package/dist/artifacts/index.js +16 -0
  24. package/dist/artifacts/types.d.ts +136 -0
  25. package/dist/artifacts/types.js +24 -0
  26. package/dist/artifacts/url.d.ts +67 -0
  27. package/dist/artifacts/url.js +116 -0
  28. package/dist/cache/build-cache.js +48 -4
  29. package/dist/cli.js +0 -0
  30. package/dist/commands/build/app.js +1 -2
  31. package/dist/commands/build/index.d.ts +17 -0
  32. package/dist/commands/build/index.js +66 -0
  33. package/dist/commands/checkout/index.d.ts +24 -0
  34. package/dist/commands/checkout/index.js +71 -0
  35. package/dist/commands/db/migrate.d.ts +11 -0
  36. package/dist/commands/db/migrate.js +33 -0
  37. package/dist/commands/index.js +8 -0
  38. package/dist/commands/publish/index.d.ts +21 -0
  39. package/dist/commands/publish/index.js +68 -0
  40. package/dist/config/defaults.js +15 -1
  41. package/dist/config/loader.js +6 -1
  42. package/dist/config/normalizers/declaration.js +4 -1
  43. package/dist/config/normalizers/page.d.ts +18 -0
  44. package/dist/config/normalizers/page.js +34 -0
  45. package/dist/config/normalizers/pattern.js +7 -1
  46. package/dist/config/runtime.d.ts +4 -0
  47. package/dist/config/runtime.js +53 -5
  48. package/dist/index.d.ts +65 -1
  49. package/dist/index.js +123 -3
  50. package/dist/nav/index.d.ts +83 -0
  51. package/dist/nav/index.js +9 -0
  52. package/dist/nav/registry.d.ts +15 -0
  53. package/dist/nav/registry.js +42 -0
  54. package/dist/nav/resolver.d.ts +8 -0
  55. package/dist/nav/resolver.js +192 -0
  56. package/dist/nav/workspace.d.ts +18 -0
  57. package/dist/nav/workspace.js +103 -0
  58. package/dist/pipeline/styles.js +7 -35
  59. package/dist/pipeline/token-transformers.d.ts +45 -0
  60. package/dist/pipeline/token-transformers.js +71 -0
  61. package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +17 -0
  62. package/dist/registry/asset-storage/adapters/vercel-blob.js +87 -0
  63. package/dist/registry/asset-storage/define.d.ts +10 -0
  64. package/dist/registry/asset-storage/define.js +13 -0
  65. package/dist/registry/asset-storage/resolve.d.ts +45 -0
  66. package/dist/registry/asset-storage/resolve.js +36 -0
  67. package/dist/registry/asset-storage/types.d.ts +51 -0
  68. package/dist/registry/asset-storage/types.js +9 -0
  69. package/dist/registry/assets/layout.d.ts +22 -0
  70. package/dist/registry/assets/layout.js +30 -0
  71. package/dist/registry/assets/sets.d.ts +18 -0
  72. package/dist/registry/assets/sets.js +44 -0
  73. package/dist/registry/assets/transfer.d.ts +45 -0
  74. package/dist/registry/assets/transfer.js +11 -0
  75. package/dist/registry/checkout/assets.d.ts +16 -0
  76. package/dist/registry/checkout/assets.js +150 -0
  77. package/dist/registry/checkout/index.d.ts +33 -0
  78. package/dist/registry/checkout/index.js +610 -0
  79. package/dist/registry/checkout/tokens.d.ts +15 -0
  80. package/dist/registry/checkout/tokens.js +171 -0
  81. package/dist/registry/client.d.ts +61 -0
  82. package/dist/registry/client.js +211 -0
  83. package/dist/registry/connection.d.ts +30 -0
  84. package/dist/registry/connection.js +36 -0
  85. package/dist/registry/db/adapter.d.ts +43 -0
  86. package/dist/registry/db/adapter.js +59 -0
  87. package/dist/registry/db/client.d.ts +35 -0
  88. package/dist/registry/db/client.js +94 -0
  89. package/dist/registry/db/index.d.ts +5 -0
  90. package/dist/registry/db/index.js +24 -0
  91. package/dist/registry/db/migrate.d.ts +23 -0
  92. package/dist/registry/db/migrate.js +64 -0
  93. package/dist/registry/db/schema.d.ts +4869 -0
  94. package/dist/registry/db/schema.js +347 -0
  95. package/dist/registry/path.d.ts +4 -0
  96. package/dist/registry/path.js +31 -0
  97. package/dist/registry/publish/artifacts.d.ts +16 -0
  98. package/dist/registry/publish/artifacts.js +132 -0
  99. package/dist/registry/publish/assets.d.ts +16 -0
  100. package/dist/registry/publish/assets.js +129 -0
  101. package/dist/registry/publish/errors.d.ts +5 -0
  102. package/dist/registry/publish/errors.js +26 -0
  103. package/dist/registry/publish/index.d.ts +27 -0
  104. package/dist/registry/publish/index.js +126 -0
  105. package/dist/registry/publish/package.d.ts +27 -0
  106. package/dist/registry/publish/package.js +257 -0
  107. package/dist/registry/publish/publish-build.d.ts +12 -0
  108. package/dist/registry/publish/publish-build.js +43 -0
  109. package/dist/registry/publish/tokens.d.ts +18 -0
  110. package/dist/registry/publish/tokens.js +112 -0
  111. package/dist/registry/tokens/sets.d.ts +59 -0
  112. package/dist/registry/tokens/sets.js +109 -0
  113. package/dist/registry/tokens/transfer.d.ts +48 -0
  114. package/dist/registry/tokens/transfer.js +10 -0
  115. package/dist/registry/transfer.d.ts +75 -0
  116. package/dist/registry/transfer.js +11 -0
  117. package/dist/store/filesystem-assets.d.ts +17 -0
  118. package/dist/store/filesystem-assets.js +147 -0
  119. package/dist/store/filesystem.d.ts +76 -0
  120. package/dist/store/filesystem.js +208 -0
  121. package/dist/store/index.d.ts +20 -0
  122. package/dist/store/index.js +38 -0
  123. package/dist/store/registry.d.ts +77 -0
  124. package/dist/store/registry.js +350 -0
  125. package/dist/store/source-files.d.ts +24 -0
  126. package/dist/store/source-files.js +147 -0
  127. package/dist/store/types.d.ts +185 -0
  128. package/dist/store/types.js +12 -0
  129. package/dist/transformers/plugins/csf-render.js +17 -8
  130. package/dist/transformers/plugins/handlebars-previews.js +10 -3
  131. package/dist/transformers/plugins/ssr-render.js +116 -49
  132. package/dist/transformers/preview/component/api.js +3 -1
  133. package/dist/transformers/preview/component/artifacts.js +8 -1
  134. package/dist/transformers/preview/component/builder.js +13 -5
  135. package/dist/transformers/preview/component/css.js +9 -1
  136. package/dist/transformers/preview/component/javascript.d.ts +9 -3
  137. package/dist/transformers/preview/component/javascript.js +45 -17
  138. package/dist/transformers/preview/component/shared-artifacts.d.ts +83 -0
  139. package/dist/transformers/preview/component/shared-artifacts.js +92 -0
  140. package/dist/transformers/preview/component.js +5 -2
  141. package/dist/transformers/preview/pattern/builder.js +28 -9
  142. package/dist/transformers/preview/pattern/html.d.ts +13 -10
  143. package/dist/transformers/preview/pattern/html.js +62 -27
  144. package/dist/transformers/preview/types.d.ts +38 -0
  145. package/dist/transformers/utils/handlebars.d.ts +3 -0
  146. package/dist/transformers/utils/handlebars.js +19 -5
  147. package/dist/types/config.d.ts +124 -31
  148. package/dist/utils/menu-shell.d.ts +60 -0
  149. package/dist/utils/menu-shell.js +221 -0
  150. package/dist/utils/stable-stringify.d.ts +2 -0
  151. package/dist/utils/stable-stringify.js +8 -0
  152. package/docs/cli.md +4 -2
  153. package/drizzle/0000_init_registry_storage.sql +95 -0
  154. package/drizzle/0001_add_review_metadata.sql +2 -0
  155. package/drizzle/0002_add_pages.sql +28 -0
  156. package/drizzle/0003_add_tokens.sql +29 -0
  157. package/drizzle/0004_add_assets.sql +37 -0
  158. package/drizzle/meta/0000_snapshot.json +642 -0
  159. package/drizzle/meta/0001_snapshot.json +654 -0
  160. package/drizzle/meta/0002_snapshot.json +835 -0
  161. package/drizzle/meta/0003_snapshot.json +1023 -0
  162. package/drizzle/meta/0004_snapshot.json +1252 -0
  163. package/drizzle/meta/_journal.json +41 -0
  164. package/drizzle.config.ts +16 -0
  165. package/package.json +10 -1
  166. package/src/app/components/Component/Preview.tsx +3 -2
  167. package/src/app/components/Component/RegistryActions.tsx +150 -0
  168. package/src/app/components/Foundations/DisplayLogo.tsx +11 -3
  169. package/src/app/components/Layout/Header.tsx +21 -3
  170. package/src/app/components/Layout/Main.tsx +11 -3
  171. package/src/app/components/Navigation/MainNav.tsx +4 -2
  172. package/src/app/components/Navigation/MobileNav.tsx +4 -2
  173. package/src/app/components/Navigation/SideNav.tsx +46 -11
  174. package/src/app/components/NotFound.tsx +24 -0
  175. package/src/app/components/context/NavProvider.tsx +71 -0
  176. package/src/app/components/old/Header.tsx +3 -3
  177. package/src/app/components/util/index.ts +203 -380
  178. package/src/app/components/util/useCollectionAssets.ts +125 -0
  179. package/src/app/components/util/useFoundationTokens.ts +67 -0
  180. package/src/app/generated/default-pages.json +1 -0
  181. package/src/app/generated/nav-shell.json +1 -0
  182. package/src/app/lib/api/query.ts +6 -0
  183. package/src/app/lib/api/redact.ts +46 -0
  184. package/src/app/lib/asset-storage.ts +81 -0
  185. package/src/app/lib/docs-api/artifacts.ts +116 -0
  186. package/src/app/lib/docs-api/backend.ts +151 -0
  187. package/src/app/lib/docs-api/errors.ts +52 -0
  188. package/src/app/lib/docs-api/handler.ts +34 -0
  189. package/src/app/lib/docs-api/index.ts +7 -0
  190. package/src/app/lib/docs-api/records.ts +189 -0
  191. package/src/app/lib/docs-api/registry.ts +130 -0
  192. package/src/app/lib/docs-api/runtime-config.ts +173 -0
  193. package/src/app/lib/docs-api/serve.ts +37 -0
  194. package/src/app/lib/docs-api/token-detail.ts +19 -0
  195. package/src/app/lib/registry-api/allowlist.ts +163 -0
  196. package/src/app/lib/registry-api/asset-transfer.ts +363 -0
  197. package/src/app/lib/registry-api/auth.ts +68 -0
  198. package/src/app/lib/registry-api/entity-routes.ts +197 -0
  199. package/src/app/lib/registry-api/errors.ts +51 -0
  200. package/src/app/lib/registry-api/files.ts +135 -0
  201. package/src/app/lib/registry-api/handler.ts +96 -0
  202. package/src/app/lib/registry-api/index.ts +4 -0
  203. package/src/app/lib/registry-api/meta.ts +87 -0
  204. package/src/app/lib/registry-api/revalidate.ts +36 -0
  205. package/src/app/lib/registry-api/store.ts +348 -0
  206. package/src/app/lib/registry-api/token-transfer.ts +231 -0
  207. package/src/app/lib/registry-api/transfer.ts +513 -0
  208. package/src/app/lib/registry-api/validation.ts +74 -0
  209. package/src/app/lib/registry-connection.ts +58 -0
  210. package/src/app/lib/utils.ts +14 -0
  211. package/src/app/next.config.mjs +72 -13
  212. package/src/app/pages/[...slug]/index.tsx +74 -20
  213. package/src/app/pages/_app.tsx +5 -2
  214. package/src/app/pages/api/docs/artifacts/[...artifactPath].ts +16 -0
  215. package/src/app/pages/api/docs/assets/[collection]/[...path].ts +50 -0
  216. package/src/app/pages/api/docs/assets/[collection]/index.ts +22 -0
  217. package/src/app/pages/api/docs/components/[id]/assets/[asset].ts +33 -0
  218. package/src/app/pages/api/docs/components/[id]/inspect/[previewId].ts +20 -0
  219. package/src/app/pages/api/docs/components/[id]/previews/[previewId].ts +21 -0
  220. package/src/app/pages/api/docs/components/[id].ts +24 -0
  221. package/src/app/pages/api/docs/components.json.ts +12 -0
  222. package/src/app/pages/api/docs/nav.json.ts +48 -0
  223. package/src/app/pages/api/docs/patterns/[id]/preview.ts +19 -0
  224. package/src/app/pages/api/docs/patterns/[id].ts +24 -0
  225. package/src/app/pages/api/docs/patterns.json.ts +12 -0
  226. package/src/app/pages/api/docs/tokens/components/[id].ts +30 -0
  227. package/src/app/pages/api/docs/tokens/foundations/[type].ts +32 -0
  228. package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +19 -0
  229. package/src/app/pages/api/registry/components/[id]/files/index.ts +19 -0
  230. package/src/app/pages/api/registry/components/[id].ts +20 -0
  231. package/src/app/pages/api/registry/components/index.ts +19 -0
  232. package/src/app/pages/api/registry/health.ts +20 -0
  233. package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +19 -0
  234. package/src/app/pages/api/registry/patterns/[id]/files/index.ts +19 -0
  235. package/src/app/pages/api/registry/patterns/[id].ts +20 -0
  236. package/src/app/pages/api/registry/patterns/index.ts +19 -0
  237. package/src/app/pages/api/registry/transfer/[kind]/[id].ts +39 -0
  238. package/src/app/pages/api/registry/transfer/assets/[collection].ts +24 -0
  239. package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +23 -0
  240. package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +11 -0
  241. package/src/app/pages/api/registry/transfer/assets/index.ts +10 -0
  242. package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +26 -0
  243. package/src/app/pages/api/registry/transfer/tokens/index.ts +11 -0
  244. package/src/app/pages/assets/fonts.tsx +41 -17
  245. package/src/app/pages/assets/index.tsx +2 -2
  246. package/src/app/pages/assets/logos.tsx +6 -3
  247. package/src/app/pages/foundations/colors.tsx +18 -8
  248. package/src/app/pages/foundations/effects.tsx +18 -8
  249. package/src/app/pages/foundations/icons/[name]/index.tsx +28 -16
  250. package/src/app/pages/foundations/icons/index.tsx +18 -12
  251. package/src/app/pages/foundations/index.tsx +1 -1
  252. package/src/app/pages/foundations/logo.tsx +12 -8
  253. package/src/app/pages/foundations/typography.tsx +19 -9
  254. package/src/app/pages/index.tsx +1 -1
  255. package/src/app/pages/system/component/[component]/index.tsx +87 -17
  256. package/src/app/pages/system/index.tsx +13 -4
  257. package/src/app/pages/system/pattern/[pattern]/index.tsx +70 -14
  258. package/src/app/pages/system/tokens/components/[component]/index.tsx +42 -14
  259. package/src/app/pages/system/tokens/foundations/colors.tsx +7 -5
  260. package/src/app/pages/system/tokens/foundations/effects.tsx +7 -5
  261. package/src/app/pages/system/tokens/foundations/typography.tsx +8 -4
  262. package/src/app/tsconfig.json +2 -3
  263. package/src/app-builder/build.ts +555 -11
  264. package/src/app-builder/client-config.ts +61 -4
  265. package/src/app-builder/config-diff/snapshot.ts +1 -11
  266. package/src/app-builder/index.ts +1 -1
  267. package/src/app-builder/nav-shell.ts +74 -0
  268. package/src/app-builder/paths.ts +27 -1
  269. package/src/app-builder/static-export.ts +242 -0
  270. package/src/app-builder/vercel-output.ts +297 -0
  271. package/src/app-builder/watchers/utils.ts +2 -0
  272. package/src/app-builder/watchers.ts +149 -25
  273. package/src/app-builder/websocket.ts +2 -2
  274. package/src/artifacts/README.md +14 -0
  275. package/src/artifacts/index.ts +24 -0
  276. package/src/artifacts/types.ts +146 -0
  277. package/src/artifacts/url.ts +122 -0
  278. package/src/cache/build-cache.ts +47 -2
  279. package/src/commands/build/app.ts +1 -2
  280. package/src/commands/build/index.ts +61 -0
  281. package/src/commands/checkout/index.ts +66 -0
  282. package/src/commands/db/migrate.ts +25 -0
  283. package/src/commands/index.ts +8 -0
  284. package/src/commands/publish/index.ts +63 -0
  285. package/src/config/defaults.ts +15 -0
  286. package/src/config/loader.ts +10 -1
  287. package/src/config/normalizers/declaration.ts +3 -0
  288. package/src/config/normalizers/page.ts +45 -0
  289. package/src/config/normalizers/pattern.ts +4 -1
  290. package/src/config/runtime.ts +48 -2
  291. package/src/index.ts +126 -3
  292. package/src/nav/index.ts +94 -0
  293. package/src/nav/registry.ts +54 -0
  294. package/src/nav/resolver.ts +217 -0
  295. package/src/nav/workspace.ts +87 -0
  296. package/src/pipeline/styles.ts +4 -35
  297. package/src/pipeline/token-transformers.ts +97 -0
  298. package/src/registry/asset-storage/adapters/vercel-blob.ts +51 -0
  299. package/src/registry/asset-storage/define.ts +15 -0
  300. package/src/registry/asset-storage/resolve.ts +72 -0
  301. package/src/registry/asset-storage/types.ts +46 -0
  302. package/src/registry/assets/layout.ts +38 -0
  303. package/src/registry/assets/sets.ts +48 -0
  304. package/src/registry/assets/transfer.ts +50 -0
  305. package/src/registry/checkout/assets.ts +126 -0
  306. package/src/registry/checkout/index.ts +596 -0
  307. package/src/registry/checkout/tokens.ts +152 -0
  308. package/src/registry/client.ts +234 -0
  309. package/src/registry/connection.ts +50 -0
  310. package/src/registry/db/adapter.ts +74 -0
  311. package/src/registry/db/client.ts +88 -0
  312. package/src/registry/db/index.ts +35 -0
  313. package/src/registry/db/migrate.ts +56 -0
  314. package/src/registry/db/schema.ts +444 -0
  315. package/src/registry/path.ts +26 -0
  316. package/src/registry/publish/artifacts.ts +147 -0
  317. package/src/registry/publish/assets.ts +129 -0
  318. package/src/registry/publish/errors.ts +34 -0
  319. package/src/registry/publish/index.ts +128 -0
  320. package/src/registry/publish/package.ts +272 -0
  321. package/src/registry/publish/publish-build.ts +51 -0
  322. package/src/registry/publish/tokens.ts +119 -0
  323. package/src/registry/tokens/sets.ts +132 -0
  324. package/src/registry/tokens/transfer.ts +53 -0
  325. package/src/registry/transfer.ts +82 -0
  326. package/src/store/filesystem-assets.ts +152 -0
  327. package/src/store/filesystem.ts +237 -0
  328. package/src/store/index.ts +62 -0
  329. package/src/store/registry.ts +334 -0
  330. package/src/store/source-files.ts +155 -0
  331. package/src/store/types.ts +210 -0
  332. package/src/transformers/plugins/csf-render.ts +31 -7
  333. package/src/transformers/plugins/handlebars-previews.ts +22 -3
  334. package/src/transformers/plugins/ssr-render.ts +145 -57
  335. package/src/transformers/preview/component/api.ts +2 -1
  336. package/src/transformers/preview/component/artifacts.ts +10 -1
  337. package/src/transformers/preview/component/builder.ts +9 -2
  338. package/src/transformers/preview/component/css.ts +10 -1
  339. package/src/transformers/preview/component/javascript.ts +55 -20
  340. package/src/transformers/preview/component/shared-artifacts.ts +146 -0
  341. package/src/transformers/preview/component.ts +5 -2
  342. package/src/transformers/preview/pattern/builder.ts +33 -7
  343. package/src/transformers/preview/pattern/html.ts +71 -27
  344. package/src/transformers/preview/types.ts +42 -0
  345. package/src/transformers/utils/handlebars.ts +31 -5
  346. package/src/types/config.ts +129 -31
  347. package/src/utils/menu-shell.ts +241 -0
  348. package/src/utils/stable-stringify.ts +7 -0
  349. package/templates/default/package.json.tpl +1 -1
  350. package/handoff.config.js +0 -90
  351. package/src/app/components/ComponentSearch.tsx +0 -93
@@ -9,10 +9,12 @@ import Footer from '../../../../components/Footer';
9
9
  import Layout from '../../../../components/Layout/Main';
10
10
  import HeadersType from '../../../../components/Typography/Headers';
11
11
  import { buttonVariants } from '../../../../components/ui/button';
12
- import { AssetDocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig, getTokens } from '../../../../components/util';
12
+ import { AssetDocumentationProps, buildTimeAssets, fetchDocPageMarkdown, getClientRuntimeConfig, getTokens, isRegistryRuntime } from '../../../../components/util';
13
+ import { useSingleAsset, type DisplayableAsset } from '../../../../components/util/useCollectionAssets';
13
14
 
14
- const DisplayIcon: React.FC<{ icon: CoreTypes.IAssetObject }> = ({ icon }) => {
15
+ const DisplayIcon: React.FC<{ icon: DisplayableAsset }> = ({ icon }) => {
15
16
  const htmlData = React.useMemo(() => {
17
+ if (!icon.data) return '';
16
18
  // For SSR
17
19
  if (typeof window === 'undefined') {
18
20
  return icon.data.replace('<svg', '<svg class="o-icon"');
@@ -30,19 +32,28 @@ const DisplayIcon: React.FC<{ icon: CoreTypes.IAssetObject }> = ({ icon }) => {
30
32
  return svgElement.outerHTML;
31
33
  }, [icon.data]);
32
34
 
35
+ // Registry: no inline body yet, so render the individual asset content URL.
36
+ if (!icon.data) {
37
+ return <img className="o-icon" src={icon.src} alt={icon.name} />;
38
+ }
33
39
  return <>{HtmlReactParser(htmlData)}</>;
34
40
  };
35
41
 
36
42
  /**
37
- * Render all index pages
43
+ * Render all index pages. In registry mode the icon set is unknown at build time (nothing is baked),
44
+ * so paths are resolved on demand via `fallback: 'blocking'` and the page hydrates from the API.
38
45
  * @returns
39
46
  */
40
47
  export async function getStaticPaths() {
41
- const paths = getTokens().assets?.icons?.map((icon) => ({
42
- params: {
43
- name: icon.name,
44
- },
45
- })) ?? [];
48
+ if (isRegistryRuntime()) {
49
+ return { paths: [], fallback: 'blocking' as const };
50
+ }
51
+ const paths =
52
+ getTokens().assets?.icons?.map((icon) => ({
53
+ params: {
54
+ name: icon.name,
55
+ },
56
+ })) ?? [];
46
57
 
47
58
  return {
48
59
  paths,
@@ -58,12 +69,12 @@ export async function getStaticPaths() {
58
69
  * @param context GetStaticProps
59
70
  * @returns
60
71
  */
61
- export const getStaticProps: GetStaticProps = () => {
72
+ export const getStaticProps: GetStaticProps = async () => {
62
73
  return {
63
74
  props: {
64
- ...fetchDocPageMarkdown('docs/foundations/', 'icons', `/foundations`).props,
75
+ ...(await fetchDocPageMarkdown('docs/foundations/', 'icons', `/foundations`)).props,
65
76
  config: getClientRuntimeConfig(),
66
- assets: getTokens().assets,
77
+ assets: buildTimeAssets(),
67
78
  },
68
79
  };
69
80
  };
@@ -73,12 +84,13 @@ export default function SingleIcon({ menu, metadata, current, config, assets }:
73
84
  const nameParam = router.query.name;
74
85
  let name = undefined;
75
86
  if (typeof nameParam === 'string') { name = nameParam } else if (Array.isArray(nameParam)) { name = nameParam[0] } else { name = undefined }
76
- const icon = assets?.icons.find((i) => i.icon === name);
87
+ // Workspace/static uses the baked asset; registry hydrates the single asset (+ its SVG body) by id.
88
+ const icon = useSingleAsset('icons', name, assets?.icons?.find((i) => i.icon === name));
77
89
 
78
90
  const copySvg = React.useCallback<React.MouseEventHandler>(
79
91
  (event) => {
80
92
  event.preventDefault();
81
- if (icon) {
93
+ if (icon?.data) {
82
94
  navigator.clipboard.writeText(icon.data);
83
95
  }
84
96
  },
@@ -112,13 +124,13 @@ export default function SingleIcon({ menu, metadata, current, config, assets }:
112
124
  Copy SVG <Code strokeWidth={1.5} />
113
125
  </Link>
114
126
 
115
- <Link
116
- href={'data:text/plain;charset=utf-8,' + encodeURIComponent(icon.data)}
127
+ <a
128
+ href={icon.src ?? 'data:text/plain;charset=utf-8,' + encodeURIComponent(icon.data ?? '')}
117
129
  download={icon.name}
118
130
  className={buttonVariants({ variant: 'outline', size: 'sm' }) + ' font-normal [&_svg]:size-3!'}
119
131
  >
120
132
  Download SVG <Download strokeWidth={1.5} />
121
- </Link>
133
+ </a>
122
134
  </div>
123
135
  </div>
124
136
 
@@ -12,10 +12,13 @@ import { MarkdownComponents, remarkCodeMeta } from '../../../components/Markdown
12
12
  import HeadersType from '../../../components/Typography/Headers';
13
13
  import { buttonVariants } from '../../../components/ui/button';
14
14
  import { Input } from '../../../components/ui/input';
15
- import { AssetDocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig, getTokens } from '../../../components/util';
15
+ import { buildAssetDownloadUrl } from '@handoff/artifacts/url';
16
+ import { AssetDocumentationProps, buildTimeAssets, fetchDocPageMarkdown, getClientRuntimeConfig } from '../../../components/util';
17
+ import { useCollectionAssets, type DisplayableAsset } from '../../../components/util/useCollectionAssets';
16
18
 
17
- export const DisplayIcon: React.FC<{ icon: CoreTypes.IAssetObject }> = ({ icon }) => {
19
+ export const DisplayIcon: React.FC<{ icon: DisplayableAsset }> = ({ icon }) => {
18
20
  const htmlData = React.useMemo(() => {
21
+ if (!icon.data) return '';
19
22
  // For SSR
20
23
  if (typeof window === 'undefined') {
21
24
  return icon.data.replace('<svg', '<svg class="o-icon"');
@@ -35,7 +38,8 @@ export const DisplayIcon: React.FC<{ icon: CoreTypes.IAssetObject }> = ({ icon }
35
38
  <div className="flex flex-col gap-2">
36
39
  <Link href={`/foundations/icons/${icon.icon}`}>
37
40
  <div className="flex flex-col items-center gap-6 rounded-lg border border-gray-100/80 bg-gray-100/80 py-12 transition-all hover:border-gray-300 hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-900 dark:hover:border-gray-700 dark:hover:bg-gray-900">
38
- <div dangerouslySetInnerHTML={{ __html: htmlData }} />
41
+ {/* Workspace/static: inline SVG body. Registry: the individual asset content URL. */}
42
+ {icon.data ? <div dangerouslySetInnerHTML={{ __html: htmlData }} /> : <img className="o-icon h-6 w-6" src={icon.src} alt={icon.name} />}
39
43
  </div>
40
44
  </Link>
41
45
  <Link href={`/foundations/icons/${icon.icon}`}>
@@ -55,9 +59,9 @@ export const DisplayIcon: React.FC<{ icon: CoreTypes.IAssetObject }> = ({ icon }
55
59
  export const getStaticProps: GetStaticProps = async () => {
56
60
  return {
57
61
  props: {
58
- ...fetchDocPageMarkdown('docs/foundations/', 'icons', `/foundations`).props,
62
+ ...(await fetchDocPageMarkdown('docs/foundations/', 'icons', `/foundations`)).props,
59
63
  config: getClientRuntimeConfig(),
60
- assets: getTokens().assets,
64
+ assets: buildTimeAssets(),
61
65
  },
62
66
  };
63
67
  };
@@ -66,11 +70,12 @@ const IconsPage = ({ content, menu, metadata, current, config, assets }: AssetDo
66
70
  const [search, setSearch] = React.useState('');
67
71
  const inputRef = React.useRef<HTMLInputElement>(null);
68
72
 
73
+ // Workspace/static hydrates from build-time props; registry hydrates from the docs read API.
74
+ const allIcons = useCollectionAssets('icons', assets?.icons);
69
75
  const icons = React.useMemo(() => {
70
- if (!assets || !Array.isArray(assets.icons)) return [];
71
- if (!search) return assets.icons;
72
- return assets.icons.filter(icon => icon.index.includes(search));
73
- }, [assets, search]);
76
+ if (!search) return allIcons;
77
+ return allIcons.filter((icon) => icon.index.includes(search));
78
+ }, [allIcons, search]);
74
79
 
75
80
  const filterList = React.useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => {
76
81
  setSearch(event.currentTarget.value.toLowerCase().replace(/[\W_]+/g, ' '));
@@ -90,12 +95,13 @@ const IconsPage = ({ content, menu, metadata, current, config, assets }: AssetDo
90
95
  <HeadersType.H1>{metadata.title}</HeadersType.H1>
91
96
  <p className="max-w-[800px] text-lg font-light text-gray-500 dark:text-gray-300">{metadata.description}</p>
92
97
  <div className="mt-3 flex flex-row gap-3">
93
- <Link
98
+ <a
94
99
  className={buttonVariants({ variant: 'outline', size: 'sm' }) + ' font-normal [&_svg]:size-3!'}
95
- href={config?.assets_zip_links?.icons ?? '/icons.zip'}
100
+ href={config?.assets_zip_links?.icons ?? buildAssetDownloadUrl('icons', process.env.HANDOFF_APP_BASE_PATH)}
101
+ download
96
102
  >
97
103
  Download Icons <Download strokeWidth={1.5} />
98
- </Link>
104
+ </a>
99
105
  </div>
100
106
  </div>
101
107
  <hr className="mb-10" />
@@ -20,7 +20,7 @@ import { DocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig } from
20
20
  export const getStaticProps: GetStaticProps = async () => {
21
21
  return {
22
22
  props: {
23
- ...fetchDocPageMarkdown('docs/', 'foundations', `/foundations`).props,
23
+ ...(await fetchDocPageMarkdown('docs/', 'foundations', `/foundations`)).props,
24
24
  config: getClientRuntimeConfig(),
25
25
  },
26
26
  };
@@ -1,7 +1,8 @@
1
1
  import type { GetStaticProps } from 'next';
2
- import Link from 'next/link';
3
2
 
4
- import { AssetDocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig, getTokens } from '../../components/util';
3
+ import { buildAssetDownloadUrl } from '@handoff/artifacts/url';
4
+ import { AssetDocumentationProps, buildTimeAssets, fetchDocPageMarkdown, getClientRuntimeConfig } from '../../components/util';
5
+ import { useCollectionAssets } from '../../components/util/useCollectionAssets';
5
6
 
6
7
  import { Download } from 'lucide-react';
7
8
  import ReactMarkdown from 'react-markdown';
@@ -39,26 +40,29 @@ import { buttonVariants } from '../../components/ui/button';
39
40
  export const getStaticProps: GetStaticProps = async () => {
40
41
  return {
41
42
  props: {
42
- ...fetchDocPageMarkdown('docs/foundations/', 'logo', `/foundations`).props,
43
+ ...(await fetchDocPageMarkdown('docs/foundations/', 'logo', `/foundations`)).props,
43
44
  config: getClientRuntimeConfig(),
44
- assets: getTokens().assets,
45
+ assets: buildTimeAssets(),
45
46
  },
46
47
  };
47
48
  };
48
49
 
49
50
  const LogoPage = ({ content, menu, metadata, current, config, assets }: AssetDocumentationProps) => {
51
+ // Workspace/static uses build-time props; registry hydrates from the docs read API.
52
+ const logos = useCollectionAssets('logos', assets?.logos);
50
53
  return (
51
54
  <Layout config={config} menu={menu} metadata={metadata} current={current}>
52
55
  <div className="flex flex-col gap-2 pb-7">
53
56
  <HeadersType.H1>{metadata.title}</HeadersType.H1>
54
57
  <p className="text-lg leading-relaxed text-gray-600 dark:text-gray-300">{metadata.description}</p>
55
58
  <div className="mt-3 flex flex-row gap-3">
56
- <Link
59
+ <a
57
60
  className={buttonVariants({ variant: 'outline', size: 'sm' }) + ' font-normal [&_svg]:size-3!'}
58
- href={config?.assets_zip_links?.logos ?? '/logos.zip'}
61
+ href={config?.assets_zip_links?.logos ?? buildAssetDownloadUrl('logos', process.env.HANDOFF_APP_BASE_PATH)}
62
+ download
59
63
  >
60
64
  Download Logos <Download strokeWidth={1.5} />
61
- </Link>
65
+ </a>
62
66
  </div>
63
67
  </div>
64
68
 
@@ -67,7 +71,7 @@ const LogoPage = ({ content, menu, metadata, current, config, assets }: AssetDoc
67
71
  <p className="mb-8">There is one main {config?.app?.client} logo that supports two variations.</p>
68
72
  </div>
69
73
  <div className="mb-8 grid grid-cols-2 gap-6">
70
- {assets?.logos?.map((logo) => (
74
+ {logos.map((logo) => (
71
75
  <DisplayLogo logo={logo} content={config?.app?.client} key={logo.path} />
72
76
  ))}
73
77
  </div>
@@ -10,7 +10,8 @@ import Layout from '../../components/Layout/Main';
10
10
  import { MarkdownComponents, remarkCodeMeta } from '../../components/Markdown/MarkdownComponents';
11
11
  import AnchorNav, { anchorSlugify } from '../../components/Navigation/AnchorNav';
12
12
  import HeadersType from '../../components/Typography/Headers';
13
- import { fetchFoundationDocPageMarkdown, FoundationDocumentationProps, getClientRuntimeConfig, getTokens } from '../../components/util';
13
+ import { fetchFoundationDocPageMarkdown, FoundationDocumentationProps, getClientRuntimeConfig, getTokens, buildTimeFoundationDesign } from '../../components/util';
14
+ import { useFoundationTokens } from '../../components/util/useFoundationTokens';
14
15
 
15
16
  export interface typographyTypes {
16
17
  [key: string]: any;
@@ -27,9 +28,9 @@ export interface typographyTypes {
27
28
  export const getStaticProps: next.GetStaticProps = async () => {
28
29
  return {
29
30
  props: {
30
- ...fetchFoundationDocPageMarkdown('docs/foundations/', 'typography', `/foundations`).props,
31
+ ...(await fetchFoundationDocPageMarkdown('docs/foundations/', 'typography', `/foundations`)).props,
31
32
  config: getClientRuntimeConfig(),
32
- design: getTokens().localStyles,
33
+ design: buildTimeFoundationDesign(),
33
34
  },
34
35
  };
35
36
  };
@@ -39,13 +40,22 @@ const Typography = ({
39
40
  menu,
40
41
  metadata,
41
42
  current,
42
- scss,
43
- css,
44
- styleDictionary,
45
- types,
43
+ scss: scssProp,
44
+ css: cssProp,
45
+ styleDictionary: styleDictionaryProp,
46
+ types: typesProp,
46
47
  config,
47
- design,
48
+ design: designProp,
48
49
  }: FoundationDocumentationProps) => {
50
+ // In registry mode the build-time token data/downloads are empty; hydrate from the docs read API.
51
+ const { design, css, scss, styleDictionary, types } = useFoundationTokens('typography', {
52
+ design: designProp,
53
+ css: cssProp,
54
+ scss: scssProp,
55
+ styleDictionary: styleDictionaryProp,
56
+ types: typesProp,
57
+ });
58
+
49
59
  const typography = design.typography.slice().sort((a, b) => {
50
60
  const l = (config?.app?.type_sort ?? []).indexOf(a.name) >>> 0;
51
61
  const r = (config?.app?.type_sort ?? []).indexOf(b.name) >>> 0;
@@ -57,7 +67,7 @@ const Typography = ({
57
67
  ...result,
58
68
  [current.values.fontFamily]: result[current.values.fontFamily]
59
69
  ? // sorts and returns unique font weights
60
- sortedUniq([...result[current.values.fontFamily], current.values.fontWeight].sort((a, b) => a - b))
70
+ sortedUniq([...result[current.values.fontFamily], current.values.fontWeight].sort((a, b) => a - b))
61
71
  : [current.values.fontWeight],
62
72
  };
63
73
  }, {} as FontFamily);
@@ -23,7 +23,7 @@ import { DocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig } from
23
23
  export const getStaticProps: GetStaticProps = async () => {
24
24
  return {
25
25
  props: {
26
- ...fetchDocPageMarkdown('docs/', 'index', `/`).props,
26
+ ...(await fetchDocPageMarkdown('docs/', 'index', `/`)).props,
27
27
  config: getClientRuntimeConfig(),
28
28
  },
29
29
  };
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import { buildArtifactUrl } from '@handoff/artifacts/url';
2
3
  import { OptionalPreviewRender } from '@handoff/transformers/preview/types';
3
4
  import { PreviewObject } from '@handoff/types/preview';
4
5
  import { evaluateFilter, type Filter } from '@handoff/utils/filter';
@@ -8,6 +9,7 @@ import ReactMarkdown from 'react-markdown';
8
9
  import rehypeRaw from 'rehype-raw';
9
10
  import remarkGfm from 'remark-gfm';
10
11
  import { ComponentPreview } from '../../../../components/Component/Preview';
12
+ import { RegistryActions } from '../../../../components/Component/RegistryActions';
11
13
  import { HotReloadProvider } from '../../../../components/context/HotReloadProvider';
12
14
  import { PreviewContextProvider } from '../../../../components/context/PreviewContext';
13
15
  import Layout from '../../../../components/Layout/Main';
@@ -18,15 +20,21 @@ import HeadersType from '../../../../components/Typography/Headers';
18
20
  import { Button } from '../../../../components/ui/button';
19
21
  import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '../../../../components/ui/drawer';
20
22
  import { JsonTreeView } from '../../../../components/ui/json-tree-view';
21
- import { fetchComponents, fetchDocPageMetadataAndContent, getClientRuntimeConfig, getCurrentSection, IParams, staticBuildMenu } from '../../../../components/util';
23
+ import { fetchComponents, fetchDocPageMetadataAndContent, getClientRuntimeConfig, getNavProps, IParams, isRegistryRuntime } from '../../../../components/util';
22
24
 
23
25
  /**
24
26
  * Render all index pages
25
27
  * @returns
26
28
  */
27
29
  export async function getStaticPaths() {
30
+ // Registry content is mutable at runtime and may be empty/unreachable at build time, so resolve
31
+ // component pages on demand against the live DB instead of freezing the path list to the build
32
+ // snapshot. The static-export target stays fully prerendered (`fallback: false`).
33
+ if (isRegistryRuntime()) {
34
+ return { paths: [], fallback: 'blocking' as const };
35
+ }
28
36
  return {
29
- paths: fetchComponents()?.map((exportable) => ({ params: { component: exportable.id } })) ?? [],
37
+ paths: (await fetchComponents()).map((exportable) => ({ params: { component: exportable.id } })),
30
38
  fallback: false, // can also be true or 'blocking'
31
39
  };
32
40
  }
@@ -72,10 +80,15 @@ export const getStaticProps = async (context) => {
72
80
 
73
81
  // const componentObject = getTokens().components[reduceSlugToString(component)] ?? null;
74
82
  // const isFigmaComponent = false;
75
- const components = fetchComponents()!;
76
- const menu = staticBuildMenu();
77
- const config = getClientRuntimeConfig();
83
+ const components = await fetchComponents();
78
84
  const componentData = components.find((c) => c.id === component);
85
+ // Under `fallback: 'blocking'` this runs on demand for arbitrary ids; surface a real 404 for ids
86
+ // that do not exist in the active mode instead of dereferencing an undefined record below.
87
+ if (!componentData) {
88
+ return { notFound: true };
89
+ }
90
+ const navProps = await getNavProps('/system');
91
+ const config = getClientRuntimeConfig();
79
92
  const docs = fetchDocPageMetadataAndContent('docs/system/', component as string);
80
93
  const componentHotReloadIsAvailable = process.env.NODE_ENV === 'development';
81
94
  const sameGroupComponents = components.filter((c) => c.group === componentData?.group);
@@ -83,7 +96,7 @@ export const getStaticProps = async (context) => {
83
96
  const previousComponent = sameGroupComponents[groupIndex - 1] ?? null;
84
97
  const nextComponent = sameGroupComponents[groupIndex + 1] ?? null;
85
98
 
86
- const fallbackTitle = componentData.name || startCase(component as string);
99
+ const fallbackTitle = componentData.title || startCase(component as string);
87
100
  const fallbackMetaTitle = `${fallbackTitle}${config?.app?.client ? ` | ${config.app.client} Design System` : ''}`;
88
101
 
89
102
  return {
@@ -91,12 +104,11 @@ export const getStaticProps = async (context) => {
91
104
  id: component,
92
105
  // isFigmaComponent: true,
93
106
  previews: { components: {} },
94
- menu,
107
+ ...navProps,
95
108
  config,
96
- current: getCurrentSection(menu, '/system') ?? [],
97
109
  metadata: {
98
110
  ...componentData,
99
- title: componentData.name || docs.metadata.title || startCase(component as string),
111
+ title: componentData.title || docs.metadata.title || startCase(component as string),
100
112
  description: componentData.description,
101
113
  metaTitle: docs.metadata.metaTitle || fallbackMetaTitle,
102
114
  metaDescription: docs.metadata.metaDescription || componentData.description,
@@ -117,28 +129,46 @@ const GenericComponentPage = ({ menu, metadata, current, id, config, componentHo
117
129
  const [component, setComponent] = useState<PreviewObject>(undefined);
118
130
  const ref = React.useRef<HTMLDivElement>(null);
119
131
  const [componentPreviews, setComponentPreviews] = useState<PreviewObject | [string, PreviewObject][]>();
132
+ const [previewsUnavailable, setPreviewsUnavailable] = useState(false);
120
133
 
121
134
  const appBasePath = process.env.HANDOFF_APP_BASE_PATH ?? '';
122
135
  const normalizedBasePath = appBasePath ? `/${appBasePath.replace(/^\/+|\/+$/g, '')}` : '';
123
136
  const componentRoute = (componentId: string) => `${normalizedBasePath}/system/component/${componentId}`;
124
137
 
125
- const fetchComponents = async () => {
126
- const data = await fetch(`${normalizedBasePath}/api/component/${id}.json`).then((res) => res.json());
127
- setComponent(data as PreviewObject);
138
+ const componentDataUrl = buildArtifactUrl(`component/${id}.json`, appBasePath);
139
+
140
+ const fetchComponents = async (signal: AbortSignal) => {
141
+ try {
142
+ const res = await fetch(componentDataUrl, { signal });
143
+ if (!res.ok) {
144
+ setPreviewsUnavailable(true);
145
+ setComponent(undefined);
146
+ return;
147
+ }
148
+ const data = await res.json();
149
+ setComponent(data as PreviewObject);
150
+ } catch (err) {
151
+ // A superseded fetch (the user navigated to another component before this one resolved) is
152
+ // aborted on effect cleanup; ignore it so a slow response can't overwrite the current one.
153
+ if ((err as Error)?.name !== 'AbortError') throw err;
154
+ }
128
155
  };
129
156
 
130
157
  const previousLink = previousComponent ? {
131
158
  href: previousComponent ? componentRoute(previousComponent.id) : null,
132
- title: previousComponent ? previousComponent.name : null,
159
+ title: previousComponent ? previousComponent.title : null,
133
160
  } : null;
134
161
  const nextLink = nextComponent ? {
135
162
  href: componentRoute(nextComponent.id),
136
- title: nextComponent.name,
163
+ title: nextComponent.title,
137
164
  } : null;
138
165
 
139
166
  useEffect(() => {
167
+ const controller = new AbortController();
140
168
  setComponent(undefined);
141
- fetchComponents();
169
+ setPreviewsUnavailable(false);
170
+ fetchComponents(controller.signal);
171
+ return () => controller.abort();
142
172
  // eslint-disable-next-line react-hooks/exhaustive-deps
143
173
  }, [id]);
144
174
 
@@ -163,8 +193,47 @@ const GenericComponentPage = ({ menu, metadata, current, id, config, componentHo
163
193
  }
164
194
  }, [component, id]);
165
195
 
166
- if (!component) return <p>Loading...</p>;
167
- const apiUrl = (window.location.origin && window.location.origin) + `${normalizedBasePath}/api/component/${id}.json`;
196
+ if (previewsUnavailable) {
197
+ return (
198
+ <Layout config={config} menu={menu} current={current} metadata={metadata}>
199
+ <div className="flex flex-col gap-3 pb-14">
200
+ <small className="text-sm font-medium text-sky-600 dark:text-gray-300">Components</small>
201
+ <HeadersType.H1>{metadata.title}</HeadersType.H1>
202
+ {metadata.description && (
203
+ <div className="prose max-w-[800px] text-xl font-light leading-relaxed text-gray-600 dark:text-gray-300">
204
+ <ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
205
+ {metadata.description}
206
+ </ReactMarkdown>
207
+ </div>
208
+ )}
209
+ <div className="mt-4 max-w-[800px] rounded-md border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800 dark:border-amber-900/50 dark:bg-amber-950/40 dark:text-amber-200">
210
+ This component has no published artifacts yet, so previews and code samples are unavailable. Publish it to the
211
+ registry to populate its documentation.
212
+ </div>
213
+ <hr className="mt-8" />
214
+ <PrevNextNav previous={previousLink} next={nextLink} />
215
+ </div>
216
+ </Layout>
217
+ );
218
+ }
219
+ if (!component) {
220
+ return (
221
+ <Layout config={config} menu={menu} current={current} metadata={metadata}>
222
+ <div className="flex flex-col gap-3 pb-14" aria-busy="true" aria-live="polite">
223
+ <small className="text-sm font-medium text-sky-600 dark:text-gray-300">Components</small>
224
+ <HeadersType.H1>{metadata.title}</HeadersType.H1>
225
+ {metadata.description && (
226
+ <div className="prose max-w-[800px] text-xl font-light leading-relaxed text-gray-600 dark:text-gray-300">
227
+ <ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
228
+ {metadata.description}
229
+ </ReactMarkdown>
230
+ </div>
231
+ )}
232
+ </div>
233
+ </Layout>
234
+ );
235
+ }
236
+ const apiUrl = (window.location.origin && window.location.origin) + componentDataUrl;
168
237
  return (
169
238
  <Layout config={config} menu={menu} current={current} metadata={metadata}>
170
239
  <div className="flex flex-col gap-3 pb-14">
@@ -178,6 +247,7 @@ const GenericComponentPage = ({ menu, metadata, current, id, config, componentHo
178
247
  </ReactMarkdown>
179
248
  </div>
180
249
  <div className="flex flex-row gap-3">
250
+ <RegistryActions mode={config.runtime.mode} connected={config.runtime.connected} entityType="component" id={id} />
181
251
  {component.figma && (
182
252
  <Button asChild variant={'outline'} size={'sm'} className="font-normal [&_svg]:size-3!">
183
253
  <a href={component.figma} target="_blank">
@@ -36,7 +36,7 @@ export const getStaticProps: GetStaticProps = async () => {
36
36
  return {
37
37
  props: {
38
38
  config,
39
- ...fetchDocPageMarkdown('docs/', 'system', `/system`).props,
39
+ ...(await fetchDocPageMarkdown('docs/', 'system', `/system`)).props,
40
40
  } as ComponentPageDocumentationProps,
41
41
  };
42
42
  };
@@ -49,13 +49,22 @@ const ComponentsPage = ({ content, menu, metadata, current, config }: ComponentP
49
49
  // Fetch components from api
50
50
  const [components, setComponents] = useState<PreviewObject[]>(undefined);
51
51
  const fetchComponents = async () => {
52
- const data = await fetch(`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/components.json`).then((res) => res.json());
52
+ const data = await fetch(`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/docs/components.json`).then((res) => res.json());
53
53
  setComponents(data as PreviewObject[]);
54
54
  };
55
55
  useEffect(() => {
56
56
  fetchComponents();
57
57
  }, []);
58
- if (!components) return <p>Loading...</p>;
58
+ if (!components) {
59
+ return (
60
+ <Layout config={config} menu={menu} current={current} metadata={metadata}>
61
+ <div className="flex flex-col gap-2 pb-7" aria-busy="true" aria-live="polite">
62
+ <HeadersType.H1>{metadata.title}</HeadersType.H1>
63
+ <p className="mt-3 text-lg leading-relaxed text-gray-600 dark:text-gray-300">{metadata.description}</p>
64
+ </div>
65
+ </Layout>
66
+ );
67
+ }
59
68
 
60
69
  if ((components ?? []).length === 0) {
61
70
  return (
@@ -90,7 +99,7 @@ const ComponentsPage = ({ content, menu, metadata, current, config }: ComponentP
90
99
  );
91
100
  }
92
101
 
93
- const apiUrl = (window.location.origin && window.location.origin) + `${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/components.json`;
102
+ const apiUrl = (window.location.origin && window.location.origin) + `${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/docs/components.json`;
94
103
  return (
95
104
  <Layout config={config} menu={menu} current={current} metadata={metadata}>
96
105
  <div className="flex flex-col gap-2 pb-7">