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

View File

@@ -0,0 +1,59 @@
import { Button, Panel } from "@void-nav/ui";
import type { PointOfInterest, Ship } from "../module_bindings/types";
export function TargetPanel({
ship,
pois,
selected,
onSelect,
}: {
ship?: Ship;
pois: PointOfInterest[];
selected?: PointOfInterest;
onSelect: (poiId: string) => void;
}) {
const current = pois.find((poi) => poi.poiId === ship?.currentPoiId);
return (
<Panel className="p-4">
<div className="flex items-start justify-between gap-3">
<div>
<p className="m-0 font-mono text-xs uppercase tracking-[0.1em] text-muted">Selected Target</p>
<h2 className="m-0 mt-1 text-lg leading-tight text-fg-bright">{selected?.name ?? "No target selected"}</h2>
</div>
<span className="rounded-md border border-border bg-bg-subtle px-2 py-1 font-mono text-xs uppercase tracking-[0.08em] text-fg-dim">
{ship?.flightMode ?? "offline"}
</span>
</div>
<dl className="mt-3 grid gap-2 text-sm">
<InfoRow label="Current" value={current?.name ?? "Unknown"} />
<InfoRow label="Target type" value={selected?.poiType.replace("_", " ") ?? "Select in scene or list"} />
</dl>
<div className="mt-4 grid gap-2">
{pois.map((poi) => (
<Button
key={poi.poiId}
tone={selected?.poiId === poi.poiId ? "primary" : "secondary"}
className="min-h-9 w-full justify-between px-3 text-left"
onClick={() => onSelect(poi.poiId)}
disabled={!ship}
>
<span className="flex-1 text-left">{poi.name}</span>
<span className="shrink-0 pl-3 font-mono text-xs opacity-75">{poi.poiType === "asteroid_belt" ? "Belt" : "Station"}</span>
</Button>
))}
</div>
</Panel>
);
}
function InfoRow({ label, value }: { label: string; value: string }) {
return (
<div className="grid grid-cols-[5.5rem_1fr] gap-2 border-t border-border pt-2">
<dt className="font-mono text-xs uppercase tracking-[0.08em] text-muted">{label}</dt>
<dd className="m-0 text-fg">{value}</dd>
</div>
);
}