Restructure into pnpm monorepo with game shell, docs, and SpacetimeDB backend

- Restructure flat static prototype into pnpm workspace monorepo
- apps/game: playable shell with R3F 3D scene, HUD, SpacetimeDB connection
- apps/docs: design docs and prototypes
- apps/site: landing page
- packages/ui: shared Button and Panel primitives
- services/spacetimedb: backend module (9 tables, 11 reducers)
- Archive legacy static files to archive/legacy-static/
- Game loop: connect, undock, target, approach, dock, mine, sell
- Add pnpm-workspace.yaml, tsconfig.base.json, spacetime.json
This commit is contained in:
2026-05-31 17:56:56 -04:00
parent 436f282fa8
commit 316a44661b
234 changed files with 3717 additions and 101 deletions

23
packages/ui/package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "@void-nav/ui",
"private": true,
"version": "0.1.0",
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
},
"./styles.css": "./src/styles.css"
},
"scripts": {
"check": "tsc --noEmit"
},
"peerDependencies": {
"react": "^18.3.1"
},
"devDependencies": {
"@types/react": "^18.3.23",
"typescript": "^5.8.3"
}
}

4
packages/ui/src/index.ts Normal file
View File

@@ -0,0 +1,4 @@
export { Button } from "./primitives/Button";
export type { ButtonProps } from "./primitives/Button";
export { Panel } from "./primitives/Panel";
export type { PanelProps } from "./primitives/Panel";

View File

@@ -0,0 +1,55 @@
import type { ButtonHTMLAttributes, AnchorHTMLAttributes, ReactNode } from "react";
type ButtonTone = "primary" | "secondary" | "ghost";
type BaseProps = {
children: ReactNode;
className?: string;
tone?: ButtonTone;
};
type NativeButtonProps = BaseProps &
ButtonHTMLAttributes<HTMLButtonElement> & {
href?: never;
};
type AnchorButtonProps = BaseProps &
AnchorHTMLAttributes<HTMLAnchorElement> & {
href: string;
};
export type ButtonProps = NativeButtonProps | AnchorButtonProps;
const tones: Record<ButtonTone, string> = {
primary: "border-accent bg-accent text-bg hover:bg-accent-hover",
secondary: "border-border bg-surface-raised text-fg hover:border-border-light hover:bg-surface-hover",
ghost: "border-transparent bg-transparent text-cyan hover:border-border-light hover:bg-surface",
};
export function Button({ children, className = "", tone = "secondary", ...props }: ButtonProps) {
const classes = [
"inline-flex min-h-10 cursor-pointer items-center justify-center gap-2 rounded-md border px-4 py-2 text-sm font-semibold transition-colors duration-150 disabled:cursor-not-allowed disabled:opacity-55",
tones[tone],
className,
]
.filter(Boolean)
.join(" ");
if ("href" in props) {
const anchorProps = props as Omit<AnchorButtonProps, keyof BaseProps>;
return (
<a className={classes} {...anchorProps}>
{children}
</a>
);
}
const buttonProps = props as Omit<NativeButtonProps, keyof BaseProps>;
return (
<button className={classes} {...buttonProps}>
{children}
</button>
);
}

View File

@@ -0,0 +1,22 @@
import type { HTMLAttributes, ReactNode } from "react";
export type PanelProps = HTMLAttributes<HTMLElement> & {
as?: "article" | "section" | "div";
children: ReactNode;
};
export function Panel({ as: Tag = "section", children, className = "", ...props }: PanelProps) {
return (
<Tag
className={[
"rounded-lg border border-border bg-surface/92 p-5 shadow-[0_16px_60px_rgba(0,0,0,0.22)]",
className,
]
.filter(Boolean)
.join(" ")}
{...props}
>
{children}
</Tag>
);
}

View File

@@ -0,0 +1,75 @@
@theme {
--color-bg: #080c14;
--color-bg-subtle: #0b1120;
--color-surface: #0f1623;
--color-surface-raised: #162032;
--color-surface-hover: #1c2d45;
--color-fg: #d4dce8;
--color-fg-bright: #f1f5f9;
--color-fg-dim: #94a3b8;
--color-muted: #5a6b82;
--color-border: #1c2a3f;
--color-border-light: #253550;
--color-accent: #f0a030;
--color-accent-hover: #fbbf24;
--color-cyan: #22d3ee;
--color-red: #ef4444;
--color-green: #22c55e;
--color-purple: #a78bfa;
--font-display: "SF Pro Display", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
--font-body: "SF Pro Text", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", ui-monospace, Menlo, monospace;
}
@layer base {
:root {
--bg: var(--color-bg);
--bg-subtle: var(--color-bg-subtle);
--surface: var(--color-surface);
--surface-raised: var(--color-surface-raised);
--surface-hover: var(--color-surface-hover);
--fg: var(--color-fg);
--fg-bright: var(--color-fg-bright);
--fg-dim: var(--color-fg-dim);
--muted: var(--color-muted);
--border: var(--color-border);
--border-light: var(--color-border-light);
--accent: var(--color-accent);
--accent-hover: var(--color-accent-hover);
--cyan: var(--color-cyan);
--red: var(--color-red);
--green: var(--color-green);
--purple: var(--color-purple);
}
*, *::before, *::after {
box-sizing: border-box;
}
html {
font-size: 14px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
margin: 0;
min-height: 100vh;
background: var(--bg);
color: var(--fg);
font-family: var(--font-body);
line-height: 1.6;
}
a {
color: inherit;
text-decoration: none;
}
button,
input {
font: inherit;
}
}

View File

@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2020"],
"types": ["react"],
"jsx": "react-jsx"
},
"include": ["src"]
}