nr-interior-design/README.md
Padreug 074ea23b1b docs: README image-discipline section reflects the assets/ pipeline
Updates the path (public/images/ → src/assets/projects/) and adds a
sentence describing why the move was made: content-hashed filenames
let the deploy's immutable cache header stay correct across image
swaps without manual cache busts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 20:54:53 +02:00

126 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Earth Walker Design
Studio site for an interior designer. Vue 3 + Vite + shadcn-vue +
Tailwind 4. Anonymous-by-default contact: inquiry submissions are
encrypted in the visitor's browser and delivered to the designer's
Nostr inbox.
## Routes
| Path | Purpose |
|---|---|
| `/` | Hero, studio voice, two project teasers, inquiry CTA |
| `/portfolio` | Two-up project grid (Boulder, Asheville) |
| `/portfolio/boulder` | Boulder project detail — editorial image scroll + lightbox |
| `/portfolio/asheville` | Asheville project detail |
| `/contact` | Inquiry form (Nostr-delivered) |
## Local dev
```sh
pnpm install
cp .env.example .env # fill in VITE_OWNER_NPUB
pnpm dev # http://localhost:5173
pnpm build # type-check + production build to dist/
pnpm preview # serve dist/
pnpm lint
pnpm format
```
The contact form requires `VITE_OWNER_NPUB` to be set — without it,
`submitInquiry` throws so visitors see an explicit error rather
than silently dropping submissions. Generate one with
[`nak key generate`](https://github.com/fiatjaf/nak) or any Nostr client.
## Env vars
| Var | Required | Default | Notes |
|---|---|---|---|
| `VITE_OWNER_NPUB` | yes | — | The designer's Nostr public key (`npub1…`). Inquiries are NIP-17 gift-wrapped to this key. |
| `VITE_NOSTR_RELAYS` | no | `wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band` | Comma-separated wss:// list. Submission succeeds if any one relay accepts. |
Both are inlined at build time by Vite. Rotating either requires a
rebuild + redeploy.
## How the contact form delivers inquiries
1. Visitor fills the form (name optional, contact method + value,
message). Validation runs client-side via `vee-validate` + `zod`.
2. `submitInquiry()` (`src/features/nostr/submitInquiry.ts`):
- generates a fresh ephemeral secp256k1 keypair
- decodes `VITE_OWNER_NPUB` to hex
- calls `nip17.wrapEvent()` to produce a kind:1059 gift-wrap with
NIP-44 v2 encryption inside (the visitor's identity is the
throwaway key, not anything they entered)
- publishes via `SimplePool` to the configured relays in parallel
3. The designer receives the inquiry as a DM in any NIP-17 capable
Nostr client (Damus, Amethyst, 0xchat, etc.) signed in with the
matching nsec.
No server in between stores the message. There is no inbox to leak.
## Theming
Light + dark stone-warm palettes live in `src/style.css` as OKLCH
CSS variables. Toggle persists to `localStorage` under
`ewd:theme` (see `src/composables/useTheme.ts`). Typography pairs
Fraunces (display, h1h3, brand wordmark) with Inter (UI/body) via
Bunny Fonts, declared in `index.html` and bound through Tailwind 4
`@theme` directives.
To recolor: edit the OKLCH triples in `src/style.css` under `:root`
and `.dark`. Keep all shadcn-vue token names intact.
## Image discipline
All project images live under `src/assets/projects/<slug>/` and flow
through Vite's asset pipeline: `src/data/projects.ts` resolves each
file via `import.meta.glob`, so every image lands in `dist/assets/`
with a content-hashed filename (`08-abc123.jpg`). Any swap changes the
hash, which busts the deploy's `cache-control: immutable` header
automatically — no manual cache-clear, no version query strings.
- Filenames are sequence-numbered (`01.jpg`, `02.jpg`, …) — never
leak addresses, neighborhood names, or owner identities in the
filename.
- EXIF / GPS / timestamp / maker metadata is stripped before commit.
Re-run on new assets:
```sh
nix-shell -p imagemagick --run \
'for f in *.jpg; do magick "$f" -auto-orient -strip -resize "2400x2400>" \
-interlace Plane -sampling-factor 4:2:0 -quality 82 "${f}.tmp" \
&& mv "${f}.tmp" "$f"; done'
```
- Alt text in `src/data/projects.ts` describes the room/feature, not
the location.
## Adding a project
1. Drop sequence-numbered images into `src/assets/projects/<slug>/`.
2. Add a new `Project` entry in `src/data/projects.ts` (typed) with
`slug`, `name`, `eyebrow`, `intro`, `cover`, `coverAlt`, and an
`images[]` array. Each image takes a `feature` tag
(`hero` | `wide` | `narrow` | `paired`) that drives the layout
slot in `ProjectDetail.vue`'s editorial scroll.
3. Add a 4-line view at `src/views/projects/<Slug>View.vue`:
```vue
<script setup lang="ts">
import ProjectDetail from '@/components/projects/ProjectDetail.vue'
import { mySlug } from '@/data/projects'
</script>
<template><ProjectDetail :project="mySlug" /></template>
```
4. Register the route in `src/router/index.ts`.
5. The `PortfolioView` already iterates `projects[]`, so the new
project's card appears automatically.
## Stack
Tracking the upstream boilerplate (`aiolabs/boilerplate-website`).
To pull dependency refreshes:
```sh
git remote add boilerplate forgejo@git.atitlan.io:aiolabs/boilerplate-website.git
git fetch boilerplate
git merge boilerplate/main
```