v2.19.4 — unified certificate-creation flow, MCP lifecycle tools, refinements - #360
Merged
Conversation
The top-bar icon-only buttons (search, shortcuts, logout) are bare <button>s, not .btn, so the focus floor in input.css — which only targeted a/summary — left them with no visible keyboard focus indicator (WCAG 2.4.7) on the app-wide top nav. Extend the focus-visible floor to button:not(.btn) and [role=button]:not(.btn); .btn keeps its own ring. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
help.html's container had py-6 sm:px-6 lg:px-8 with no base px-4, so every content card sat flush to the viewport edge < 640px (rounded corners clipped, looks broken) while the search/nav above were inset. Same defect fixed for index/settings/activity in #337 but missed on help. Add px-4 to match index.html. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Snoozing or unsnoozing an expiry warning fired CertMate.toast(..., 'info') — a neutral blue toast — for a completed positive action. Same success-shown-as-info class as the dashboard cert-created bug, separate call site. Use the 'success' (green) variant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The empty, all-snoozed and load-error states put the icon and text in a text-center div relying on `block` on the <i>, but the FontAwesome webfont keeps the icon inline, so icon and label rendered glued on one line. Switch the container to flex flex-col items-center gap-3 and wrap the label in <p>, which stacks reliably regardless of the icon display. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Auto/Light/Dark segmented control marked the active option with aria-pressed:bg-surface on a bg-surface-2 track; in dark mode those two grays are nearly identical, so the selected segment was indistinguishable — the control's whole job. Add an aria-pressed primary ring so the active state reads clearly in both themes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 11-tab Settings strip lives in an overflow-x-auto container, but on overlay-scrollbar platforms (macOS) the scrollbar is hidden until you scroll, so at tablet width five tabs (Probe/Users/API Keys/SSO/Backup) clipped off-screen with no hint they exist. Add a .scroll-x-affordance utility (thin always-present scrollbar once content overflows) and apply it to the tab container. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Snooze control is a <details>/<summary> that opens a duration menu, but with list-none and no marker it looked identical to the plain View link beside it — nothing signalled it expands. Add a chevron-down so the disclosure affordance is visible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 'Common endpoints' list was a bare font-mono <ul>, unlike the curl blocks beside it which use <pre overflow-x-auto>. The longest path reached the card edge and would wrap mid-token / clip on narrow screens. Add overflow-x-auto + whitespace-nowrap so long endpoints scroll cleanly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…p form The setup wizard auto-launched on pathname '/', but '/' also serves setup.html (the admin-creation form) when no users exist yet, so on first run the 'Welcome to CertMate' wizard rendered on top of the username/password setup form — two competing first-run flows on the most trust-sensitive screen. Gate the auto-launch on the absence of the setup form (#setupForm): setup.html owns first-run, the wizard owns dashboard onboarding once an admin account exists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Related Activity-log display defects from the screenshot review:
- Filter matched only the exact operation, so 'Created' hid create_account
and batch_create, and update/deploy were not filterable at all. Match the
operation family (op, op_*, batch_op); add Updated/Deployed options.
- 'All user activity' relabelled 'All operations' (it filters operations,
not users).
- opLabel humanizes any unmapped operation instead of leaking raw
snake_case (update, create_account, oidc_config_changed, ...).
- Filtered-empty state no longer claims 'No activity recorded yet' when a
filter matched nothing: shows 'No activity matches the current filters'
plus a Clear-filters action; filter changes reset pagination.
- Resource line humanizes resource_type and drops the tautological id
('settings: settings').
- Copy-JSON confirmation uses a success (not info) toast.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The mobile/tablet table reused the desktop column layout with hard
truncation:
- Domain truncated to a few characters on phones (max-w-0 + truncate).
Let it wrap below md (break-words md:truncate, md:max-w-0) so the full
name is visible.
- Mobile 'days left' was clipped to a bare number ('8 …'). Drop the
truncate on the mobile expiry line so the unit and date survive.
- Tablet (768-1023px) showed neither the Provider/Deployment columns
(lg-only) nor the mobile meta block (md:hidden), so both vanished. Show
the meta block through lg (lg:hidden) and keep the expiry line
mobile-only (md:hidden, the Expires column covers tablet), filling the
gap without double-rendering.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 'Deployed' stat card showed a pulsing '...' placeholder with a static
'reachable' subtitle even with zero certs. Render 0 / 'none deployed'
when there are no certs, and use a calmer em-dash loading glyph otherwise.
- 'Expired' card sub-label read 'N expiring soon' under the expired count
— two unrelated metrics. Lead with the action ('renew now') and append
the expiring count as clearly-secondary context.
- Row action icons were text-gray-400, barely visible on dark rows until
hover. Use text-gray-500 dark:text-gray-300 for a visible resting state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On the long form tabs (General/DNS/CA/Storage) the Save button sat at the very bottom, reachable only after scrolling the whole form — never visible beside the fields being edited. Pin the submit row to the viewport bottom (sticky bottom-0) while scrolling; it already lives in #formControls which x-shows only on the four form tabs, so the auto-save tabs are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regenerate static/css/tailwind.min.css so the new utility classes used by the fixes (scroll-x-affordance, aria-pressed ring, break-words/md:max-w-0, flex-col empty states, sticky save footer) are present in the committed bundle (frontend-css stale-bundle gate). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UI_SHOTS_ANALYSIS.md is an internal DO-NOT-COMMIT working doc, like UI_AUDIT.md and UI_DRACONIAN_ANALYSIS.md which are already ignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… CTA The Generate button (mints a secure API token) was a low-emphasis gray control that read as disabled next to the icon-only eye toggle. Make it an outline-primary secondary CTA so it reads as the recommended action. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ty color The summary line, the group headers and every row's meta all repeated the same red/amber, so the card read as a wall of color. Make the summary text neutral (text-foreground) with a small coloured count badge, letting the grouped rows carry the severity colour and giving the card tonal hierarchy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Help prose used the full max-w-7xl card width (~150+ chars/line) on wide viewports. Cap paragraphs inside help sections ([data-help-section] p) to a 65ch reading measure; tables, code blocks and the API grid are not <p>, so they keep full width. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "What's new" section and nav pill only linked to RELEASE_NOTES.md on GitHub, duplicating the "Full changelog" link two pills over. Remove both; the changelog link stays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…glyph The top-bar nav marked the active item with colour only (text-primary), invisible to colour-blind users, and the Certificates glyph (fa-certificate) read as a starburst. Add a border-b-2 underline accent on the active item (border-transparent otherwise so heights match) and switch to fa-shield-halved. Stays icon-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each row showed only a relative time ('2h ago') with the absolute behind a
title tooltip, unreachable on touch. Add a compact absolute line (month/day
+ HH:MM) under the relative so the exact moment is always visible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The icon column fell back to an identical grey fa-circle for the dominant operations (update, create_account, ...), so it carried no information. Map all ~25 operations to a family-based glyph+colour system (green=create, blue=update, cyan=renew, red=delete/revoke, purple=deploy, indigo=download, teal=restore, amber=auto-renew/test, slate=maintenance) so the column triages at a glance. Unknown ops still fall back to a grey circle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The three sections (Appearance / Certificate Management / API Security) were identical flat tinted boxes of equal weight, so security-sensitive settings read the same as the cosmetic theme picker. Drop the tinted boxes for plain sections separated by border-t dividers with stronger semibold headings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The action column showed 5-6 identical icons (renew, force-renew, download, API, auto-renew, delete) with the destructive delete inline at the same weight. Keep the daily-use Renew + Download inline and move force-renew, API, auto-renew and delete into a '...' overflow menu (delete separated and red). The menu is appended to <body> as position:fixed so the table's overflow wrappers don't clip it, with click-away + Escape to close. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…C1/S1) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cluster (redesign phase 1) First phase of the unified creation-flow redesign (variant C). Replace the in-body Server/Client tab buttons + the inline 'Create New Certificate' expand-in-place form (which pushed the page down on open) with: - a page-header cluster [ Server | Client | New ]: segments are ghost state toggles, New is a filled imperative action that opens the drawer; - a right-side creation DRAWER (overlay, scrim + Esc + scroll-lock) holding the existing create form, relocated out of the inline layout. No field IDs changed, so the submit handler is untouched; it now also closes the drawer on success. Stats, the certificate table and the client view are unchanged this phase. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e 2) Phase 2 of the creation-flow redesign. The drawer now holds BOTH types, switched by a [ Server | Client ] type segment (preset from the active view, locally overridable). Client creation (single + bulk CSV) was extracted from the inline card in _client_certs.html into partials/_client_create.html and included in the drawer; all field IDs are unchanged, so client-certs.js binds them as before (lazily re-init'd when the client form is first shown). The drawer moved out of the server x-show so it is available from either view, and '+ New' now opens it preset to the current view. This kills the original asymmetry (server-behind-toggle vs client-always-inline) — both types use the same overlay now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UI_REDESIGN_PLAN.md and UI_UX_Opinioni_CertMate.pdf are internal DO-NOT-COMMIT working docs, like the other UI_* docs already ignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ign phase 3) Phase 3 of the creation-flow redesign. Fresh server-cert creates now opt into async issuance (async:true on the body). On a 202 the drawer closes immediately and an optimistic "Issuing" row is prepended to the table; a poller on /api/certificates/jobs/<id> resolves it to the real certificate on success (via loadCertificates) or to a "Failed" row carrying the failure reason plus Retry / Dismiss actions. Reissue stays synchronous — it edits a row that already exists, so an optimistic new row would be wrong. When the server has no async executor it ignores the flag and replies synchronously (200), which the same handler falls through to, so the change degrades gracefully. SSE is unchanged: certificate_created already owns the success toast + reload, and there is no certificate_failed handler for creates, so the poller is the sole owner of the failure UX (no duplicate toast). - displayCertificates re-attaches pending rows after any full rebuild, so an SSE/reload triggered by another event never drops an in-flight optimistic row; a job whose real certificate has already landed is skipped to avoid a duplicate Issuing/Valid pair. - Retry re-POSTs the captured payload; Dismiss clears a failed row. Both are exposed on window for the inline row handlers. - The sync-success field reset was factored into clearCreateFormAfterSubmit() and shared by both paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…redesign phase 4) Phase 4 of the creation-flow redesign. The global command palette becomes the one search surface (closes the D1 "triple search" opinion), and the redundant per-page search boxes are retired. cmd-palette.js: - Cross-type search: prefetches client certificates (/api/client-certs) alongside server certs and groups results under "Server Certificates" / "Client Certificates". Results refresh when either fetch resolves while the palette is open (previously certs only appeared after the first keystroke). - Open-drawer actions: "New Server Certificate" / "New Client Certificate" open the creation drawer preset to that type (the old "Create Certificate" focused the retired inline #domain). Cross-page, they stash cm_open_drawer and navigate to the dashboard, which opens the drawer on load. - Jump-and-flash: selecting a server cert ensures the server view and pulses the matching row in place (cmd-flash, a brief primary-tinted double-pulse); from another page it navigates with ?flash=<domain> and pulses once the list loads. Client-cert results switch to the client view (row-level flash lands with the client table in phase 5). Retired per-page searches → palette: - Dashboard #certificateSearch and client #searchInput removed; filterCertificates / ccFilterCertificates now filter by status/usage only; clearFilters and the empty-state "isFiltered" check updated. The status (and client usage/status) filters stay — they become chips in phase 5. - The "/" shortcut now always opens the palette (was: focus the dashboard search). - Kept the help page's in-page section filter: it filters documentation blocks, not cert data, so the palette doesn't replace it — removing it would be a regression. (Plan listed it for removal; deliberately deviated.) Supporting: rows carry data-row-domain for flash targeting; window.flashCertRow + maybeFlashCertFromQuery added; the two view-cluster buttons got ids (certViewServerBtn / certViewClientBtn) so the palette can switch views. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (redesign phase 5) Phase 5 of the creation-flow redesign. Status chips replace the #statusFilter <select>: a row of [All|Valid|Expiring| Expired] chips, each showing a live count and lighting up in its status colour when active (aria-pressed drives the styling, so it stays accessible). They are the single source of truth via a currentStatusFilter module var — filterCertificates, clearFilters and the empty-state "isFiltered" check all read it; updateStats keeps the counts in sync. The chips are filtering only (jump-to-cert lives in the ⌘K palette — separate gestures, per the variant-C decisions). Compact stat strip: the four KPI tiles (server and client) drop from chunky cards to a tighter single row — icon+label inline, a smaller hero number, the subtitle hidden below sm so four tiles fit in one row even on phones. Server (statCard + skeleton in dashboard.js) and client (_client_certs.html) share one vocabulary. Client list: the sortable table already existed; its bare "No client certificates found" row becomes a styled empty-state — an id-card glyph, a short pitch, and a "New Client Certificate" CTA that opens the client drawer. A filtered-but-empty result shows a distinct "no matches" hint instead. The By Usage tile truncates to one line with the full breakdown on hover. Verified (browser, light + dark): chips filter + colour correctly across all four states; the strip renders as a single row both views; the empty-state CTA opens the client drawer. No console errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…11y polish (redesign phase 6) Final phase of the creation-flow redesign — polish across what phases 1-5 built. Mono machine-values: a .cm-mono class (JetBrains Mono stack + tabular-nums, one semantic hook) renders identifiers in the mono face so hostnames are easy to scan and compare. Applied to server domain names and client common names; the cert detail panel already used font-mono for its title/SANs. A11y — drawer focus trap: the creation drawer is aria-modal, but Tab could escape to the page behind it. openCertDrawer now traps Tab/Shift+Tab within the drawer's visible controls (the hidden server/client form is excluded via offsetParent) and remembers the opener; closeCertDrawer restores focus to it (the +New button, a palette action, or the empty-state CTA). prefers-reduced-motion is already handled globally, and the slide uses motion-reduce:transition-none. Responsive: verified the redesigned surface at 375 / 560 / 1280 — no horizontal overflow, the compact stat strip stays a single row of four, the status chips fit on one row by 560 (wrap to two on the narrowest phones), the nav cluster fits, and the table falls back to its stacked mobile-card layout. Confirmed in light and dark. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
POST /api/web/certificates/download/batch called certificate_manager.get_certificate_path(), which does not exist on the manager — every batch export raised AttributeError and returned 500. The loop already has the per-domain directory from _sanitize_domain(), so bundle <domain>/fullchain.pem directly as <domain>.crt. Cert-only by design: a bulk export must never leak private keys. This unblocks the new "Export ZIP" list action. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…icon-only - The labelled "Search ⌘K" box becomes a plain magnifying-glass icon (a click just opens the command palette, so the box was redundant). It now sits where the Certificates link was — visible at all widths so mobile keeps search. - Removed the stale Certificates topbar link: the logo still links home and the Server/Client toggle handles cert navigation. - Logout is icon-only now (the red danger border/hover stays). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…up, list-action icon group
Several requested refinements to the server dashboard:
- Counters moved up into the Server/Client/+New toggle's row at the same
height, freeing the separate stat row. Dropped the Deployed counter; number
and label sit on one line ("7 TOTAL"). State shows in the number colour.
- Cert-detail modal regrouped: Auto-Renew moved into the status banner; the
Deployment and Actions sections are now two columns of quick-action buttons
on one row each; the two deploy indicators became squares matching the
buttons; the delete action is danger-red (matching Logout) and still confirms.
- The "Check All" button became a quick-action icon group: Check all / Export
ZIP / Refresh (Debug folded in, still hidden by default). Added
exportAllCertificates() driving the batch-download endpoint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…er, filter chips + actions Brings the client view to parity with the server one: - Counters mirror the server strip in the toggle row (Total / Active / Revoked; By Usage dropped to match the three-counter shape). - The list header matches the server table: same thead (sticky + blur), card chrome (no more blue gradient), rows carry the status-coloured left border (active/expiring/revoked) and the mono CN. - The usage/status selects became filter chips (status with live counts) plus a Refresh action — same vocabulary as the server list header. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds .cm-icon-btn (quick-action icon button) and .cm-chip (filter chip base) in @layer components so single-utility overrides like `hidden` still win over the component's own display, and rebuilds the minified bundle for the new classes used by the dashboard and client list headers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dashboard header's toggle was relabelled from "Server Certificates" / "Client Certificates" to "Server" / "Client" in the creation-flow redesign. Pin the stable button ids (certViewServerBtn / certViewClientBtn) instead of the prose so the test tracks the toggle's presence, not its wording. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#358) Three tools toward REST/UI parity for agentic certificate management: - certmate_delete_certificate(domain) -> DELETE /api/certificates/<domain>. - certmate_update_certificate(domain, sans?, domain_alias?) -> POST /api/certificates/<domain>/reissue. Omitting sans keeps the current SAN set ([] drops all); the primary domain cannot change. May return a job_id (202). - certmate_get_certificate_file(domain, file) -> GET .../download?file=<file>, returned as raw PEM text (not JSON-wrapped) for direct use by HAProxy/nginx. The versions/rollback tool from the issue is deferred — it needs a backend certificate-revision-history endpoint that does not exist yet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Spawns the server against a local mock CertMate (no running instance needed), calls each new tool over stdio, and asserts the HTTP method, path and body it emits — including that update omits san_domains when sans is absent and that get-file returns raw PEM rather than a JSON wrapper. Wired into `npm test` alongside the existing static + handshake smoke test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fabriziosalmi
force-pushed
the
ui-shots-bugfixes
branch
from
June 28, 2026 04:02
3d96540 to
5805d6f
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #360 +/- ##
=======================================
Coverage 70.99% 70.99%
=======================================
Files 50 50
Lines 11953 11953
=======================================
Hits 8486 8486
Misses 3467 3467
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
The jump-and-flash row lookup interpolated the (user-derived) domain into a CSS attribute selector with an incomplete fallback escape (only quotes, not backslashes — CodeQL js/incomplete-string-escaping). Match the row by reading data-row-domain directly instead, removing the selector and the escaping subtlety entirely. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump version and add release notes for the three MCP certificate-lifecycle tools (delete / update / get-file, #358) on top of the unified creation-flow redesign and the broader UI sweep. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fabriziosalmi
force-pushed
the
ui-shots-bugfixes
branch
from
June 28, 2026 04:30
5805d6f to
48d6f26
Compare
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.
Unifies the certificate-creation and list surfaces into one flow, adds three MCP lifecycle tools toward REST parity, plus the refinements and broader UI sweep that landed alongside. No change to the issuance protocol.
Highlights
[ Server | Client | + New ]page-header cluster — no layout jump, available from either view.certmate_delete_certificate,certmate_update_certificate(reissue in place), andcertmate_get_certificate_file(raw PEM). A versions/rollback tool was deferred — it needs a backend revision-history endpoint that does not exist yet.Fix
POST /api/web/certificates/download/batchreturned 500 — it calledcertificate_manager.get_certificate_path(), which does not exist. It now bundles each domain'sfullchain.pemas<domain>.crt(cert-only), which the new Export ZIP action drives.Testing
npm test: static + stdio-handshake smoke (16 tools) plus an offline behavioural test pinning each new tool's HTTP method/path/body.test_pagestoggle assertion was updated to the redesigned markup.Full notes in
RELEASE_NOTES.md(v2.19.4 section).