diff --git a/.env.example b/.env.example deleted file mode 100644 index 046f296..0000000 --- a/.env.example +++ /dev/null @@ -1,15 +0,0 @@ -# Earth Walker Design — site env vars -# -# Both are inlined at build time by Vite. To rotate either, edit -# values here, then rebuild + redeploy. - -# The Nostr public key that receives encrypted inquiry submissions. -# Bech32 npub1... form. Generate via `nak key generate` (fiatjaf/nak) -# or any Nostr client. -VITE_OWNER_NPUB= - -# Optional. Comma-separated wss:// relay URLs the inquiry form -# publishes to. If unset, defaults to: -# wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band -# The submission succeeds if at least one relay accepts the event. -VITE_NOSTR_RELAYS= diff --git a/README.md b/README.md index defaaef..977732b 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,74 @@ -# Earth Walker Design +# boilerplate-website -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. +Opinionated Vue 3 starter — the base every new aiolabs website forks from. -## Routes +## Stack -| 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) | +- **Vue 3.5** + **Vite 8** + **TypeScript 6** +- **shadcn-vue** (via `reka-ui` + `class-variance-authority` + `tailwind-merge`) — components.json pre-wired, run `pnpm dlx shadcn-vue@latest add ` to copy components in +- **Tailwind CSS 4** (via `@tailwindcss/vite` — no `tailwind.config.js`) +- **Pinia 3** — sample `useCounterStore` in `src/stores/` +- **Vue Router 5** — file in `src/router/index.ts`, lazy-loaded views in `src/views/` +- **Vue I18n 11** — `src/i18n/locales/{en,es}.json` +- **vee-validate 4** + **zod 3** — form validation primitives (zod pinned to ^3 until `@vee-validate/zod` ships a v4-compatible resolver) +- **@lucide/vue** — icon set (`lucide-vue-next` is deprecated upstream) +- **ESLint 10** (flat config) + **Prettier 3** -## Local dev +## Quick start ```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 dev # vite dev server +pnpm build # type-check + production build +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. +## Layout -## Env vars +``` +src/ +├─ App.vue # router-view shell +├─ main.ts # plugin wiring +├─ style.css # tailwind + shadcn-vue CSS variables +├─ lib/utils.ts # cn() — shadcn-vue's class merger +├─ router/index.ts +├─ stores/counter.ts # example pinia store +├─ i18n/ +│ ├─ index.ts +│ └─ locales/{en,es}.json +├─ views/HomeView.vue # proof-of-wiring page (i18n + pinia + tailwind) +└─ features/ + ├─ nostr/README.md # opt-in: nostr-tools wiring (see file) + └─ lnbits/README.md # opt-in: LNbits payments wiring (see file) +``` -| 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. | +## Optional features -Both are inlined at build time by Vite. Rotating either requires a -rebuild + redeploy. +Both nostr and LNbits live as **documentation-only** folders. The deps +aren't installed by default — bundle stays small for sites that don't +need them. Each folder's README walks you through enabling. -## How the contact form delivers inquiries +- **`src/features/nostr/`** — connect to relays, sign/publish events, contact forms that DM the site owner's npub +- **`src/features/lnbits/`** — create invoices, accept Lightning payments via an LNbits instance -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. +## Versioning strategy -No server in between stores the message. There is no inbox to leak. +The boilerplate's `main` branch is **the "tools wired, no content" baseline**. New site = clone (or fork on Forgejo) → start adding content immediately. -## 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, h1–h3, 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 images live under `public/images//`. The build pipeline -expects: - -- 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 `public/images//`. -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/View.vue`: - ```vue - - - ``` -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: +To pull dep refreshes from the boilerplate into an existing site: ```sh git remote add boilerplate forgejo@git.atitlan.io:aiolabs/boilerplate-website.git git fetch boilerplate git merge boilerplate/main ``` + +When a site diverges enough to no longer benefit from these merges, drop the remote — it becomes its own thing. + +## Keeping deps current + +- The boilerplate gets dep bumps via PRs (Renovate / Dependabot configurable; otherwise periodic `pnpm update --latest` + manual review). +- The Vue ecosystem (vue, pinia, router, i18n, vee-validate) versions in lockstep — when one majors, the others usually follow within weeks. +- Tailwind 4 + shadcn-vue + reka-ui is the current modern combo. shadcn-vue uses tw-animate-css (not the deprecated tailwindcss-animate plugin). diff --git a/env.d.ts b/env.d.ts index ad3afc1..11f02fe 100644 --- a/env.d.ts +++ b/env.d.ts @@ -1,12 +1 @@ /// - -interface ImportMetaEnv { - /** Hex- or bech32-encoded npub of the inquiry recipient. */ - readonly VITE_OWNER_NPUB?: string - /** Comma-separated wss:// relay list; falls back to a default set if unset. */ - readonly VITE_NOSTR_RELAYS?: string -} - -interface ImportMeta { - readonly env: ImportMetaEnv -} diff --git a/package.json b/package.json index 1486224..8c678b7 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "@vueuse/core": "^14.3.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "nostr-tools": "^2.23.5", "pinia": "^3.0.4", "reka-ui": "^2.9.8", "tailwind-merge": "^3.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f90a373..48b159c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,6 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 - nostr-tools: - specifier: ^2.23.5 - version: 2.23.5(typescript@6.0.3) pinia: specifier: ^3.0.4 version: 3.0.4(typescript@6.0.3)(vue@3.5.34(typescript@6.0.3)) @@ -260,18 +257,6 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@noble/ciphers@2.1.1': - resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} - engines: {node: '>= 20.19.0'} - - '@noble/curves@2.0.1': - resolution: {integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==} - engines: {node: '>= 20.19.0'} - - '@noble/hashes@2.0.1': - resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} - engines: {node: '>= 20.19.0'} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -389,15 +374,6 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@scure/base@2.0.0': - resolution: {integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==} - - '@scure/bip32@2.0.1': - resolution: {integrity: sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA==} - - '@scure/bip39@2.0.1': - resolution: {integrity: sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg==} - '@swc/helpers@0.5.23': resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} @@ -1150,17 +1126,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - nostr-tools@2.23.5: - resolution: {integrity: sha512-Fa7ZlUdjfUW1P4E7H3yBexhOHYi18XNyvd2n7eNHkYR085xADX6Y8V8Vm7nT/XQajaFOBrptXmVIGkJ2E4vfVw==} - peerDependencies: - typescript: '>=5.0.0' - peerDependenciesMeta: - typescript: - optional: true - - nostr-wasm@0.1.0: - resolution: {integrity: sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==} - nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -1702,14 +1667,6 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@noble/ciphers@2.1.1': {} - - '@noble/curves@2.0.1': - dependencies: - '@noble/hashes': 2.0.1 - - '@noble/hashes@2.0.1': {} - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1777,19 +1734,6 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@scure/base@2.0.0': {} - - '@scure/bip32@2.0.1': - dependencies: - '@noble/curves': 2.0.1 - '@noble/hashes': 2.0.1 - '@scure/base': 2.0.0 - - '@scure/bip39@2.0.1': - dependencies: - '@noble/hashes': 2.0.1 - '@scure/base': 2.0.0 - '@swc/helpers@0.5.23': dependencies: tslib: 2.8.1 @@ -2533,20 +2477,6 @@ snapshots: natural-compare@1.4.0: {} - nostr-tools@2.23.5(typescript@6.0.3): - dependencies: - '@noble/ciphers': 2.1.1 - '@noble/curves': 2.0.1 - '@noble/hashes': 2.0.1 - '@scure/base': 2.0.0 - '@scure/bip32': 2.0.1 - '@scure/bip39': 2.0.1 - nostr-wasm: 0.1.0 - optionalDependencies: - typescript: 6.0.3 - - nostr-wasm@0.1.0: {} - nth-check@2.1.1: dependencies: boolbase: 1.0.0 diff --git a/public/images/asheville/01-living.jpg b/public/images/asheville/01-living.jpg index 5bbffff..03d8973 100644 Binary files a/public/images/asheville/01-living.jpg and b/public/images/asheville/01-living.jpg differ diff --git a/public/images/asheville/02-kitchen.jpg b/public/images/asheville/02-kitchen.jpg index 3d1359b..ccda9f9 100644 Binary files a/public/images/asheville/02-kitchen.jpg and b/public/images/asheville/02-kitchen.jpg differ diff --git a/public/images/asheville/03-bath.jpg b/public/images/asheville/03-bath.jpg index ae844ab..6bfdbe3 100644 Binary files a/public/images/asheville/03-bath.jpg and b/public/images/asheville/03-bath.jpg differ diff --git a/public/images/asheville/04-dining-wide.jpg b/public/images/asheville/04-dining-wide.jpg index 2edff18..3621a40 100644 Binary files a/public/images/asheville/04-dining-wide.jpg and b/public/images/asheville/04-dining-wide.jpg differ diff --git a/public/images/asheville/05-dining-detail.jpg b/public/images/asheville/05-dining-detail.jpg index 00674f5..87b82e9 100644 Binary files a/public/images/asheville/05-dining-detail.jpg and b/public/images/asheville/05-dining-detail.jpg differ diff --git a/public/images/boulder/01.jpg b/public/images/boulder/01.jpg deleted file mode 100644 index b9c0ab2..0000000 Binary files a/public/images/boulder/01.jpg and /dev/null differ diff --git a/public/images/boulder/02.jpg b/public/images/boulder/02.jpg deleted file mode 100644 index 3b36bef..0000000 Binary files a/public/images/boulder/02.jpg and /dev/null differ diff --git a/public/images/boulder/03.jpg b/public/images/boulder/03.jpg deleted file mode 100644 index 8a5bf9c..0000000 Binary files a/public/images/boulder/03.jpg and /dev/null differ diff --git a/public/images/boulder/04.jpg b/public/images/boulder/04.jpg deleted file mode 100644 index c8881b5..0000000 Binary files a/public/images/boulder/04.jpg and /dev/null differ diff --git a/public/images/boulder/05.jpg b/public/images/boulder/05.jpg deleted file mode 100644 index 9114b57..0000000 Binary files a/public/images/boulder/05.jpg and /dev/null differ diff --git a/public/images/boulder/06.jpg b/public/images/boulder/06.jpg deleted file mode 100644 index e94bce8..0000000 Binary files a/public/images/boulder/06.jpg and /dev/null differ diff --git a/public/images/boulder/07.jpg b/public/images/boulder/07.jpg deleted file mode 100644 index ff12658..0000000 Binary files a/public/images/boulder/07.jpg and /dev/null differ diff --git a/public/images/boulder/08.jpg b/public/images/boulder/08.jpg deleted file mode 100644 index d0bee43..0000000 Binary files a/public/images/boulder/08.jpg and /dev/null differ diff --git a/public/images/boulder/09.jpg b/public/images/boulder/09.jpg deleted file mode 100644 index a651b3e..0000000 Binary files a/public/images/boulder/09.jpg and /dev/null differ diff --git a/public/images/boulder/10.jpg b/public/images/boulder/10.jpg deleted file mode 100644 index ee5fc2f..0000000 Binary files a/public/images/boulder/10.jpg and /dev/null differ diff --git a/public/images/boulder/11.jpg b/public/images/boulder/11.jpg deleted file mode 100644 index 4cf1508..0000000 Binary files a/public/images/boulder/11.jpg and /dev/null differ diff --git a/public/images/boulder/12.jpg b/public/images/boulder/12.jpg deleted file mode 100644 index e554d02..0000000 Binary files a/public/images/boulder/12.jpg and /dev/null differ diff --git a/src/components/contact/ContactForm.vue b/src/components/contact/ContactForm.vue deleted file mode 100644 index 91478b5..0000000 --- a/src/components/contact/ContactForm.vue +++ /dev/null @@ -1,220 +0,0 @@ - - -