# ThinkWork Mobile

Mobile app for ThinkWork.

## EAS Account

- **Account:** thinkwork-ai
- **Project:** @thinkwork-ai/thinkwork-mobile
- **Bundle ID:** ai.thinkwork.agent
- **App Store Connect App ID:** 6762098524

> The `production` and `preview` profiles in `eas.json` pin `node` to `22.13.1`. The root `.npmrc` has `engine-strict=true` and the root package requires Node >=22, so the default EAS runner Node may fail `pnpm install --frozen-lockfile`. Don't remove the pin without checking the root Node engine floor.

## Deployment

### OTA Update (JS-only changes, fast)

```bash
eas update --channel production --message "Description of changes"
```

⚠️ **Important:** Use `--channel production`, not `--branch main`. The production build is configured to pull updates from the `production` channel.

### TestFlight Build (full native build, 10-20 min)

```bash
eas build --platform ios --auto-submit
```

This will build and submit to TestFlight automatically.

## Development

```bash
pnpm install
pnpm start
```

## Wiki graph architecture

The Wiki tab's force-directed graph viewer lives under `components/wiki/graph/`.

Top-level composition:
- `WikiGraphView` — fetches via `useWikiGraph`, adapts to internal types, carries node positions across urql re-emits via `prevInternalRef`, and mounts `KnowledgeGraph` + `NodeDetailModal`. Used by the home-tab Wiki view.
- `WikiDetailSubgraph` — filters the same `wikiGraph` payload to a 1-hop neighborhood (`layout/neighborhood.ts`) and mounts `KnowledgeGraph` + `NodeDetailModal` scoped to a single page. Rendered inside `app/wiki/[type]/[slug].tsx` when the topology-star toggle is on (40% split above the scrolling wiki content).
- `KnowledgeGraph` — owns the camera (`useGraphCamera`), sim (`useForceSimulation`), composed gesture, reveal animation, and position/camera persistence. Props steer variants: `cacheKey` (persist state across unmounts), `showLabels`, `showRevealLoader`, `simConfig`.
- `GraphCanvas` — pure Skia renderer. Edges + circles + optional Skia `<Text>` labels inside a transformed `<Group>`.
- `NodeDetailModal` — centered modal preview on node tap. Scrollable body with summary + sections from `useWikiPage`. External-link icon opens the full `/wiki/[type]/[slug]` detail screen.

Persistence layers (all three required for "drill in → come back → exact same state"):
1. `graphStateCache.ts` — module-level `Map` keyed on `${tenantId}:${agentId}`; written on `KnowledgeGraph` unmount, read on mount.
2. `prevInternalRef` in `WikiGraphView` — carries `n.x/n.y` forward when urql re-emits the same query with a new object ref.
3. `useForceSimulation` detects "all nodes have positions" on init and starts the sim with `alpha(0).stop()` so d3 doesn't agitate a restored layout.

See `docs/plans/2026-04-19-006-feat-mobile-wiki-force-graph-plan.md` (v1) and `docs/plans/2026-04-20-015-feat-mobile-graph-refinements-plan.md` (v2) for design intent, what shipped vs. what diverged, and the list of gotchas to watch out for before changing graph code.
