bsmnt 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +2 -0
- package/CHANGELOG.md +31 -0
- package/CLAUDE.md +42 -24
- package/README.md +33 -12
- package/biome.json +1 -0
- package/bun.lock +2 -1
- package/docs/architecture.drawio +250 -0
- package/docs/architecture.mermaid +85 -0
- package/package.json +42 -42
- package/{bin → packages/cli/bin}/index.js +28 -31
- package/packages/cli/package.json +16 -0
- package/{src → packages/cli/src}/commands/add-integration.js +22 -37
- package/{src → packages/cli/src}/commands/create.js +125 -131
- 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 +5 -33
- package/{src → packages/create-basement-app/src}/mergers/index.js +89 -98
- 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 +38 -0
- package/packages/create-basement-app/templates/webgl/components/webgl/components/scene/index.tsx +29 -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/renderer.ts +7 -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/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/integrations/basehub/README.md +0 -3
- 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/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
package/packages/create-basement-app/templates/experiment/lib/styles/scripts/postcss-functions.mjs
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// THIS FILE HAS TO STAY .mjs AS ITS CONSUMED BY POSTCSS
|
|
2
|
+
import { breakpoints } from "../layout.mjs"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a context size to a pixel value.
|
|
6
|
+
* @param {string | number} context - Context identifier or pixel value
|
|
7
|
+
* @returns {number} Pixel value
|
|
8
|
+
*/
|
|
9
|
+
function resolveContext(context) {
|
|
10
|
+
if (typeof context === "number") {
|
|
11
|
+
return context
|
|
12
|
+
}
|
|
13
|
+
const numContext = Number.parseFloat(context)
|
|
14
|
+
if (!Number.isNaN(numContext)) {
|
|
15
|
+
return numContext
|
|
16
|
+
}
|
|
17
|
+
return breakpoints[context] ?? breakpoints.desktop
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validates and parses a pixel value.
|
|
22
|
+
* @param {string} value - Pixel value to parse
|
|
23
|
+
* @returns {number} Parsed pixel value
|
|
24
|
+
*/
|
|
25
|
+
function parsePixels(value) {
|
|
26
|
+
const numValue = Number.parseFloat(value)
|
|
27
|
+
if (Number.isNaN(numValue)) {
|
|
28
|
+
throw new Error(`Invalid pixel value: ${value}`)
|
|
29
|
+
}
|
|
30
|
+
return numValue
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Rounds a number to a maximum of 4 decimal places, removing trailing zeros.
|
|
35
|
+
* @param {number} value - Number to round
|
|
36
|
+
* @returns {string} Rounded number as string without trailing zeros
|
|
37
|
+
*/
|
|
38
|
+
function roundToMaxDecimals(value) {
|
|
39
|
+
// Round to 4 decimal places
|
|
40
|
+
const rounded = Math.round(value * 10000) / 10000
|
|
41
|
+
// Convert to string and remove trailing zeros only after decimal point
|
|
42
|
+
return rounded
|
|
43
|
+
.toString()
|
|
44
|
+
.replace(/\.0+$/, "")
|
|
45
|
+
.replace(/(\.\d*?)0+$/, "$1")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const functions = {
|
|
49
|
+
/**
|
|
50
|
+
* Converts a pixel value to viewport width units (vw).
|
|
51
|
+
* Optionally applies a minimum value using CSS max().
|
|
52
|
+
*
|
|
53
|
+
* Supports flexible parameter patterns:
|
|
54
|
+
* - `tovw(target)` - Uses default desktop context
|
|
55
|
+
* - `tovw(target, min)` - Sets minimum value (numeric)
|
|
56
|
+
* - `tovw(target, context)` - Sets context (string identifier)
|
|
57
|
+
* - `tovw(target, min, context)` - Sets both min and context
|
|
58
|
+
*
|
|
59
|
+
* @param {string} target - Target pixel value to convert
|
|
60
|
+
* @param {string} [minOrContext] - Minimum pixel value OR context identifier
|
|
61
|
+
* @param {string} [context] - Context size identifier (if min was provided)
|
|
62
|
+
* @returns {string} CSS string with vw units
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* tovw(100) // "6.94vw"
|
|
66
|
+
* tovw(100, 50) // "max(50px, 6.94vw)"
|
|
67
|
+
* tovw(16, 'mobile') // "4.27vw" (no need for undefined!)
|
|
68
|
+
* tovw(100, 50, 'mobile') // "max(50px, 6.94vw)" with mobile context
|
|
69
|
+
*/
|
|
70
|
+
tovw: (target, minOrContext, context) => {
|
|
71
|
+
const numTarget = parsePixels(target)
|
|
72
|
+
if (numTarget === 0) {
|
|
73
|
+
return "0"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let min
|
|
77
|
+
let resolvedContext = "desktop"
|
|
78
|
+
|
|
79
|
+
if (minOrContext !== undefined && minOrContext !== "") {
|
|
80
|
+
// Fast check: is it a known breakpoint key?
|
|
81
|
+
if (minOrContext in breakpoints) {
|
|
82
|
+
// Second param is context
|
|
83
|
+
resolvedContext = minOrContext
|
|
84
|
+
if (context !== undefined && context !== "") {
|
|
85
|
+
resolvedContext = context
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
// Second param is min value (numeric)
|
|
89
|
+
min = parsePixels(minOrContext)
|
|
90
|
+
if (context !== undefined && context !== "") {
|
|
91
|
+
resolvedContext = context
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} else if (context !== undefined && context !== "") {
|
|
95
|
+
// Only context provided as third param (legacy support)
|
|
96
|
+
resolvedContext = context
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const contextSize = resolveContext(resolvedContext)
|
|
100
|
+
const vwValue = (numTarget / contextSize) * 100
|
|
101
|
+
|
|
102
|
+
if (min !== undefined) {
|
|
103
|
+
return `max(${min}px, ${roundToMaxDecimals(vwValue)}vw)`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return `${roundToMaxDecimals(vwValue)}vw`
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Converts a pixel value to rem units.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} target - Target pixel value to convert
|
|
113
|
+
* @param {string} [context='16'] - Base font size in pixels
|
|
114
|
+
* @returns {string} CSS string with rem units
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* torem(24) // "1.5rem"
|
|
118
|
+
* torem(18, 14) // "1.29rem"
|
|
119
|
+
*/
|
|
120
|
+
torem: (target, context = "16") => {
|
|
121
|
+
const numTarget = parsePixels(target)
|
|
122
|
+
if (numTarget === 0) {
|
|
123
|
+
return "0"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const numContext = parsePixels(context)
|
|
127
|
+
return `${roundToMaxDecimals(numTarget / numContext)}rem`
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Converts a pixel value to em units.
|
|
132
|
+
*
|
|
133
|
+
* @param {string} target - Target pixel value to convert
|
|
134
|
+
* @param {string} context - Context size in pixels (required)
|
|
135
|
+
* @returns {string} CSS string with em units
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* toem(24, 16) // "1.5em"
|
|
139
|
+
* toem(18, 14) // "1.29em"
|
|
140
|
+
*/
|
|
141
|
+
toem: (target, context) => {
|
|
142
|
+
const numTarget = parsePixels(target)
|
|
143
|
+
if (numTarget === 0) {
|
|
144
|
+
return "0"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!context) {
|
|
148
|
+
throw new Error("toem requires a context parameter")
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const numContext = parsePixels(context)
|
|
152
|
+
return `${roundToMaxDecimals(numTarget / numContext)}em`
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Calculates column width based on number of columns.
|
|
157
|
+
*
|
|
158
|
+
* @param {string} columns - Number of columns
|
|
159
|
+
* @returns {string} CSS calc expression
|
|
160
|
+
*/
|
|
161
|
+
columns: (columns) => {
|
|
162
|
+
const numColumns = Number.parseFloat(columns)
|
|
163
|
+
if (Number.isNaN(numColumns)) {
|
|
164
|
+
throw new Error(`Invalid column value: ${columns}`)
|
|
165
|
+
}
|
|
166
|
+
return `calc((${numColumns} * var(--column-width)) + ((${numColumns} - 1) * var(--gap)))`
|
|
167
|
+
},
|
|
168
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as config from "../config"
|
|
2
|
+
import { generateRoot } from "./generate-root"
|
|
3
|
+
import { generateTailwind } from "./generate-tailwind"
|
|
4
|
+
|
|
5
|
+
const tailwind = generateTailwind(config)
|
|
6
|
+
const root = generateRoot(config)
|
|
7
|
+
|
|
8
|
+
const banner = `/*
|
|
9
|
+
* THIS FILE IS GENERATED BY setup-styles.ts
|
|
10
|
+
* DO NOT EDIT IT DIRECTLY.
|
|
11
|
+
*/`
|
|
12
|
+
|
|
13
|
+
const tailwindcss = [banner, tailwind]
|
|
14
|
+
const rootcss = [banner, root]
|
|
15
|
+
|
|
16
|
+
await Bun.write("./lib/styles/css/tailwind.css", tailwindcss.join("\n\n"))
|
|
17
|
+
await Bun.write("./lib/styles/css/root.css", rootcss.join("\n\n"))
|
|
18
|
+
|
|
19
|
+
console.log(
|
|
20
|
+
Bun.color("green", "ansi"),
|
|
21
|
+
"✓",
|
|
22
|
+
Bun.color("black", "ansi"),
|
|
23
|
+
"Style config generated successfully"
|
|
24
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function scalingCalc(value: number) {
|
|
2
|
+
return `calc(((${value} * 100) / var(--device-width)) * 1vw)`
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Format an object into a string of CSS variables
|
|
7
|
+
* @param obj - The object to format
|
|
8
|
+
* @param mapper - A function that maps the object's entries to a string
|
|
9
|
+
* @param joiner - The string to join the mapped entries with
|
|
10
|
+
* @returns A string of CSS variables
|
|
11
|
+
*/
|
|
12
|
+
export function formatObject<Obj extends Record<string, unknown>>(
|
|
13
|
+
obj: Obj,
|
|
14
|
+
mapper: (args: [key: keyof Obj, value: Obj[keyof Obj]]) => string,
|
|
15
|
+
joiner = "\n\t"
|
|
16
|
+
) {
|
|
17
|
+
return Object.entries(obj)
|
|
18
|
+
.map(([key, value]) => mapper([key as keyof Obj, value as Obj[keyof Obj]]))
|
|
19
|
+
.join(joiner)
|
|
20
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CSSProperties } from "react"
|
|
2
|
+
|
|
3
|
+
const fonts = {
|
|
4
|
+
mono: "--geist-mono", // this should be the variable name defined in fonts.ts
|
|
5
|
+
} as const
|
|
6
|
+
|
|
7
|
+
const typography: TypeStyles = {
|
|
8
|
+
"test-mono": {
|
|
9
|
+
"font-family": `var(${fonts.mono})`,
|
|
10
|
+
"font-style": "normal",
|
|
11
|
+
"font-weight": 400,
|
|
12
|
+
"line-height": "90%",
|
|
13
|
+
"letter-spacing": "0em",
|
|
14
|
+
"font-size": { mobile: 20, desktop: 24 },
|
|
15
|
+
},
|
|
16
|
+
} as const
|
|
17
|
+
|
|
18
|
+
export { fonts, typography }
|
|
19
|
+
|
|
20
|
+
// UTIL TYPES
|
|
21
|
+
type TypeStyles = Record<
|
|
22
|
+
string,
|
|
23
|
+
{
|
|
24
|
+
"font-family": string
|
|
25
|
+
"font-style": CSSProperties["fontStyle"]
|
|
26
|
+
"font-weight": CSSProperties["fontWeight"]
|
|
27
|
+
"line-height":
|
|
28
|
+
| `${number}%`
|
|
29
|
+
| { mobile: `${number}%`; desktop: `${number}%` }
|
|
30
|
+
"letter-spacing":
|
|
31
|
+
| `${number}em`
|
|
32
|
+
| { mobile: `${number}em`; desktop: `${number}em` }
|
|
33
|
+
"font-feature-settings"?: string
|
|
34
|
+
"font-size": number | { mobile: number; desktop: number }
|
|
35
|
+
}
|
|
36
|
+
>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Utils
|
|
2
|
+
|
|
3
|
+
Pure utility functions organized by concern.
|
|
4
|
+
|
|
5
|
+
Use explicit imports for clarity and better tree-shaking:
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { clamp, lerp, mapRange } from '@/utils/math'
|
|
9
|
+
import { slugify } from '@/utils/strings'
|
|
10
|
+
import { generatePageMetadata } from '@/utils/metadata'
|
|
11
|
+
import { tovw, torem, toem } from '@/utils/viewport'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Modules
|
|
15
|
+
|
|
16
|
+
| Module | Functions |
|
|
17
|
+
|--------|-----------|
|
|
18
|
+
| `math` | `clamp`, `lerp`, `mapRange`, `modulo`, `normalize`, `distance` |
|
|
19
|
+
| `easings` | All easing functions (`easeOutCubic`, etc.) |
|
|
20
|
+
| `viewport` | `tovw`, `torem`, `toem` |
|
|
21
|
+
| `strings` | `slugify`, `mergeRefs`, `capitalizeFirstLetter` |
|
|
22
|
+
| `metadata` | `generatePageMetadata` |
|
|
23
|
+
|
|
24
|
+
## Common Patterns
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
// Math
|
|
28
|
+
clamp(0, value, 100)
|
|
29
|
+
lerp(0, 100, 0.5) // → 50
|
|
30
|
+
mapRange(0, 1000, scrollY, 0, 1)
|
|
31
|
+
|
|
32
|
+
// Viewport units
|
|
33
|
+
tovw(100, 50, 'desktop') // → "max(50px, 6.94vw)"
|
|
34
|
+
tovw(16, undefined, 'mobile') // → "4.27vw"
|
|
35
|
+
torem(24) // → "1.5rem"
|
|
36
|
+
toem(16, 14) // → "1.14em"
|
|
37
|
+
|
|
38
|
+
// SEO
|
|
39
|
+
export const metadata = generatePageMetadata({ title: 'About' })
|
|
40
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// CSS module type declarations (ambient - no imports allowed)
|
|
2
|
+
|
|
3
|
+
declare module "*.module.css" {
|
|
4
|
+
const classes: { [key: string]: string }
|
|
5
|
+
export default classes
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare module "*.module.scss" {
|
|
9
|
+
const classes: { [key: string]: string }
|
|
10
|
+
export default classes
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare module "*.module.sass" {
|
|
14
|
+
const classes: { [key: string]: string }
|
|
15
|
+
export default classes
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Regular CSS imports
|
|
19
|
+
declare module "*.css"
|
|
20
|
+
declare module "*.scss"
|
|
21
|
+
declare module "*.sass"
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Easing Functions
|
|
3
|
+
*
|
|
4
|
+
* A complete collection of easing curves for animations.
|
|
5
|
+
* All functions take a progress value (0-1) and return an eased value (0-1).
|
|
6
|
+
*
|
|
7
|
+
* @see https://easings.net for visual references
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { easings, type EasingName } from '@/utils/easings'
|
|
12
|
+
*
|
|
13
|
+
* // Direct usage
|
|
14
|
+
* const eased = easings.easeOutCubic(0.5)
|
|
15
|
+
*
|
|
16
|
+
* // With a variable easing name
|
|
17
|
+
* const easingName: EasingName = 'easeInOutQuart'
|
|
18
|
+
* const value = easings[easingName](progress)
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* ## Choosing an Easing
|
|
22
|
+
*
|
|
23
|
+
* | Category | Use Case |
|
|
24
|
+
* |----------|----------|
|
|
25
|
+
* | `easeOut*` | UI feedback, appearing elements (most common) |
|
|
26
|
+
* | `easeIn*` | Exiting elements, building tension |
|
|
27
|
+
* | `easeInOut*` | State transitions, loading indicators |
|
|
28
|
+
* | `*Cubic/Quart` | Natural, balanced motion |
|
|
29
|
+
* | `*Expo` | Dramatic, snappy motion |
|
|
30
|
+
* | `*Elastic/Bounce` | Playful, attention-grabbing |
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const pow = Math.pow
|
|
34
|
+
const sqrt = Math.sqrt
|
|
35
|
+
const sin = Math.sin
|
|
36
|
+
const cos = Math.cos
|
|
37
|
+
const PI = Math.PI
|
|
38
|
+
|
|
39
|
+
// Easing constants
|
|
40
|
+
const c1 = 1.70158
|
|
41
|
+
const c2 = c1 * 1.525
|
|
42
|
+
const c3 = c1 + 1
|
|
43
|
+
const c4 = (2 * PI) / 3
|
|
44
|
+
const c5 = (2 * PI) / 4.5
|
|
45
|
+
|
|
46
|
+
/** Bounce out helper (used by bounce easings) */
|
|
47
|
+
const bounceOut = (x: number): number => {
|
|
48
|
+
const n1 = 7.5625
|
|
49
|
+
const d1 = 2.75
|
|
50
|
+
|
|
51
|
+
if (x < 1 / d1) {
|
|
52
|
+
return n1 * x * x
|
|
53
|
+
}
|
|
54
|
+
if (x < 2 / d1) {
|
|
55
|
+
return n1 * (x - 1.5 / d1) * x + 0.75
|
|
56
|
+
}
|
|
57
|
+
if (x < 2.5 / d1) {
|
|
58
|
+
return n1 * (x - 2.25 / d1) * x + 0.9375
|
|
59
|
+
}
|
|
60
|
+
return n1 * (x - 2.625 / d1) * x + 0.984375
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Collection of easing functions.
|
|
65
|
+
* Each function takes progress (0-1) and returns eased value (0-1).
|
|
66
|
+
*/
|
|
67
|
+
export const easings = {
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
// Linear (no easing)
|
|
70
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
/** No easing - constant speed */
|
|
73
|
+
linear: (x: number): number => x,
|
|
74
|
+
|
|
75
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
// Quadratic (power of 2)
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
/** Slow start */
|
|
80
|
+
easeInQuad: (x: number): number => x * x,
|
|
81
|
+
|
|
82
|
+
/** Slow end */
|
|
83
|
+
easeOutQuad: (x: number): number => 1 - (1 - x) * (1 - x),
|
|
84
|
+
|
|
85
|
+
/** Slow start and end */
|
|
86
|
+
easeInOutQuad: (x: number): number =>
|
|
87
|
+
x < 0.5 ? 2 * x * x : 1 - pow(-2 * x + 2, 2) / 2,
|
|
88
|
+
|
|
89
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
+
// Cubic (power of 3) - Most commonly used
|
|
91
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
/** Slow start, natural feel */
|
|
94
|
+
easeInCubic: (x: number): number => x * x * x,
|
|
95
|
+
|
|
96
|
+
/** Slow end, natural feel - great for UI interactions */
|
|
97
|
+
easeOutCubic: (x: number): number => 1 - pow(1 - x, 3),
|
|
98
|
+
|
|
99
|
+
/** Smooth start and end - great for transitions */
|
|
100
|
+
easeInOutCubic: (x: number): number =>
|
|
101
|
+
x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2,
|
|
102
|
+
|
|
103
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
// Quartic (power of 4)
|
|
105
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/** Slower start than cubic */
|
|
108
|
+
easeInQuart: (x: number): number => x * x * x * x,
|
|
109
|
+
|
|
110
|
+
/** Slower end than cubic */
|
|
111
|
+
easeOutQuart: (x: number): number => 1 - pow(1 - x, 4),
|
|
112
|
+
|
|
113
|
+
/** More pronounced ease than cubic */
|
|
114
|
+
easeInOutQuart: (x: number): number =>
|
|
115
|
+
x < 0.5 ? 8 * x * x * x * x : 1 - pow(-2 * x + 2, 4) / 2,
|
|
116
|
+
|
|
117
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
// Quintic (power of 5)
|
|
119
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/** Very slow start */
|
|
122
|
+
easeInQuint: (x: number): number => x * x * x * x * x,
|
|
123
|
+
|
|
124
|
+
/** Very slow end */
|
|
125
|
+
easeOutQuint: (x: number): number => 1 - pow(1 - x, 5),
|
|
126
|
+
|
|
127
|
+
/** Very pronounced ease */
|
|
128
|
+
easeInOutQuint: (x: number): number =>
|
|
129
|
+
x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2,
|
|
130
|
+
|
|
131
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
132
|
+
// Sinusoidal - Gentle, wave-like
|
|
133
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
/** Gentle slow start */
|
|
136
|
+
easeInSine: (x: number): number => 1 - cos((x * PI) / 2),
|
|
137
|
+
|
|
138
|
+
/** Gentle slow end */
|
|
139
|
+
easeOutSine: (x: number): number => sin((x * PI) / 2),
|
|
140
|
+
|
|
141
|
+
/** Gentle ease both ends */
|
|
142
|
+
easeInOutSine: (x: number): number => -(cos(PI * x) - 1) / 2,
|
|
143
|
+
|
|
144
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
145
|
+
// Exponential - Dramatic, snappy
|
|
146
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
/** Dramatic slow start - almost stationary then fast */
|
|
149
|
+
easeInExpo: (x: number): number => (x === 0 ? 0 : pow(2, 10 * x - 10)),
|
|
150
|
+
|
|
151
|
+
/** Dramatic slow end - fast then almost stops */
|
|
152
|
+
easeOutExpo: (x: number): number => (x === 1 ? 1 : 1 - pow(2, -10 * x)),
|
|
153
|
+
|
|
154
|
+
/** Dramatic both ends */
|
|
155
|
+
easeInOutExpo: (x: number): number => {
|
|
156
|
+
if (x === 0) return 0
|
|
157
|
+
if (x === 1) return 1
|
|
158
|
+
if (x < 0.5) return pow(2, 20 * x - 10) / 2
|
|
159
|
+
return (2 - pow(2, -20 * x + 10)) / 2
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
163
|
+
// Circular - Based on circle quarter
|
|
164
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
/** Circular slow start */
|
|
167
|
+
easeInCirc: (x: number): number => 1 - sqrt(1 - pow(x, 2)),
|
|
168
|
+
|
|
169
|
+
/** Circular slow end */
|
|
170
|
+
easeOutCirc: (x: number): number => sqrt(1 - pow(x - 1, 2)),
|
|
171
|
+
|
|
172
|
+
/** Circular both ends */
|
|
173
|
+
easeInOutCirc: (x: number): number =>
|
|
174
|
+
x < 0.5
|
|
175
|
+
? (1 - sqrt(1 - pow(2 * x, 2))) / 2
|
|
176
|
+
: (sqrt(1 - pow(-2 * x + 2, 2)) + 1) / 2,
|
|
177
|
+
|
|
178
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
179
|
+
// Back - Overshoots then returns
|
|
180
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
/** Pulls back before accelerating */
|
|
183
|
+
easeInBack: (x: number): number => c3 * x * x * x - c1 * x * x,
|
|
184
|
+
|
|
185
|
+
/** Overshoots target then settles */
|
|
186
|
+
easeOutBack: (x: number): number =>
|
|
187
|
+
1 + c3 * pow(x - 1, 3) + c1 * pow(x - 1, 2),
|
|
188
|
+
|
|
189
|
+
/** Pulls back, overshoots, settles */
|
|
190
|
+
easeInOutBack: (x: number): number =>
|
|
191
|
+
x < 0.5
|
|
192
|
+
? (pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2
|
|
193
|
+
: (pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2,
|
|
194
|
+
|
|
195
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
196
|
+
// Elastic - Spring-like oscillation
|
|
197
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
/** Wobbles at start */
|
|
200
|
+
easeInElastic: (x: number): number => {
|
|
201
|
+
if (x === 0) return 0
|
|
202
|
+
if (x === 1) return 1
|
|
203
|
+
return -pow(2, 10 * x - 10) * sin((x * 10 - 10.75) * c4)
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
/** Wobbles at end - great for attention */
|
|
207
|
+
easeOutElastic: (x: number): number => {
|
|
208
|
+
if (x === 0) return 0
|
|
209
|
+
if (x === 1) return 1
|
|
210
|
+
return pow(2, -10 * x) * sin((x * 10 - 0.75) * c4) + 1
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
/** Wobbles both ends */
|
|
214
|
+
easeInOutElastic: (x: number): number => {
|
|
215
|
+
if (x === 0) return 0
|
|
216
|
+
if (x === 1) return 1
|
|
217
|
+
if (x < 0.5) return -(pow(2, 20 * x - 10) * sin((20 * x - 11.125) * c5)) / 2
|
|
218
|
+
return (pow(2, -20 * x + 10) * sin((20 * x - 11.125) * c5)) / 2 + 1
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
222
|
+
// Bounce - Ball bouncing effect
|
|
223
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
224
|
+
|
|
225
|
+
/** Bounces at start */
|
|
226
|
+
easeInBounce: (x: number): number => 1 - bounceOut(1 - x),
|
|
227
|
+
|
|
228
|
+
/** Bounces at end - like a ball settling */
|
|
229
|
+
easeOutBounce: bounceOut,
|
|
230
|
+
|
|
231
|
+
/** Bounces both ends */
|
|
232
|
+
easeInOutBounce: (x: number): number =>
|
|
233
|
+
x < 0.5 ? (1 - bounceOut(1 - 2 * x)) / 2 : (1 + bounceOut(2 * x - 1)) / 2,
|
|
234
|
+
} as const
|
|
235
|
+
|
|
236
|
+
/** All available easing function names */
|
|
237
|
+
export type EasingName = keyof typeof easings
|
|
238
|
+
|
|
239
|
+
/** An easing function signature */
|
|
240
|
+
export type EasingFunction = (progress: number) => number
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch Utilities
|
|
3
|
+
*
|
|
4
|
+
* Wrapper around fetch that adds timeout protection to prevent hanging requests.
|
|
5
|
+
* Use this for all external API calls to improve reliability.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface FetchWithTimeoutOptions extends RequestInit {
|
|
9
|
+
timeout?: number // Timeout in milliseconds (default: 10000ms)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Fetch with automatic timeout protection
|
|
14
|
+
*
|
|
15
|
+
* @param url - The URL to fetch
|
|
16
|
+
* @param options - Fetch options with optional timeout
|
|
17
|
+
* @returns Promise<Response>
|
|
18
|
+
* @throws AbortError if timeout is reached
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* try {
|
|
23
|
+
* const response = await fetchWithTimeout('https://api.example.com/data', {
|
|
24
|
+
* timeout: 5000, // 5 second timeout
|
|
25
|
+
* method: 'POST',
|
|
26
|
+
* body: JSON.stringify(data)
|
|
27
|
+
* })
|
|
28
|
+
* const result = await response.json()
|
|
29
|
+
* } catch (error) {
|
|
30
|
+
* if (error.name === 'AbortError') {
|
|
31
|
+
* console.error('Request timed out')
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export async function fetchWithTimeout(
|
|
37
|
+
url: string,
|
|
38
|
+
options: FetchWithTimeoutOptions = {}
|
|
39
|
+
): Promise<Response> {
|
|
40
|
+
const { timeout = 10000, signal: externalSignal, ...fetchOptions } = options
|
|
41
|
+
|
|
42
|
+
const controller = new AbortController()
|
|
43
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
|
44
|
+
|
|
45
|
+
// If an external signal is provided, listen to it and abort our controller
|
|
46
|
+
if (externalSignal) {
|
|
47
|
+
externalSignal.addEventListener("abort", () => controller.abort())
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch(url, {
|
|
52
|
+
...fetchOptions,
|
|
53
|
+
signal: controller.signal,
|
|
54
|
+
})
|
|
55
|
+
return response
|
|
56
|
+
} finally {
|
|
57
|
+
clearTimeout(timeoutId)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Fetch JSON with timeout protection
|
|
63
|
+
*
|
|
64
|
+
* Convenience wrapper that automatically parses JSON and handles errors
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const data = await fetchJSON<{ name: string }>('https://api.example.com/user', {
|
|
69
|
+
* timeout: 5000
|
|
70
|
+
* })
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export async function fetchJSON<T = unknown>(
|
|
74
|
+
url: string,
|
|
75
|
+
options: FetchWithTimeoutOptions = {}
|
|
76
|
+
): Promise<T> {
|
|
77
|
+
const response = await fetchWithTimeout(url, options)
|
|
78
|
+
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return response.json() as Promise<T>
|
|
84
|
+
}
|