handoff-app 1.2.2-6 → 2.0.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.
- package/Changelog.md +37 -15
- package/README.md +372 -118
- package/dist/app-builder/build.d.ts +41 -2
- package/dist/app-builder/build.js +455 -18
- package/dist/app-builder/client-config.d.ts +15 -2
- package/dist/app-builder/client-config.js +43 -4
- package/dist/app-builder/config-diff/snapshot.d.ts +1 -6
- package/dist/app-builder/config-diff/snapshot.js +2 -9
- package/dist/app-builder/index.d.ts +1 -1
- package/dist/app-builder/index.js +3 -1
- package/dist/app-builder/nav-shell.d.ts +23 -0
- package/dist/app-builder/nav-shell.js +80 -0
- package/dist/app-builder/static-export.d.ts +17 -0
- package/dist/app-builder/static-export.js +181 -0
- package/dist/app-builder/vercel-output.d.ts +39 -0
- package/dist/app-builder/vercel-output.js +259 -0
- package/dist/app-builder/watchers/utils.d.ts +2 -0
- package/dist/app-builder/watchers.d.ts +14 -4
- package/dist/app-builder/watchers.js +123 -28
- package/dist/app-builder/websocket.js +1 -1
- package/dist/artifacts/index.d.ts +2 -0
- package/dist/artifacts/index.js +13 -0
- package/dist/artifacts/types.d.ts +136 -0
- package/dist/artifacts/types.js +24 -0
- package/dist/artifacts/url.d.ts +49 -0
- package/dist/artifacts/url.js +86 -0
- package/dist/cache/build-cache.js +48 -4
- package/dist/commands/build/app.js +1 -2
- package/dist/commands/build/index.d.ts +17 -0
- package/dist/commands/build/index.js +66 -0
- package/dist/commands/checkout/index.d.ts +24 -0
- package/dist/commands/checkout/index.js +71 -0
- package/dist/commands/db/migrate.d.ts +11 -0
- package/dist/commands/db/migrate.js +33 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/publish/index.d.ts +21 -0
- package/dist/commands/publish/index.js +68 -0
- package/dist/config/defaults.js +15 -1
- package/dist/config/loader.js +6 -1
- package/dist/config/normalizers/declaration.js +4 -1
- package/dist/config/normalizers/page.d.ts +18 -0
- package/dist/config/normalizers/page.js +34 -0
- package/dist/config/normalizers/pattern.js +7 -1
- package/dist/config/runtime.d.ts +4 -0
- package/dist/config/runtime.js +53 -5
- package/dist/index.d.ts +65 -1
- package/dist/index.js +123 -3
- package/dist/nav/index.d.ts +83 -0
- package/dist/nav/index.js +9 -0
- package/dist/nav/registry.d.ts +15 -0
- package/dist/nav/registry.js +42 -0
- package/dist/nav/resolver.d.ts +8 -0
- package/dist/nav/resolver.js +192 -0
- package/dist/nav/workspace.d.ts +18 -0
- package/dist/nav/workspace.js +103 -0
- package/dist/pipeline/styles.js +7 -35
- package/dist/pipeline/token-transformers.d.ts +45 -0
- package/dist/pipeline/token-transformers.js +71 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +17 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.js +87 -0
- package/dist/registry/asset-storage/define.d.ts +10 -0
- package/dist/registry/asset-storage/define.js +13 -0
- package/dist/registry/asset-storage/resolve.d.ts +45 -0
- package/dist/registry/asset-storage/resolve.js +36 -0
- package/dist/registry/asset-storage/types.d.ts +51 -0
- package/dist/registry/asset-storage/types.js +9 -0
- package/dist/registry/assets/layout.d.ts +22 -0
- package/dist/registry/assets/layout.js +30 -0
- package/dist/registry/assets/sets.d.ts +18 -0
- package/dist/registry/assets/sets.js +44 -0
- package/dist/registry/assets/transfer.d.ts +45 -0
- package/dist/registry/assets/transfer.js +11 -0
- package/dist/registry/checkout/assets.d.ts +16 -0
- package/dist/registry/checkout/assets.js +150 -0
- package/dist/registry/checkout/index.d.ts +33 -0
- package/dist/registry/checkout/index.js +610 -0
- package/dist/registry/checkout/tokens.d.ts +15 -0
- package/dist/registry/checkout/tokens.js +171 -0
- package/dist/registry/client.d.ts +61 -0
- package/dist/registry/client.js +211 -0
- package/dist/registry/connection.d.ts +30 -0
- package/dist/registry/connection.js +36 -0
- package/dist/registry/db/adapter.d.ts +43 -0
- package/dist/registry/db/adapter.js +59 -0
- package/dist/registry/db/client.d.ts +35 -0
- package/dist/registry/db/client.js +94 -0
- package/dist/registry/db/index.d.ts +5 -0
- package/dist/registry/db/index.js +24 -0
- package/dist/registry/db/migrate.d.ts +23 -0
- package/dist/registry/db/migrate.js +64 -0
- package/dist/registry/db/schema.d.ts +4869 -0
- package/dist/registry/db/schema.js +347 -0
- package/dist/registry/path.d.ts +4 -0
- package/dist/registry/path.js +31 -0
- package/dist/registry/publish/artifacts.d.ts +16 -0
- package/dist/registry/publish/artifacts.js +132 -0
- package/dist/registry/publish/assets.d.ts +16 -0
- package/dist/registry/publish/assets.js +129 -0
- package/dist/registry/publish/errors.d.ts +5 -0
- package/dist/registry/publish/errors.js +26 -0
- package/dist/registry/publish/index.d.ts +27 -0
- package/dist/registry/publish/index.js +126 -0
- package/dist/registry/publish/package.d.ts +27 -0
- package/dist/registry/publish/package.js +257 -0
- package/dist/registry/publish/publish-build.d.ts +12 -0
- package/dist/registry/publish/publish-build.js +43 -0
- package/dist/registry/publish/tokens.d.ts +18 -0
- package/dist/registry/publish/tokens.js +112 -0
- package/dist/registry/tokens/sets.d.ts +59 -0
- package/dist/registry/tokens/sets.js +109 -0
- package/dist/registry/tokens/transfer.d.ts +48 -0
- package/dist/registry/tokens/transfer.js +10 -0
- package/dist/registry/transfer.d.ts +75 -0
- package/dist/registry/transfer.js +11 -0
- package/dist/store/filesystem-assets.d.ts +17 -0
- package/dist/store/filesystem-assets.js +147 -0
- package/dist/store/filesystem.d.ts +76 -0
- package/dist/store/filesystem.js +208 -0
- package/dist/store/index.d.ts +20 -0
- package/dist/store/index.js +38 -0
- package/dist/store/registry.d.ts +77 -0
- package/dist/store/registry.js +350 -0
- package/dist/store/source-files.d.ts +24 -0
- package/dist/store/source-files.js +147 -0
- package/dist/store/types.d.ts +185 -0
- package/dist/store/types.js +12 -0
- package/dist/transformers/plugins/csf-render.js +17 -8
- package/dist/transformers/plugins/handlebars-previews.js +10 -3
- package/dist/transformers/plugins/ssr-render.js +116 -49
- package/dist/transformers/preview/component/api.js +3 -1
- package/dist/transformers/preview/component/artifacts.js +8 -1
- package/dist/transformers/preview/component/builder.js +13 -5
- package/dist/transformers/preview/component/css.js +9 -1
- package/dist/transformers/preview/component/javascript.d.ts +9 -3
- package/dist/transformers/preview/component/javascript.js +45 -17
- package/dist/transformers/preview/component/shared-artifacts.d.ts +83 -0
- package/dist/transformers/preview/component/shared-artifacts.js +92 -0
- package/dist/transformers/preview/component.js +5 -2
- package/dist/transformers/preview/pattern/builder.js +28 -9
- package/dist/transformers/preview/pattern/html.d.ts +13 -10
- package/dist/transformers/preview/pattern/html.js +62 -27
- package/dist/transformers/preview/types.d.ts +38 -0
- package/dist/transformers/utils/handlebars.d.ts +3 -0
- package/dist/transformers/utils/handlebars.js +19 -5
- package/dist/types/config.d.ts +118 -25
- package/dist/utils/menu-shell.d.ts +60 -0
- package/dist/utils/menu-shell.js +221 -0
- package/dist/utils/stable-stringify.d.ts +2 -0
- package/dist/utils/stable-stringify.js +8 -0
- package/docs/cli.md +4 -2
- package/drizzle/0000_init_registry_storage.sql +95 -0
- package/drizzle/0001_add_review_metadata.sql +2 -0
- package/drizzle/0002_add_pages.sql +28 -0
- package/drizzle/0003_add_tokens.sql +29 -0
- package/drizzle/0004_add_assets.sql +37 -0
- package/drizzle/meta/0000_snapshot.json +642 -0
- package/drizzle/meta/0001_snapshot.json +654 -0
- package/drizzle/meta/0002_snapshot.json +835 -0
- package/drizzle/meta/0003_snapshot.json +1023 -0
- package/drizzle/meta/0004_snapshot.json +1252 -0
- package/drizzle/meta/_journal.json +41 -0
- package/drizzle.config.ts +16 -0
- package/package.json +10 -1
- package/src/app/components/Component/ComponentLists.tsx +102 -102
- package/src/app/components/Component/Preview.tsx +3 -2
- package/src/app/components/Component/RegistryActions.tsx +150 -0
- package/src/app/components/Foundations/DisplayLogo.tsx +5 -3
- package/src/app/components/Layout/Header.tsx +21 -3
- package/src/app/components/Layout/Main.tsx +11 -3
- package/src/app/components/Navigation/MainNav.tsx +4 -2
- package/src/app/components/Navigation/MobileNav.tsx +4 -2
- package/src/app/components/Navigation/SideNav.tsx +46 -11
- package/src/app/components/NotFound.tsx +24 -0
- package/src/app/components/context/NavProvider.tsx +71 -0
- package/src/app/components/old/Header.tsx +3 -3
- package/src/app/components/util/index.ts +203 -380
- package/src/app/components/util/useCollectionAssets.ts +125 -0
- package/src/app/components/util/useFoundationTokens.ts +67 -0
- package/src/app/generated/default-pages.json +1 -0
- package/src/app/generated/nav-shell.json +1 -0
- package/src/app/lib/api/query.ts +6 -0
- package/src/app/lib/api/redact.ts +46 -0
- package/src/app/lib/asset-storage.ts +81 -0
- package/src/app/lib/docs-api/artifacts.ts +116 -0
- package/src/app/lib/docs-api/backend.ts +151 -0
- package/src/app/lib/docs-api/errors.ts +52 -0
- package/src/app/lib/docs-api/handler.ts +34 -0
- package/src/app/lib/docs-api/index.ts +7 -0
- package/src/app/lib/docs-api/records.ts +189 -0
- package/src/app/lib/docs-api/registry.ts +130 -0
- package/src/app/lib/docs-api/runtime-config.ts +173 -0
- package/src/app/lib/docs-api/serve.ts +37 -0
- package/src/app/lib/docs-api/token-detail.ts +19 -0
- package/src/app/lib/registry-api/allowlist.ts +163 -0
- package/src/app/lib/registry-api/asset-transfer.ts +363 -0
- package/src/app/lib/registry-api/auth.ts +68 -0
- package/src/app/lib/registry-api/entity-routes.ts +197 -0
- package/src/app/lib/registry-api/errors.ts +51 -0
- package/src/app/lib/registry-api/files.ts +135 -0
- package/src/app/lib/registry-api/handler.ts +96 -0
- package/src/app/lib/registry-api/index.ts +4 -0
- package/src/app/lib/registry-api/meta.ts +87 -0
- package/src/app/lib/registry-api/revalidate.ts +36 -0
- package/src/app/lib/registry-api/store.ts +348 -0
- package/src/app/lib/registry-api/token-transfer.ts +231 -0
- package/src/app/lib/registry-api/transfer.ts +513 -0
- package/src/app/lib/registry-api/validation.ts +74 -0
- package/src/app/lib/registry-connection.ts +58 -0
- package/src/app/lib/utils.ts +14 -0
- package/src/app/next.config.mjs +72 -13
- package/src/app/pages/[...slug]/index.tsx +74 -20
- package/src/app/pages/_app.tsx +5 -2
- package/src/app/pages/api/docs/artifacts/[...artifactPath].ts +16 -0
- package/src/app/pages/api/docs/assets/[collection]/[...path].ts +50 -0
- package/src/app/pages/api/docs/assets/[collection]/index.ts +22 -0
- package/src/app/pages/api/docs/components/[id]/assets/[asset].ts +33 -0
- package/src/app/pages/api/docs/components/[id]/inspect/[previewId].ts +20 -0
- package/src/app/pages/api/docs/components/[id]/previews/[previewId].ts +21 -0
- package/src/app/pages/api/docs/components/[id].ts +24 -0
- package/src/app/pages/api/docs/components.json.ts +12 -0
- package/src/app/pages/api/docs/nav.json.ts +48 -0
- package/src/app/pages/api/docs/patterns/[id]/preview.ts +19 -0
- package/src/app/pages/api/docs/patterns/[id].ts +24 -0
- package/src/app/pages/api/docs/patterns.json.ts +12 -0
- package/src/app/pages/api/docs/tokens/components/[id].ts +30 -0
- package/src/app/pages/api/docs/tokens/foundations/[type].ts +32 -0
- package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/components/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/components/[id].ts +20 -0
- package/src/app/pages/api/registry/components/index.ts +19 -0
- package/src/app/pages/api/registry/health.ts +20 -0
- package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/patterns/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/patterns/[id].ts +20 -0
- package/src/app/pages/api/registry/patterns/index.ts +19 -0
- package/src/app/pages/api/registry/transfer/[kind]/[id].ts +39 -0
- package/src/app/pages/api/registry/transfer/assets/[collection].ts +24 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +23 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +11 -0
- package/src/app/pages/api/registry/transfer/assets/index.ts +10 -0
- package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +26 -0
- package/src/app/pages/api/registry/transfer/tokens/index.ts +11 -0
- package/src/app/pages/assets/fonts.tsx +41 -17
- package/src/app/pages/assets/index.tsx +2 -2
- package/src/app/pages/assets/logos.tsx +2 -2
- package/src/app/pages/foundations/colors.tsx +18 -8
- package/src/app/pages/foundations/effects.tsx +18 -8
- package/src/app/pages/foundations/icons/[name]/index.tsx +26 -14
- package/src/app/pages/foundations/icons/index.tsx +13 -9
- package/src/app/pages/foundations/index.tsx +1 -1
- package/src/app/pages/foundations/logo.tsx +7 -4
- package/src/app/pages/foundations/typography.tsx +19 -9
- package/src/app/pages/index.tsx +1 -1
- package/src/app/pages/system/component/[component]/index.tsx +87 -17
- package/src/app/pages/system/index.tsx +13 -4
- package/src/app/pages/system/pattern/[pattern]/index.tsx +70 -14
- package/src/app/pages/system/tokens/components/[component]/index.tsx +42 -14
- package/src/app/pages/system/tokens/foundations/colors.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/effects.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/typography.tsx +8 -4
- package/src/app/tsconfig.json +2 -3
- package/src/app-builder/build.ts +555 -11
- package/src/app-builder/client-config.ts +61 -4
- package/src/app-builder/config-diff/snapshot.ts +1 -11
- package/src/app-builder/index.ts +1 -1
- package/src/app-builder/nav-shell.ts +74 -0
- package/src/app-builder/static-export.ts +215 -0
- package/src/app-builder/vercel-output.ts +297 -0
- package/src/app-builder/watchers/utils.ts +2 -0
- package/src/app-builder/watchers.ts +149 -25
- package/src/app-builder/websocket.ts +2 -2
- package/src/artifacts/README.md +14 -0
- package/src/artifacts/index.ts +17 -0
- package/src/artifacts/types.ts +146 -0
- package/src/artifacts/url.ts +90 -0
- package/src/cache/build-cache.ts +47 -2
- package/src/commands/build/app.ts +1 -2
- package/src/commands/build/index.ts +61 -0
- package/src/commands/checkout/index.ts +66 -0
- package/src/commands/db/migrate.ts +25 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/publish/index.ts +63 -0
- package/src/config/defaults.ts +15 -0
- package/src/config/loader.ts +10 -1
- package/src/config/normalizers/declaration.ts +3 -0
- package/src/config/normalizers/page.ts +45 -0
- package/src/config/normalizers/pattern.ts +4 -1
- package/src/config/runtime.ts +48 -2
- package/src/index.ts +126 -3
- package/src/nav/index.ts +94 -0
- package/src/nav/registry.ts +54 -0
- package/src/nav/resolver.ts +217 -0
- package/src/nav/workspace.ts +87 -0
- package/src/pipeline/styles.ts +4 -35
- package/src/pipeline/token-transformers.ts +97 -0
- package/src/registry/asset-storage/adapters/vercel-blob.ts +51 -0
- package/src/registry/asset-storage/define.ts +15 -0
- package/src/registry/asset-storage/resolve.ts +72 -0
- package/src/registry/asset-storage/types.ts +46 -0
- package/src/registry/assets/layout.ts +38 -0
- package/src/registry/assets/sets.ts +48 -0
- package/src/registry/assets/transfer.ts +50 -0
- package/src/registry/checkout/assets.ts +126 -0
- package/src/registry/checkout/index.ts +596 -0
- package/src/registry/checkout/tokens.ts +152 -0
- package/src/registry/client.ts +234 -0
- package/src/registry/connection.ts +50 -0
- package/src/registry/db/adapter.ts +74 -0
- package/src/registry/db/client.ts +88 -0
- package/src/registry/db/index.ts +35 -0
- package/src/registry/db/migrate.ts +56 -0
- package/src/registry/db/schema.ts +444 -0
- package/src/registry/path.ts +26 -0
- package/src/registry/publish/artifacts.ts +147 -0
- package/src/registry/publish/assets.ts +129 -0
- package/src/registry/publish/errors.ts +34 -0
- package/src/registry/publish/index.ts +128 -0
- package/src/registry/publish/package.ts +272 -0
- package/src/registry/publish/publish-build.ts +51 -0
- package/src/registry/publish/tokens.ts +119 -0
- package/src/registry/tokens/sets.ts +132 -0
- package/src/registry/tokens/transfer.ts +53 -0
- package/src/registry/transfer.ts +82 -0
- package/src/store/filesystem-assets.ts +152 -0
- package/src/store/filesystem.ts +237 -0
- package/src/store/index.ts +62 -0
- package/src/store/registry.ts +334 -0
- package/src/store/source-files.ts +155 -0
- package/src/store/types.ts +210 -0
- package/src/transformers/plugins/csf-render.ts +31 -7
- package/src/transformers/plugins/handlebars-previews.ts +22 -3
- package/src/transformers/plugins/ssr-render.ts +145 -57
- package/src/transformers/preview/component/api.ts +2 -1
- package/src/transformers/preview/component/artifacts.ts +10 -1
- package/src/transformers/preview/component/builder.ts +9 -2
- package/src/transformers/preview/component/css.ts +10 -1
- package/src/transformers/preview/component/javascript.ts +55 -20
- package/src/transformers/preview/component/shared-artifacts.ts +146 -0
- package/src/transformers/preview/component.ts +5 -2
- package/src/transformers/preview/pattern/builder.ts +33 -7
- package/src/transformers/preview/pattern/html.ts +71 -27
- package/src/transformers/preview/types.ts +42 -0
- package/src/transformers/utils/handlebars.ts +31 -5
- package/src/types/config.ts +123 -25
- package/src/utils/menu-shell.ts +241 -0
- package/src/utils/stable-stringify.ts +7 -0
- package/templates/default/package.json.tpl +1 -1
- package/handoff.config.js +0 -90
- package/src/app/components/ComponentSearch.tsx +0 -93
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "7",
|
|
3
|
+
"dialect": "postgresql",
|
|
4
|
+
"entries": [
|
|
5
|
+
{
|
|
6
|
+
"idx": 0,
|
|
7
|
+
"version": "7",
|
|
8
|
+
"when": 1781979217509,
|
|
9
|
+
"tag": "0000_init_registry_storage",
|
|
10
|
+
"breakpoints": true
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idx": 1,
|
|
14
|
+
"version": "7",
|
|
15
|
+
"when": 1782045350977,
|
|
16
|
+
"tag": "0001_add_review_metadata",
|
|
17
|
+
"breakpoints": true
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"idx": 2,
|
|
21
|
+
"version": "7",
|
|
22
|
+
"when": 1782713759115,
|
|
23
|
+
"tag": "0002_add_pages",
|
|
24
|
+
"breakpoints": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"idx": 3,
|
|
28
|
+
"version": "7",
|
|
29
|
+
"when": 1783354008332,
|
|
30
|
+
"tag": "0003_add_tokens",
|
|
31
|
+
"breakpoints": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"idx": 4,
|
|
35
|
+
"version": "7",
|
|
36
|
+
"when": 1783442253186,
|
|
37
|
+
"tag": "0004_add_assets",
|
|
38
|
+
"breakpoints": true
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'drizzle-kit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drizzle Kit config for generating the registry's package-owned migrations.
|
|
5
|
+
*
|
|
6
|
+
* Migrations are generated into `./drizzle` and shipped with the `handoff-app` package; the
|
|
7
|
+
* bundled runner (`handoff-app db:migrate`) applies them at install/deploy time. Both built-in
|
|
8
|
+
* adapters (`pg`, `neon`) target the Postgres dialect, so a single migration set serves both.
|
|
9
|
+
*
|
|
10
|
+
* Generate after schema changes with: `npx drizzle-kit generate`
|
|
11
|
+
*/
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
dialect: 'postgresql',
|
|
14
|
+
schema: './src/registry/db/schema.ts',
|
|
15
|
+
out: './drizzle',
|
|
16
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "handoff-app",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-0",
|
|
4
4
|
"description": "Automated documentation toolchain for building client side documentation from figma",
|
|
5
5
|
"author ": {
|
|
6
6
|
"name": "Convertiv",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@clack/prompts": "^0.7.0",
|
|
56
|
+
"@neondatabase/serverless": "^1.1.0",
|
|
56
57
|
"@radix-ui/react-accordion": "^1.2.8",
|
|
57
58
|
"@radix-ui/react-collapsible": "^1.1.2",
|
|
58
59
|
"@radix-ui/react-dialog": "^1.1.4",
|
|
@@ -78,6 +79,7 @@
|
|
|
78
79
|
"@types/react-syntax-highlighter": "^15.5.7",
|
|
79
80
|
"@types/refractor": "^3.0.2",
|
|
80
81
|
"@types/ws": "^8.18.1",
|
|
82
|
+
"@vercel/blob": "^1.1.1",
|
|
81
83
|
"@vitejs/plugin-react": "^4.5.2",
|
|
82
84
|
"archiver": "^5.3.1",
|
|
83
85
|
"chalk": "^4.1.2",
|
|
@@ -86,6 +88,7 @@
|
|
|
86
88
|
"clsx": "^2.1.1",
|
|
87
89
|
"cross-spawn": "^7.0.3",
|
|
88
90
|
"dotenv": "^16.0.3",
|
|
91
|
+
"drizzle-orm": "^0.45.2",
|
|
89
92
|
"esbuild": "^0.25.0",
|
|
90
93
|
"eslint": "^8.23.1",
|
|
91
94
|
"eslint-config-next": "^15.3.4",
|
|
@@ -101,6 +104,7 @@
|
|
|
101
104
|
"next": "^15.3.4",
|
|
102
105
|
"next-themes": "^0.4.4",
|
|
103
106
|
"node-html-parser": "^6.1.4",
|
|
107
|
+
"pg": "^8.22.0",
|
|
104
108
|
"postcss": "^8.4.16",
|
|
105
109
|
"prettier": "^3.4.2",
|
|
106
110
|
"react": "^19.1.0",
|
|
@@ -121,13 +125,18 @@
|
|
|
121
125
|
"ws": "^8.18.3",
|
|
122
126
|
"yargs": "^17.7.2"
|
|
123
127
|
},
|
|
128
|
+
"peerDependencies": {
|
|
129
|
+
"typescript": ">=5.6 <7"
|
|
130
|
+
},
|
|
124
131
|
"devDependencies": {
|
|
125
132
|
"@types/archiver": "^5.3.1",
|
|
126
133
|
"@types/cross-spawn": "^6.0.2",
|
|
127
134
|
"@types/fs-extra": "^11.0.4",
|
|
128
135
|
"@types/handlebars": "^4.1.0",
|
|
129
136
|
"@types/node": "^20.11.18",
|
|
137
|
+
"@types/pg": "^8.20.0",
|
|
130
138
|
"@types/yargs": "^17.0.33",
|
|
139
|
+
"drizzle-kit": "^0.31.10",
|
|
131
140
|
"np": "^10.2.0",
|
|
132
141
|
"prettier-plugin-tailwindcss": "^0.7.0",
|
|
133
142
|
"sass-embedded": "^1.89.0",
|
|
@@ -76,114 +76,114 @@ export const ComponentList = ({
|
|
|
76
76
|
return (
|
|
77
77
|
<TooltipProvider delayDuration={0}>
|
|
78
78
|
<div className="mx-auto w-full mb-4">
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
79
|
+
<div className="flex justify-between bg-accent rounded-xl mb-8">
|
|
80
|
+
<div className="mr-auto flex items-center gap-3 bg-accent p-2.5 rounded-md">
|
|
81
|
+
<div className="relative">
|
|
82
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-gray-400" />
|
|
83
|
+
<Input
|
|
84
|
+
type="text"
|
|
85
|
+
placeholder="Search components..."
|
|
86
|
+
className="rounded-md pl-9 pr-3 py-1 text-sm border-none shadow-none"
|
|
87
|
+
onChange={e => setSearch(e.currentTarget.value)}
|
|
88
|
+
aria-label="Search components"
|
|
89
|
+
style={{ minWidth: 200 }}
|
|
90
|
+
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
93
|
|
|
94
|
-
</div>
|
|
95
|
-
<div className="flex justify-end p-2.5 gap-2.5">
|
|
96
|
-
<div className="flex items-center gap-0.5 border border-input bg-background shadow-xs hover:text-accent-foreground p-0.5 rounded-md">
|
|
97
|
-
<Select
|
|
98
|
-
value={category}
|
|
99
|
-
onValueChange={val => {
|
|
100
|
-
setCategory(val);
|
|
101
|
-
}}
|
|
102
|
-
aria-label="Filter by category"
|
|
103
|
-
>
|
|
104
|
-
<SelectTrigger className="rounded-md [&_span]:text-xs [&_svg]:ml-2.5 [&_svg]:size-3 py-1 px-2 pl-3 text-sm border-none shadow-none bg-transparent focus:border-blue-400 focus:outline-none">
|
|
105
|
-
<SelectValue placeholder="All Categories" />
|
|
106
|
-
</SelectTrigger>
|
|
107
|
-
<SelectContent>
|
|
108
|
-
<SelectItem value="all">All Categories</SelectItem>
|
|
109
|
-
{categories.map((category) => (
|
|
110
|
-
<SelectItem key={category} value={category}>
|
|
111
|
-
{category}
|
|
112
|
-
</SelectItem>
|
|
113
|
-
))}
|
|
114
|
-
</SelectContent>
|
|
115
|
-
</Select>
|
|
116
|
-
|
|
117
|
-
<hr className="h-7 w-px bg-input" />
|
|
118
|
-
|
|
119
|
-
<Tooltip>
|
|
120
|
-
<TooltipTrigger asChild>
|
|
121
|
-
<Button variant="ghost" className={cn("w-9", groupBy && 'bg-accent text-accent-foreground')} onClick={() => setGroupBy(!groupBy)}>
|
|
122
|
-
<Rows className="size-3.5" strokeWidth={1.5} />
|
|
123
|
-
<span className="sr-only">Group by category</span>
|
|
124
|
-
</Button>
|
|
125
|
-
</TooltipTrigger>
|
|
126
|
-
<TooltipContent>
|
|
127
|
-
Group by category
|
|
128
|
-
</TooltipContent>
|
|
129
|
-
</Tooltip>
|
|
130
94
|
</div>
|
|
131
|
-
<
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
95
|
+
<div className="flex justify-end p-2.5 gap-2.5">
|
|
96
|
+
<div className="flex items-center gap-0.5 border border-input bg-background shadow-xs hover:text-accent-foreground p-0.5 rounded-md">
|
|
97
|
+
<Select
|
|
98
|
+
value={category}
|
|
99
|
+
onValueChange={val => {
|
|
100
|
+
setCategory(val);
|
|
101
|
+
}}
|
|
102
|
+
aria-label="Filter by category"
|
|
103
|
+
>
|
|
104
|
+
<SelectTrigger className="rounded-md [&_span]:text-xs [&_svg]:ml-2.5 [&_svg]:size-3 py-1 px-2 pl-3 text-sm border-none shadow-none bg-transparent focus:border-blue-400 focus:outline-none">
|
|
105
|
+
<SelectValue placeholder="All Categories" />
|
|
106
|
+
</SelectTrigger>
|
|
107
|
+
<SelectContent>
|
|
108
|
+
<SelectItem value="all">All Categories</SelectItem>
|
|
109
|
+
{categories.filter((category) => category !== '').map((category) => (
|
|
110
|
+
<SelectItem key={category} value={category}>
|
|
111
|
+
{category}
|
|
112
|
+
</SelectItem>
|
|
113
|
+
))}
|
|
114
|
+
</SelectContent>
|
|
115
|
+
</Select>
|
|
116
|
+
|
|
117
|
+
<hr className="h-7 w-px bg-input" />
|
|
118
|
+
|
|
119
|
+
<Tooltip>
|
|
120
|
+
<TooltipTrigger asChild>
|
|
121
|
+
<Button variant="ghost" className={cn("w-9", groupBy && 'bg-accent text-accent-foreground')} onClick={() => setGroupBy(!groupBy)}>
|
|
122
|
+
<Rows className="size-3.5" strokeWidth={1.5} />
|
|
123
|
+
<span className="sr-only">Group by category</span>
|
|
124
|
+
</Button>
|
|
125
|
+
</TooltipTrigger>
|
|
126
|
+
<TooltipContent>
|
|
127
|
+
Group by category
|
|
128
|
+
</TooltipContent>
|
|
129
|
+
</Tooltip>
|
|
130
|
+
</div>
|
|
131
|
+
<ToggleGroup className='flex gap-0.5 border border-input bg-background shadow-xs hover:text-accent-foreground p-0.5 rounded-md' type="single" value={layout} onValueChange={(value) => storeLayout(value)}>
|
|
132
|
+
<Tooltip>
|
|
133
|
+
<TooltipTrigger asChild>
|
|
134
|
+
<ToggleGroupItem className={cn("size-9 py-2 px-2 flex items-center justify-center hover:bg-accent rounded-md", layout === 'grid' && 'bg-accent text-accent-foreground')} value="grid" aria-label="Grid layout" >
|
|
135
|
+
<LayoutGrid className="size-3.5" strokeWidth={1.5} />
|
|
136
|
+
</ToggleGroupItem>
|
|
137
|
+
</TooltipTrigger>
|
|
138
|
+
<TooltipContent>
|
|
139
|
+
Grid layout
|
|
140
|
+
</TooltipContent>
|
|
141
|
+
</Tooltip>
|
|
142
|
+
<Tooltip>
|
|
143
|
+
<TooltipTrigger asChild>
|
|
144
|
+
<ToggleGroupItem className={cn("size-9 py-2 px-2 flex items-center justify-center hover:bg-accent rounded-md", layout === 'single' && 'bg-accent text-accent-foreground')} value="single" aria-label="Single column layout" >
|
|
145
|
+
<AlignJustify className="size-3.5" strokeWidth={1.5} />
|
|
146
|
+
</ToggleGroupItem>
|
|
147
|
+
</TooltipTrigger>
|
|
148
|
+
<TooltipContent>
|
|
149
|
+
Column layout
|
|
150
|
+
</TooltipContent>
|
|
151
|
+
</Tooltip>
|
|
152
|
+
</ToggleGroup>
|
|
153
|
+
</div>
|
|
153
154
|
</div>
|
|
154
|
-
</div>
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
156
|
+
{groupBy ? (
|
|
157
|
+
<>
|
|
158
|
+
{Object.keys(groupedList).map((group) => {
|
|
159
|
+
return (
|
|
160
|
+
<div key={`group-${group}`} className="mb-8">
|
|
161
|
+
<HeadersType.H3 className="mb-4 text-lg font-medium border-b border-gray-200 pb-2">{group}</HeadersType.H3>
|
|
162
|
+
<div
|
|
163
|
+
className={cn(
|
|
164
|
+
'grid',
|
|
165
|
+
layout === 'grid' ? 'grid-cols-1 gap-10 min-[800px]:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4' : 'grid-cols-1 gap-2'
|
|
166
|
+
)}
|
|
167
|
+
>
|
|
168
|
+
{groupedList[group].map((component) => {
|
|
169
|
+
return <ComponentsPageCard key={`component-${component.id}`} component={component} layout={layout} />;
|
|
170
|
+
})}
|
|
171
|
+
</div>
|
|
171
172
|
</div>
|
|
172
|
-
|
|
173
|
-
)
|
|
173
|
+
);
|
|
174
|
+
})}
|
|
175
|
+
</>
|
|
176
|
+
) : (
|
|
177
|
+
<div
|
|
178
|
+
className={cn(
|
|
179
|
+
'grid',
|
|
180
|
+
layout === 'grid' ? 'grid-cols-1 gap-10 min-[800px]:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4' : 'grid-cols-1 gap-2'
|
|
181
|
+
)}
|
|
182
|
+
> {list.map((component) => {
|
|
183
|
+
return <ComponentsPageCard key={`component-${component.id}`} component={component} layout={layout} />;
|
|
174
184
|
})}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
<div
|
|
178
|
-
className={cn(
|
|
179
|
-
'grid',
|
|
180
|
-
layout === 'grid' ? 'grid-cols-1 gap-10 min-[800px]:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4' : 'grid-cols-1 gap-2'
|
|
181
|
-
)}
|
|
182
|
-
> {list.map((component) => {
|
|
183
|
-
return <ComponentsPageCard key={`component-${component.id}`} component={component} layout={layout} />;
|
|
184
|
-
})}
|
|
185
|
-
</div>
|
|
186
|
-
)}
|
|
185
|
+
</div>
|
|
186
|
+
)}
|
|
187
187
|
</div>
|
|
188
188
|
</TooltipProvider>
|
|
189
189
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildArtifactUrl } from '@handoff/artifacts/url';
|
|
1
2
|
import { SlotMetadata } from '@handoff/transformers/preview/component';
|
|
2
3
|
import { PageSlice } from '@handoff/transformers/preview/types';
|
|
3
4
|
import { PreviewObject } from '@handoff/types/preview';
|
|
@@ -314,7 +315,7 @@ export const ComponentDisplay: React.FC<{
|
|
|
314
315
|
className="h-7 px-3 hover:bg-gray-300 [&_svg]:size-3"
|
|
315
316
|
onClick={() => {
|
|
316
317
|
// open in new tab
|
|
317
|
-
window.open(
|
|
318
|
+
window.open(buildArtifactUrl(`component/${previewUrl}`, process.env.HANDOFF_APP_BASE_PATH ?? ''), '_blank');
|
|
318
319
|
}}
|
|
319
320
|
variant="ghost"
|
|
320
321
|
>
|
|
@@ -344,7 +345,7 @@ export const ComponentDisplay: React.FC<{
|
|
|
344
345
|
display: 'block',
|
|
345
346
|
margin: '0 auto',
|
|
346
347
|
}}
|
|
347
|
-
src={
|
|
348
|
+
src={buildArtifactUrl(`component/${previewUrl}`, process.env.HANDOFF_APP_BASE_PATH ?? '')}
|
|
348
349
|
/>
|
|
349
350
|
</div>
|
|
350
351
|
) : (
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { CheckIcon, CopyIcon, Download } from 'lucide-react';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import type { RuntimeMode } from '@handoff/types/config';
|
|
6
|
+
import { cn } from '../../lib/utils';
|
|
7
|
+
import { Button } from '../ui/button';
|
|
8
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
|
9
|
+
|
|
10
|
+
type RegistryEntityType = 'component' | 'pattern';
|
|
11
|
+
|
|
12
|
+
interface RegistryActionsProps {
|
|
13
|
+
/** Resolved runtime mode projected to the client. */
|
|
14
|
+
mode: RuntimeMode;
|
|
15
|
+
/** Whether this is a connected workspace (workspace mode + resolvable registry URL). */
|
|
16
|
+
connected: boolean;
|
|
17
|
+
/** The kind of entity the page documents. */
|
|
18
|
+
entityType: RegistryEntityType;
|
|
19
|
+
/** Stable id of the component/pattern, used in the copyable command. */
|
|
20
|
+
id: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type RegistryAction = {
|
|
24
|
+
label: string;
|
|
25
|
+
title: string;
|
|
26
|
+
description: React.ReactNode;
|
|
27
|
+
command: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Registry transfer affordance for the single component/pattern page. Surfaces a context-appropriate
|
|
32
|
+
* helper based on the runtime:
|
|
33
|
+
* - registry mode -> "Checkout" instructions (pull the entity into a connected workspace);
|
|
34
|
+
* - connected workspace (workspace mode + registry URL) -> "Publish" instructions.
|
|
35
|
+
*
|
|
36
|
+
* A static export is never a live runtime, so the publish affordance is suppressed there using the
|
|
37
|
+
* same `HANDOFF_BUILD_TARGET` gate the runtime-mode badge uses. Renders nothing when no action
|
|
38
|
+
* applies (e.g. a plain, unconnected workspace).
|
|
39
|
+
*/
|
|
40
|
+
export const RegistryActions: React.FC<RegistryActionsProps> = ({ mode, connected, entityType, id }) => {
|
|
41
|
+
// The static export is a frozen snapshot, not a live workspace, so the publish hint carries no
|
|
42
|
+
// meaning there and is omitted (mirrors the runtime-mode badge in the header).
|
|
43
|
+
const isStaticSnapshot = process.env.HANDOFF_BUILD_TARGET === 'static';
|
|
44
|
+
|
|
45
|
+
const action = resolveAction({ mode, connected, entityType, id, isStaticSnapshot });
|
|
46
|
+
if (!action) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Popover>
|
|
52
|
+
<PopoverTrigger asChild>
|
|
53
|
+
<Button variant="outline" size="sm" className="font-normal [&_svg]:size-3!">
|
|
54
|
+
<Download aria-hidden="true" />
|
|
55
|
+
{action.label}
|
|
56
|
+
</Button>
|
|
57
|
+
</PopoverTrigger>
|
|
58
|
+
<PopoverContent align="start" className="w-[360px] p-4">
|
|
59
|
+
<div className="flex flex-col gap-3">
|
|
60
|
+
<div className="flex items-center gap-2">
|
|
61
|
+
<Download className="size-4 text-gray-700 dark:text-gray-200" aria-hidden="true" />
|
|
62
|
+
<p className="text-sm font-semibold text-gray-900 dark:text-gray-100">{action.title}</p>
|
|
63
|
+
</div>
|
|
64
|
+
<p className="text-sm text-muted-foreground">{action.description}</p>
|
|
65
|
+
<div className="border-t" />
|
|
66
|
+
<div className="flex items-stretch overflow-hidden rounded-md border">
|
|
67
|
+
<code className="flex flex-1 items-center truncate px-3 py-2 font-mono text-xs text-gray-700 dark:text-gray-300">
|
|
68
|
+
{action.command}
|
|
69
|
+
</code>
|
|
70
|
+
<CopyCommandButton command={action.command} />
|
|
71
|
+
</div>
|
|
72
|
+
<p className="text-xs text-muted-foreground">Requires a workspace configured with registry access.</p>
|
|
73
|
+
</div>
|
|
74
|
+
</PopoverContent>
|
|
75
|
+
</Popover>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** Copy-to-clipboard control for the command box. No tooltip — just a copy/checkmark toggle. */
|
|
80
|
+
const CopyCommandButton: React.FC<{ command: string }> = ({ command }) => {
|
|
81
|
+
const [copied, setCopied] = React.useState(false);
|
|
82
|
+
|
|
83
|
+
const handleCopy = async () => {
|
|
84
|
+
try {
|
|
85
|
+
await navigator.clipboard.writeText(command);
|
|
86
|
+
setCopied(true);
|
|
87
|
+
setTimeout(() => setCopied(false), 1500);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error('Failed to copy command: ', err);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Button
|
|
95
|
+
type="button"
|
|
96
|
+
variant="ghost"
|
|
97
|
+
size="icon"
|
|
98
|
+
onClick={handleCopy}
|
|
99
|
+
aria-label={copied ? 'Copied' : 'Copy command'}
|
|
100
|
+
className="relative h-auto w-9 self-stretch rounded-none border-0 border-l shadow-none"
|
|
101
|
+
>
|
|
102
|
+
<span className={cn('transition-all', copied ? 'scale-100 opacity-100' : 'scale-0 opacity-0')}>
|
|
103
|
+
<CheckIcon className="stroke-gray-600" size={16} aria-hidden="true" />
|
|
104
|
+
</span>
|
|
105
|
+
<span className={cn('absolute transition-all', copied ? 'scale-0 opacity-0' : 'scale-100 opacity-100')}>
|
|
106
|
+
<CopyIcon size={16} aria-hidden="true" />
|
|
107
|
+
</span>
|
|
108
|
+
</Button>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const resolveAction = ({
|
|
113
|
+
mode,
|
|
114
|
+
connected,
|
|
115
|
+
entityType,
|
|
116
|
+
id,
|
|
117
|
+
isStaticSnapshot,
|
|
118
|
+
}: RegistryActionsProps & { isStaticSnapshot: boolean }): RegistryAction | null => {
|
|
119
|
+
if (mode === 'registry') {
|
|
120
|
+
return {
|
|
121
|
+
label: 'Checkout',
|
|
122
|
+
title: `Checkout ${entityType}`,
|
|
123
|
+
description: (
|
|
124
|
+
<>
|
|
125
|
+
Run this in a connected workspace to pull <strong className="font-medium text-foreground">{id}</strong> from the
|
|
126
|
+
registry.
|
|
127
|
+
</>
|
|
128
|
+
),
|
|
129
|
+
command: `npm run checkout -- ${entityType} ${id}`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (mode === 'workspace' && connected && !isStaticSnapshot) {
|
|
134
|
+
return {
|
|
135
|
+
label: 'Publish',
|
|
136
|
+
title: `Publish ${entityType}`,
|
|
137
|
+
description: (
|
|
138
|
+
<>
|
|
139
|
+
Run this in your connected workspace to publish <strong className="font-medium text-foreground">{id}</strong> to
|
|
140
|
+
the registry.
|
|
141
|
+
</>
|
|
142
|
+
),
|
|
143
|
+
command: `npm run publish -- ${entityType} ${id}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return null;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default RegistryActions;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Types as CoreTypes } from 'handoff-core';
|
|
2
1
|
import React from 'react';
|
|
2
|
+
import type { DisplayableAsset } from '../util/useCollectionAssets';
|
|
3
3
|
|
|
4
|
-
export const DisplayLogo: React.FC<{ logo:
|
|
4
|
+
export const DisplayLogo: React.FC<{ logo: DisplayableAsset; content?: string; correct?: boolean }> = ({ logo, content }) => {
|
|
5
|
+
// Registry serves the individual asset content URL; workspace/static serves the mirrored file.
|
|
6
|
+
const src = logo.src ?? `${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/assets/logos/${logo.path}`;
|
|
5
7
|
return (
|
|
6
8
|
<div className="flex flex-col gap-5">
|
|
7
|
-
<img src={
|
|
9
|
+
<img src={src} alt="Usage Cards" className="rounded-3xl" />
|
|
8
10
|
<div className="flex flex-row gap-4">
|
|
9
11
|
<img src={`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/assets/images/check-circle.svg`} alt="Do" className="h-6 w-6" />
|
|
10
12
|
<p className="leading-normal text-gray-500">
|
|
@@ -4,11 +4,22 @@ import { ModeToggle } from '../../components/ModeSwitcher';
|
|
|
4
4
|
import { MainNav } from '../../components/Navigation/MainNav';
|
|
5
5
|
import { MobileNav } from '../../components/Navigation/MobileNav';
|
|
6
6
|
import { cn } from '../../lib/utils';
|
|
7
|
+
import { Badge } from '../ui/badge';
|
|
7
8
|
import { useConfigContext } from '../context/ConfigContext';
|
|
8
9
|
|
|
10
|
+
const RUNTIME_MODE_LABEL: Record<'workspace' | 'registry', string> = {
|
|
11
|
+
workspace: 'Workspace',
|
|
12
|
+
registry: 'Registry',
|
|
13
|
+
};
|
|
14
|
+
|
|
9
15
|
export function Header() {
|
|
10
16
|
const context = useConfigContext();
|
|
11
17
|
const [isScrolled, setIsScrolled] = useState(false);
|
|
18
|
+
const runtimeMode = context.config?.runtime?.mode ?? 'workspace';
|
|
19
|
+
const runtimeModeLabel = RUNTIME_MODE_LABEL[runtimeMode];
|
|
20
|
+
// The static export is a self-contained snapshot of the workspace, not a live runtime, so the
|
|
21
|
+
// runtime-mode badge carries no meaning there and is omitted.
|
|
22
|
+
const isStaticSnapshot = process.env.HANDOFF_BUILD_TARGET === 'static';
|
|
12
23
|
|
|
13
24
|
useEffect(() => {
|
|
14
25
|
const handleScroll = () => {
|
|
@@ -28,9 +39,16 @@ export function Header() {
|
|
|
28
39
|
>
|
|
29
40
|
<header className="border-grid container mx-auto w-full max-w-[1500px] bg-transparent px-8">
|
|
30
41
|
<div className="mx-auto flex items-center justify-between @container">
|
|
31
|
-
<
|
|
32
|
-
<
|
|
33
|
-
|
|
42
|
+
<div className="flex items-center gap-3">
|
|
43
|
+
<Link href="/">
|
|
44
|
+
<img className="max-h-5" src={`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/logo.svg`} alt={context.config?.app?.title} />
|
|
45
|
+
</Link>
|
|
46
|
+
{!isStaticSnapshot && (
|
|
47
|
+
<Badge variant={runtimeMode === 'registry' ? 'info' : 'default'} aria-label={`Runtime mode: ${runtimeModeLabel}`}>
|
|
48
|
+
{runtimeModeLabel}
|
|
49
|
+
</Badge>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
34
52
|
<div className="hidden items-center gap-4 @2xl:flex">
|
|
35
53
|
<MainNav />
|
|
36
54
|
<ModeToggle />
|
|
@@ -5,6 +5,7 @@ import { Header } from '../../components/Layout/Header';
|
|
|
5
5
|
import { ThemeProvider } from '../../components/util/theme-provider';
|
|
6
6
|
import SideNav from '../Navigation/SideNav';
|
|
7
7
|
import { ConfigContextProvider } from '../context/ConfigContext';
|
|
8
|
+
import { hasRenderableNav } from '../../lib/utils';
|
|
8
9
|
import { SidebarInset, SidebarProvider } from '../ui/sidebar';
|
|
9
10
|
import { SectionLink } from '../util';
|
|
10
11
|
|
|
@@ -21,6 +22,13 @@ interface LayoutComponentProps {
|
|
|
21
22
|
fullWidthHero?: boolean;
|
|
22
23
|
}
|
|
23
24
|
export default function Layout<LayoutComponentProps>({ children, config, menu, metadata, current, fullWidthHero = false }) {
|
|
25
|
+
// In registry mode the per-page `current` is empty for lambda-rendered (fallback) pages, so mount
|
|
26
|
+
// the sidebar regardless and let SideNav resolve its section from the cached shell (and collapse
|
|
27
|
+
// itself to full width when that section has no renderable nav). Workspace/static know the section
|
|
28
|
+
// at render time, so drop the left column entirely for pages with no side-nav content (e.g. a
|
|
29
|
+
// standalone page with no children) and let the content go full width.
|
|
30
|
+
const isRegistry = config?.runtime?.mode === 'registry';
|
|
31
|
+
const showSidebar = isRegistry || hasRenderableNav(current as SectionLink);
|
|
24
32
|
return (
|
|
25
33
|
<div>
|
|
26
34
|
<ConfigContextProvider defaultConfig={config} defaultMenu={menu}>
|
|
@@ -48,10 +56,10 @@ export default function Layout<LayoutComponentProps>({ children, config, menu, m
|
|
|
48
56
|
<div className="mx-auto w-full">{children}</div>
|
|
49
57
|
</div>
|
|
50
58
|
</div>
|
|
51
|
-
) :
|
|
59
|
+
) : showSidebar ? (
|
|
52
60
|
<SidebarProvider>
|
|
53
61
|
<div className="flex w-full">
|
|
54
|
-
<SideNav
|
|
62
|
+
<SideNav />
|
|
55
63
|
<SidebarInset className="relative bg-transparent py-8 pl-8 pr-8 md:pl-8 lg:gap-10 lg:py-16 lg:pl-16">
|
|
56
64
|
<div className="mx-auto w-full">{children}</div>
|
|
57
65
|
</SidebarInset>
|
|
@@ -59,7 +67,7 @@ export default function Layout<LayoutComponentProps>({ children, config, menu, m
|
|
|
59
67
|
</SidebarProvider>
|
|
60
68
|
) : (
|
|
61
69
|
<div className="flex w-full">
|
|
62
|
-
<div className="relative bg-transparent px-16 py-8 lg:gap-10 lg:py-16
|
|
70
|
+
<div className="relative w-full bg-transparent px-16 py-8 lg:gap-10 lg:py-16">
|
|
63
71
|
<div className="mx-auto w-full">{children}</div>
|
|
64
72
|
</div>
|
|
65
73
|
</div>
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '../../components/ui/navigation-menu';
|
|
11
11
|
import { cn } from '../../lib/utils';
|
|
12
12
|
import { useConfigContext } from '../context/ConfigContext';
|
|
13
|
+
import { useNav } from '../context/NavProvider';
|
|
13
14
|
|
|
14
15
|
const trimSlashes = (input: string): string => {
|
|
15
16
|
return input.replace(/^\/+|\/+$/g, '');
|
|
@@ -18,11 +19,12 @@ const trimSlashes = (input: string): string => {
|
|
|
18
19
|
export function MainNav() {
|
|
19
20
|
const context = useConfigContext();
|
|
20
21
|
const router = useRouter();
|
|
22
|
+
const { menu } = useNav();
|
|
21
23
|
return (
|
|
22
24
|
<NavigationMenu>
|
|
23
25
|
<NavigationMenuList>
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
+
{menu &&
|
|
27
|
+
menu.map((section) => {
|
|
26
28
|
const isActive = trimSlashes(router.asPath).startsWith(trimSlashes(section.path));
|
|
27
29
|
return (
|
|
28
30
|
<NavigationMenuItem key={section.title}>
|