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
|
@@ -7,6 +7,7 @@ import { Button } from '../../components/ui/button';
|
|
|
7
7
|
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '../../components/ui/sheet';
|
|
8
8
|
import { cn } from '../../lib/utils';
|
|
9
9
|
import { useConfigContext } from '../context/ConfigContext';
|
|
10
|
+
import { useNav } from '../context/NavProvider';
|
|
10
11
|
|
|
11
12
|
const trimSlashes = (input: string): string => {
|
|
12
13
|
return input.replace(/^\/+|\/+$/g, '');
|
|
@@ -16,6 +17,7 @@ export function MobileNav() {
|
|
|
16
17
|
const context = useConfigContext();
|
|
17
18
|
const router = useRouter();
|
|
18
19
|
const { theme, setTheme } = useTheme();
|
|
20
|
+
const { menu } = useNav();
|
|
19
21
|
|
|
20
22
|
const toggleTheme = () => {
|
|
21
23
|
if (theme === 'light') {
|
|
@@ -48,8 +50,8 @@ export function MobileNav() {
|
|
|
48
50
|
</SheetTrigger>
|
|
49
51
|
</SheetHeader>
|
|
50
52
|
<div className="mt-8 flex flex-col space-y-4">
|
|
51
|
-
{
|
|
52
|
-
|
|
53
|
+
{menu &&
|
|
54
|
+
menu.map((section) => {
|
|
53
55
|
const isActive = trimSlashes(router.asPath).startsWith(trimSlashes(section.path));
|
|
54
56
|
return (
|
|
55
57
|
<Link
|
|
@@ -16,8 +16,9 @@ import {
|
|
|
16
16
|
} from 'lucide-react';
|
|
17
17
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../../components/ui/collapsible';
|
|
18
18
|
|
|
19
|
+
import Link from 'next/link';
|
|
19
20
|
import { useRouter } from 'next/router';
|
|
20
|
-
import React from 'react';
|
|
21
|
+
import React, { useEffect, useState } from 'react';
|
|
21
22
|
import {
|
|
22
23
|
Sidebar,
|
|
23
24
|
SidebarContent,
|
|
@@ -30,8 +31,22 @@ import {
|
|
|
30
31
|
SidebarMenuSub,
|
|
31
32
|
SidebarSeparator,
|
|
32
33
|
} from '../../components/ui/sidebar';
|
|
33
|
-
import { normalizePathForMatch
|
|
34
|
-
import {
|
|
34
|
+
import { normalizePathForMatch } from '../../lib/utils';
|
|
35
|
+
import { useNav } from '../context/NavProvider';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* `next/link` re-applies `basePath`, but the menu item paths already carry the base prefix (built by
|
|
39
|
+
* the server navigation resolver). Strip the leading base segment once so the link is not
|
|
40
|
+
* double-prefixed; with no base path this just returns a leading-slash route.
|
|
41
|
+
*/
|
|
42
|
+
const stripBasePath = (p: string): string => {
|
|
43
|
+
const base = (process.env.HANDOFF_APP_BASE_PATH ?? '').replace(/^\/+|\/+$/g, '');
|
|
44
|
+
let rel = p.replace(/^\/+/, '');
|
|
45
|
+
if (base && (rel === base || rel.startsWith(`${base}/`))) {
|
|
46
|
+
rel = rel.slice(base.length).replace(/^\/+/, '');
|
|
47
|
+
}
|
|
48
|
+
return `/${rel}`;
|
|
49
|
+
};
|
|
35
50
|
|
|
36
51
|
const NormalMenuItem = ({ title, icon, path }) => {
|
|
37
52
|
const router = useRouter();
|
|
@@ -39,10 +54,13 @@ const NormalMenuItem = ({ title, icon, path }) => {
|
|
|
39
54
|
return (
|
|
40
55
|
<SidebarMenuItem>
|
|
41
56
|
<SidebarMenuButton asChild isActive={isActive}>
|
|
42
|
-
|
|
57
|
+
{/* Soft (client-side) navigation so moving between entity pages reuses the already-loaded
|
|
58
|
+
cached nav with no reload/flash. Prefetch is off: each prefetch would invoke the
|
|
59
|
+
fallback:'blocking' lambda data route, so fetch the target's data on click instead. */}
|
|
60
|
+
<Link href={stripBasePath(path)} prefetch={false} className="gap-3">
|
|
43
61
|
<MenuIcon icon={icon} isActive={isActive} />
|
|
44
62
|
<span>{title}</span>
|
|
45
|
-
</
|
|
63
|
+
</Link>
|
|
46
64
|
</SidebarMenuButton>
|
|
47
65
|
</SidebarMenuItem>
|
|
48
66
|
);
|
|
@@ -53,8 +71,15 @@ const CollapsibleMenuItem = ({ title, icon, path, menu }) => {
|
|
|
53
71
|
const isActive = menu.some(
|
|
54
72
|
(item) => normalizePathForMatch(router.asPath).startsWith(normalizePathForMatch(item.path))
|
|
55
73
|
);
|
|
74
|
+
// Controlled so the active group auto-opens on soft navigation (an uncontrolled `defaultOpen` is
|
|
75
|
+
// only read on mount and would not re-open the active group when the route changes client-side),
|
|
76
|
+
// while still letting the user collapse/expand groups manually.
|
|
77
|
+
const [open, setOpen] = useState(isActive);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (isActive) setOpen(true);
|
|
80
|
+
}, [isActive]);
|
|
56
81
|
return (
|
|
57
|
-
<Collapsible
|
|
82
|
+
<Collapsible open={open} onOpenChange={setOpen} className="group/collapsible">
|
|
58
83
|
<SidebarMenuItem>
|
|
59
84
|
<CollapsibleTrigger asChild>
|
|
60
85
|
<SidebarMenuButton className="h-9 gap-3">
|
|
@@ -118,13 +143,23 @@ const MenuIcon = ({ icon, isActive = false }) => {
|
|
|
118
143
|
}
|
|
119
144
|
};
|
|
120
145
|
|
|
121
|
-
const SideNav = (
|
|
146
|
+
const SideNav = () => {
|
|
147
|
+
const { current } = useNav();
|
|
148
|
+
const sections = current?.subSections ?? [];
|
|
149
|
+
|
|
150
|
+
// Collapse the sidebar entirely (render nothing, reserve no gutter) when the resolved section has
|
|
151
|
+
// no renderable content, so the page content goes full width. A `dynamic` slot counts as
|
|
152
|
+
// renderable even before its entities load, to avoid a full-width → gutter flash in registry mode.
|
|
153
|
+
const hasContent = sections.some((section) => (section.menu && section.menu.length > 0) || !!section.dynamic);
|
|
154
|
+
if (!hasContent) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
122
158
|
return (
|
|
123
159
|
<Sidebar className="sticky left-auto">
|
|
124
160
|
<SidebarContent className="px-4 pt-5">
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
menu.subSections.map((section, index) => (
|
|
161
|
+
{sections.length > 0 &&
|
|
162
|
+
sections.map((section, index) => (
|
|
128
163
|
<React.Fragment key={index}>
|
|
129
164
|
<SidebarGroup>
|
|
130
165
|
{!section.path && <SidebarGroupLabel>{section.title}</SidebarGroupLabel>}
|
|
@@ -138,7 +173,7 @@ const SideNav = ({ menu }: { menu: SectionLink }) => {
|
|
|
138
173
|
</SidebarGroupContent>
|
|
139
174
|
)}
|
|
140
175
|
</SidebarGroup>
|
|
141
|
-
{index <
|
|
176
|
+
{index < sections.length && <SidebarSeparator className="mx-4" />}
|
|
142
177
|
</React.Fragment>
|
|
143
178
|
))}
|
|
144
179
|
</SidebarContent>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface NotFoundProps {
|
|
4
|
+
title?: string;
|
|
5
|
+
description?: React.ReactNode;
|
|
6
|
+
linkHref?: string;
|
|
7
|
+
linkLabel?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const NotFound: React.FC<NotFoundProps> = () => {
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex flex-col items-center">
|
|
13
|
+
<div className="mb-2 text-7xl font-bold text-gray-800 dark:text-white">404</div>
|
|
14
|
+
<h1 className="mb-4 text-2xl font-semibold text-gray-700 dark:text-gray-300">Oops! Page not found.</h1>
|
|
15
|
+
<p className="mb-6 max-w-md text-center text-gray-500 dark:text-gray-400">
|
|
16
|
+
This page doesn’t exist or has been moved.
|
|
17
|
+
<br />
|
|
18
|
+
Please check the URL or return to the homepage.
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default NotFound;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { NavData, SectionLink } from '@handoff/nav';
|
|
4
|
+
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
export type { NavData, NavEntity } from '@handoff/nav';
|
|
7
|
+
|
|
8
|
+
interface INavContext {
|
|
9
|
+
nav: NavData;
|
|
10
|
+
currentSectionId: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const EMPTY_NAV: NavData = { shell: [] };
|
|
14
|
+
const NavContext = createContext<INavContext>({ nav: EMPTY_NAV, currentSectionId: '' });
|
|
15
|
+
const isRegistryMode = process.env.HANDOFF_RUNTIME_MODE === 'registry';
|
|
16
|
+
|
|
17
|
+
// A module-level fulfilled cache and in-flight guard make the registry refresh a single hard-load
|
|
18
|
+
// operation, including StrictMode remounts. A browser hard reload resets both and observes publishes.
|
|
19
|
+
let cachedNav: NavData | null = null;
|
|
20
|
+
let inFlight: Promise<NavData | null> | null = null;
|
|
21
|
+
|
|
22
|
+
const loadNav = (): Promise<NavData | null> => {
|
|
23
|
+
if (cachedNav) return Promise.resolve(cachedNav);
|
|
24
|
+
if (inFlight) return inFlight;
|
|
25
|
+
inFlight = fetch(`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/docs/nav.json`)
|
|
26
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
27
|
+
.then((data: NavData | null) => {
|
|
28
|
+
if (data) cachedNav = data;
|
|
29
|
+
return cachedNav;
|
|
30
|
+
})
|
|
31
|
+
.catch(() => null)
|
|
32
|
+
.finally(() => {
|
|
33
|
+
inFlight = null;
|
|
34
|
+
});
|
|
35
|
+
return inFlight;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const NavProvider: React.FC<{
|
|
39
|
+
children: React.ReactNode;
|
|
40
|
+
initialNav?: NavData;
|
|
41
|
+
currentSectionId?: string;
|
|
42
|
+
}> = ({ children, initialNav = EMPTY_NAV, currentSectionId = '' }) => {
|
|
43
|
+
// Registry alone owns refresh state. Workspace renders the latest page prop directly, so soft
|
|
44
|
+
// navigation cannot briefly expose the preceding page's tree and triggers no provider effect.
|
|
45
|
+
const [registryNav, setRegistryNav] = useState<NavData>(() => cachedNav ?? initialNav);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!isRegistryMode || cachedNav) return;
|
|
49
|
+
let active = true;
|
|
50
|
+
loadNav().then((data) => {
|
|
51
|
+
if (active && data) setRegistryNav(data);
|
|
52
|
+
});
|
|
53
|
+
return () => {
|
|
54
|
+
active = false;
|
|
55
|
+
};
|
|
56
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
57
|
+
}, []);
|
|
58
|
+
|
|
59
|
+
const nav = isRegistryMode ? registryNav : initialNav;
|
|
60
|
+
return <NavContext.Provider value={{ nav, currentSectionId }}>{children}</NavContext.Provider>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** The sole view-facing navigation API: one active tree and an exact-match active section. */
|
|
64
|
+
export const useNav = (): { nav: NavData; menu: SectionLink[]; current: SectionLink | null } => {
|
|
65
|
+
const { nav, currentSectionId } = useContext(NavContext);
|
|
66
|
+
return {
|
|
67
|
+
nav,
|
|
68
|
+
menu: nav.shell,
|
|
69
|
+
current: nav.shell.find((section) => section.path === currentSectionId) ?? null,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
@@ -2,14 +2,14 @@ import { ClientConfig } from '@handoff/types/config';
|
|
|
2
2
|
import Link from 'next/link';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import NavLink from '../NavLink';
|
|
5
|
-
import {
|
|
5
|
+
import { useNav } from '../context/NavProvider';
|
|
6
6
|
|
|
7
7
|
interface HeaderProps {
|
|
8
|
-
menu: SectionLink[];
|
|
9
8
|
config: ClientConfig;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
function Header({
|
|
11
|
+
function Header({ config }: HeaderProps) {
|
|
12
|
+
const { menu } = useNav();
|
|
13
13
|
const [mobile, setMobile] = React.useState('');
|
|
14
14
|
const toggle = () => {
|
|
15
15
|
if (mobile === 'is-active') {
|