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:
23
packages/ui/package.json
Normal file
23
packages/ui/package.json
Normal 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
4
packages/ui/src/index.ts
Normal 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";
|
||||
55
packages/ui/src/primitives/Button.tsx
Normal file
55
packages/ui/src/primitives/Button.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
22
packages/ui/src/primitives/Panel.tsx
Normal file
22
packages/ui/src/primitives/Panel.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
75
packages/ui/src/styles.css
Normal file
75
packages/ui/src/styles.css
Normal 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;
|
||||
}
|
||||
}
|
||||
9
packages/ui/tsconfig.json
Normal file
9
packages/ui/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
||||
"types": ["react"],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in New Issue
Block a user