bsmnt 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/.changeset/2026-02-11-test-patch-bump.md +5 -0
  2. package/.changeset/README.md +10 -0
  3. package/.changeset/config.json +16 -0
  4. package/.cursor/rules/README.md +184 -0
  5. package/.cursor/rules/architecture.mdc +437 -0
  6. package/.cursor/rules/components.mdc +436 -0
  7. package/.cursor/rules/integrations.mdc +447 -0
  8. package/.cursor/rules/main.mdc +278 -0
  9. package/.cursor/rules/styling.mdc +433 -0
  10. package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  11. package/.github/workflows/.gitkeep +0 -0
  12. package/.github/workflows/ci.yml +37 -0
  13. package/.github/workflows/release.yml +54 -0
  14. package/.tldr/cache/call_graph.json +7 -0
  15. package/.tldr/languages.json +6 -0
  16. package/.tldr/status +1 -0
  17. package/.tldrignore +84 -0
  18. package/.vscode/extensions.json +20 -0
  19. package/.vscode/settings.json +98 -0
  20. package/CHANGELOG.md +13 -0
  21. package/CLAUDE.md +138 -0
  22. package/README.md +176 -0
  23. package/bin/index.js +262 -0
  24. package/biome.json +44 -0
  25. package/bun.lock +496 -0
  26. package/changelog/04-02-26.md +86 -0
  27. package/changelog/05-02-26.md +101 -0
  28. package/changelog/09-02-26.md +83 -0
  29. package/docs/fix-studio-hydration.md +46 -0
  30. package/docs/plans/2026-01-29-sanity-smart-merge-design.md +196 -0
  31. package/docs/plans/2026-01-29-sanity-smart-merge-implementation.md +695 -0
  32. package/docs/sanity-setup-steps.md +199 -0
  33. package/integrations/basehub/README.md +3 -0
  34. package/integrations/sanity/app/api/draft-mode/disable/route.ts +7 -0
  35. package/integrations/sanity/app/api/draft-mode/enable/route.ts +21 -0
  36. package/integrations/sanity/app/api/revalidate/route.ts +37 -0
  37. package/integrations/sanity/app/layout.tsx +111 -0
  38. package/integrations/sanity/app/sitemap.ts +80 -0
  39. package/integrations/sanity/app/studio/[[...tool]]/page.tsx +8 -0
  40. package/integrations/sanity/app/studio/layout.tsx +7 -0
  41. package/integrations/sanity/components/ui/sanity-image/index.tsx +37 -0
  42. package/integrations/sanity/lib/integrations/README.md +58 -0
  43. package/integrations/sanity/lib/integrations/check-integration.ts +62 -0
  44. package/integrations/sanity/lib/integrations/sanity/README.md +144 -0
  45. package/integrations/sanity/lib/integrations/sanity/client.ts +30 -0
  46. package/integrations/sanity/lib/integrations/sanity/components/disable-draft-mode.tsx +29 -0
  47. package/integrations/sanity/lib/integrations/sanity/components/rich-text.tsx +73 -0
  48. package/integrations/sanity/lib/integrations/sanity/env.ts +38 -0
  49. package/integrations/sanity/lib/integrations/sanity/live/index.tsx +34 -0
  50. package/integrations/sanity/lib/integrations/sanity/queries.ts +99 -0
  51. package/integrations/sanity/lib/integrations/sanity/sanity.cli.ts +20 -0
  52. package/integrations/sanity/lib/integrations/sanity/sanity.config.ts +94 -0
  53. package/integrations/sanity/lib/integrations/sanity/sanity.types.ts +337 -0
  54. package/integrations/sanity/lib/integrations/sanity/schema.json +1850 -0
  55. package/integrations/sanity/lib/integrations/sanity/schemas/article.ts +132 -0
  56. package/integrations/sanity/lib/integrations/sanity/schemas/example.ts +203 -0
  57. package/integrations/sanity/lib/integrations/sanity/schemas/index.ts +37 -0
  58. package/integrations/sanity/lib/integrations/sanity/schemas/link.ts +127 -0
  59. package/integrations/sanity/lib/integrations/sanity/schemas/metadata.ts +68 -0
  60. package/integrations/sanity/lib/integrations/sanity/schemas/navigation.ts +39 -0
  61. package/integrations/sanity/lib/integrations/sanity/schemas/page.ts +77 -0
  62. package/integrations/sanity/lib/integrations/sanity/schemas/richText.ts +59 -0
  63. package/integrations/sanity/lib/integrations/sanity/structure.ts +5 -0
  64. package/integrations/sanity/lib/integrations/sanity/utils/image.ts +11 -0
  65. package/integrations/sanity/lib/integrations/sanity/utils/link.ts +61 -0
  66. package/integrations/sanity/lib/scripts/copy-sanity-mcp.ts +23 -0
  67. package/integrations/sanity/lib/scripts/generate-page.ts +310 -0
  68. package/integrations/sanity/lib/utils/metadata.ts +190 -0
  69. package/layers/experiment/components/layout/header/index.tsx +58 -0
  70. package/layers/experiment/components/layout/navigation-menu.tsx +127 -0
  71. package/layers/experiment/lib/constants.ts +12 -0
  72. package/layers/webgl/app/page.tsx +10 -0
  73. package/layers/webgl/components/webgl/canvas/dynamic.tsx +34 -0
  74. package/layers/webgl/components/webgl/canvas/index.tsx +43 -0
  75. package/layers/webgl/components/webgl/components/scene/index.tsx +21 -0
  76. package/layers/webgpu/.gitkeep +0 -0
  77. package/package.json +44 -0
  78. package/plugins/README.md +21 -0
  79. package/plugins/no-anchor-element.grit +11 -0
  80. package/plugins/no-relative-parent-imports.grit +6 -0
  81. package/plugins/no-unnecessary-forwardref.grit +5 -0
  82. package/src/commands/add-integration.js +325 -0
  83. package/src/commands/create.js +415 -0
  84. package/src/commands/setup-sanity.js +426 -0
  85. package/src/commands/worktree.js +805 -0
  86. package/src/mergers/check-integration-merger.js +105 -0
  87. package/src/mergers/config.js +137 -0
  88. package/src/mergers/index.js +355 -0
  89. package/src/mergers/layout-merger.js +223 -0
  90. package/src/mergers/next-config-merger.js +63 -0
  91. package/src/mergers/sitemap-merger.js +121 -0
  92. package/tasks/prd-next-starter-dynamic-layers.md +184 -0
  93. package/tasks/prd.json +153 -0
  94. package/tasks/progress.txt +115 -0
  95. package/template-hooks/use-battery.ts +126 -0
  96. package/template-hooks/use-device-perf.ts +184 -0
  97. package/template-hooks/use-intersection-observer.ts +32 -0
  98. package/template-hooks/use-media.ts +33 -0
@@ -0,0 +1,58 @@
1
+ "use client";
2
+
3
+ import { usePathname } from "next/navigation";
4
+ import { Link } from "@/components/ui/link";
5
+ import EXPERIMENTS from "@/lib/constants";
6
+ import { cn } from "@/lib/styles/cn";
7
+ import {
8
+ NavigationMenu,
9
+ NavigationMenuContent,
10
+ NavigationMenuItem,
11
+ NavigationMenuLink,
12
+ NavigationMenuList,
13
+ NavigationMenuTrigger,
14
+ } from "../navigation-menu";
15
+
16
+ export const Header = () => {
17
+ const pathname = usePathname();
18
+
19
+ return (
20
+ <nav className="fixed top-0 z-50 w-full py-2">
21
+ <div className="mx-auto flex items-center justify-between px-4">
22
+ <Link href="/" className="flex items-center gap-2 text-xl font-bold">
23
+ Basement Experiments Starter
24
+ </Link>
25
+ <NavigationMenu>
26
+ <NavigationMenuList>
27
+ <NavigationMenuItem>
28
+ <NavigationMenuTrigger className="text-sm font-medium px-8 py-6">
29
+ Experiments
30
+ </NavigationMenuTrigger>
31
+ <NavigationMenuContent>
32
+ <ul className="grid w-48 p-2 gap-1 bg-indigo-950/70 backdrop-blur-3xl text-white">
33
+ {EXPERIMENTS.map((experiment) => (
34
+ <li key={experiment.path}>
35
+ <NavigationMenuLink asChild>
36
+ <Link
37
+ href={experiment.path}
38
+ className={cn(
39
+ "block px-2 py-2 text-sm transition-colors duration-200 rounded-sm font-medium",
40
+ pathname === experiment.path
41
+ ? "bg-purple-600 text-white"
42
+ : "text-white hover:bg-purple-500/20",
43
+ )}
44
+ >
45
+ {experiment.name}
46
+ </Link>
47
+ </NavigationMenuLink>
48
+ </li>
49
+ ))}
50
+ </ul>
51
+ </NavigationMenuContent>
52
+ </NavigationMenuItem>
53
+ </NavigationMenuList>
54
+ </NavigationMenu>
55
+ </div>
56
+ </nav>
57
+ );
58
+ };
@@ -0,0 +1,127 @@
1
+ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
2
+ import { cva } from "class-variance-authority";
3
+ import { ChevronDown } from "lucide-react";
4
+
5
+ import { cn } from "@/lib/styles/cn";
6
+
7
+ function NavigationMenu({
8
+ className,
9
+ children,
10
+ ...props
11
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Root>) {
12
+ return (
13
+ <NavigationMenuPrimitive.Root
14
+ className={cn(
15
+ "relative z-10 flex max-w-max flex-1 items-center justify-center",
16
+ className,
17
+ )}
18
+ {...props}
19
+ >
20
+ {children}
21
+ <NavigationMenuViewport />
22
+ </NavigationMenuPrimitive.Root>
23
+ );
24
+ }
25
+
26
+ function NavigationMenuList({
27
+ className,
28
+ ...props
29
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
30
+ return (
31
+ <NavigationMenuPrimitive.List
32
+ className={cn(
33
+ "group flex flex-1 list-none items-center justify-center space-x-1",
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ const NavigationMenuItem = NavigationMenuPrimitive.Item;
42
+
43
+ const navigationMenuTriggerStyle = cva(
44
+ "bg-indigo-950/30 backdrop-blur-3xl border border-purple-400/50 text-white group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-purple-800/50 hover:text-white focus:bg-purple-800/50 focus:text-white focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-white data-[state=open]:bg-indigo-900/70 data-[state=open]:hover:bg-purple-800/50 data-[state=open]:focus:bg-purple-800/50 cursor-pointer",
45
+ );
46
+
47
+ function NavigationMenuTrigger({
48
+ className,
49
+ children,
50
+ ...props
51
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
52
+ return (
53
+ <NavigationMenuPrimitive.Trigger
54
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
55
+ {...props}
56
+ >
57
+ {children}{" "}
58
+ <ChevronDown
59
+ className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
60
+ aria-hidden="true"
61
+ />
62
+ </NavigationMenuPrimitive.Trigger>
63
+ );
64
+ }
65
+
66
+ function NavigationMenuContent({
67
+ className,
68
+ ...props
69
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
70
+ return (
71
+ <NavigationMenuPrimitive.Content
72
+ className={cn(
73
+ "left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto",
74
+ className,
75
+ )}
76
+ {...props}
77
+ />
78
+ );
79
+ }
80
+
81
+ const NavigationMenuLink = NavigationMenuPrimitive.Link;
82
+
83
+ function NavigationMenuViewport({
84
+ className,
85
+ ...props
86
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
87
+ return (
88
+ <div className={cn("absolute left-0 top-full flex justify-center")}>
89
+ <NavigationMenuPrimitive.Viewport
90
+ className={cn(
91
+ "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-purple-400/50 bg-indigo-950/70 text-white shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
92
+ className,
93
+ )}
94
+ {...props}
95
+ />
96
+ </div>
97
+ );
98
+ }
99
+
100
+ function NavigationMenuIndicator({
101
+ className,
102
+ ...props
103
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
104
+ return (
105
+ <NavigationMenuPrimitive.Indicator
106
+ className={cn(
107
+ "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
108
+ className,
109
+ )}
110
+ {...props}
111
+ >
112
+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-purple-400 shadow-md" />
113
+ </NavigationMenuPrimitive.Indicator>
114
+ );
115
+ }
116
+
117
+ export {
118
+ NavigationMenu,
119
+ NavigationMenuContent,
120
+ NavigationMenuIndicator,
121
+ NavigationMenuItem,
122
+ NavigationMenuLink,
123
+ NavigationMenuList,
124
+ NavigationMenuTrigger,
125
+ navigationMenuTriggerStyle,
126
+ NavigationMenuViewport,
127
+ };
@@ -0,0 +1,12 @@
1
+ const EXPERIMENTS = [
2
+ {
3
+ name: "EXPERIMENT 1",
4
+ path: "/experiment-1",
5
+ },
6
+ {
7
+ name: "EXPERIMENT 2",
8
+ path: "/experiment-2",
9
+ },
10
+ ];
11
+
12
+ export default EXPERIMENTS;
@@ -0,0 +1,10 @@
1
+ import { Wrapper } from "@/components/layout/wrapper";
2
+ import DynamicCanvas from "@/components/webgl/canvas/dynamic";
3
+
4
+ export default function Home() {
5
+ return (
6
+ <Wrapper theme="dark">
7
+ <DynamicCanvas />
8
+ </Wrapper>
9
+ );
10
+ }
@@ -0,0 +1,34 @@
1
+ "use client";
2
+
3
+ import dynamic from "next/dynamic";
4
+ import { Suspense, useRef } from "react";
5
+ import { cn } from "@/lib/styles/cn";
6
+
7
+ const WebGLCanvas = dynamic(() => import("./index"), {
8
+ ssr: false,
9
+ });
10
+
11
+ export default function DynamicCanvas() {
12
+ const eventSourceRef = useRef<HTMLDivElement>(null);
13
+
14
+ return (
15
+ <div
16
+ ref={eventSourceRef}
17
+ className={cn(
18
+ "pointer-events-none fixed top-0 left-0 z-50 h-lvh w-full overflow-hidden",
19
+ )}
20
+ >
21
+ <Suspense
22
+ fallback={
23
+ <div className="flex h-full w-full items-center justify-center bg-black/10">
24
+ <span className="font-mono text-xs uppercase tracking-widest opacity-30">
25
+ Loading WebGL...
26
+ </span>
27
+ </div>
28
+ }
29
+ >
30
+ <WebGLCanvas eventSource={eventSourceRef} />
31
+ </Suspense>
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,43 @@
1
+ "use client";
2
+
3
+ import { Preload } from "@react-three/drei";
4
+ import { Canvas } from "@react-three/fiber";
5
+ import dynamic from "next/dynamic";
6
+
7
+ import { cn } from "@/lib/styles/cn";
8
+
9
+ const Scene = dynamic(
10
+ () =>
11
+ import("@/components/webgl/components/scene").then((mod) => mod.default),
12
+ {
13
+ ssr: false,
14
+ },
15
+ );
16
+
17
+ export default function WebGLCanvas({ ...props }) {
18
+ return (
19
+ <Canvas
20
+ className={cn("pointer-events-none h-lvh! w-full")}
21
+ dpr={[1, 2]}
22
+ eventPrefix="client"
23
+ frameloop="always"
24
+ camera={{
25
+ far: 1000,
26
+ near: 0.01,
27
+ fov: 16,
28
+ position: [0, 17, 350],
29
+ }}
30
+ gl={{
31
+ alpha: true,
32
+ antialias: true,
33
+ logarithmicDepthBuffer: true,
34
+ powerPreference: "high-performance",
35
+ premultipliedAlpha: true,
36
+ }}
37
+ {...props}
38
+ >
39
+ <Scene />
40
+ <Preload all />
41
+ </Canvas>
42
+ );
43
+ }
@@ -0,0 +1,21 @@
1
+ import { useFrame } from "@react-three/fiber";
2
+ import { useRef } from "react";
3
+ import type { Mesh } from "three";
4
+
5
+ const Scene = () => {
6
+ const meshRef = useRef<Mesh>(null);
7
+
8
+ useFrame((_state, delta) => {
9
+ meshRef.current.rotation.x += delta;
10
+ meshRef.current.rotation.y += delta;
11
+ });
12
+
13
+ return (
14
+ <mesh ref={meshRef} scale={10}>
15
+ <boxGeometry />
16
+ <meshBasicMaterial color="orange" />
17
+ </mesh>
18
+ );
19
+ };
20
+
21
+ export default Scene;
File without changes
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "bsmnt",
3
+ "version": "0.0.0",
4
+ "description": "CLI to scaffold basement projects and add integrations",
5
+ "type": "module",
6
+ "bin": {
7
+ "basement": "./bin/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "bun pm pack --dry-run --ignore-scripts",
11
+ "check": "for f in $(rg --files bin src -g '*.js'); do node --check \"$f\"; done",
12
+ "lint": "biome check .",
13
+ "lint:fix": "biome check --write .",
14
+ "test": "echo \"Error: no test specified\" && exit 1",
15
+ "changeset": "changeset",
16
+ "version-packages": "changeset version",
17
+ "release": "changeset publish",
18
+ "prepublishOnly": "bun run build"
19
+ },
20
+ "keywords": [
21
+ "webgpu",
22
+ "r3f",
23
+ "agents",
24
+ "claude",
25
+ "opencode"
26
+ ],
27
+ "author": "",
28
+ "license": "ISC",
29
+ "dependencies": {
30
+ "chalk": "^5.6.2",
31
+ "dotenv": "^17.2.3",
32
+ "fs-extra": "^11.3.3",
33
+ "inquirer": "^9.3.8",
34
+ "ora": "^9.1.0",
35
+ "picocolors": "^1.1.1",
36
+ "prompts": "^2.4.2",
37
+ "tiged": "^2.12.7"
38
+ },
39
+ "devDependencies": {
40
+ "@biomejs/biome": "2.3.14",
41
+ "@changesets/changelog-github": "^0.5.2",
42
+ "@changesets/cli": "^2.29.8"
43
+ }
44
+ }
@@ -0,0 +1,21 @@
1
+ ## Plugins
2
+
3
+ ### 1. `no-anchor-element.grit`
4
+ Enforces using Next.js `<Link>` component instead of HTML `<a>` elements.
5
+
6
+ ### 2. `no-unnecessary-forwardref.grit`
7
+ Checks for unnecessary `forwardRef` usage in React 19 with the compiler.
8
+
9
+ ### 3. `no-relative-parent-imports.grit`
10
+ Forbids relative parent imports (`../`) and encourages alias imports (`@/`).
11
+
12
+ ## Plugin Configuration
13
+
14
+ The plugins are configured in `biome.json`:
15
+ ```json
16
+ "plugins": [
17
+ "./biome-plugins/no-anchor-element.grit",
18
+ "./biome-plugins/no-unnecessary-forwardref.grit",
19
+ "./biome-plugins/no-relative-parent-imports.grit"
20
+ ]
21
+ ```
@@ -0,0 +1,11 @@
1
+ language js
2
+
3
+ `<a $attrs>$content</a>` as $anchor where {
4
+ ! $anchor <: within `if ($condition) { return ($jsx) }` where {
5
+ $condition <: contains or {
6
+ `isExternal`,
7
+ `isExternalSSR`
8
+ }
9
+ } ,
10
+ register_diagnostic(span=$anchor, message="Use custom Link component instead of <a> element. The Link component handles both internal and external links automatically.", severity="error")
11
+ }
@@ -0,0 +1,6 @@
1
+ language js
2
+
3
+ `import $imports from $source` as $import where {
4
+ $source <: r"['\"]\.\.\/\.\.\/.*['\"]",
5
+ register_diagnostic(span=$import, message="Use alias imports (~/dir/) instead of deep relative imports (../../). Single level imports (../) are allowed for colocated files.", severity="error")
6
+ }
@@ -0,0 +1,5 @@
1
+ language js
2
+
3
+ `forwardRef($func)` as $ref where {
4
+ register_diagnostic(span=$ref, message="forwardRef is unnecessary in React 19 with the compiler", severity="warning")
5
+ }