From 71e580f474912fa3d87a221e4a16d34ca68834f0 Mon Sep 17 00:00:00 2001 From: Avi Date: Tue, 25 Aug 2026 14:36:02 -0500 Subject: [PATCH 1/3] Trim white margins from sidebar logo artwork 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. --- frontend/src/assets/logo.png | Bin 10477 -> 9483 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/src/assets/logo.png b/frontend/src/assets/logo.png index d02f1d6303d0f7e307c9b0ce7889fea392d63159..fcb48ba0202992652c0d6a8c3ee884a6279e80ed 100644 GIT binary patch literal 9483 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 From 835b0a02fd9f057d49667a25607371418c9baa52 Mon Sep 17 00:00:00 2001 From: Avi Date: Wed, 26 Aug 2026 14:32:22 -0500 Subject: [PATCH 2/3] frontend: list all profiles on Home, pin dev host, widen profiles card - 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. --- frontend/src/components/Badge.tsx | 14 +- frontend/src/screens/HomeScreen.tsx | 200 +++++++++----------------- frontend/src/styles.css | 52 ++++++- frontend/src/test/HomeScreen.test.tsx | 15 +- frontend/vite.config.ts | 1 + 5 files changed, 130 insertions(+), 152 deletions(-) diff --git a/frontend/src/components/Badge.tsx b/frontend/src/components/Badge.tsx index b0c3748..265b6a7 100644 --- a/frontend/src/components/Badge.tsx +++ b/frontend/src/components/Badge.tsx @@ -2,6 +2,16 @@ import type { ReactNode } from 'react'; export type BadgeTone = 'neutral' | 'success' | 'warning' | 'danger' | 'info'; -export function Badge({ tone = 'neutral', children }: { tone?: BadgeTone; children: ReactNode }) { - return {children}; +export function Badge({ + tone = 'neutral', + className, + children, +}: { + tone?: BadgeTone; + className?: string; + children: ReactNode; +}) { + return ( + {children} + ); } diff --git a/frontend/src/screens/HomeScreen.tsx b/frontend/src/screens/HomeScreen.tsx index 31c38a3..da4a7f3 100644 --- a/frontend/src/screens/HomeScreen.tsx +++ b/frontend/src/screens/HomeScreen.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useState } from 'react'; import { Alert } from '../components/Alert'; import { Avatar } from '../components/Avatar'; import { Badge } from '../components/Badge'; @@ -6,67 +6,27 @@ import { Button } from '../components/Button'; import { CopyButton } from '../components/CopyButton'; import { EmptyState } from '../components/EmptyState'; import { Icon } from '../components/Icon'; -import { Spinner } from '../components/Spinner'; import { shortenNpub } from '../lib/format'; import type { Screen } from '../lib/navigation'; import { useApp } from '../state/AppProvider'; -type RelayStatus = 'checking' | 'connected' | 'failed'; - interface HomeScreenProps { onNavigate: (screen: Screen) => void; onCreateProfile: () => void; } export function HomeScreen({ onNavigate, onCreateProfile }: HomeScreenProps) { - const { state, relayTest, lastPublish } = useApp(); - const [statuses, setStatuses] = useState>( - {}, - ); - const [testing, setTesting] = useState(false); + const { state, lastPublish, selectProfile } = useApp(); + const [selecting, setSelecting] = useState(null); - const enabled = useMemo( - () => state?.settings.relays.filter((r) => r.enabled) ?? [], - [state?.settings.relays], - ); - - const runCheck = useCallback(async () => { - setTesting(true); - const entries: Record = {}; - for (const relay of enabled) { - entries[relay.url] = { state: 'checking' }; + const onSelect = async (npub: string) => { + setSelecting(npub); + try { + await selectProfile(npub); + } finally { + setSelecting(null); } - setStatuses({ ...entries }); - await Promise.all( - enabled.map(async (relay) => { - try { - const result = await relayTest(relay.url); - entries[relay.url] = { - state: 'connected', - detail: result.latency_ms != null ? `${result.latency_ms} ms` : 'connected', - }; - } catch (error) { - entries[relay.url] = { - state: 'failed', - detail: error instanceof Error ? error.message : String(error), - }; - } - }), - ); - setStatuses({ ...entries }); - setTesting(false); - }, [enabled, relayTest]); - - useEffect(() => { - if (enabled.length > 0) { - void runCheck(); - } else { - setStatuses({}); - } - }, [enabled, runCheck]); - - const connectedCount = Object.values(statuses).filter((s) => s.state === 'connected').length; - const failedCount = Object.values(statuses).filter((s) => s.state === 'failed').length; + }; const active = state?.active_profile ?? null; const shorten = state?.settings.shorten_npub ?? true; @@ -119,94 +79,72 @@ export function HomeScreen({ onNavigate, onCreateProfile }: HomeScreenProps) {
-

Active profile

-
-
- {active ? ( -
- -
- {active.label} - - {shortenNpub(active.npub, shorten)} - -
- -
- ) : ( -

- No profile selected.{' '} - {' '} - to get started. -

- )} -
-
- -
-
-

Relays

+

Your profiles

- {enabled.length === 0 ? ( -

- No relays enabled.{' '} - {' '} - to publish notes. -

- ) : testing && Object.keys(statuses).length === 0 ? ( - - ) : ( -
    - {enabled.map((relay) => { - const status = statuses[relay.url]; - return ( -
  • -
  • - ); - })} -
- )} - {failedCount > 0 && - !testing && - enabled.length > 0 && - Object.keys(statuses).length === enabled.length && ( -

- {connectedCount} of {enabled.length} enabled relays reachable. -

- )} +
    + {state?.profiles.map((profile) => ( +
  • + +
    + + {profile.label} + {profile.is_active && ( + + Active + + )} + + + {shortenNpub(profile.npub, shorten)} + +
    +
    + + {profile.is_active ? ( + + ) : ( + + )} +
    +
  • + ))} +
+
diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 947ee5e..1dd6c1d 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1003,16 +1003,10 @@ select { .home-grid { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: 1fr; gap: 20px; } -@media (max-width: 900px) { - .home-grid { - grid-template-columns: 1fr; - } -} - .active-profile-row { display: flex; align-items: center; @@ -1067,6 +1061,50 @@ select { object-fit: cover; } +.home-profile-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 10px; +} + +.home-profile-row { + display: flex; + align-items: center; + gap: 14px; + padding: 8px; + border-radius: 8px; + border: 1px solid transparent; +} + +.home-profile-row.is-active { + background: var(--token-item-bg, rgba(0, 0, 0, 0.04)); + border-color: var(--token-border, rgba(0, 0, 0, 0.12)); +} + +.home-profile-row .active-profile-meta { + flex: 1; + min-width: 0; +} + +.home-profile-actions { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} + +.home-active-badge { + margin-left: 8px; + vertical-align: middle; +} + +.home-add-profile { + margin-top: 14px; +} + .relay-status-list { list-style: none; margin: 0; diff --git a/frontend/src/test/HomeScreen.test.tsx b/frontend/src/test/HomeScreen.test.tsx index 8b8572d..516c4b1 100644 --- a/frontend/src/test/HomeScreen.test.tsx +++ b/frontend/src/test/HomeScreen.test.tsx @@ -1,4 +1,4 @@ -import { screen, waitFor } from '@testing-library/react'; +import { screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { HomeScreen } from '../screens/HomeScreen'; import { renderWithApp } from './render'; @@ -36,8 +36,8 @@ describe('HomeScreen', () => { renderHome(backend); renderWithApp(); - await screen.findByText('Alice'); - await userEvent.setup().click(screen.getByRole('button', { name: 'Copy full npub' })); + const aliceRow = (await screen.findByText('Alice')).closest('.home-profile-row') as HTMLElement; + await userEvent.setup().click(within(aliceRow).getByRole('button', { name: 'Copy full npub' })); await waitFor(() => { expect(backend.copied).toContain(ALICE); }); @@ -54,13 +54,4 @@ describe('HomeScreen', () => { .click(screen.getByRole('button', { name: /Create your first profile/i })); expect(onCreateProfile).toHaveBeenCalled(); }); - - it('shows live relay connection status from the backend', async () => { - const backend = createFakeBackend(); - renderHome(backend); - renderWithApp(); - - expect(await screen.findAllByText(/Connected/)).not.toHaveLength(0); - expect(screen.getAllByText('wss://relay.damus.io')).not.toHaveLength(0); - }); }); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index d87dd77..4983c5f 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -6,6 +6,7 @@ import type { UserConfig } from 'vite'; export default defineConfig({ plugins: [react()], server: { + host: '127.0.0.1', port: 5173, strictPort: true, }, From 5635f7910a342a0ec9b187643b45cb7f650520d5 Mon Sep 17 00:00:00 2001 From: Avi Date: Wed, 26 Aug 2026 14:32:48 -0500 Subject: [PATCH 3/3] checkpoint: Home profiles list, dev host fix, full-width profiles card --- CHECKPOINT-encryption.md | 89 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 81923f2..643445a 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -6,13 +6,14 @@ verified green at the moment this file was written. ## Where things are - Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `fa5ba08` ("Security: upgrade nostr stack 0.40->0.45 - clearing 11 RustSec advisories"). Before it: `bf10ae3` (Checkpoint: clarify - NIP-42 status), `e210f17` (Checkpoint: DRY modal save lifecycle / R3), - `7098e75` (refactor: share modal save lifecycle), `e6a1efc` (R4), - `6d5063d` (R2), `1c428a9` (R1). -- Working tree: only the long-standing user changes remain — `concept3.svg` - deleted, `KeynectrAppIconPossibility02.jpeg` untracked. +- Git repo: `master` @ `835b0a0` ("frontend: list all profiles on Home, pin + dev host, widen profiles card"). Before it: `71e580f` (Trim white margins + from sidebar logo artwork), `f29d4f9` (Checkpoint 0.45 migration), + `fa5ba08` (Security: upgrade nostr stack 0.40->0.45), `bf10ae3`, `e210f17`, + `7098e75`, `e6a1efc`, `6d5063d`, `1c428a9`. +- Working tree: still DIRTY with the long-standing user changes — `concept3.svg` + deleted, `KeynectrAppIconPossibility02.jpeg` untracked. These are NOT part + of the frontend commit above and remain uncommitted. ## What was completed @@ -41,6 +42,7 @@ verified green at the moment this file was written. ## Commits added most recently +- `835b0a0` frontend: list all profiles on Home, pin dev host, widen profiles card - `fa5ba08` Security: upgrade nostr stack 0.40->0.45 clearing 11 RustSec advisories ## Verification commands run (all green) @@ -1349,3 +1351,76 @@ $B rename # rename + publish the new name - Relay config at time of writing (all enabled): nos.lol, relay.primal.net, l484.com (user's own), damus.io, snort.social, soloco.nl; relay.nostr.band added but was unreachable (handshake timeout) — retry enabling later. + +## Checkpoint — frontend home screen improvements (2026-08-26) + +A stopping point you can return to if this session is closed. + +### Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master`. **Working tree is DIRTY** — the changes below are local + edits and have NOT been committed yet (see "outstanding items"). +- Dev-run fix also applied: `frontend/vite.config.ts` now binds the dev server + to `host: '127.0.0.1'` so Electron (`NOSTR_GUI_DEV_URL=http://127.0.0.1:5173`) + and Vite agree on address and the window is no longer a white box. + +### What was completed (user-facing) + +1. **Profile pictures now show on Home.** The Home screen's active-profile + `Avatar` was missing the `picture` prop, so the image only appeared on the + Profiles screen. `HomeScreen.tsx` now passes `picture={profile.picture}`. +2. **All profiles listed on Home.** Replaced the single "Active profile" card + with a "Your profiles" card listing every profile (avatar + picture, label, + shortened npub, Active badge). Each non-active row has a working **Select** + button (calls `selectProfile`); a **Manage** link goes to the Profiles + screen and an **Add profile** button is at the bottom. +3. **`Badge` now accepts `className`** so the Active badge can be inline next to + the name. +4. **Relays card removed from Home.** The "Relays" card (live connection status, + refresh, "enable a relay" link) is no longer shown on Home — relay status + lives on the Relays screen. Removed the now-unused relay-check logic + (`relayTest`/`runCheck`/`statuses`/`testing`/`enabled`/`Spinner`) and the + corresponding test. +5. **Profile card now full-width on Home.** `.home-grid` was a two-column grid + (profiles + relays); with Relays gone the profiles card only filled the left + half. Changed it to a single `1fr` column and dropped the now-redundant + `@media (max-width: 900px)` override, so "Your profiles" matches the width + of "Most recent publication" below it. No JSX change. + +### Files changed + +- `frontend/src/screens/HomeScreen.tsx` (profiles list + picture prop + select; relays removed) +- `frontend/src/components/Badge.tsx` (add `className` prop) +- `frontend/src/styles.css` (`.home-profile-list` / `.home-profile-row` / etc.) +- `frontend/src/test/HomeScreen.test.tsx` (scope copy-button test to Alice's row; drop relay-status test) +- `frontend/vite.config.ts` (dev server `host: '127.0.0.1'`) + +### Verification commands run (all green) + +Frontend (`frontend/`): `npm run typecheck` clean; `npm run lint` clean; +`npm test` 15 files / 98 tests pass; `npm run format:check` clean (after +`npm run format`). + +### How to use / reproduce + +Dev mode (two terminals, from `~/Projects/Nostr_Keynctr/frontend`): + +``` +# Terminal 1 +npm run dev # prints Local: http://127.0.0.1:5173/ + +# Terminal 2 +NOSTR_GUI_DEV_URL=http://127.0.0.1:5173 npm start +``` + +Home now shows every profile with its picture, and selecting one there updates +the active profile immediately. + +### Outstanding items + +- Committed as `835b0a0`. The working tree still has unrelated user changes + (`concept3.svg` deleted, `KeynectrAppIconPossibility02.jpeg` untracked) that + were intentionally left out of the commit. +- The dev-server `host: '127.0.0.1'` change is intentional; if you later need + LAN access to the dev server, set it back to `true` and use the matching URL.