Commit graph

349 commits

Author SHA1 Message Date
shakespeare.diy
b4b2375921 Security: fix stored XSS in map popups + add web CSP
Critical: Leaflet marker popups built untrusted Nostr event fields (and
BTC Map API fields) into innerHTML, bypassing React's output encoding. Any
user could publish a spot/tour/crew/event with an <img onerror> payload in a
text field; it executed on /map render with no interaction, letting injected
script read the plaintext nsec from localStorage (nostr:login) and the Cashu
seed (cashu-mnemonic) — permanent, unrotatable account + wallet takeover.

Fixes in src/components/SpotMap.tsx:
- Rebuild all popup DOM with createElement + textContent instead of innerHTML
  for every untrusted field: spot.location; tour.title/location/meetingPoint;
  evt.title/location; crew.name/location; BTC Map name/address/opening_hours/
  phone/description.
- Validate every event/API URL with sanitizeUrl() before it reaches an
  href/src: crew.image (popup + marker icon) and BTC Map website. Drop
  anything that isn't http(s). tel: links reduced to safe characters.
- Replace inline onerror handler on the crew marker image (would be blocked
  by CSP anyway) with a scripted img.onerror fallback.
- Add popupRow() helper so untrusted detail rows can never be innerHTML'd.

Defense in depth (web only, native Capacitor build unaffected):
- Add public/_headers with a strict Content-Security-Policy: default-src
  'none'; script-src 'self' 'wasm-unsafe-eval' (no unsafe-inline/eval);
  object-src 'none'; base-uri 'none'; frame-ancestors 'none'; plus the
  https:/wss: allowances a Nostr client genuinely needs for relays, media,
  and mints. Also X-Content-Type-Options, X-Frame-Options, Referrer-Policy,
  Permissions-Policy.
- Move the inline boot-crash-guard script to public/boot-guard.js so
  script-src can stay 'self' with no 'unsafe-inline'; index.html now loads it
  externally. The guard also now paints errors via textContent, not innerHTML.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-16 22:12:26 +07:00
shakespeare.diy
b7e932d514 Fix crew detail page 404 with robust d-tag fallback lookup
useCrew previously relied on a single combined filter (kind + #d + authors,
limit 1). Some relays don't reliably match combined author + d-tag filters,
causing the crew detail page to render NotFound even when the crew exists.

Now, if the precise addressable lookup returns nothing and the author pubkey
is known, we fall back to fetching that author's crews (kind + authors) and
matching the identifier client-side. This survives relays with quirky tag
indexing while still preferring the exact match when available.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-16 17:44:52 +07:00
shakespeare.diy
ede0440244 Make meeting point coordinates optional on tour creation
- Latitude/longitude fields are now optional for local/national tours
- If provided, both must be filled and valid; otherwise geohash and
  coords tags are omitted from the published event
- Updated input placeholders and added helper text clarifying coordinates
  are optional

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-16 17:17:06 +07:00
shakespeare.diy
8abdb25593 Fix CreateTourPage crash: add missing usePlatformFee import
CreateTourPage called usePlatformFee('featuredTour') but never imported the
hook, causing a "usePlatformFee is not defined" ReferenceError that crashed
the /create-tour page. Added the import to match the other pages that use it.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-16 16:15:09 +07:00
shakespeare.diy
8b080fb7f1 Spot detail: make mapper clickable + add Message Mapper button
The "Tip the Mapper" card now links the avatar/name to the mapper's profile
(/{npub} via the NIP-19 router) and adds a "Message Mapper" button that opens
an encrypted DM (/messages/{npub}). Makes it easy to contact whoever added a
spot — e.g. to ask them to add photos to a photoless submission. The message
button is hidden when you're viewing your own spot.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-15 10:03:58 +07:00
shakespeare.diy
aba79900e4 Newsletter Drop #005: OpenCrack roast, bigger Lab, Crews web-of-trust
Add issue #5 (2026-08-08) to NEWSLETTER_ISSUES with a Groucho-Marx satiric
tone. Highlights: the 3x-bigger Lab (10 crafts), the Lab mobile grid fix,
Crews covering every scene with opt-in visibility + web-of-trust vouching,
the redesigned CrewDetail page, and Music<->Events interconnect. Mocks
"OpenCrack" for volunteering to fix Bitcoin's crypto libraries, and owns
that the app has never been audited (one day it will be — and we'll shoot
ourselves in both feet). Includes matching nostrText teaser for kind 1.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 23:15:50 +07:00
shakespeare.diy
8a36e281f2 Bump version to 1.1.2 + release notes for Lab mobile grid fix
1.1.2 maps to versionCode 10102 via the sync-android-version scheme.
Release notes lead with the Lab craft-filter mobile grid fix, keeping the
1.1.1 highlights since this is the release carrying all of it to Zapstore.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 23:08:44 +07:00
shakespeare.diy
d476896efe Lab: replace horizontal-scroll craft tabs with a wrapping grid
On mobile the 10 craft filters (Coaches, Filmers, Photo, Editors, Builders,
Artists, Graffiti, Tattoo, Trainers, Music) were in a sideways-scrolling
strip, hiding options off-screen. Switch to a 5-column grid on phones (two
tidy rows of five) while keeping the flex-wrap layout on desktop, so every
craft is visible at a glance with no horizontal scrolling.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 23:07:13 +07:00
shakespeare.diy
ceea96eac6 Bump version to 1.1.1
Single source of truth is package.json (scripts/sync-android-version.mjs
derives versionName/versionCode from it at android build time). 1.1.1 maps
to versionCode 10101 via the major*10000+minor*100+patch scheme.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 22:48:40 +07:00
shakespeare.diy
85e41b4ea4 Update release notes to v1.1.1 for Zapstore publish
Rewrite release-notes.txt (was stale at v1.0.93) with the changes shipped
since then: expanded Lab crafts (Designer/Artist, Graffiti, Tattoo,
Trainer/Physio, Musician), 7-scene Crews with opt-in member visibility and
web-of-trust vouching, Music<->Events interconnect, Newsletter Drop #004,
BETA disclaimer, and the mobile Sasquatch panel overflow fix.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 22:45:08 +07:00
shakespeare.diy
eed8552e37 Fix Sasquatch panel overflow on mobile: cap height to dvh + safe-area, pin header, scroll body internally
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 22:23:48 +07:00
shakespeare.diy
6e11aaab7d Crew form UX: expand Crew Type to 7 scenes (Art, Music, Wellness) mapping to new Lab crafts; rethink Crew Band with Lab-music interconnect nudge; surface member visibility to founders; add Artist/Musician open-call options; update Crews page filters + NIP.md
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 22:14:21 +07:00
shakespeare.diy
96f7c3192d Interconnect Music ↔ Events: featured musician on events + gig list on musician profiles; count new Lab crafts in growth stats; document visibility/vouch/gig conventions in NIP.md
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 22:02:22 +07:00
shakespeare.diy
926470a86e Crews: opt-in member visibility + Lab vouch/web-of-trust anti-fraud + CrewDetail redesign
Step 3 — member visibility (best-of-both, honest):
- MemberVisibility ('public' | 'crew') stored on the member's OWN kind-1985
  attestation via a `visibility` tag, so a public crew can contain a private
  member. Confirmed crew-mates + founder see them; outsiders see anonymised
  "hidden member" slots. Framed honestly in-UI (app-level discovery privacy).
- VisibilityPicker in ConfirmMembershipCard (choose on join) +
  MemberVisibilityControl for confirmed members to change it anytime.
- useCollectiveMembers parses visibility; CollectiveMembers hides crew-only
  members from non-members.

Step 4 — anti-fraud vouch layer:
- useVouches/useVouchFor: NIP-32 vouch labels, trust-WEIGHTED via web-of-trust
  so fake-account vouch rings carry no weight. VouchCard with new/vouched/
  scene-verified tiers, wired into every Lab profile detail page.

Step 4 — CrewDetail UX redesign:
- Activity consolidated into clean Feed/Events/Chat tabs (no more giant scroll)
- Sidebar reordered: Members → Support → Band → Connections → Founder
- Member visibility control surfaced; tighter hero

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 21:01:57 +07:00
shakespeare.diy
0071bcba36 Lab: add Designer/Artist, Graffiti, Tattoo, Trainer/Physio & Musician crafts (kinds 35007–35011)
- New addressable kinds 35007–35011 with types, label maps and parsers
- Shared parseCraftBase helper + parseLabProfile dispatcher in useLabProfiles
- Reusable, config-driven CraftProfileForm (polished shared shell, per-craft
  skill sections + extra fields, entity/collective, CLINK, Pro upgrade)
- Five thin Become* pages wired into AppRouter and the Lab "Offer" menu
- LabPage: extended ServiceType, accents, scrollable tab strip, filter
  sections, counts, empty states and ProfileCard to all 10 crafts
- CoachDetailPage renders all craft types (map-driven accents/icons/labels)
  plus craft-specific fields (studio, books-open, credentials, remote,
  graffiti crew, music links)
- NIP.md documents all five new craft schemas

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 20:28:59 +07:00
shakespeare.diy
f4318a8b50 Add Newsletter Drop #004 (Sasquatch Pro, Crews, Angor, Coldcard PSA)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-06 09:55:52 +07:00
shakespeare.diy
3c960b9753 Bump version to 1.1.0 (beta disclaimer dialog)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-01 12:16:40 +07:00
shakespeare.diy
6e226d5561 Add first-launch beta disclaimer dialog + footer BETA badge
- BetaDisclaimerDialog: shown once on first load, versioned storage key
  (bump DISCLAIMER_VERSION to re-show after terms change), soft single
  "Got it" button, matches dark + #ff0040 style, cannot be dismissed by
  outside click/Esc
- Permanent BETA badge in desktop footer
- Wired into App.tsx

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-01 12:14:42 +07:00
shakespeare.diy
4bef95132b Bump to 1.0.93 (1.0.92 already live) + versionCode scheme matches published 1.0.x
1.0.92 was already published to Zapstore, so move to 1.0.93. Adjusted
sync-android-version.mjs so 1.0.x maps versionCode = patch (1.0.93 -> 93),
continuing the existing published scheme instead of jumping to 10093.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 22:43:32 +07:00
shakespeare.diy
41595159d9 Auto-sync Android version from package.json (no more manual gradle bumps)
Adds scripts/sync-android-version.mjs which writes versionName (semver) and a
monotonic versionCode (major*10000+minor*100+patch) into android/app/build.gradle
from package.json. Wired into `npm run android:build` and `android:sync`, plus a
standalone `npm run android:version`. Now you bump ONE number (package.json) and
never hit Zapstore's "already exists" again. Exits quietly if android/ isn't
generated yet.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 22:38:13 +07:00
shakespeare.diy
38343eb438 Fix profile page horizontal overflow on narrow/mobile screens
The public profile page overflowed sideways on phones, pushing the Info /
Contributions / Crews sidebar off-screen. Cause: the About text contained a
64-char unbreakable URL (ContextVM server link) with no word-breaking, forcing
the layout wider than the viewport.

- About text now uses break-words + overflow-wrap:anywhere so long URLs wrap
- Added min-w-0 to both grid columns so they can shrink instead of overflowing

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 22:00:43 +07:00
shakespeare.diy
d056f41ac7 Align version to 1.0.92 (1.0.91 already live on Zapstore)
Local android/ build reported 1.0.91 which is already published, so bump the
repo to 1.0.92 to stay consistent. Note: android/app/build.gradle versionCode/
versionName must be bumped locally too (cap sync does not propagate it from
package.json).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 21:54:09 +07:00
shakespeare.diy
c3a4721f21 Release v1.0.88 — bump version + release notes
Bump package.json 1.0.87 -> 1.0.88 (Capacitor propagates this into the
generated Android versionName/versionCode at android:build time).

Release notes cover this batch: richer profiles (crews/bot/edit-link),
searchable New Message picker, crew bands & gigs, CLINK tips on profiles,
and navigation cleanup.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 21:51:02 +07:00
shakespeare.diy
1528c7b343 Unify the two "Profiles": public page + Edit, cross-linked and clearly named
The app had two things both called "Profile": the /settings editor and the
public /npub page. They're not duplicates (edit vs. view) but the naming and
lack of links between them was confusing. Now:

- Public profile (/npub) gets an owner-only "Edit Profile" button that deep-
  links to /settings?section=profile
- Settings row renamed "Profile" -> "Edit Profile" (section title already said
  Edit Profile); it already has a "View my public profile" link back
- Result: one Profile (the public page), one obvious way to edit it, linked
  both directions — the standard IG/GitHub pattern

Bonus fix: mobile menu "My Data" used a broken ?tab=mydata param; corrected to
?section=mydata (the settings page reads ?section=).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 21:44:05 +07:00
shakespeare.diy
433f59415b Enrich profiles (crews, bot, CLINK), best-in-class New Message picker, map toolbar declutter
Profile page (NIP19Page):
- Add "member of crew X" summary under the header (with founder crown)
- Add a full Crews sidebar card listing every crew the user founds/belongs to
- Add crews count to the Contributions stats (now Spots/Shops/Crews)
- Show a Bot badge for kind 0 bot accounts
- CLINK "Tip in Bitcoin" already surfaces on profiles (from prior work)
- New useUserCrews(pubkey) hook powering the above

Messages:
- Replace paste-only "New Message" dialog with NewMessageDialog: a searchable
  people picker (crew-mates + following, grouped, name-searchable) that also
  accepts a pasted npub/hex as a fallback. Starting a DM now feels like a
  real contacts screen.

Map toolbar consistency:
- Consolidate secondary add actions (Shop / Gear / Tour) into a single
  "+ More" popover; keep primary "Add Spot" prominent. 4 buttons -> 2 controls.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 21:36:42 +07:00
shakespeare.diy
ee9e04d3e0 Profile access, real gig messaging, desktop Local Chat removal + audit fixes
#0 Crew band gig booking:
- Rename "Book them for a gig" -> "Set up a gig" (honest: it creates an event)
- Add "Message about a gig" -> pre-filled encrypted DM (NIP-17) to the crew
  founder via existing /messages/:npub?message= flow (real communication)

#1 Profile access (was missing/mislabeled everywhere):
- Desktop header: add "View my profile" icon linking to /npub
- Mobile user menu: split mislabeled "My Profile"(->Settings) into
  "View Profile" (-> /npub) + "Settings"
- Burger menu: add "View Profile" entry
- Settings > Edit Profile: add "View my public profile" quick link

#2 Remove standalone Local Chat button on desktop map toolbar (kept on mobile)

Audit fixes:
- Mobile menu "Wallet & Stash" -> "Wallet" (Stash is already its own item)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 21:16:15 +07:00
shakespeare.diy
8056b02ad1 Crew band gig booking, hide treasury, CLINK on profiles, friendlier CLINK, remove desktop messages icon
- Add "punk" + "rap/hip-hop" crew vibe presets
- Hide Crew Treasury behind SHOW_CREW_TREASURY flag (detail page + form), code kept
- Improve crew band section into a dedicated card with a "Book them for a gig"
  action that deep-links to a Concert event hosted by the crew
- CreateEventPage: prefill event type + title from ?type= / ?title= params
- CLINK (a): expose CLINK tip code on user profiles (EditProfileForm field +
  "Tip in Bitcoin" button on profile view, stored as custom kind 0 field)
- CLINK (c): relabel "Pay via CLINK" -> "Tip in Bitcoin" with a "what's this?"
  tooltip explaining it in plain language
- Remove standalone desktop Messages icon (kept in the More menu)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 20:29:44 +07:00
shakespeare.diy
d465eabd87 ux(crews): rename "Start a Crew" -> "Create Crew" + clarify Crew vs Lab
"Start a Crew" was ambiguous (team? chat? listing?). Renamed to the clearer
"Create Crew" across the Crews page button/empty state, Create Crew page
title/header/login gate, and Sasquatch FAQ. Added a subtitle distinguishing a
Crew (skate WITH) from The Lab (hire).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 20:02:17 +07:00
shakespeare.diy
0ac5c1019d fix: eliminate all render-path nip44 getter crashes (Browser extension not available)
Root cause: NUser's extension signer exposes .nip44 as a getter that calls
.nostr (window.nostr) and THROWS when the extension isn't present. Any
synchronous access during render crashes the whole tree — the actual culprit
was useStash's `isEncryptionAvailable: !!user?.signer?.nip44` in its return
object, evaluated every render even when logged out.

- Add src/lib/signerCapabilities.ts: hasNip44/hasNip04 helpers that determine
  capability via login type + direct window.nostr probe (no throwing getter).
- useStash: use the helper for hasNip44 + isEncryptionAvailable
- useDirectMessages: replace all render-path `enabled: !!user?.signer?.nip44`
  with a pre-computed nip44Available across all 4 conversation hooks
- CrewChat, CrewRequestToJoin, ConversationPage, DirectMessagesPage,
  ContactsPage: use the safe helper in render/guard paths

Async accesses (inside queryFn/callbacks) are left as-is — they reject
gracefully within react-query and never crash render.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 19:37:08 +07:00
shakespeare.diy
73f51662af fix(stash): avoid nip44 getter entirely — check login type + window.nostr directly
The NUser extension signer's .nip44 getter calls .nostr which throws
"Browser extension not available" when window.nostr is absent. A try/catch
around ?.nip44 doesn't work because esbuild optimises it away.

Fix: check login type + window.nostr directly (no getter, no throw) to
determine nip44 availability, and wrap all actual signer accesses at
call-time in their own try/catch.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 19:21:28 +07:00
shakespeare.diy
4c4e7159ba fix(stash): guard nip44 getter to prevent crash when no signer/extension
Some signers throw "Browser extension not available" when the .nip44
getter is accessed rather than returning undefined. Wrap the check in a
try/catch so useStash never throws on mount for logged-out users or users
with signers that don't support NIP-44.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 19:15:44 +07:00
shakespeare.diy
af6847c669 chore: bump to v1.0.87, add release notes for Zapstore publish
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 13:32:29 +07:00
shakespeare.diy
ef9bbea465 feat: replace Geyser with Angor across the entire app
Remove all Geyser.fund references and replace with the SkateSpots Angor
project (angor1qtunna00uqyx52rr0nvqa059z2gknhrmn7urd6z).

- SupportersPage: remove Geyser leaderboard + API calls entirely; replace
  Supporters section with an Angor CTA card including the on-chain wallet
  address (bc1q2hdfyqa7utrxrd95rlr27shsdr6gtk7dzdqres)
- useGrowthStats: delete useGeyserStats hook + GEYSER_API_URL constant
- CommunityStatsPage: remove Geyser stats widget, replace with Angor CTA
- SasquatchGuide: update system prompt + FAQ (is it free, supporters)
- NewsletterPage: update both Geyser links (body + footer) to Angor
- AboutPage: remove Geyser row, update Angor entry with real project URL
- README.md: update crowdfunding section + credits

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 13:23:55 +07:00
shakespeare.diy
d6b50de349 feat(sasquatch): Pro receipt DM + local Pro FAQ + tougher free-tier answers
1. Pro purchase confirmation: on a paid pass, send the buyer an encrypted
   NIP-17 receipt to their own inbox (tier, expiry, unlocked perks). Best-effort
   so it never blocks the purchase. Success toast now points to the DM.

2. Frictionless Pro answers: add local static-FAQ entries for "sasquatch pro",
   "pro perks/version", pricing, "go pro", "upgrade" — so those questions are
   answered instantly and never dead-end when the free AI model is rate-limited
   (the exact failure seen when asking "what perks has the pro version?").

3. FAQ matching now prefers the longest matching key so specific phrases win
   over broad ones ("sasquatch pro" > "pro").

4. Refresh the free-model fallback list (add deepseek-v3, gemini-2.0-flash-exp,
   qwen-2.5-72b) for better free-tier AI availability.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 00:21:34 +07:00
shakespeare.diy
1c18163a90 chore(sasquatch): remove joke book, clean up default suggested questions
- Delete sasquatchJokes.ts and all joke usages (idle chat line, fallback
  taglines, Lab/Crew guide messages).
- Tidy the default/generic suggested questions to a clean Nostr-focused set
  (Plan a trip, spots, events, crews, zaps, Nostr, Stash, free).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 00:08:52 +07:00
shakespeare.diy
1efeae0960 feat(sasquatch): add Trip Planner + tougher free-tier fallback + new joke book
- Trip Planner: `plan_trip` tool orchestrates spots+events+coaches+crews+weather
  into a weather-aware, day-by-day itinerary card (TripItineraryCard). Actions:
  save all spots to a NIP-51 Collection, book/zap a coach via their profile,
  and deep-link out to flights/stays (assist, never fake-book, no KYC).
- Free-tier resilience: local intent parser answers "spots/events/coaches/
  crews/gear/weather in <city>" and full trip plans directly via CVM even when
  both AI providers are rate-limited — no more dead-end fallback.
- New shared joke book (sasquatchJokes.ts): skate humor + gentle roasts of the
  KYC-your-audience Bitcoin-influencer crowd. Idle joke on the empty chat state,
  ethos jokes in fallbacks. Dedupes the old inline joke arrays.
- UX polish: taller chat scroll area, trip-planner suggested questions, FAQ
  entries for the planner.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 00:04:28 +07:00
shakespeare.diy
5655fbc976 Sasquatch Pro Phase 2: action drafting (draft → you confirm)
Sasquatch can now DRAFT actions from natural language and publish them
only after the user reviews & confirms — it never publishes on its own.

- useSasquatchActions: publishes confirmed drafts via the app's normal
  publish path. Supports post_status (NIP-38 kind 30315, auto-expiring)
  and list_gear (NIP-99 kind 30402, skateboarding-tagged), reusing the
  existing event schemas.
- SasquatchActionCard: inline "review & confirm" card with editable key
  fields (status text / listing title, price, condition), Publish +
  Cancel, and done/cancelled states. Nothing is signed until Publish.
- SasquatchGuide: new draft_action tool (Pro-gated). The AI emits a
  draft; a card renders in-chat; confirmDraft signs & publishes. Non-Pro
  users get an upgrade nudge. Draft lifecycle tracked per message.
- Prompt teaches the model to use draft_action for "post/sell/list/set
  my status" requests and to fill gear fields from the stash first.

Examples now working (Pro): "post that I'm skating Southbank for 3 hours"
· "list my Spitfire wheels for 8000 sats".

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:41:11 +07:00
shakespeare.diy
79d1d47823 Sasquatch Pro Phase 3: npub-bound Lightning paywall + gating
Adds the Sasquatch Pro supporter pass with strong, forge-resistant
verification bound to the paying user's npub — no backend required.

Verification (useSasquatchPro):
- STRONG: NIP-57 zap receipt (kind 9735). Purchase signs a zap request
  tagged pro-tier/pro-expiry; the receipt embeds it, cryptographically
  binding payment → identity (unforgeable by the user).
- FALLBACK: self-signed kind-32671 receipt so paying users are never
  locked out if the provider emits no zap receipt.
- Platform owners always Pro. Access = max unexpired expiry.

Purchase (usePurchasePro):
- Zap to skatespots@minibits.cash (falls back to plain invoice), pay via
  NWC → WebLN → manual QR. Publishes the fallback receipt on success.

Pricing (sasquatchPro.ts): 4 tiers — 7d/2k, 1mo/5k, 3mo/12k (popular),
1yr/36k sats.

UI (SasquatchProDialog): upgrade sheet with tiers, perk list, QR
fallback, "already Pro" state, extend flow.

Gating (SasquatchGuide):
- Personal tools (get_my_*) now require login + Pro; prompt only exposes
  them to Pro users; tool switch guards with a pro_required nudge.
- ⚡ Pro badge when active, ⚡ Go Pro button otherwise; local upgrade
  nudge for "my/mine" questions from non-Pro users.

Docs: kind 32671 + verification model documented in NIP.md.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:33:39 +07:00
shakespeare.diy
a37224d8a2 Sasquatch Pro Phase 1: read-only personal assistant tools
Sasquatch can now answer questions about the logged-in user's OWN data
("what's in my stash?", "my crews", "events near my crew", "my spots",
"what am I selling?").

- New usePersonalContext hook: client-side getters for the user's Stash
  (decrypted locally), crews (founder/member), marketplace listings, and
  created spots. Reads own data only — nothing personal leaves the device.
- Registered 4 personal tools (get_my_stash / get_my_crews /
  get_my_listings / get_my_spots) in the Sasquatch tool switch.
- Personal-tools prompt block is appended to the system prompt ONLY when
  a user is logged in, so logged-out UX is unchanged.
- needsLiveData now routes "my/mine" questions to the AI tool path.

Phase 1 is intentionally ungated so we can validate the experience
before wrapping the Pro paywall (Phase 3).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:21:37 +07:00
shakespeare.diy
5d07524751 Glossary: drop difficulty levels, expand & correct tricks
- Removed difficulty categorization entirely (type, badges, styles) —
  cleaner and no arbitrary skill labels.
- Added many requested/missing tricks with proper research and correct
  alt-names, including:
  • Rotations category: FS/BS 180, FS/BS 360, half-cab, full cab
    (caballerial), fakie 180s.
  • Flip tricks: backside flip, frontside flip, FS/BS heelflip, double
    heelflip, 360 hardflip, 360 pressure flip.
  • Grinds: feeble grind now notes "Grosso grind" aka; added Willy/
    Barley, pole jam; inventor credits (Suski, Losi, etc.).
  • Fundamentals: nose ollie/north, one-foot, no-foot/nothing, nose
    manual, caveman, tic-tac.
  • Shoves: nollie shove, ghetto bird.
  • Grabs: tailbone, airwalk. Stance/slang: pop, line, kook, sticker
    slap, etc. Hardware: durometer.
- Updated the local glossary answerer index to cover all new aka names
  (e.g. "grosso grind", "half cab", "north ollie" now answer locally).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:08:37 +07:00
shakespeare.diy
cc84713fe4 Make Sasquatch resilient: local glossary answers + backup AI provider
Fixes the "brain's catching traffic" dead-end for trick/terminology
questions and reduces reliance on a single flaky free API.

- Local glossary answerer (answerFromGlossary): trick/how-to/terminology
  questions ("what is a feeble grind", "how do I do a kickflip", "grosso
  grind") are now answered instantly from the 150+ entry glossary with
  ZERO API calls — so they never hit a rate limit.
- Wired into SasquatchGuide as (a) a fast path before the AI and (b) a
  last-resort fallback when the AI is unreachable.
- sasquatchBrain now has multi-provider fallback: OpenRouter free models
  (with 429 backoff) → keyless Pollinations backup. Only throws if both
  fail, letting the caller fall back to local knowledge.
- Replaced the unhelpful fallback message with useful local links +
  a nudge to ask about a specific trick.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:04:41 +07:00
shakespeare.diy
309d012db1 Massively expand Trick Glossary (tricks + terminology + hardware)
Grew the glossary from ~80 to ~150 entries across 15 categories.

- Added many more tricks per the community list: impossible, gazelle
  flip, pressure/hospital/dolphin flips, bigger spin/flip, suski/willy/
  losi/hurricane grinds, japan/madonna/slob/seatbelt/roastbeef/benihana/
  lien/cannonball/christ grabs, 720/1080, casper/anti-casper, spacewalk,
  walk the dog, g-turn, and more — with inventor credits and aka names.
- New reference categories: "Transition & Vert Terms", "Stance &
  Movement", and "Hardware & Equipment" (deck, trucks, bushings,
  bearings, risers, wheel bite, etc.).
- Reference categories hide difficulty badges (added `reference` flag +
  `hideLevel` on TrickRow); search still spans everything.
- Updated hero copy + SEO description to reflect the broader scope.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:54:40 +07:00
shakespeare.diy
b3cda77224 Add optional crew band / music project to Skate Crews
Skate scenes and local music go hand in hand — crews can now list a band
name + a music link (Spotify/Bandcamp/YouTube/Nostr).

- SkateCrew type: new optional bandName / bandUrl fields.
- Parser reads `band` + `band-url` tags (band-url validated as http(s)).
- CrewForm: dedicated optional "Crew Band" section.
- CrewDetailPage: purple band row (linked when a valid URL is present).
- CrewsPage cards: small "Band" badge when a crew has a band.
- Kept scoped to crews only (not The Lab) since bands aren't a
  skate-specific craft.
- NIP.md: documented the new `band` / `band-url` crew tags.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:44:17 +07:00
shakespeare.diy
a371ff3645 Add Trick Glossary + harden Sasquatch AI rate-limit handling
Glossary:
- New /glossary page: 80+ tricks across 11 categories (fundamentals,
  flips, shoves/spins, grinds, slides, grabs, airs, lip, stalls,
  handplants, boneless, freestyle) with alternate names, difficulty
  badges, live search, and category filters.
- Added to desktop "More" dropdown and mobile burger menu.

Sasquatch AI:
- Fixed/expanded free-model fallback list with valid OpenRouter slugs
  (dropped the non-existent gemma-4 slug).
- Added exponential backoff + one retry on 429 rate limits, respecting
  Retry-After headers.
- SasquatchGuide now reuses the shared sasquatchBrain caller instead of
  a duplicated implementation.
- Friendlier fallback message when the AI is saturated.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:39:51 +07:00
shakespeare.diy
e6517b5c70 Add owner-only ContextVM server health check (Settings > Advanced)
Probes the ContextVM relay for the CVM server's own events and reports
online/stale/offline with a last-seen timestamp. Gated to the two
platform owner pubkeys (renders null for everyone else). Notes that
Sasquatch's data layer queries relays directly and is unaffected by the
server being down.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:27:22 +07:00
shakespeare.diy
148ae251ef Add desktop top-nav customization (Settings > Appearance > Navigation)
Option B: a lightweight reorder / show-hide list for the desktop header.

- useDesktopNavPreferences: separate localStorage-backed preference for which
  catalog items appear as primary top-nav tabs and their order (Map fixed
  first, no hard slot cap). Reuses the shared NAV_CATALOG.
- DesktopNavManager: settings UI to reorder primary tabs (up/down) and toggle
  items between the top bar and the "More" menu; Map locked; reset to default.
- Layout desktop header now renders fixed Map + user's primary tabs, and the
  "More" dropdown dynamically lists the hidden catalog items (auth-gated),
  so each item appears in exactly one place.
- Manager shown only on md+ screens (mobile uses the bottom-bar customizer).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:09:20 +07:00
shakespeare.diy
834f33d53e Make Sasquatch bubble open reliably on first tap (mobile)
The "1st tap does nothing, 2nd/3rd works" bug came from relying solely on
pointer-capture tap detection, which is flaky on the first interaction after
mount in mobile WebViews — and the earlier e.preventDefault() on pointerup was
suppressing the click we could otherwise use.

Fix: add a plain onClick as a reliable open path (the most dependable "tapped"
signal in a WebView), extract an idempotent openPanel() used by both pointerup
and click, and drop the preventDefault so the click isn't swallowed. The
justOpenedRef lock still blocks the ghost-click that would re-close it. Double
firing is harmless since openPanel just sets visible=true.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 15:36:11 +07:00
shakespeare.diy
ebbd0fd125 Fix Sasquatch panel opening then instantly closing on mobile
On touch devices, the bubble's pointerup opened the panel and unmounted the
bubble; the synthesized "ghost click" from the same tap then landed on the
panel's minimize/close button (which renders in roughly the same bottom-left
spot), instantly re-closing it.

Fix: preventDefault/stopPropagation on pointerup to suppress the ghost click,
guard releasePointerCapture against unmount throws, and add a 400ms
"just opened" lock so stray close/minimize events from the same tap are
ignored.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 15:24:05 +07:00
shakespeare.diy
5ec1c84ef0 Fix Near Me crash on GrapheneOS: avoid Play-Services position API
Root cause: @capacitor/geolocation v8 fetches position via Google Play
Services (play-services-location / iongeolocation-android). On de-Googled
devices (GrapheneOS) those classes don't exist, so the plugin's
getCurrentPosition/watchPosition throw NoClassDefFoundError on the Android
main thread -> FATAL EXCEPTION -> app killed. No JS try/catch can intercept it.

Fix: use the native plugin ONLY for the OS location-permission prompt (pure
Android, no Play Services), then fetch the actual position via the WebView's
navigator.geolocation, which uses Android's LocationManager directly and works
without Google. Removed the now-dead raceForFirstFix() and the native
position/watch typings so the Play-Services code path is never invoked.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 14:08:23 +07:00
shakespeare.diy
a46be4be68 Fix generic npub names: query profile aggregators in parallel in useAuthor
Previously useAuthor only hit the global profile-aggregator relays as a
fallback when the main pool returned zero kind 0 events. On the curated skate
relay many followed users have no profile, so names fell back to genUserName.
Now query the main pool and aggregators (purplepag.es, nostr.band, nos.lol,
damus) in parallel and keep the newest kind 0, so real display names resolve.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 14:01:55 +07:00