- Replace plain numbered list with icon circles (primary-soft bg)
- Each step uses flex layout: indicator + content
- Step titles and body text have clear hierarchy
- Remove legacy padding-left on ol, use gap-based spacing
- Consistent 14px font size for step content
- Profile list rows are now focusable (tabIndex) and operable via
Enter/Space (onKeyDown handler)
- Added role=listbox on the <ul> and role=option + aria-selected on
non-active rows
- Added descriptive aria-label including profile name and active state
- Added focus-visible CSS ring matching the design system pattern
- Updated test role from 'list' to 'listbox'
Wire the existing profiles::import_profile through the JSON-lines IPC
protocol so the GUI can add an existing account: ImportProfile request
and handler in src/ipc.rs, import_profile added to the Electron method
allowlist, api/AppProvider importProfile, and the Add existing account
buttons in ProfilesScreen.
Also add the active signing identity card on Home and fix the HomeScreen
tests whose text queries now match the identity card as well as the
profile row.
Mirrors the Profiles screen: the whole non-active row in the Home
'Your profiles' list now selects the profile on click, guarded so the
Copy/Select buttons don't double-trigger. Added a pointer cursor for
non-active rows and a test for row-click selection.
Avatar was rendered without the 'picture' prop, so the active profile's
image only appeared on Home. Added picture={active.picture} to both
Compose avatars (write + preview) and the sidebar active-profile avatar.
The Profiles screen is still reachable via Home's 'Manage profiles'
button, so the left-nav Profiles tab was redundant. Dropped the
'profiles' entry from the sidebar NAV_ITEMS and updated App.test.tsx
to navigate to the screen via 'Manage profiles' instead.
- Home now shows every profile with its picture, an inline Active badge,
and a working Select button (was only the active profile).
- Remove the Relays card from Home (status lives on the Relays screen) and
its unused connection-check logic/test.
- Pin Vite dev server to 127.0.0.1 so Electron and the dev server agree on
the address (fixes white-box window in dev mode).
- Make the profiles card full-width to match the publication card below.
- Badge now accepts className.
The 512px source had ~25% baked-in white padding on every side, so the
38px sidebar tile showed a small bird floating in a white square.
Cropped to the content bounding box plus a 6% pad (keeps wing/beak
tips clear of the rounded corners); the img already fills the tile via
object-fit: cover. Window/packaged icons untouched.
The theme dropdown popup inherited the near-transparent select
background and rendered white-on-white under the glass theme.
Options now get an explicit dark surface, plus color-scheme: dark
so native controls render correctly. User-facing name is Aurora;
the internal 'glass' id is unchanged so saved settings keep working.
Commit a01f258 removed Theme::System but missed Settings::default(),
so the crate failed to compile and every desktop launch kept spawning
a stale backend binary that rejected 'glass'. Also untangles the
glass palette CSS that was duplicated as nested blocks inside the
neon rule, and re-applies the theme reactively when settings change.
- Store an optional nip05 on each profile; publish it in kind 0 metadata
- set-nip05 CLI (+ validation, lower-casing, clear) and nip05-file helper
that prints the .well-known/nostr.json document for a domain
- Profiles screen: NIP-05 button, handle shown on cards, Nip05Modal with
client-side validation and Remove action
- Fix Modal stealing focus from autoFocus inputs one frame after open
- Crate/binary: nostr-manager-backend -> keynectr
- Data directory: nost-feed-manager -> keynectr, migrated automatically
on first data_dir() call (existing vaults, settings and backups move)
- Electron extraResources/spawn path, executableName, productName,
window title and CLI usage strings updated to match
- Deliberately unchanged: crypto.rs KDF verifier string, so previously
encrypted vault backups remain decryptable
Verified live: existing vault with two profiles migrated to
~/.local/share/keynectr and loads correctly.
set_profile_picture and publish_profile_metadata were rejected by the
renderer-method gate added in the 2026-08-21 hardening ('rejected renderer
method' in the log), so the GUI buttons could never reach the backend.
Also allow delete_profile / undo_delete from the deletion feature, which
were missing from the list as well.
Profiles created in the app never published a kind 0 metadata event, so
clients like Iris and Yakihonne showed generated petnames ("evil iguana")
or a truncated npub instead of the user's chosen name.
- Publish kind 0 metadata (name/display_name) automatically on creation
- Add "Publish name" action (GUI button + publish-name CLI) for existing
profiles, with a per-relay success/failure report
- Add profile pictures: optional picture URL persisted in the vault,
set via GUI modal (URL paste or nostr.build upload), set-picture CLI,
and included in the published metadata; avatars render it in-app
- Run metadata publishing on its own thread so sync and async callers
never nest tokio runtimes
- Expose undo_history in the state view and fix typecheck errors left by
the unfinished delete/undo work (variants, icon, null-safety)
- Use offline relay settings in tests: Settings::default() points at real
relays, which tests were silently publishing to (suite: 126s -> ~3s)
Verification: cargo test 96 passed; clippy/fmt/build clean. Frontend:
78 tests, typecheck, lint, format, vite and electron builds all pass.
- src/feed.rs: contact_feed + contact_pubkeys fetch the active profile's
kind 3 contact list and aggregate notes only from those authors;
aggregate_for + FeedBuilder accept an optional author whitelist
- IPC: FeedGet accepts contacts_only, resolves the active profile npub
- CLI: feed --contacts [limit] filters to the active profile's contacts
- Frontend: Feed screen Everyone/My contacts toggle, scope-aware empty
states, feedGet(limit, contactsOnly) threading
- Tests for author filtering and the contacts scope in feed.rs and
FeedScreen.test.tsx
- src/feed.rs: aggregate recent kind 1 notes from enabled relays into a
newest-first, de-duplicated feed using a throwaway identity
- IPC: FeedGet; CLI: feed [limit]; frontend: Feed screen + nav + tests
- README: mark feed aggregation as shipped