bsmnt 0.0.2 → 0.1.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/.github/workflows/release.yml +2 -0
- package/CHANGELOG.md +21 -0
- package/CLAUDE.md +42 -23
- package/README.md +33 -11
- package/biome.json +1 -0
- package/bun.lock +1 -1
- package/docs/architecture.drawio +266 -0
- package/docs/architecture.mermaid +91 -0
- package/package.json +1 -1
- package/{bin → packages/cli/bin}/index.js +28 -27
- package/packages/cli/package.json +16 -0
- package/{src → packages/cli/src}/commands/add-integration.js +23 -25
- package/{src → packages/cli/src}/commands/create.js +104 -133
- package/packages/create-basement-app/integrations/basehub/config.js +21 -0
- package/packages/create-basement-app/integrations/sanity/config.js +46 -0
- package/{src → packages/create-basement-app/integrations/sanity}/mergers/check-integration-merger.js +1 -1
- package/{src → packages/create-basement-app/integrations/sanity}/mergers/layout-merger.js +1 -1
- package/{src → packages/create-basement-app/integrations/sanity}/mergers/sitemap-merger.js +1 -1
- package/packages/create-basement-app/package.json +10 -0
- package/packages/create-basement-app/src/configs/animations.js +28 -0
- package/packages/create-basement-app/src/index.js +15 -0
- package/packages/create-basement-app/src/mergers/check-integration-merger.js +105 -0
- package/{src → packages/create-basement-app/src}/mergers/config.js +1 -7
- package/{src → packages/create-basement-app/src}/mergers/index.js +87 -93
- package/packages/create-basement-app/src/mergers/layout-merger.js +223 -0
- package/packages/create-basement-app/src/mergers/sitemap-merger.js +121 -0
- package/packages/create-basement-app/template-hooks/config.js +38 -0
- package/packages/create-basement-app/templates/default/.biome/plugins/README.md +21 -0
- package/packages/create-basement-app/templates/default/.biome/plugins/no-anchor-element.grit +12 -0
- package/packages/create-basement-app/templates/default/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/packages/create-basement-app/templates/default/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/README.md +184 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/architecture.mdc +437 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/components.mdc +436 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/integrations.mdc +447 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/main.mdc +278 -0
- package/packages/create-basement-app/templates/default/.cursor/rules/styling.mdc +433 -0
- package/packages/create-basement-app/templates/default/.editorconfig +40 -0
- package/packages/create-basement-app/templates/default/.env.example +81 -0
- package/packages/create-basement-app/templates/default/.gitattributes +19 -0
- package/packages/create-basement-app/templates/default/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/packages/create-basement-app/templates/default/.github/workflows/lighthouse-to-slack.yml +136 -0
- package/packages/create-basement-app/templates/default/.vscode/extensions.json +20 -0
- package/packages/create-basement-app/templates/default/.vscode/settings.json +105 -0
- package/packages/create-basement-app/templates/default/README.md +221 -0
- package/packages/create-basement-app/templates/default/_gitignore +67 -0
- package/packages/create-basement-app/templates/default/app/favicon.ico +0 -0
- package/packages/create-basement-app/templates/default/app/layout.tsx +104 -0
- package/packages/create-basement-app/templates/default/app/page.tsx +275 -0
- package/packages/create-basement-app/templates/default/app/robots.ts +15 -0
- package/packages/create-basement-app/templates/default/app/sitemap.ts +16 -0
- package/packages/create-basement-app/templates/default/biome.json +250 -0
- package/packages/create-basement-app/templates/default/components/basement.svg +1 -0
- package/packages/create-basement-app/templates/default/components/layout/footer/index.tsx +27 -0
- package/packages/create-basement-app/templates/default/components/layout/header/index.tsx +11 -0
- package/packages/create-basement-app/templates/default/components/layout/theme/index.tsx +66 -0
- package/packages/create-basement-app/templates/default/components/layout/wrapper/index.tsx +65 -0
- package/packages/create-basement-app/templates/default/components/ui/README.md +77 -0
- package/packages/create-basement-app/templates/default/components/ui/image/README.md +37 -0
- package/packages/create-basement-app/templates/default/components/ui/image/index.tsx +224 -0
- package/packages/create-basement-app/templates/default/components/ui/link/index.tsx +146 -0
- package/packages/create-basement-app/templates/default/lib/README.md +33 -0
- package/packages/create-basement-app/templates/default/lib/hooks/index.ts +12 -0
- package/packages/create-basement-app/templates/default/lib/hooks/use-device-detection.ts +81 -0
- package/packages/create-basement-app/templates/default/lib/hooks/use-media-breakpoint.ts +15 -0
- package/packages/create-basement-app/templates/default/lib/hooks/use-prefetch.ts +74 -0
- package/packages/create-basement-app/templates/default/lib/scripts/dev.ts +52 -0
- package/packages/create-basement-app/templates/default/lib/scripts/generate-component.ts +322 -0
- package/packages/create-basement-app/templates/default/lib/scripts/generate-page.ts +193 -0
- package/packages/create-basement-app/templates/default/lib/scripts/generate.ts +79 -0
- package/packages/create-basement-app/templates/default/lib/scripts/utils.ts +246 -0
- package/packages/create-basement-app/templates/default/lib/store/app.ts +11 -0
- package/packages/create-basement-app/templates/default/lib/store/index.ts +11 -0
- package/packages/create-basement-app/templates/default/lib/styles/README.md +64 -0
- package/packages/create-basement-app/templates/default/lib/styles/cn.ts +7 -0
- package/packages/create-basement-app/templates/default/lib/styles/colors.ts +63 -0
- package/packages/create-basement-app/templates/default/lib/styles/config.ts +34 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/global.css +85 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/index.css +6 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/reset.css +166 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/root.css +68 -0
- package/packages/create-basement-app/templates/default/lib/styles/css/tailwind.css +132 -0
- package/packages/create-basement-app/templates/default/lib/styles/easings.ts +21 -0
- package/packages/create-basement-app/templates/default/lib/styles/fonts.ts +28 -0
- package/packages/create-basement-app/templates/default/lib/styles/index.ts +12 -0
- package/packages/create-basement-app/templates/default/lib/styles/layout.mjs +27 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/README.md +29 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/generate-root.ts +57 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/generate-tailwind.ts +162 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/postcss-functions.mjs +168 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/setup-styles.ts +24 -0
- package/packages/create-basement-app/templates/default/lib/styles/scripts/utils.ts +20 -0
- package/packages/create-basement-app/templates/default/lib/styles/typography.ts +36 -0
- package/packages/create-basement-app/templates/default/lib/utils/README.md +40 -0
- package/packages/create-basement-app/templates/default/lib/utils/css.d.ts +21 -0
- package/packages/create-basement-app/templates/default/lib/utils/easings.ts +240 -0
- package/packages/create-basement-app/templates/default/lib/utils/fetch.ts +84 -0
- package/packages/create-basement-app/templates/default/lib/utils/math.test.ts +221 -0
- package/packages/create-basement-app/templates/default/lib/utils/math.ts +236 -0
- package/packages/create-basement-app/templates/default/lib/utils/metadata.ts +126 -0
- package/packages/create-basement-app/templates/default/lib/utils/strings.test.ts +166 -0
- package/packages/create-basement-app/templates/default/lib/utils/strings.ts +246 -0
- package/packages/create-basement-app/templates/default/lib/utils/types.d.ts +15 -0
- package/packages/create-basement-app/templates/default/lib/utils/viewport.test.ts +256 -0
- package/packages/create-basement-app/templates/default/lib/utils/viewport.ts +193 -0
- package/packages/create-basement-app/templates/default/next.config.ts +142 -0
- package/packages/create-basement-app/templates/default/package.json +62 -0
- package/packages/create-basement-app/templates/default/postcss.config.mjs +42 -0
- package/packages/create-basement-app/templates/default/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/packages/create-basement-app/templates/default/tsconfig.json +43 -0
- package/packages/create-basement-app/templates/experiment/.biome/plugins/README.md +21 -0
- package/packages/create-basement-app/templates/experiment/.biome/plugins/no-anchor-element.grit +12 -0
- package/packages/create-basement-app/templates/experiment/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/packages/create-basement-app/templates/experiment/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/README.md +184 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/architecture.mdc +437 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/components.mdc +436 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/integrations.mdc +447 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/main.mdc +278 -0
- package/packages/create-basement-app/templates/experiment/.cursor/rules/styling.mdc +433 -0
- package/packages/create-basement-app/templates/experiment/.editorconfig +40 -0
- package/packages/create-basement-app/templates/experiment/.env.example +81 -0
- package/packages/create-basement-app/templates/experiment/.gitattributes +19 -0
- package/packages/create-basement-app/templates/experiment/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/packages/create-basement-app/templates/experiment/.github/workflows/lighthouse-to-slack.yml +136 -0
- package/packages/create-basement-app/templates/experiment/.vscode/extensions.json +20 -0
- package/packages/create-basement-app/templates/experiment/.vscode/settings.json +105 -0
- package/packages/create-basement-app/templates/experiment/README.md +221 -0
- package/packages/create-basement-app/templates/experiment/_gitignore +67 -0
- package/packages/create-basement-app/templates/experiment/app/favicon.ico +0 -0
- package/packages/create-basement-app/templates/experiment/app/layout.tsx +104 -0
- package/packages/create-basement-app/templates/experiment/app/page.tsx +275 -0
- package/packages/create-basement-app/templates/experiment/app/robots.ts +15 -0
- package/packages/create-basement-app/templates/experiment/app/sitemap.ts +16 -0
- package/packages/create-basement-app/templates/experiment/biome.json +250 -0
- package/packages/create-basement-app/templates/experiment/components/basement.svg +1 -0
- package/packages/create-basement-app/templates/experiment/components/layout/footer/index.tsx +27 -0
- package/packages/create-basement-app/templates/experiment/components/layout/header/index.tsx +58 -0
- package/packages/create-basement-app/templates/experiment/components/layout/navigation-menu.tsx +127 -0
- package/packages/create-basement-app/templates/experiment/components/layout/theme/index.tsx +66 -0
- package/packages/create-basement-app/templates/experiment/components/layout/wrapper/index.tsx +65 -0
- package/packages/create-basement-app/templates/experiment/components/ui/README.md +77 -0
- package/packages/create-basement-app/templates/experiment/components/ui/image/README.md +37 -0
- package/packages/create-basement-app/templates/experiment/components/ui/image/index.tsx +224 -0
- package/packages/create-basement-app/templates/experiment/components/ui/link/index.tsx +146 -0
- package/packages/create-basement-app/templates/experiment/lib/README.md +33 -0
- package/packages/create-basement-app/templates/experiment/lib/constants.ts +12 -0
- package/packages/create-basement-app/templates/experiment/lib/hooks/index.ts +12 -0
- package/packages/create-basement-app/templates/experiment/lib/hooks/use-device-detection.ts +81 -0
- package/packages/create-basement-app/templates/experiment/lib/hooks/use-media-breakpoint.ts +15 -0
- package/packages/create-basement-app/templates/experiment/lib/hooks/use-prefetch.ts +74 -0
- package/packages/create-basement-app/templates/experiment/lib/integrations/.gitkeep +0 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/dev.ts +52 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/generate-component.ts +322 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/generate-page.ts +193 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/generate.ts +79 -0
- package/packages/create-basement-app/templates/experiment/lib/scripts/utils.ts +246 -0
- package/packages/create-basement-app/templates/experiment/lib/store/app.ts +11 -0
- package/packages/create-basement-app/templates/experiment/lib/store/index.ts +11 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/README.md +64 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/cn.ts +7 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/colors.ts +63 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/config.ts +34 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/global.css +85 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/index.css +6 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/reset.css +166 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/root.css +68 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/css/tailwind.css +132 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/easings.ts +21 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/fonts.ts +28 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/index.ts +12 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/layout.mjs +27 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/README.md +29 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/generate-root.ts +57 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/generate-tailwind.ts +162 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/postcss-functions.mjs +168 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/setup-styles.ts +24 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/scripts/utils.ts +20 -0
- package/packages/create-basement-app/templates/experiment/lib/styles/typography.ts +36 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/README.md +40 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/css.d.ts +21 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/easings.ts +240 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/fetch.ts +84 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/math.test.ts +221 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/math.ts +236 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/metadata.ts +126 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/strings.test.ts +166 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/strings.ts +246 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/types.d.ts +15 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/viewport.test.ts +256 -0
- package/packages/create-basement-app/templates/experiment/lib/utils/viewport.ts +193 -0
- package/packages/create-basement-app/templates/experiment/next.config.ts +142 -0
- package/packages/create-basement-app/templates/experiment/package.json +69 -0
- package/packages/create-basement-app/templates/experiment/postcss.config.mjs +42 -0
- package/packages/create-basement-app/templates/experiment/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/packages/create-basement-app/templates/experiment/tsconfig.json +43 -0
- package/packages/create-basement-app/templates/webgl/.biome/plugins/README.md +21 -0
- package/packages/create-basement-app/templates/webgl/.biome/plugins/no-anchor-element.grit +12 -0
- package/packages/create-basement-app/templates/webgl/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/packages/create-basement-app/templates/webgl/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/README.md +184 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/architecture.mdc +437 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/components.mdc +436 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/integrations.mdc +447 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/main.mdc +278 -0
- package/packages/create-basement-app/templates/webgl/.cursor/rules/styling.mdc +433 -0
- package/packages/create-basement-app/templates/webgl/.editorconfig +40 -0
- package/packages/create-basement-app/templates/webgl/.env.example +81 -0
- package/packages/create-basement-app/templates/webgl/.gitattributes +19 -0
- package/packages/create-basement-app/templates/webgl/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/packages/create-basement-app/templates/webgl/.github/workflows/lighthouse-to-slack.yml +136 -0
- package/packages/create-basement-app/templates/webgl/.vscode/extensions.json +20 -0
- package/packages/create-basement-app/templates/webgl/.vscode/settings.json +105 -0
- package/packages/create-basement-app/templates/webgl/README.md +221 -0
- package/packages/create-basement-app/templates/webgl/_gitignore +67 -0
- package/packages/create-basement-app/templates/webgl/app/favicon.ico +0 -0
- package/packages/create-basement-app/templates/webgl/app/layout.tsx +104 -0
- package/packages/create-basement-app/templates/webgl/app/page.tsx +10 -0
- package/packages/create-basement-app/templates/webgl/app/robots.ts +15 -0
- package/packages/create-basement-app/templates/webgl/app/sitemap.ts +16 -0
- package/packages/create-basement-app/templates/webgl/biome.json +250 -0
- package/packages/create-basement-app/templates/webgl/components/basement.svg +1 -0
- package/packages/create-basement-app/templates/webgl/components/layout/footer/index.tsx +27 -0
- package/packages/create-basement-app/templates/webgl/components/layout/header/index.tsx +11 -0
- package/packages/create-basement-app/templates/webgl/components/layout/theme/index.tsx +66 -0
- package/packages/create-basement-app/templates/webgl/components/layout/wrapper/index.tsx +65 -0
- package/packages/create-basement-app/templates/webgl/components/ui/README.md +77 -0
- package/packages/create-basement-app/templates/webgl/components/ui/image/README.md +37 -0
- package/packages/create-basement-app/templates/webgl/components/ui/image/index.tsx +224 -0
- package/packages/create-basement-app/templates/webgl/components/ui/link/index.tsx +146 -0
- package/packages/create-basement-app/templates/webgl/components/webgl/canvas/dynamic.tsx +34 -0
- package/packages/create-basement-app/templates/webgl/components/webgl/canvas/index.tsx +43 -0
- package/packages/create-basement-app/templates/webgl/components/webgl/components/scene/index.tsx +21 -0
- package/packages/create-basement-app/templates/webgl/lib/README.md +33 -0
- package/packages/create-basement-app/templates/webgl/lib/hooks/index.ts +12 -0
- package/packages/create-basement-app/templates/webgl/lib/hooks/use-device-detection.ts +81 -0
- package/packages/create-basement-app/templates/webgl/lib/hooks/use-media-breakpoint.ts +15 -0
- package/packages/create-basement-app/templates/webgl/lib/hooks/use-prefetch.ts +74 -0
- package/packages/create-basement-app/templates/webgl/lib/integrations/.gitkeep +0 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/dev.ts +52 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/generate-component.ts +322 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/generate-page.ts +193 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/generate.ts +79 -0
- package/packages/create-basement-app/templates/webgl/lib/scripts/utils.ts +246 -0
- package/packages/create-basement-app/templates/webgl/lib/store/app.ts +11 -0
- package/packages/create-basement-app/templates/webgl/lib/store/index.ts +11 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/README.md +64 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/cn.ts +7 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/colors.ts +63 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/config.ts +34 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/global.css +85 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/index.css +6 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/reset.css +166 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/root.css +68 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/css/tailwind.css +132 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/easings.ts +21 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/fonts.ts +28 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/index.ts +12 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/layout.mjs +27 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/README.md +29 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/generate-root.ts +57 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/generate-tailwind.ts +162 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/postcss-functions.mjs +168 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/setup-styles.ts +24 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/scripts/utils.ts +20 -0
- package/packages/create-basement-app/templates/webgl/lib/styles/typography.ts +36 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/README.md +40 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/css.d.ts +21 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/easings.ts +240 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/fetch.ts +84 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/math.test.ts +221 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/math.ts +236 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/metadata.ts +126 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/strings.test.ts +166 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/strings.ts +246 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/types.d.ts +15 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/viewport.test.ts +256 -0
- package/packages/create-basement-app/templates/webgl/lib/utils/viewport.ts +193 -0
- package/packages/create-basement-app/templates/webgl/next.config.ts +142 -0
- package/packages/create-basement-app/templates/webgl/package.json +68 -0
- package/packages/create-basement-app/templates/webgl/postcss.config.mjs +42 -0
- package/packages/create-basement-app/templates/webgl/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/packages/create-basement-app/templates/webgl/tsconfig.json +43 -0
- package/packages/create-basement-app/templates/webgpu/.biome/plugins/README.md +21 -0
- package/packages/create-basement-app/templates/webgpu/.biome/plugins/no-anchor-element.grit +12 -0
- package/packages/create-basement-app/templates/webgpu/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/packages/create-basement-app/templates/webgpu/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/packages/create-basement-app/templates/webgpu/.cursor/rules/README.md +184 -0
- package/packages/create-basement-app/templates/webgpu/.cursor/rules/architecture.mdc +437 -0
- package/packages/create-basement-app/templates/webgpu/.cursor/rules/components.mdc +436 -0
- package/packages/create-basement-app/templates/webgpu/.cursor/rules/integrations.mdc +447 -0
- package/packages/create-basement-app/templates/webgpu/.cursor/rules/main.mdc +278 -0
- package/packages/create-basement-app/templates/webgpu/.cursor/rules/styling.mdc +433 -0
- package/packages/create-basement-app/templates/webgpu/.editorconfig +40 -0
- package/packages/create-basement-app/templates/webgpu/.env.example +81 -0
- package/packages/create-basement-app/templates/webgpu/.gitattributes +19 -0
- package/packages/create-basement-app/templates/webgpu/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/packages/create-basement-app/templates/webgpu/.github/workflows/lighthouse-to-slack.yml +136 -0
- package/packages/create-basement-app/templates/webgpu/.vscode/extensions.json +20 -0
- package/packages/create-basement-app/templates/webgpu/.vscode/settings.json +105 -0
- package/packages/create-basement-app/templates/webgpu/README.md +221 -0
- package/packages/create-basement-app/templates/webgpu/_gitignore +67 -0
- package/packages/create-basement-app/templates/webgpu/app/favicon.ico +0 -0
- package/packages/create-basement-app/templates/webgpu/app/layout.tsx +104 -0
- package/packages/create-basement-app/templates/webgpu/app/page.tsx +275 -0
- package/packages/create-basement-app/templates/webgpu/app/robots.ts +15 -0
- package/packages/create-basement-app/templates/webgpu/app/sitemap.ts +16 -0
- package/packages/create-basement-app/templates/webgpu/biome.json +250 -0
- package/packages/create-basement-app/templates/webgpu/components/basement.svg +1 -0
- package/packages/create-basement-app/templates/webgpu/components/layout/footer/index.tsx +27 -0
- package/packages/create-basement-app/templates/webgpu/components/layout/header/index.tsx +11 -0
- package/packages/create-basement-app/templates/webgpu/components/layout/theme/index.tsx +66 -0
- package/packages/create-basement-app/templates/webgpu/components/layout/wrapper/index.tsx +65 -0
- package/packages/create-basement-app/templates/webgpu/components/ui/README.md +77 -0
- package/packages/create-basement-app/templates/webgpu/components/ui/image/README.md +37 -0
- package/packages/create-basement-app/templates/webgpu/components/ui/image/index.tsx +224 -0
- package/packages/create-basement-app/templates/webgpu/components/ui/link/index.tsx +146 -0
- package/packages/create-basement-app/templates/webgpu/lib/README.md +33 -0
- package/packages/create-basement-app/templates/webgpu/lib/hooks/index.ts +12 -0
- package/packages/create-basement-app/templates/webgpu/lib/hooks/use-device-detection.ts +81 -0
- package/packages/create-basement-app/templates/webgpu/lib/hooks/use-media-breakpoint.ts +15 -0
- package/packages/create-basement-app/templates/webgpu/lib/hooks/use-prefetch.ts +74 -0
- package/packages/create-basement-app/templates/webgpu/lib/integrations/.gitkeep +0 -0
- package/packages/create-basement-app/templates/webgpu/lib/scripts/dev.ts +52 -0
- package/packages/create-basement-app/templates/webgpu/lib/scripts/generate-component.ts +322 -0
- package/packages/create-basement-app/templates/webgpu/lib/scripts/generate-page.ts +193 -0
- package/packages/create-basement-app/templates/webgpu/lib/scripts/generate.ts +79 -0
- package/packages/create-basement-app/templates/webgpu/lib/scripts/utils.ts +246 -0
- package/packages/create-basement-app/templates/webgpu/lib/store/app.ts +11 -0
- package/packages/create-basement-app/templates/webgpu/lib/store/index.ts +11 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/README.md +64 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/cn.ts +7 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/colors.ts +63 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/config.ts +34 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/css/global.css +85 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/css/index.css +6 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/css/reset.css +166 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/css/root.css +68 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/css/tailwind.css +132 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/easings.ts +21 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/fonts.ts +28 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/index.ts +12 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/layout.mjs +27 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/scripts/README.md +29 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/scripts/generate-root.ts +57 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/scripts/generate-tailwind.ts +162 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/scripts/postcss-functions.mjs +168 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/scripts/setup-styles.ts +24 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/scripts/utils.ts +20 -0
- package/packages/create-basement-app/templates/webgpu/lib/styles/typography.ts +36 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/README.md +40 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/css.d.ts +21 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/easings.ts +240 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/fetch.ts +84 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/math.test.ts +221 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/math.ts +236 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/metadata.ts +126 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/strings.test.ts +166 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/strings.ts +246 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/types.d.ts +15 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/viewport.test.ts +256 -0
- package/packages/create-basement-app/templates/webgpu/lib/utils/viewport.ts +193 -0
- package/packages/create-basement-app/templates/webgpu/next.config.ts +142 -0
- package/packages/create-basement-app/templates/webgpu/package.json +69 -0
- package/packages/create-basement-app/templates/webgpu/postcss.config.mjs +42 -0
- package/packages/create-basement-app/templates/webgpu/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/packages/create-basement-app/templates/webgpu/tsconfig.json +43 -0
- package/tasks/.last-branch +1 -0
- package/tasks/CLAUDE.md +104 -0
- package/tasks/archive/2026-02-09-next-starter-dynamic-layers/prd.json +153 -0
- package/tasks/archive/2026-02-09-next-starter-dynamic-layers/progress.txt +115 -0
- package/tasks/prd-project-restructure.md +375 -0
- package/tasks/prd.json +227 -91
- package/tasks/progress.txt +281 -87
- package/tasks/ralph.sh +113 -0
- package/layers/experiment/components/layout/header/index.tsx +0 -58
- package/layers/experiment/components/layout/navigation-menu.tsx +0 -127
- package/layers/experiment/lib/constants.ts +0 -12
- package/layers/webgl/app/page.tsx +0 -10
- package/layers/webgl/components/webgl/canvas/dynamic.tsx +0 -34
- package/layers/webgl/components/webgl/canvas/index.tsx +0 -43
- package/layers/webgl/components/webgl/components/scene/index.tsx +0 -21
- package/src/mergers/next-config-merger.js +0 -63
- /package/{src → packages/cli/src}/commands/setup-sanity.js +0 -0
- /package/{src → packages/cli/src}/commands/worktree.js +0 -0
- /package/{integrations/basehub → packages/create-basement-app/integrations/basehub/files}/README.md +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/draft-mode/disable/route.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/draft-mode/enable/route.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/api/revalidate/route.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/layout.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/sitemap.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/studio/[[...tool]]/page.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/app/studio/layout.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/components/ui/sanity-image/index.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/README.md +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/check-integration.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/README.md +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/client.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/components/disable-draft-mode.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/components/rich-text.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/env.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/live/index.tsx +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/queries.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.cli.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.config.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/sanity.types.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schema.json +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/article.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/example.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/index.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/link.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/metadata.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/navigation.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/page.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/schemas/richText.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/structure.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/utils/image.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/integrations/sanity/utils/link.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/scripts/copy-sanity-mcp.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/scripts/generate-page.ts +0 -0
- /package/{integrations/sanity → packages/create-basement-app/integrations/sanity/files}/lib/utils/metadata.ts +0 -0
- /package/{plugins → packages/create-basement-app/plugins}/README.md +0 -0
- /package/{plugins → packages/create-basement-app/plugins}/no-anchor-element.grit +0 -0
- /package/{plugins → packages/create-basement-app/plugins}/no-relative-parent-imports.grit +0 -0
- /package/{plugins → packages/create-basement-app/plugins}/no-unnecessary-forwardref.grit +0 -0
- /package/{template-hooks → packages/create-basement-app/template-hooks}/use-battery.ts +0 -0
- /package/{template-hooks → packages/create-basement-app/template-hooks}/use-device-perf.ts +0 -0
- /package/{template-hooks → packages/create-basement-app/template-hooks}/use-intersection-observer.ts +0 -0
- /package/{template-hooks → packages/create-basement-app/template-hooks}/use-media.ts +0 -0
- /package/{layers/webgpu → packages/create-basement-app/templates/default/lib/integrations}/.gitkeep +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Image Component
|
|
2
|
+
|
|
3
|
+
Optimized images with smart loading, blur placeholders, and responsive sizing.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Image } from '@/components/ui/image'
|
|
9
|
+
|
|
10
|
+
// Basic
|
|
11
|
+
<Image src="/hero.jpg" alt="Hero" aspectRatio={16/9} />
|
|
12
|
+
|
|
13
|
+
// Priority (LCP images)
|
|
14
|
+
<Image src="/hero.jpg" alt="Hero" aspectRatio={16/9} priority />
|
|
15
|
+
|
|
16
|
+
// Responsive
|
|
17
|
+
<Image
|
|
18
|
+
src="/product.jpg"
|
|
19
|
+
alt="Product"
|
|
20
|
+
mobileSize="100vw"
|
|
21
|
+
desktopSize="33vw"
|
|
22
|
+
/>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
| Prop | Description |
|
|
28
|
+
|------|-------------|
|
|
29
|
+
| `aspectRatio` | Prevents layout shift, enables blur placeholder |
|
|
30
|
+
| `priority` | Eager loading for above-the-fold images |
|
|
31
|
+
| `mobileSize` / `desktopSize` | Responsive sizing |
|
|
32
|
+
|
|
33
|
+
## Best Practices
|
|
34
|
+
|
|
35
|
+
- Always provide `aspectRatio` (prevents CLS)
|
|
36
|
+
- Use `priority` for LCP images
|
|
37
|
+
- Never use `next/image` directly
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Image Component
|
|
3
|
+
*
|
|
4
|
+
* Next.js Image wrapper with optimized defaults and error handling.
|
|
5
|
+
* Always use this component instead of next/image directly.
|
|
6
|
+
*/
|
|
7
|
+
"use client"
|
|
8
|
+
|
|
9
|
+
import cn from "clsx"
|
|
10
|
+
import NextImage, { type ImageProps as NextImageProps } from "next/image"
|
|
11
|
+
import type { CSSProperties, Ref } from "react"
|
|
12
|
+
import { breakpoints } from "@/lib/styles/config"
|
|
13
|
+
import s from "./image.module.css"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Enhanced Image component props extending Next.js Image.
|
|
17
|
+
*
|
|
18
|
+
* Adds responsive sizing, aspect ratio support, and automatic blur placeholders.
|
|
19
|
+
* Always use this component instead of next/image directly.
|
|
20
|
+
*/
|
|
21
|
+
export type ImageProps = Omit<NextImageProps, "objectFit" | "alt"> & {
|
|
22
|
+
/** CSS object-fit property for image positioning */
|
|
23
|
+
objectFit?: CSSProperties["objectFit"]
|
|
24
|
+
/** Display as block element (adds display: block) */
|
|
25
|
+
block?: boolean
|
|
26
|
+
/** Size on mobile devices (e.g., "100vw", "50vw") */
|
|
27
|
+
mobileSize?: `${number}vw`
|
|
28
|
+
/** Size on desktop devices (e.g., "33vw", "25vw") */
|
|
29
|
+
desktopSize?: `${number}vw`
|
|
30
|
+
/** Ref for accessing the underlying img element */
|
|
31
|
+
ref?: Ref<HTMLImageElement>
|
|
32
|
+
/** Alt text for accessibility (required for meaningful images) */
|
|
33
|
+
alt?: string
|
|
34
|
+
/** Aspect ratio for automatic placeholder and layout stability */
|
|
35
|
+
aspectRatio?: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Memoize helper functions to avoid recreation
|
|
39
|
+
const toBase64 = (str: string) =>
|
|
40
|
+
typeof window === "undefined"
|
|
41
|
+
? Buffer.from(str).toString("base64")
|
|
42
|
+
: window.btoa(str)
|
|
43
|
+
|
|
44
|
+
// Helper to generate blur placeholder with transparent background by default
|
|
45
|
+
const generateShimmer = (w: number, h: number) => `
|
|
46
|
+
<svg width="${w}" height="${h}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
47
|
+
<defs>
|
|
48
|
+
<linearGradient id="g">
|
|
49
|
+
<stop stop-color="rgba(255,255,255,0.1)" offset="20%" />
|
|
50
|
+
<stop stop-color="rgba(255,255,255,0.2)" offset="50%" />
|
|
51
|
+
<stop stop-color="rgba(255,255,255,0.1)" offset="70%" />
|
|
52
|
+
</linearGradient>
|
|
53
|
+
</defs>
|
|
54
|
+
<rect width="${w}" height="${h}" fill="rgba(0,0,0,0)" />
|
|
55
|
+
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
|
|
56
|
+
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
|
|
57
|
+
</svg>`
|
|
58
|
+
|
|
59
|
+
// Helper to determine if blur placeholder should be used
|
|
60
|
+
const shouldUseBlurPlaceholder = (
|
|
61
|
+
src: NextImageProps["src"],
|
|
62
|
+
placeholder: string,
|
|
63
|
+
blurDataURL: string | undefined
|
|
64
|
+
): boolean => {
|
|
65
|
+
if (!src) return false
|
|
66
|
+
const isSvg = typeof src === "string" && src.includes(".svg")
|
|
67
|
+
return !isSvg && placeholder === "blur" && !blurDataURL
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Helper to generate blur data URL
|
|
71
|
+
const generateBlurDataURL = (
|
|
72
|
+
shouldUse: boolean,
|
|
73
|
+
aspectRatio: number | undefined,
|
|
74
|
+
existingBlurDataURL: string | undefined
|
|
75
|
+
): string | undefined => {
|
|
76
|
+
if (!(shouldUse && aspectRatio)) return existingBlurDataURL
|
|
77
|
+
|
|
78
|
+
const shimmerSvg = generateShimmer(700, Math.round(700 / aspectRatio))
|
|
79
|
+
return `data:image/svg+xml;base64,${toBase64(shimmerSvg)}`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Helper to determine final placeholder value
|
|
83
|
+
const getFinalPlaceholder = (
|
|
84
|
+
shouldUse: boolean,
|
|
85
|
+
aspectRatio: number | undefined,
|
|
86
|
+
blurDataURL: string | undefined,
|
|
87
|
+
originalPlaceholder: NextImageProps["placeholder"]
|
|
88
|
+
): NextImageProps["placeholder"] => {
|
|
89
|
+
if (!shouldUse) {
|
|
90
|
+
return originalPlaceholder === "blur" && !blurDataURL
|
|
91
|
+
? "empty"
|
|
92
|
+
: originalPlaceholder
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return aspectRatio || blurDataURL ? "blur" : "empty"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Enhanced Image component with responsive sizing and automatic optimizations.
|
|
100
|
+
*
|
|
101
|
+
* Always use this component instead of next/image directly. Provides:
|
|
102
|
+
* - Automatic responsive sizes generation
|
|
103
|
+
* - Smart blur placeholders with aspect ratio support
|
|
104
|
+
* - Performance optimizations (lazy loading by default)
|
|
105
|
+
* - Preload for LCP images
|
|
106
|
+
*
|
|
107
|
+
* @param props - Image props extending Next.js Image
|
|
108
|
+
* @param props.aspectRatio - Aspect ratio for layout stability and blur placeholder
|
|
109
|
+
* @param props.mobileSize - Size on mobile (e.g., "100vw")
|
|
110
|
+
* @param props.desktopSize - Size on desktop (e.g., "50vw")
|
|
111
|
+
* @param props.block - Display as block element
|
|
112
|
+
* @param props.preload - Enable preloading for LCP images
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```tsx
|
|
116
|
+
* // Basic usage with aspect ratio
|
|
117
|
+
* <Image
|
|
118
|
+
* src="/hero.jpg"
|
|
119
|
+
* alt="Hero image"
|
|
120
|
+
* aspectRatio={16/9}
|
|
121
|
+
* />
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```tsx
|
|
126
|
+
* // LCP image with preload
|
|
127
|
+
* <Image
|
|
128
|
+
* src="/hero.jpg"
|
|
129
|
+
* alt="Hero image"
|
|
130
|
+
* aspectRatio={16/9}
|
|
131
|
+
* preload // Preloads image for LCP
|
|
132
|
+
* />
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```tsx
|
|
137
|
+
* // Responsive grid image
|
|
138
|
+
* <Image
|
|
139
|
+
* src="/product.jpg"
|
|
140
|
+
* alt="Product"
|
|
141
|
+
* aspectRatio={1}
|
|
142
|
+
* mobileSize="100vw"
|
|
143
|
+
* desktopSize="33vw"
|
|
144
|
+
* />
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export function Image({
|
|
148
|
+
style,
|
|
149
|
+
className,
|
|
150
|
+
loading,
|
|
151
|
+
objectFit = "cover",
|
|
152
|
+
quality = 90,
|
|
153
|
+
alt = "",
|
|
154
|
+
fill,
|
|
155
|
+
block = !fill,
|
|
156
|
+
width = block ? 1 : undefined,
|
|
157
|
+
height = block ? 1 : undefined,
|
|
158
|
+
mobileSize = "100vw",
|
|
159
|
+
desktopSize = "100vw",
|
|
160
|
+
sizes,
|
|
161
|
+
src,
|
|
162
|
+
unoptimized,
|
|
163
|
+
ref,
|
|
164
|
+
aspectRatio,
|
|
165
|
+
placeholder = "blur",
|
|
166
|
+
preload = false,
|
|
167
|
+
...props
|
|
168
|
+
}: ImageProps) {
|
|
169
|
+
// Determine loading strategy
|
|
170
|
+
const finalLoading = loading ?? (preload ? "eager" : "lazy")
|
|
171
|
+
|
|
172
|
+
// Generate responsive sizes if not provided
|
|
173
|
+
const finalSizes =
|
|
174
|
+
sizes ||
|
|
175
|
+
`(max-width: ${breakpoints.desktop}px) ${mobileSize}, ${desktopSize}`
|
|
176
|
+
|
|
177
|
+
// Early return after hooks
|
|
178
|
+
if (!src) return null
|
|
179
|
+
|
|
180
|
+
// Determine SVG status and placeholder logic
|
|
181
|
+
const isSvg = typeof src === "string" && src.includes(".svg")
|
|
182
|
+
const shouldUsePlaceholder = shouldUseBlurPlaceholder(
|
|
183
|
+
src,
|
|
184
|
+
placeholder,
|
|
185
|
+
props.blurDataURL
|
|
186
|
+
)
|
|
187
|
+
const blurDataURL = generateBlurDataURL(
|
|
188
|
+
shouldUsePlaceholder,
|
|
189
|
+
aspectRatio,
|
|
190
|
+
props.blurDataURL
|
|
191
|
+
)
|
|
192
|
+
const finalPlaceholder = getFinalPlaceholder(
|
|
193
|
+
shouldUsePlaceholder,
|
|
194
|
+
aspectRatio,
|
|
195
|
+
props.blurDataURL,
|
|
196
|
+
placeholder
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<NextImage
|
|
201
|
+
ref={ref}
|
|
202
|
+
fill={!block}
|
|
203
|
+
{...(width !== undefined && { width })}
|
|
204
|
+
{...(height !== undefined && { height })}
|
|
205
|
+
loading={finalLoading}
|
|
206
|
+
quality={quality}
|
|
207
|
+
alt={alt}
|
|
208
|
+
style={{
|
|
209
|
+
objectFit,
|
|
210
|
+
...style,
|
|
211
|
+
}}
|
|
212
|
+
className={cn(className, block && s.block)}
|
|
213
|
+
sizes={finalSizes}
|
|
214
|
+
src={src}
|
|
215
|
+
unoptimized={unoptimized || isSvg}
|
|
216
|
+
draggable={false}
|
|
217
|
+
onDragStart={(e) => e.preventDefault()}
|
|
218
|
+
{...(finalPlaceholder && { placeholder: finalPlaceholder })}
|
|
219
|
+
{...(blurDataURL && { blurDataURL })}
|
|
220
|
+
preload={preload}
|
|
221
|
+
{...props}
|
|
222
|
+
/>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import NextLink from "next/link"
|
|
4
|
+
import { usePathname } from "next/navigation"
|
|
5
|
+
import {
|
|
6
|
+
type AnchorHTMLAttributes,
|
|
7
|
+
type ComponentProps,
|
|
8
|
+
type MouseEvent,
|
|
9
|
+
useEffect,
|
|
10
|
+
useState,
|
|
11
|
+
} from "react"
|
|
12
|
+
|
|
13
|
+
// Helper to extract props safe for button elements
|
|
14
|
+
function getButtonProps(props: Record<string, unknown>) {
|
|
15
|
+
const {
|
|
16
|
+
href,
|
|
17
|
+
target,
|
|
18
|
+
rel,
|
|
19
|
+
"data-external": _dataExternal,
|
|
20
|
+
...buttonProps
|
|
21
|
+
} = props
|
|
22
|
+
return buttonProps
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Helper to extract props safe for div elements
|
|
26
|
+
function getDivProps(props: Record<string, unknown>) {
|
|
27
|
+
const {
|
|
28
|
+
href,
|
|
29
|
+
target,
|
|
30
|
+
rel,
|
|
31
|
+
onClick,
|
|
32
|
+
"data-external": _dataExternal,
|
|
33
|
+
...divProps
|
|
34
|
+
} = props
|
|
35
|
+
return divProps
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type CustomLinkProps = Omit<
|
|
39
|
+
AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
40
|
+
keyof ComponentProps<typeof NextLink> | "href"
|
|
41
|
+
> &
|
|
42
|
+
Omit<ComponentProps<typeof NextLink>, "href"> & {
|
|
43
|
+
href?: string
|
|
44
|
+
onClick?: (e: MouseEvent<HTMLElement>) => void
|
|
45
|
+
scroll?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function Link({
|
|
49
|
+
href,
|
|
50
|
+
children,
|
|
51
|
+
onClick,
|
|
52
|
+
scroll = false, // Default to false to prevent scroll restoration warnings with fixed/sticky elements
|
|
53
|
+
...props
|
|
54
|
+
}: CustomLinkProps) {
|
|
55
|
+
const [shouldPrefetch, setShouldPrefetch] = useState(false)
|
|
56
|
+
const [isExternal, setIsExternal] = useState(false)
|
|
57
|
+
const [isActive, setIsActive] = useState(false)
|
|
58
|
+
|
|
59
|
+
// Get pathname - deferred to avoid blocking static generation
|
|
60
|
+
// usePathname is safe to call but we defer the active check to useEffect
|
|
61
|
+
const pathname = usePathname()
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
// Check if this link is active (current page)
|
|
65
|
+
if (href && pathname) {
|
|
66
|
+
setIsActive(pathname === href)
|
|
67
|
+
}
|
|
68
|
+
}, [href, pathname])
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
// Skip if no href
|
|
72
|
+
if (!href) return
|
|
73
|
+
|
|
74
|
+
// Check if external link
|
|
75
|
+
try {
|
|
76
|
+
const url = new URL(href, window.location.href)
|
|
77
|
+
setIsExternal(url.host !== window.location.host)
|
|
78
|
+
} catch {
|
|
79
|
+
setIsExternal(false)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Only prefetch on good connections
|
|
83
|
+
const connection = (
|
|
84
|
+
navigator as Navigator & {
|
|
85
|
+
connection?: { effectiveType: string; saveData: boolean }
|
|
86
|
+
}
|
|
87
|
+
).connection
|
|
88
|
+
if (connection) {
|
|
89
|
+
const { effectiveType, saveData } = connection
|
|
90
|
+
setShouldPrefetch(effectiveType === "4g" && !saveData)
|
|
91
|
+
} else {
|
|
92
|
+
// Default to prefetching if API not available
|
|
93
|
+
setShouldPrefetch(true)
|
|
94
|
+
}
|
|
95
|
+
}, [href])
|
|
96
|
+
|
|
97
|
+
// If no href is provided but there's an onClick, render a button
|
|
98
|
+
if (!href && onClick) {
|
|
99
|
+
return (
|
|
100
|
+
<button
|
|
101
|
+
onClick={(e: MouseEvent<HTMLButtonElement>) => onClick(e)}
|
|
102
|
+
type="button"
|
|
103
|
+
{...getButtonProps(props)}
|
|
104
|
+
>
|
|
105
|
+
{children}
|
|
106
|
+
</button>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// If no href and no onClick, render a div
|
|
111
|
+
if (!href) {
|
|
112
|
+
return <div {...getDivProps(props)}>{children}</div>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// For SSR, check if it's external based on the href pattern
|
|
116
|
+
const isExternalSSR =
|
|
117
|
+
href.startsWith("http://") || href.startsWith("https://")
|
|
118
|
+
|
|
119
|
+
if (isExternalSSR || isExternal) {
|
|
120
|
+
return (
|
|
121
|
+
<a
|
|
122
|
+
href={href}
|
|
123
|
+
target="_blank"
|
|
124
|
+
rel="noopener noreferrer"
|
|
125
|
+
data-external
|
|
126
|
+
onClick={onClick}
|
|
127
|
+
{...props}
|
|
128
|
+
>
|
|
129
|
+
{children}
|
|
130
|
+
</a>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<NextLink
|
|
136
|
+
href={href as ComponentProps<typeof NextLink>["href"]}
|
|
137
|
+
prefetch={shouldPrefetch}
|
|
138
|
+
scroll={scroll}
|
|
139
|
+
data-active={isActive || undefined}
|
|
140
|
+
{...(onClick && { onClick })}
|
|
141
|
+
{...props}
|
|
142
|
+
>
|
|
143
|
+
{children}
|
|
144
|
+
</NextLink>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Lib
|
|
2
|
+
|
|
3
|
+
Non-UI code: hooks, integrations, styles, and utilities.
|
|
4
|
+
|
|
5
|
+
> **Rule**: Renders UI → `components/` · Everything else → `lib/`
|
|
6
|
+
|
|
7
|
+
## Quick Imports
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
// Utilities - explicit imports for better tree-shaking
|
|
11
|
+
import { clamp, lerp } from '@/lib/utils/math'
|
|
12
|
+
import { slugify } from '@/lib/utils/strings'
|
|
13
|
+
|
|
14
|
+
// Styles
|
|
15
|
+
import { colors, themes, breakpoints } from '@/lib/styles/config'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Directories
|
|
19
|
+
|
|
20
|
+
| Directory | Purpose | Optional? |
|
|
21
|
+
|-----------|---------|-----------|
|
|
22
|
+
| [hooks/](hooks/README.md) | React hooks + Zustand store | ❌ Core |
|
|
23
|
+
| [styles/](styles/README.md) | CSS & Tailwind config | ❌ Core |
|
|
24
|
+
| [utils/](utils/README.md) | Pure utility functions | ❌ Core |
|
|
25
|
+
|
|
26
|
+
## Scripts
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bun dev # Start dev server
|
|
30
|
+
bun run generate # Generate pages/components
|
|
31
|
+
bun run setup:project # Configure integrations
|
|
32
|
+
bun setup:styles # Regenerate CSS
|
|
33
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom React Hooks
|
|
3
|
+
*
|
|
4
|
+
* Import individual hooks:
|
|
5
|
+
* import { useDeviceDetection } from '@/hooks/use-device-detection'
|
|
6
|
+
*
|
|
7
|
+
* Or import from barrel:
|
|
8
|
+
* import { useDeviceDetection, usePrefetch } from '@/hooks/use-device-detection'
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export { useDeviceDetection } from "./use-device-detection"
|
|
12
|
+
export { usePrefetch } from "./use-prefetch"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useEffect, useState } from "react"
|
|
2
|
+
import { useMedia } from "react-use"
|
|
3
|
+
import { breakpoints } from "@/lib/styles/config"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook for detecting device capabilities and characteristics.
|
|
7
|
+
*
|
|
8
|
+
* Provides comprehensive device detection including screen size, input methods,
|
|
9
|
+
* performance preferences, and browser capabilities. Useful for responsive design,
|
|
10
|
+
* performance optimization, and feature detection.
|
|
11
|
+
*
|
|
12
|
+
* @returns Object with device detection results
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { useDeviceDetection } from '@/hooks/use-device-detection'
|
|
17
|
+
*
|
|
18
|
+
* function ResponsiveComponent() {
|
|
19
|
+
* const {
|
|
20
|
+
* isMobile,
|
|
21
|
+
* isDesktop,
|
|
22
|
+
* isReducedMotion,
|
|
23
|
+
* isWebGL,
|
|
24
|
+
* isLowPowerMode,
|
|
25
|
+
* dpr,
|
|
26
|
+
* isSafari
|
|
27
|
+
* } = useDeviceDetection()
|
|
28
|
+
*
|
|
29
|
+
* // Adapt behavior based on device capabilities
|
|
30
|
+
* if (isReducedMotion) {
|
|
31
|
+
* // Disable animations
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* if (isWebGL && !isLowPowerMode) {
|
|
35
|
+
* // Enable WebGL features
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* return (
|
|
39
|
+
* <div>
|
|
40
|
+
* {isMobile ? <MobileLayout /> : <DesktopLayout />}
|
|
41
|
+
* {isSafari && <SafariSpecificStyles />}
|
|
42
|
+
* </div>
|
|
43
|
+
* )
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* // Performance optimizations
|
|
50
|
+
* const { isLowPowerMode, dpr, isReducedMotion } = useDeviceDetection()
|
|
51
|
+
*
|
|
52
|
+
* // Reduce quality on low-power devices
|
|
53
|
+
* const quality = isLowPowerMode ? 'low' : 'high'
|
|
54
|
+
* const pixelRatio = Math.min(dpr || 1, 2) // Cap DPR
|
|
55
|
+
*
|
|
56
|
+
* // Respect user motion preferences
|
|
57
|
+
* const enableAnimations = !isReducedMotion
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function useDeviceDetection() {
|
|
61
|
+
const isMobile = useMedia(`(max-width: ${breakpoints.mobile - 1}px)`, true)
|
|
62
|
+
const isReducedMotion = useMedia("(prefers-reduced-motion: reduce)")
|
|
63
|
+
const [dpr, setDpr] = useState<number | undefined>(undefined)
|
|
64
|
+
const [isSafari, setIsSafari] = useState<boolean | undefined>(undefined)
|
|
65
|
+
|
|
66
|
+
// Check for low power mode with fallback for unsupported browsers
|
|
67
|
+
const isTouchDevice = useMedia("(any-pointer: coarse) and (hover: none)")
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
setDpr(window.devicePixelRatio)
|
|
71
|
+
setIsSafari(/^((?!chrome|android).)*safari/i.test(navigator.userAgent))
|
|
72
|
+
}, [])
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
isMobile,
|
|
76
|
+
isReducedMotion,
|
|
77
|
+
isTouchDevice,
|
|
78
|
+
dpr,
|
|
79
|
+
isSafari,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useMedia } from "react-use"
|
|
2
|
+
import { breakpoints } from "@/lib/styles/config"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook for detecting if the viewport is at a specific breakpoint.
|
|
6
|
+
*
|
|
7
|
+
* @param breakpoint - The breakpoint to detect
|
|
8
|
+
* @returns True if the viewport is at the breakpoint
|
|
9
|
+
*/
|
|
10
|
+
export function useMediaBreakpoint(
|
|
11
|
+
breakpoint: keyof typeof breakpoints,
|
|
12
|
+
defaultState = false
|
|
13
|
+
) {
|
|
14
|
+
return useMedia(`(min-width: ${breakpoints[breakpoint]}px)`, defaultState)
|
|
15
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Route } from "next"
|
|
2
|
+
import { useRouter } from "next/navigation"
|
|
3
|
+
import { useEffect, useRef } from "react"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook to prefetch a route when an element becomes visible in the viewport
|
|
7
|
+
* @param href - The route to prefetch
|
|
8
|
+
* @param options - Intersection Observer options
|
|
9
|
+
* @returns ref to attach to the element that should trigger prefetching
|
|
10
|
+
*/
|
|
11
|
+
export function usePrefetch<T extends HTMLElement = HTMLElement>(
|
|
12
|
+
href: Route | null | undefined,
|
|
13
|
+
options?: IntersectionObserverInit
|
|
14
|
+
) {
|
|
15
|
+
const ref = useRef<T>(null)
|
|
16
|
+
const router = useRouter()
|
|
17
|
+
const prefetchedRef = useRef(false)
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
// Early return if no href or already prefetched
|
|
21
|
+
if (!href || prefetchedRef.current) return
|
|
22
|
+
|
|
23
|
+
const element = ref.current
|
|
24
|
+
if (!element) return
|
|
25
|
+
|
|
26
|
+
// Reset prefetched state when href changes
|
|
27
|
+
prefetchedRef.current = false
|
|
28
|
+
|
|
29
|
+
const handleIntersection = (entries: IntersectionObserverEntry[]) => {
|
|
30
|
+
const [entry] = entries
|
|
31
|
+
if (entry?.isIntersecting && !prefetchedRef.current) {
|
|
32
|
+
// Check network conditions before prefetching
|
|
33
|
+
const connection = (
|
|
34
|
+
navigator as Navigator & {
|
|
35
|
+
connection?: NetworkInformation
|
|
36
|
+
}
|
|
37
|
+
).connection
|
|
38
|
+
|
|
39
|
+
const shouldPrefetch =
|
|
40
|
+
!connection ||
|
|
41
|
+
(connection.effectiveType !== "slow-2g" &&
|
|
42
|
+
connection.effectiveType !== "2g" &&
|
|
43
|
+
!connection.saveData)
|
|
44
|
+
|
|
45
|
+
if (shouldPrefetch) {
|
|
46
|
+
router.prefetch(href)
|
|
47
|
+
prefetchedRef.current = true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const observer = new IntersectionObserver(handleIntersection, {
|
|
53
|
+
rootMargin: "50px",
|
|
54
|
+
...options,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
observer.observe(element)
|
|
58
|
+
|
|
59
|
+
return () => {
|
|
60
|
+
observer.disconnect()
|
|
61
|
+
}
|
|
62
|
+
}, [href, options, router])
|
|
63
|
+
|
|
64
|
+
// Return null ref if href is not provided
|
|
65
|
+
return href ? ref : { current: null }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// TypeScript types for Network Information API
|
|
69
|
+
interface NetworkInformation {
|
|
70
|
+
readonly effectiveType: "slow-2g" | "2g" | "3g" | "4g"
|
|
71
|
+
readonly saveData: boolean
|
|
72
|
+
readonly rtt?: number
|
|
73
|
+
readonly downlink?: number
|
|
74
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform parallel dev script
|
|
3
|
+
* Replaces npm-run-all for running multiple processes simultaneously
|
|
4
|
+
* Works on Windows, macOS, and Linux using Bun's native APIs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { bunExecutable, colorEnv } from "./utils"
|
|
8
|
+
|
|
9
|
+
const isHttps = process.argv.includes("--https")
|
|
10
|
+
const isInspect = process.argv.includes("--inspect")
|
|
11
|
+
|
|
12
|
+
// Build next dev command args
|
|
13
|
+
const nextDevArgs = [bunExecutable, "next", "dev"]
|
|
14
|
+
if (isHttps) nextDevArgs.push("--experimental-https")
|
|
15
|
+
if (isInspect) nextDevArgs.push("--inspect")
|
|
16
|
+
|
|
17
|
+
// Build environment with FORCE_COLOR
|
|
18
|
+
const devEnv = colorEnv()
|
|
19
|
+
|
|
20
|
+
const processes = [
|
|
21
|
+
// Style watcher
|
|
22
|
+
Bun.spawn(
|
|
23
|
+
[bunExecutable, "--watch", "./lib/styles/scripts/setup-styles.ts"],
|
|
24
|
+
{
|
|
25
|
+
stdout: "inherit",
|
|
26
|
+
stderr: "inherit",
|
|
27
|
+
env: devEnv,
|
|
28
|
+
}
|
|
29
|
+
),
|
|
30
|
+
|
|
31
|
+
// Next.js dev server
|
|
32
|
+
Bun.spawn(nextDevArgs, {
|
|
33
|
+
stdout: "inherit",
|
|
34
|
+
stderr: "inherit",
|
|
35
|
+
env: devEnv,
|
|
36
|
+
}),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
// Handle graceful shutdown
|
|
40
|
+
const cleanup = () => {
|
|
41
|
+
for (const proc of processes) {
|
|
42
|
+
proc.kill()
|
|
43
|
+
}
|
|
44
|
+
process.exit(0)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
process.on("SIGINT", cleanup)
|
|
48
|
+
process.on("SIGTERM", cleanup)
|
|
49
|
+
|
|
50
|
+
// Wait for any process to exit (if one crashes, we want to know)
|
|
51
|
+
await Promise.race(processes.map((p) => p.exited))
|
|
52
|
+
cleanup()
|