From 6ed6b84ef8f97f60201eb1f7e0e06b3302aa4703 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 20 Apr 2026 09:30:29 +0100 Subject: [PATCH] feat(connections): polish explainer with agent capability + trust chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first pass explained both modes but gave users no reason to choose the agent when the API also covers their platform. Polish the layout and add concrete positives grounded in what the agent actually ships: - Two-column split with a divider and a subtle blue-tinted accent on the Agent panel. Agent icon badge uses the same accent so it pairs visually without shouting. - "Recommended" pill next to Pulse Unified Agent — it unlocks more features, so the default bias is toward installing it. - Capability chips on the agent panel: Hardware telemetry, Assistant commands, Patrol remediation. These match what agentexec, hostagent, and ai/patrol_findings actually do at runtime. - Trust strip of verifiable facts: Single Go binary · ~13 MB download · No runtime dependencies · Open source. Specific numbers beat vague adjectives like "lightweight" — the ~13 MB figure is the actual compressed size of pulse-agent-v6.0.0-rc.1-linux-amd64.tar.gz. - Tests pin the capability labels and trust facts so the benefit copy can't silently drift. --- .../Settings/ConnectionsExplainer.tsx | 138 ++++++++++++------ .../__tests__/ConnectionsExplainer.test.tsx | 11 ++ 2 files changed, 108 insertions(+), 41 deletions(-) diff --git a/frontend-modern/src/components/Settings/ConnectionsExplainer.tsx b/frontend-modern/src/components/Settings/ConnectionsExplainer.tsx index ace826319..e614e0001 100644 --- a/frontend-modern/src/components/Settings/ConnectionsExplainer.tsx +++ b/frontend-modern/src/components/Settings/ConnectionsExplainer.tsx @@ -1,4 +1,4 @@ -import { Component, Show, createSignal } from 'solid-js'; +import { Component, For, Show, createSignal } from 'solid-js'; import { Cloud, Cpu, X } from 'lucide-solid'; const DISMISS_KEY = 'pulse.infrastructure.explainer.dismissed.v1'; @@ -21,6 +21,19 @@ const persistDismissed = () => { } }; +const AGENT_CAPABILITIES = [ + 'Hardware telemetry', + 'Assistant commands', + 'Patrol remediation', +]; + +const AGENT_FACTS = [ + 'Single Go binary', + '~13 MB download', + 'No runtime dependencies', + 'Open source', +]; + export const ConnectionsExplainer: Component = () => { const [dismissed, setDismissed] = createSignal(readDismissed()); @@ -33,61 +46,104 @@ export const ConnectionsExplainer: Component = () => {
- + -
-

- Two ways to connect infrastructure -

-

- Pick whichever fits the target. You can mix both on the same host. -

-
+
+

+ Two ways to connect infrastructure +

+

+ Pick whichever fits the target. You can mix both on the same host. +

+
-
-
- -
-
Platform API
-

+

+
+
+ +
+
Platform API
+
Polled from the platform
+
+
+

Pulse polls the platform's own API (Proxmox VE / PBS / PMG, VMware, TrueNAS). Fastest to set up; coverage matches what the platform exposes.

-
-
- -
-
Pulse Unified Agent
-

+

+
-
); diff --git a/frontend-modern/src/components/Settings/__tests__/ConnectionsExplainer.test.tsx b/frontend-modern/src/components/Settings/__tests__/ConnectionsExplainer.test.tsx index 5ec4af30c..027bd63c5 100644 --- a/frontend-modern/src/components/Settings/__tests__/ConnectionsExplainer.test.tsx +++ b/frontend-modern/src/components/Settings/__tests__/ConnectionsExplainer.test.tsx @@ -21,6 +21,17 @@ describe('ConnectionsExplainer', () => { expect(screen.getByText('Pulse Unified Agent')).toBeInTheDocument(); }); + it('surfaces concrete agent capabilities and trust facts users care about', () => { + render(() => ); + + for (const capability of ['Hardware telemetry', 'Assistant commands', 'Patrol remediation']) { + expect(screen.getByText(capability)).toBeInTheDocument(); + } + for (const fact of ['Single Go binary', '~13 MB download', 'No runtime dependencies', 'Open source']) { + expect(screen.getByText(fact)).toBeInTheDocument(); + } + }); + it('hides itself and persists dismissal to localStorage when closed', () => { const { container } = render(() => );