Client
packages/client is the end-user app at greengoods.app: one codebase,
two presentations. Visitors get an editorial public website; gardeners get an installed PWA that
captures work offline in the field. Most business logic comes from shared; this
package owns route composition and presentation.
The two shells
- Public browser routes render through
PublicShellwith the site header, editorial navigation, and public-safe data access. - Installed and authenticated PWA routes render through
AppShellwith the bottom navigation bar, offline capture, and the app experience.
Never mix the two. If a route or shell change blurs the paths, check router.tsx, the shell
component, and the matching Storybook story in the same pass. To tour the PWA without a wallet,
use the mock-auth URL from Getting Started.
Follow one work submission to see the app’s capture, queue and review path.
Inside src
A request enters through main.tsx, which picks the public or PWA bootstrap by presentation.
router.tsx composes the route table in config/routes.tsx into the matching shell from
routes/; each view then renders client components over shared hooks, and the service worker in
sw/ keeps the installed app working without signal.
| Directory | What lives there |
|---|---|
src/main.tsx, src/PublicApp.tsx, src/PwaApp.tsx, src/bootstrapPublic.tsx, src/bootstrapPwa.tsx | Entry points and bootstraps for the public site and the installed app |
src/routes/ | The shells themselves: PublicShell, AppShell, the root route, the session gate, and the PWA runtime |
src/router.tsx and src/config/routes.tsx | How routes are composed for both presentations |
src/views/ | Screens by area: Home, Garden, Profile, Login, and the public pages |
src/components/ | Client-owned building blocks by role: Actions, Cards, Communication, Display, Errors, Features, Inputs, Layout, Navigation, Public, Pwa, and Sheets |
src/sw/ | The service worker: the offline shell, media caching, the share target, and background work |
src/config/ | The PWA manifest, routing rules, IPFS settings, and app bootstrap |
src/styles/ | Client stylesheets layered on the shared tokens |
src/content/ | Curated public content and social previews |
src/webmcp.ts | WebMCP tools for public-safe browser routes |
src/__tests__/ | Vitest suites for views, shells, and the service worker |
Run and test
bun run --cwd packages/client test
bun run --cwd packages/client build
Run the build whenever route wiring, render output, or PWA packaging moves; tests alone don't prove those.
How it connects
The client reads garden and membership state through the indexer's read models and reads work through shared's EAS data layer, which only queries. Writing takes a different path: captured work goes into the shared job queue, and its executors encode and send the attestations. The Anatomy of a Work Submission traces that whole path.
Conventions that will bite you
- Work submission must preserve the offline-first queue flow. Bypassing the queue for passkey users breaks recovery.
- Blob URLs go through shared's
mediaResourceManager; a strayURL.createObjectURLleaks memory on capture-heavy flows. - Authentication branches through shared auth APIs, and wallet chain state is never the source of truth for app defaults.
- No local hooks or providers for logic that belongs in shared.
- New user-facing strings ship in
en,es, andpttogether.
Go deeper
packages/client/AGENTS.mdis the full package contract, including the design-surface rules.- Personas and Surfaces defines the five personas and the surfaces
that may name each one; the route table itself lives in
src/config/routes.tsxandsrc/config/pwaRouting.ts.