Initial Commit
This commit is contained in:
129
openspec/changes/enterprise-r3f-earth/proposal.md
Normal file
129
openspec/changes/enterprise-r3f-earth/proposal.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# OpenSpec Change: enterprise-r3f-earth
|
||||
|
||||
- **Change ID**: enterprise-r3f-earth
|
||||
- **Package**: `@suaveplan/r3f-earth`
|
||||
- **Category**: r3f/geo
|
||||
- **Target path**: `r3f/geo/r3f-earth`
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-04-24
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/r3f-earth.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver `@suaveplan/r3f-earth` on the genesis branch from scratch at enterprise quality. The package provides a React Three Fiber 3D globe component with port markers, shipping lanes, GeoJSON overlays, interactive country highlighting, and smooth camera orbiting. It ships `<Earth />`, `<EarthMarker />`, `<EarthLane />`, `<EarthOverlay />`, and `<EarthScene />` components with full TypeScript props, Storybook stories, Vitest + `@testing-library/react` tests, OTel counters for render events, and Zod-validated prop schemas.
|
||||
|
||||
---
|
||||
|
||||
## Current-state audit (genesis)
|
||||
|
||||
Does not exist on genesis — fresh build.
|
||||
|
||||
---
|
||||
|
||||
## Available workspace deps on genesis
|
||||
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| `@suaveplan/error` (`workspace:^`) | Typed `R3F_EARTH.*` error codes |
|
||||
| `@suaveplan/types` (`workspace:^`) | `LoggerLike` |
|
||||
| `@suaveplan/logger` (`workspace:^`) | Structured logs |
|
||||
| `zod` (`catalog:forms`) | Prop schemas |
|
||||
| `@opentelemetry/api` (`catalog:observability`) | Render counters |
|
||||
| `three` (`catalog:r3f`) | 3D engine |
|
||||
| `@react-three/fiber` (`catalog:r3f`) | React renderer |
|
||||
| `@react-three/drei` (`catalog:r3f`) | Camera, helpers |
|
||||
| `react` / `react-dom` (`catalog:react`) | React |
|
||||
| `tslib` (`catalog:build`) | Runtime helpers |
|
||||
|
||||
---
|
||||
|
||||
## Public API surface
|
||||
|
||||
```tsx
|
||||
// Main globe component
|
||||
function Earth(props: EarthProps): JSX.Element
|
||||
function EarthScene(props: EarthSceneProps): JSX.Element // includes Canvas + OrbitControls
|
||||
function EarthMarker(props: EarthMarkerProps): JSX.Element
|
||||
function EarthLane(props: EarthLaneProps): JSX.Element
|
||||
function EarthOverlay(props: EarthOverlayProps): JSX.Element
|
||||
|
||||
interface EarthProps {
|
||||
radius?: number // default: 1
|
||||
segments?: number // default: 64
|
||||
texturePath?: string // earth texture URL
|
||||
bumpMapPath?: string
|
||||
cloudTexturePath?: string
|
||||
showClouds?: boolean
|
||||
showAtmosphere?: boolean
|
||||
atmosphereColor?: string
|
||||
onCountryClick?: (code: string) => void
|
||||
highlightedCountries?: string[]
|
||||
highlightColor?: string
|
||||
}
|
||||
|
||||
interface EarthMarkerProps {
|
||||
lat: number
|
||||
lng: number
|
||||
label?: string
|
||||
color?: string
|
||||
size?: number
|
||||
onClick?: () => void
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
interface EarthLaneProps {
|
||||
from: [lat: number, lng: number]
|
||||
to: [lat: number, lng: number]
|
||||
color?: string
|
||||
animated?: boolean
|
||||
speed?: number
|
||||
dashed?: boolean
|
||||
}
|
||||
|
||||
interface EarthOverlayProps {
|
||||
geojson: GeoJSON.FeatureCollection
|
||||
color?: string
|
||||
opacity?: number
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
```text
|
||||
r3f/geo/r3f-earth/
|
||||
package.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
src/
|
||||
index.ts
|
||||
error-codes.ts
|
||||
types/
|
||||
r3f-earth.types.ts
|
||||
r3f-earth.types.md
|
||||
components/
|
||||
earth/
|
||||
earth.tsx / earth.md / earth.types.ts / earth.test.tsx / earth.stories.tsx / index.ts
|
||||
earth-scene/
|
||||
earth-scene.tsx / earth-scene.md / earth-scene.types.ts / earth-scene.test.tsx / earth-scene.stories.tsx / index.ts
|
||||
earth-marker/
|
||||
earth-marker.tsx / .md / .types.ts / .test.tsx / .stories.tsx / index.ts
|
||||
earth-lane/
|
||||
earth-lane.tsx / .md / .types.ts / .test.tsx / .stories.tsx / index.ts
|
||||
earth-overlay/
|
||||
earth-overlay.tsx / .md / .types.ts / .test.tsx / .stories.tsx / index.ts
|
||||
utils/
|
||||
geo-math/
|
||||
geo-math.ts / geo-math.md / geo-math.test.ts / index.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Impact
|
||||
|
||||
Downstream: logistics, port-tracking, and shipping visualisation packages.
|
||||
|
||||
**Prerequisites on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/logger`. Requires `@testing-library/react` + `@testing-library/jest-dom` for component tests.
|
||||
68
openspec/changes/enterprise-r3f-earth/tasks.md
Normal file
68
openspec/changes/enterprise-r3f-earth/tasks.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Tasks: enterprise-r3f-earth
|
||||
|
||||
**Change ID**: enterprise-r3f-earth
|
||||
**Package**: `@suaveplan/r3f-earth`
|
||||
**Target path**: `r3f/geo/r3f-earth`
|
||||
**Prerequisites on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/logger`
|
||||
|
||||
---
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [ ] Create directory tree per `proposal.md`.
|
||||
- [ ] `package.json` at `0.1.0`, `"sideEffects": false`, exports `"."` + per-component subpaths + `utils/geo-math`.
|
||||
- [ ] Deps: `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `@suaveplan/logger workspace:^`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `three catalog:r3f`, `@react-three/fiber catalog:r3f`, `@react-three/drei catalog:r3f`, `react catalog:react`, `tslib catalog:build`.
|
||||
- [ ] Peer deps: `react`, `three`.
|
||||
- [ ] DevDeps: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `@suaveplan/testing workspace:^`, `@suaveplan/testing-dom workspace:^`, `react-dom catalog:react`, `@testing-library/react catalog:testing`, `@storybook/react catalog:storybook`.
|
||||
- [ ] `tsconfig.json` extending `react-library.json`; `vitest.config.ts` with `pool: 'forks'`, jsdom environment, `fileParallelism: false`.
|
||||
- [ ] Scripts: `lint`, `typecheck`, `test`, `test:coverage`, `build`, `docs:check`.
|
||||
|
||||
## 2. Types and errors
|
||||
|
||||
- [ ] Define all prop interfaces and Zod schemas in `src/types/r3f-earth.types.ts`.
|
||||
- [ ] Write `r3f-earth.types.md` (≥200 words).
|
||||
- [ ] `src/error-codes.ts`: `R3F_EARTH_INVALID_COORDS`, `R3F_EARTH_GEOJSON_PARSE_FAILED`; `registerR3fEarthErrorCodes()`; eager call.
|
||||
|
||||
## 3. Geo math utility
|
||||
|
||||
- [ ] Implement `src/utils/geo-math/geo-math.ts`:
|
||||
- `latLngToVector3(lat, lng, radius): THREE.Vector3` — spherical to Cartesian.
|
||||
- `vector3ToLatLng(v: THREE.Vector3): [lat, lng]` — Cartesian to spherical.
|
||||
- `createArcPoints(from, to, numPoints, height): THREE.Vector3[]` — arc for shipping lanes.
|
||||
- `createGeoJsonGeometry(feature, radius): THREE.BufferGeometry` — polygon to geometry.
|
||||
- [ ] Write `geo-math.test.ts`: round-trip lat/lng ↔ Vector3; arc midpoint above surface; known coordinate values.
|
||||
- [ ] Write `geo-math.md` (≥200 words).
|
||||
|
||||
## 4. Components
|
||||
|
||||
For each of Earth, EarthScene, EarthMarker, EarthLane, EarthOverlay:
|
||||
|
||||
- [ ] Implement the React component using `@react-three/fiber` primitives and `@react-three/drei` helpers.
|
||||
- [ ] `Earth`: sphere mesh with texture, bump map, optional cloud sphere, atmosphere; country highlight via mouse raycast.
|
||||
- [ ] `EarthScene`: wraps `Earth` in a `<Canvas>` with `OrbitControls`, background, and lighting.
|
||||
- [ ] `EarthMarker`: position via `latLngToVector3`; optional label via `<Html>` from drei.
|
||||
- [ ] `EarthLane`: arc curve via `createArcPoints`; animated via shader or `TubeGeometry`.
|
||||
- [ ] `EarthOverlay`: GeoJSON polygon rendered as `LineSegments` on the sphere surface.
|
||||
- [ ] Write `.test.tsx` for each: renders without crash; key props change updates mesh; click handlers fire; `{ timeout: 5000 }`.
|
||||
- [ ] Write `.stories.tsx` for each: default story + playground + variants story (per `CLAUDE.md` requirement).
|
||||
- [ ] Write `.md` per component (≥200 words, simple + advanced examples, API reference).
|
||||
|
||||
## 5. OTel
|
||||
|
||||
- [ ] Counter `r3f_earth.renders_total` incremented on `Earth` mount; `r3f_earth.markers_total` on marker mount.
|
||||
|
||||
## 6. Docs + verify + publish
|
||||
|
||||
- [ ] Root `README.md`: quick-start globe with markers and lanes, GeoJSON overlay example, API table.
|
||||
- [ ] `bunx turbo lint --filter=@suaveplan/r3f-earth` — zero errors.
|
||||
- [ ] `bunx turbo typecheck --filter=@suaveplan/r3f-earth` — zero errors.
|
||||
- [ ] `bunx turbo test --filter=@suaveplan/r3f-earth` — all pass.
|
||||
- [ ] `bunx turbo test:coverage --filter=@suaveplan/r3f-earth` — 100%.
|
||||
- [ ] `bun scripts/publish.ts --package @suaveplan/r3f-earth`.
|
||||
- [ ] Update `CLAUDE.md`; archive to `openspec/archive/2026-04-24-enterprise-r3f-earth/`.
|
||||
Reference in New Issue
Block a user