feat: sidebar dashboard + Connect dialog (JDBC/SSH/RDP/copy-command)#8
Merged
Merged
Conversation
Reskins the vault page from a single flat grid into a Supabase-Studio-style sidebar dashboard, and adds a one-click Connect action that bridges saved credentials to the user's terminal, RDP client, or JDBC tool. ## Schema `VaultLoginPlaintext` gains five optional fields, all encrypted client-side like the rest of the credential body. Existing items keep working — when `protocol` is absent the UI infers it from the port (5432 -> psql, 22 -> ssh, 3389 -> rdp, ...). - `protocol` — drives which Connect actions appear and which engine tile + protocol pill render in the grid - `database` — DB connect-string suffix - `serviceName` — Oracle SERVICE_NAME (preferred over SID) - `domain` — Windows AD domain for RDP, used by the .rdp file builder - `environment` — free-form prod/staging/dev/cache, surfaced as a row tag ## Connect module (packages/web/src/connect) Pure functions, no React. Tested with 37 unit tests: - `protocol.ts` — port -> protocol inference, engine codes, default ports - `jdbc.ts` — `buildJdbcUrl` for Postgres / MySQL / MariaDB / Oracle / MSSQL / Mongo. Returns null for protocols without standard JDBC drivers (SSH, RDP, Redis). Never embeds the password — JDBC tools want it separately. - `command.ts` — `buildConnectCommand` produces `psql 'postgres://...'`, `mysql -h ... -p`, `sqlplus '...'`, `redis-cli -a "$PASSWORD"`, etc. Placeholders for the password, never the literal. - `ssh.ts` — `ssh://user@host:port` URL builder + `launchSshUrl` to invoke the OS protocol handler. - `rdp.ts` — `buildRdpFile` writes a Windows-format .rdp keyed for modern clients (RDP 8+); `downloadRdpFile` triggers a browser download via Blob + revocable object URL. Password is NOT embedded — RDP requires DPAPI/keychain for that. - `clipboard.ts` — `copySensitive` with a 30 s self-clear timer; checks the clipboard hasn't been replaced before wiping. ## UI components Vault page split into focused pieces under `packages/web/src/pages/vault/`: - `Sidebar.tsx` — logo, view filter, Vault list, Group sections (protocol / IP / port / user) with live counts, signed-in user card. Sidebar clicks set a scope filter that composes with the search box. - `CredentialsGrid.tsx` — 12-col grid with checkbox / engine tile / name + url subtitle / hostname (clickable to copy) / IP / user / password (mask + reveal/copy hover-revealed) / port / protocol pill / env tag / last-used relative time / Connect button / delete. - `ConnectDialog.tsx` — modal with up to four actions (JDBC / SSH / copy-command / RDP), each disabled cleanly when not applicable. Esc closes; backdrop click closes; the footer reaffirms zero-knowledge. - `AddCredentialForm.tsx` — protocol-aware: picking PostgreSQL auto-fills port 5432; picking RDP surfaces a Windows AD domain field; picking Oracle surfaces both Database (SID) and Service name (preferred) fields. - `lastUsed.ts` — local-only timestamps in localStorage, never sent to the server. Includes `formatRelative` for "2m ago", "yesterday", "3d ago". `VaultPage.tsx` orchestrates: search, scope filtering, selection, toast, ⌘K-to-search, and bulk delete. ## Styles `styles.css` is a from-scratch design system: dark surface tokens, signature green brand accent (#3ECF8E), per-engine swatches (PG / OR / MY / RD / MG / SSH / RDP), JetBrains Mono for technical fields, dot grid background. Compiles to 19 KB / 4.5 KB gzipped. The auth pages (`auth-container`) get a coherent restyle — same dark panel + green primary button as the vault. ## Tests - `packages/web/tests/connect.test.ts` — 37 tests covering URL builders, command builder, RDP file format (CRLF, full-address, domain encoding, no-password-leak), clipboard auto-clear behaviour. - All existing core tests still pass (25/26, 1 skipped integration). - `npm run typecheck --workspaces` clean across core / web / extension. - `npm run build --workspace=@passman/web` clean. ## Docs - `docs/USER_GUIDE.md` rewritten — full walkthrough of the new dashboard, the Connect dialog with per-engine JDBC URL formats, and the protocol-aware add form. - `docs/preview/` mockups updated to mirror the new live DOM exactly. `docs/preview/design/` keeps the four design-direction prototypes (Studio / Aurora / Sidebar / Cards) for future reference. - `docs/img/` regenerated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
valehdba
added a commit
that referenced
this pull request
May 9, 2026
Self-review of #8 surfaced six runtime/UX bugs that the test suite didn't catch. Tests added for each. 1. handleJdbc had an empty `if (p.password) { /* comment */ }` block whose comment claimed to queue the password on the clipboard but did nothing. Removed the misleading code and updated the toast to be honest: "JDBC URL copied · use the row's Copy button for the password". 2. handleCommand copied the command and then copied the password, leaving the password on the clipboard while the toast said "command copied" — the user would paste the password instead of the command. Now copies the command alone; password lives on the per-row Copy button. 3. launchSshUrl assigned to `window.location.href`, which is unreliable in Chromium for custom-scheme URLs: when no handler is registered Chrome navigates the tab to an `ERR_UNKNOWN_URL_SCHEME` page, throwing the user out of the app. Switched to the synthetic-anchor-click pattern used by 1Password / Bitwarden — the click invokes the protocol handler if present and is a silent no-op otherwise. 4. The "Open RDP session" option was enabled for any credential with a host, then called `buildRdpFile({ ...item, protocol: "rdp" })` — which overrode the protocol but kept the credential's port. For a Postgres credential on port 5432 the resulting .rdp pointed at `host:5432`, which doesn't run RDP. The option is now gated through a new `canBuildRdp` helper that requires `effectiveProtocol === "rdp"`, and the disabled-state hint guides the user to set the protocol field. 5. VaultPage.toggleSelectAll compared `s.size === filtered.length` to decide between select-all and deselect-all. With cross-scope selection that comparison can be coincidentally true even when the visible rows aren't the selected ones, causing a deselect when the user expected a select. Switched to a per-row `every`-based check that matches what CredentialsGrid uses for the header-checkbox visual. 6. The Connect dialog's "where this points" subtitle ran a dedupe pass keyed on exact string equality — but "db-prod-01" and "db-prod-01:5432" are different strings, so the host appeared twice in the rendered header (visible in the v0 vault-connect screenshot). Extracted the logic into a tested `buildTargetSubtitle` helper that keeps host:port as the canonical segment and only adds the IP separately when the hostname was used as the primary identifier. Tests: 48 passing (was 37) — +11 covering buildTargetSubtitle's dedupe and canBuildRdp's gating, including the regression case where a Postgres credential on port 5432 must NOT enable the RDP option. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
valehdba
added a commit
that referenced
this pull request
May 9, 2026
* fix(connect): six bugs in the new sidebar dashboard / Connect dialog Self-review of #8 surfaced six runtime/UX bugs that the test suite didn't catch. Tests added for each. 1. handleJdbc had an empty `if (p.password) { /* comment */ }` block whose comment claimed to queue the password on the clipboard but did nothing. Removed the misleading code and updated the toast to be honest: "JDBC URL copied · use the row's Copy button for the password". 2. handleCommand copied the command and then copied the password, leaving the password on the clipboard while the toast said "command copied" — the user would paste the password instead of the command. Now copies the command alone; password lives on the per-row Copy button. 3. launchSshUrl assigned to `window.location.href`, which is unreliable in Chromium for custom-scheme URLs: when no handler is registered Chrome navigates the tab to an `ERR_UNKNOWN_URL_SCHEME` page, throwing the user out of the app. Switched to the synthetic-anchor-click pattern used by 1Password / Bitwarden — the click invokes the protocol handler if present and is a silent no-op otherwise. 4. The "Open RDP session" option was enabled for any credential with a host, then called `buildRdpFile({ ...item, protocol: "rdp" })` — which overrode the protocol but kept the credential's port. For a Postgres credential on port 5432 the resulting .rdp pointed at `host:5432`, which doesn't run RDP. The option is now gated through a new `canBuildRdp` helper that requires `effectiveProtocol === "rdp"`, and the disabled-state hint guides the user to set the protocol field. 5. VaultPage.toggleSelectAll compared `s.size === filtered.length` to decide between select-all and deselect-all. With cross-scope selection that comparison can be coincidentally true even when the visible rows aren't the selected ones, causing a deselect when the user expected a select. Switched to a per-row `every`-based check that matches what CredentialsGrid uses for the header-checkbox visual. 6. The Connect dialog's "where this points" subtitle ran a dedupe pass keyed on exact string equality — but "db-prod-01" and "db-prod-01:5432" are different strings, so the host appeared twice in the rendered header (visible in the v0 vault-connect screenshot). Extracted the logic into a tested `buildTargetSubtitle` helper that keeps host:port as the canonical segment and only adds the IP separately when the hostname was used as the primary identifier. Tests: 48 passing (was 37) — +11 covering buildTargetSubtitle's dedupe and canBuildRdp's gating, including the regression case where a Postgres credential on port 5432 must NOT enable the RDP option. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add hero screenshots to README — vault grid + Connect dialog The README's "this is what it does" was an ASCII architecture diagram — informative but it doesn't communicate the product. Added two screenshots above the diagram: 1. The vault dashboard (sidebar groupings, engine-coloured rows, Connect button per row) — the at-a-glance "what does Passman look like?" answer for someone landing on the repo. 2. The Connect dialog with JDBC / SSH / copy-command / RDP options — the headline feature that distinguishes this from a generic password manager. Both images already exist under docs/img/ and are kept in sync with the live styles via the docs/preview/ mockups, so README screenshots track the actual product without a separate maintenance burden. Tightened the description's first line to mention the DBA / infrastructure focus that the screenshots make obvious. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reskins the vault page from a single flat grid into a Supabase-Studio-style sidebar dashboard, and adds the headline feature: a one-click Connect action per credential that bridges saved logins to the user's terminal, RDP client, or JDBC tool.
Screenshots
The dashboard with sidebar grouping (protocol / IP / port / user), engine-coloured rows, env tags, last-used relative time, and a Connect button per row:
The Connect dialog. Adapts per credential — for a Postgres entry it offers JDBC URL → SSH → copy-command → RDP. Each disabled cleanly when not applicable:
The protocol-aware Add form — picking PostgreSQL auto-fills port 5432 and surfaces a Database field; picking Oracle adds Service name; picking RDP adds Windows AD domain:
What's in the diff
Schema — backwards compatible
VaultLoginPlaintextgains five optional fields, all encrypted client-side like the rest of the credential body. Existing items keep working — when+ "protocol" +is absent the UI infers it from the port (5432 → psql, 22 → ssh, 3389 → rdp, …).+ "protocol" ++ "database" ++ "serviceName" ++ "domain" ++ "environment" ++ "packages/web/src/connect/" +— pure, tested utilitiesSix modules, 37 unit tests, no React deps:
+ "protocol.ts" +— port → protocol inference, engine codes, default ports+ "jdbc.ts" +—+ "buildJdbcUrl" +for Postgres / MySQL / MariaDB / Oracle / MSSQL / Mongo. Returns null for protocols without standard JDBC drivers (SSH, RDP, Redis). Never embeds the password — JDBC tools want it separately.+ "command.ts" +—+ "buildConnectCommand" +produces+ "psql 'postgres://...'" +,+ "mysql -h ... -p" +,+ "sqlplus '...'" +,+ "redis-cli -a "$PASSWORD"" +, etc. Placeholders for the password, never the literal.+ "ssh.ts" +—+ "ssh://user@host:port" +URL builder ++ "launchSshUrl" +to invoke the OS protocol handler.+ "rdp.ts" +—+ "buildRdpFile" +writes a Windows-format+ ".rdp" +keyed for modern clients (RDP 8+);+ "downloadRdpFile" +triggers a browser download via Blob + revocable object URL. Password is not embedded — RDP requires DPAPI/keychain for that, neither of which a browser can produce.+ "clipboard.ts" +—+ "copySensitive" +with a 30 s self-clear timer; checks the clipboard hasn't been replaced before wiping.UI components
Vault page split into focused pieces under
+ "packages/web/src/pages/vault/" +:+ "Sidebar.tsx" +— logo, view filter, Vault list, Group sections (protocol / IP / port / user) with live counts, signed-in user card. Sidebar clicks set a scope filter that composes with the search box.+ "CredentialsGrid.tsx" +— 12-col grid with checkbox / engine tile / name + url subtitle / hostname (click to copy) / IP / user / password (mask + reveal/copy hover-revealed) / port / protocol pill / env tag / last-used relative time / Connect button / delete.+ "ConnectDialog.tsx" +— modal with up to four actions, each disabled cleanly when not applicable. Esc closes; backdrop click closes; the footer reaffirms zero-knowledge.+ "AddCredentialForm.tsx" +— protocol-aware: picking PostgreSQL auto-fills port 5432; picking RDP surfaces a Windows AD domain field; picking Oracle surfaces both Database (SID) and Service name (preferred) fields.+ "lastUsed.ts" +— local-only timestamps in localStorage, never sent to the server. Stays consistent with the zero-knowledge posture.Styles
+ "styles.css" +is a from-scratch design system: dark surface tokens, signature green brand accent (#3ECF8E), per-engine swatches (PG / OR / MY / RD / MG / SSH / RDP), JetBrains Mono for technical fields, dot-grid background. Compiles to 19 KB / 4.5 KB gzipped. The auth pages (+ "auth-container" +) get a coherent restyle — same dark panel + green primary button as the vault.Verification
+ "npm run typecheck --workspaces" +— clean across core / web / extension+ "npm test --workspaces" +— 62 passed (25 core + 37 web), 1 skipped (integration)+ "npm run build --workspace=@passman/web" +— clean (19 KB CSS, 75 KB JS gzipped)Docs
+ "docs/USER_GUIDE.md" +rewritten — full walkthrough of the new dashboard, the Connect dialog with per-engine JDBC URL formats, and the protocol-aware add form.+ "docs/preview/" +mockups updated to mirror the new live DOM byte-for-byte (they+ "" +the live+ "styles.css" +, so any future style change auto-propagates to the screenshots when you re-capture).+ "docs/preview/design/" +keeps the four design-direction prototypes (Studio / Aurora / Sidebar / Cards) for future reference.Out of scope (deliberate, for follow-up PRs)
Test plan
+ "ssh user@host -p 2222" ++ "DOMAIN\\user" +🤖 Generated with Claude Code