From 3f11df031c1d2cfdfdf80d89b915406c8a088ee6 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Thu, 23 Apr 2026 20:18:30 -0700 Subject: [PATCH] fix: six UX bugs (peers auth, scroll, chat tabs, config persist, + visibility) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Six bugs reported from a live session — all shippable in one commit: 1. Peers tab 401 on local Docker. The /registry/:id/peers endpoint demands a workspace-scoped bearer token (validateDiscoveryCaller) which the canvas session doesn't hold. Added the same Tier-1b dev-mode fail-open hatch that AdminAuth and WorkspaceAuth already use — gated by MOLECULE_ENV=development + empty ADMIN_TOKEN, so SaaS production stays strict. Exported IsDevModeFailOpen from the middleware package for the handler layer to reuse. 2. Org Templates list unscrollable. OrgTemplatesSection was rendered in the TemplatePalette footer — a div without overflow — so when it expanded to 15+ entries the list extended past the viewport with no scroll. Moved it to the top of the flex-1 overflow-y-auto container. Tall lists now scroll naturally. 3. Chat tab: "My Chat" and "Agent Comms" rendered stacked instead of switching. HTML `hidden` attribute was being overridden by Tailwind's `flex` class (display: flex beats the attribute), so both tabpanels rendered concurrently. Swapped to a conditional Tailwind `hidden`/`flex` class so the inactive panel is display:none with proper CSS specificity. 4. Hermes Config form never persists. handleSave wrote config.yaml but name / tier / runtime / model all live on the workspace row (or the dedicated /workspaces/:id/model endpoint) — the form edited in-memory, the request returned 200, the next reload wiped everything back. Hermes + external runtimes manage their own config inside the container anyway, so writing config.yaml is a no-op for them; skip it. Always diff and PATCH the DB-backed fields that actually changed. 5. Channels "+ Connect" dropdown empty on first open. ChannelsTab's load() used Promise.all with a silent catch — if EITHER the channels or adapters fetch failed, both setters were skipped with no error visible. Switched to Promise.allSettled so each endpoint settles independently, and the adapters failure now surfaces via the top-level error state. 6. Plugin registry always "No plugins in registry". Same silent catch pattern in SkillsTab.tsx — load errors for /plugins, /plugins/sources, and /workspaces/:id/plugins swallowed without logging. Replaced the empty catches with console.warn so future failures are at least visible in devtools. Tests: 923 passing (unchanged). Go handler tests pass. Server rebuilt and running with the peers-auth + collapsed-persistence fixes (pid 15875). Co-Authored-By: Claude Opus 4.7 (1M context) --- canvas/src/components/TemplatePalette.tsx | 6 ++- canvas/src/components/tabs/ChannelsTab.tsx | 31 +++++++---- canvas/src/components/tabs/ChatTab.tsx | 26 +++++++-- canvas/src/components/tabs/ConfigTab.tsx | 54 ++++++++++++++++--- canvas/src/components/tabs/SkillsTab.tsx | 20 +++++-- .../internal/handlers/discovery.go | 9 ++++ .../internal/middleware/devmode.go | 9 ++++ 7 files changed, 126 insertions(+), 29 deletions(-) diff --git a/canvas/src/components/TemplatePalette.tsx b/canvas/src/components/TemplatePalette.tsx index 710d01b1..3f67bcba 100644 --- a/canvas/src/components/TemplatePalette.tsx +++ b/canvas/src/components/TemplatePalette.tsx @@ -400,6 +400,11 @@ export function TemplatePalette() {
+ {/* Org templates live INSIDE the scroll container so an + * expanded list (15+ entries) is reachable instead of + * overflowing the fixed footer below. */} + + {loading && (
@@ -467,7 +472,6 @@ export function TemplatePalette() {
-
{/* Content — both panels are always in the DOM so aria-controls targets exist. - The inactive panel is hidden via the HTML `hidden` attribute (removed from - display and accessibility tree, but present in the DOM for WCAG 4.1.2). */} -