Camera/QR (validated live with a v4l2loopback virtual camera):
- decode non-MJPEG frames on Linux (raw YUYV was undecodable: eternal
spinner), enumerate devices off the UI thread without unwrap, open
cameras by their real device index (list position broke whenever
/dev/video0 was absent), show "No camera found" after 5s
- scan-to-pay: QR button in the send recipient search row and the
mobile home header; in-surface camera panel feeding recipient
resolution; only text payloads accepted (seeds/slatepacks refused)
- receive QR was unscannable: full cells (0.5px gaps fragmented finder
patterns at 4.5px cells), always ink-on-white plate (inverted codes
fail many scanners), center mark 26% -> 19% (zbar chokes above);
rqrr+zbar both decode the live card now; unit tests cover the exact
widget geometry
First-run onboarding (replaces the stock empty state only; the wallet
list, add-wallet modal and GRIM creation flow stay for later wallets):
- intro -> node choice (Private integrated / Instant external with
URL) -> create or restore (wraps MnemonicSetup; word grid, paste,
SeedQR scan) -> optional @username claim with prominent skip
- fonts bind at creation context so frame one can use Geist families
Sweep fixes (22 confirmed findings, the simple ones):
- yellow theme: active sidebar nav was dark-on-dark (P1)
- window frame ring painted with theme bg: transparent clear color
rendered as a black band without a compositor (P2)
- import/rotate identity inputs get visible field wells (P2)
- receive buttons: verb labels + transient "Copied" feedback
- review hero card fills the column; fee row copy; pay-tab stale and
duplicated hints; unlock modal copy; node card subtitle truncation
- build number stays current (.git/logs/HEAD rerun trigger)
Backup restore: import accepts the export-time password and re-encrypts
under the current wallet password; cross-device restores work.
31 lib tests green. Mainnet-validated: both flows restored real
wallets; 0.1 grin sent B->A through NIP-17 over Tor with async
open/close ping-pong; tx 71fbfce4f591 posted on-chain.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stability (found via GUI testing):
- tor: create arti runtime on a clean thread; lazy TOR_STATE init panicked
inside tokio contexts and poisoned the whole Tor/nostr stack
- store: open rkv SafeMode envs with capacity headroom; reopening at
exactly DEFAULT_MAX_DBS crashed every wallet restart (DbsFull)
- goblin ui: centered_column hands children a full-height rect; ScrollAreas
inside clipped everything below the first widget
- build: webtunnel client was silently embedded as 0 bytes without Go;
warn at build, extract with create_dir_all + exec bit at runtime
- price: CoinGecko requires a User-Agent (403 otherwise); add retry-once
backoff and a parse-failure diagnostic
- tor: retry http_request up to 3x on fresh isolated circuits
UX overhaul (per owner direction + Cash App references):
- floating icon-only 3-tab pill: Wallet / center accent ツ Pay puck /
Activity (requests badge kept); Me opens via header avatar
- Pay tab: amount-first surface (numpad on mobile, typed on desktop) with
Request and Pay; Pay carries the amount straight to Review
- Request flips Receive into "Requesting Nツ" state with a clear chip
- full-bleed goblin surface: GRIM title panel and network column hidden
while a wallet is open; node status card lives in the sidebar above the
profile chip; Lock wallet row added (Settings -> Wallet)
- goblin branding: titlebar, wallet-list logo + GOBLIN, new mark assets
(white master, theme-tinted) in wordmark, QR center, wallet list
- build-based versioning: Build N = commits since the GRIM fork base,
emitted by build.rs; About leads with it, Third party credits GRIM,
grin node, nostr-sdk, arti, egui and the implemented NIPs
Accessibility & settings:
- surface_text{,_dim,_mute} tokens: yellow theme has dark cards on a
bright bg; all on-surface text now readable in every theme (incl. QR)
- settings rows clickable across the whole row; profile card fills width;
density option removed (comfy fixed)
- editable Node connections (integrated/external, add/remove) and Relays
(add/remove + live service restart); NIPs explainer page with goblin.st
context; third-party rows link to upstream projects
- standardized npub truncation (head 12 ... tail 6) shown in profile
Identity (owner decision: drop NIP-06 seed binding):
- random standalone nsec (Keys::generate); seed proves nothing about the
identity and cannot resurrect it; legacy Derived identities still unlock
- key rotation: double warning (pending payments disrupted), typed RESET +
wallet password, fresh random key, username moved atomically via the
name server transfer endpoint; aborts cleanly if the move fails
- encrypted identity backup export (NIP-49 ncryptsec JSON, includes
username + history) and import accepting nsec or backup JSON
- nip05d: POST /api/v1/transfer (NIP-98 by current owner, atomic
owner-guarded swap, one-name-per-pubkey enforced) + SQL invariant tests
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>