feat(ui): integration setup guidance and settings mesh background#88
feat(ui): integration setup guidance and settings mesh background#88DevVig wants to merge 1 commit into
Conversation
Give every Integrations tile a clear next-step checklist, clarify auto-discovered “Detected” labels, and fill Settings whitespace with a subtle mouse-reactive mesh that never blocks clicks.
📝 WalkthroughWalkthroughThe PR adds structured, adapter-specific integration guidance, detected-adapter state handling, guidance rendering in integration details, expanded enablement behavior, and an animated mesh background for the settings surface. ChangesIntegration settings experience
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Settings
participant integrationView
participant integrationGuidance
participant IntegrationDetail
Settings->>integrationView: adapter state and enabled flag
integrationView-->>Settings: integration card view
Settings->>integrationGuidance: adapter state, enabled flag, label
integrationGuidance-->>Settings: guidance title and steps
Settings->>IntegrationDetail: card view and guidance
IntegrationDetail-->>Settings: render integration details
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Pull request overview
This PR improves the Settings → Integrations UX by adding structured, per-adapter setup guidance (as numbered checklists) and by refining how auto-discovered-but-disabled adapters are labeled, while also introducing a subtle canvas-based mesh background behind the Settings chrome.
Changes:
- Added
integrationGuidance()to provide structured per-adapter “next steps” (including idle/journal-based hosts and always-on watchers), and surfaced it in the Integrations detail panel as a numbered checklist. - Updated
integrationView()to label auto-discoveredneeds_setupadapters with configenabled=falseas “Detected — click to install”. - Introduced a
MeshBackgroundcanvas component and layered it behind Settings layout content.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/microbridge-ui/src/surfaces/Settings.tsx | Adds mesh background layering and wires structured guidance into the Integrations detail panel; updates enable-on-click behavior for detected adapters. |
| apps/microbridge-ui/src/lib/integrationSetup.ts | Adds integrationGuidance() structured guidance for integrations, expanding beyond the legacy setupNextStep(). |
| apps/microbridge-ui/src/lib/integrationSetup.test.ts | Adds test coverage for the new integrationGuidance() behavior. |
| apps/microbridge-ui/src/lib/hosts.ts | Extends integrationView() to accept config-enabled state and label auto-discovered-but-disabled adapters as “Detected”. |
| apps/microbridge-ui/src/lib/hosts.test.ts | Adds coverage for the new “Detected — click to install” labeling. |
| apps/microbridge-ui/src/components/MeshBackground.tsx | New canvas-based mesh background component for Settings chrome. |
| apps/microbridge-ui/src/components/IntegrationCard.tsx | Extends IntegrationDetail to display guidance checklists in the details panel. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (label === "Idle" || state === "connected" || state === "limited") { | ||
| return { | ||
| title: `${hostName} via journals`, | ||
| steps: [ | ||
| `Idle is normal — start ${hostName} (or use Claude/Codex through it).`, | ||
| "Sessions appear automatically from Claude & Codex journals; no separate adapter or pairing.", | ||
| ], | ||
| primaryAction: "none", | ||
| }; | ||
| } |
| <ol className="mt-1.5 list-decimal space-y-1 pl-4"> | ||
| {guidance.steps.map((step) => ( | ||
| <li key={step}>{step}</li> | ||
| ))} | ||
| </ol> |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
apps/microbridge-ui/src/components/MeshBackground.tsx (1)
41-63: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winEffect fully re-runs (listeners + ResizeObserver + rAF loop torn down/rebuilt) on every
darktoggle.
darkonly affectsbaseAlpha/hotAlpha/stroke, but it's in the effect's dependency array (line 156), so toggling the theme tears down and recreates the resize observer and mouse listeners unnecessarily. Consider trackingdarkin a ref updated by a separate lightweight effect, and reading it insidedraw(), so the main setup effect only depends onactive.Also applies to: 156-156
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/microbridge-ui/src/components/MeshBackground.tsx` around lines 41 - 63, Update the MeshBackground effect so dark-theme values are read from a ref inside draw() rather than triggering the main setup effect. Add a lightweight effect to keep the ref synchronized with dark, remove dark from the main effect’s dependency array, and preserve active-based setup and teardown of the observers, listeners, and animation loop.apps/microbridge-ui/src/surfaces/Settings.tsx (2)
752-757: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
primaryActionis computed byintegrationGuidancebut never reaches the UI. It's dropped when building theguidanceprop, so downstream consumers can't use it to drive behavior or styling — leaving action-button visibility and callout emphasis to be re-derived independently, with no single source of truth.
apps/microbridge-ui/src/surfaces/Settings.tsx#L752-L757: includeprimaryActionin theguidanceobject passed toIntegrationDetail, and consider driving the Enable/Open App/Pair button block (~L801-829) from it instead of separate ad-hoc conditions.apps/microbridge-ui/src/components/IntegrationCard.tsx#L182-L197: addprimaryActionto theguidanceprop type and use it to vary the callout's styling/emphasis (e.g., neutral for"none"vs. actionable colors otherwise).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/microbridge-ui/src/surfaces/Settings.tsx` around lines 752 - 757, Preserve integrationGuidance.primaryAction through the IntegrationDetail guidance prop in apps/microbridge-ui/src/surfaces/Settings.tsx lines 752-757, and use it as the source of truth for the Enable/Open App/Pair actions where applicable. In apps/microbridge-ui/src/components/IntegrationCard.tsx lines 182-197, add primaryAction to the guidance prop type and vary callout emphasis based on it, using neutral styling for "none" and actionable styling otherwise.
214-214: 🚀 Performance & Scalability | 🔵 TrivialConsider pausing/throttling the mesh animation and respecting reduced-motion.
Based on
MeshBackground's implementation, therequestAnimationFrameloop redraws every frame indefinitely regardless of pointer activity, and there's noprefers-reduced-motioncheck. For a background decoration this is a needless steady CPU/battery cost, and can be uncomfortable for motion-sensitive users.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/microbridge-ui/src/surfaces/Settings.tsx` at line 214, Update the MeshBackground usage and implementation to avoid an unconditional requestAnimationFrame loop: pause or throttle redraws when there is no pointer activity, and detect prefers-reduced-motion to disable or substantially reduce animation. Preserve the existing dark and active behavior while ensuring reduced-motion users receive a static or minimal-motion background.apps/microbridge-ui/src/lib/hosts.ts (1)
164-173: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAuto-discovered/"detected" condition is duplicated across two files with different adapter-id scope.
hosts.tslabels anyneeds_setup && enabled===falseadapter as "Detected — click to install," whileintegrationSetup.ts's matching guidance only fires for a hardcoded list of four adapter ids. They happen to agree today, but nothing enforces it going forward.
apps/microbridge-ui/src/lib/hosts.ts#L164-L173: extract the same adapter-id allowlist used inintegrationSetup.ts(or a shared exported constant) so the label only applies where matching guidance exists.apps/microbridge-ui/src/lib/integrationSetup.ts#L44-L51: import/reuse that same shared allowlist constant instead of a local hardcoded list, so both conditions can't drift apart.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/microbridge-ui/src/lib/hosts.ts` around lines 164 - 173, Share the adapter-id allowlist between the detected-state branch in hosts.ts and the matching guidance in integrationSetup.ts. Add or export one shared allowlist constant, update hosts.ts so the "Detected — click to install" label requires adapter.state === "needs_setup", options?.enabled === false, and membership in that allowlist, and replace integrationSetup.ts's local hardcoded list with the shared constant; update apps/microbridge-ui/src/lib/hosts.ts lines 164-173 and apps/microbridge-ui/src/lib/integrationSetup.ts lines 44-51 accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/microbridge-ui/src/components/MeshBackground.tsx`:
- Around line 55-63: Cache the canvas bounding rectangle in a ref within the
resize function near the existing rect calculation, then update onMove to reuse
that cached rect instead of calling canvas.getBoundingClientRect() for each
mousemove. Preserve the existing coordinate calculations and ensure the cache is
refreshed whenever resize runs.
In `@apps/microbridge-ui/src/lib/integrationSetup.ts`:
- Around line 244-277: Update the guidance logic for the
synara/chatgpt/claude_desktop/conductor branch to use a typed idle signal rather
than comparing the rendered label to `"Idle"`. Add or propagate an `isIdle`
boolean from the caller, derived from the presence state such as `presence.count
=== 0`, and use it alongside the connected and limited states while preserving
the existing guidance behavior.
---
Nitpick comments:
In `@apps/microbridge-ui/src/components/MeshBackground.tsx`:
- Around line 41-63: Update the MeshBackground effect so dark-theme values are
read from a ref inside draw() rather than triggering the main setup effect. Add
a lightweight effect to keep the ref synchronized with dark, remove dark from
the main effect’s dependency array, and preserve active-based setup and teardown
of the observers, listeners, and animation loop.
In `@apps/microbridge-ui/src/lib/hosts.ts`:
- Around line 164-173: Share the adapter-id allowlist between the detected-state
branch in hosts.ts and the matching guidance in integrationSetup.ts. Add or
export one shared allowlist constant, update hosts.ts so the "Detected — click
to install" label requires adapter.state === "needs_setup", options?.enabled ===
false, and membership in that allowlist, and replace integrationSetup.ts's local
hardcoded list with the shared constant; update
apps/microbridge-ui/src/lib/hosts.ts lines 164-173 and
apps/microbridge-ui/src/lib/integrationSetup.ts lines 44-51 accordingly.
In `@apps/microbridge-ui/src/surfaces/Settings.tsx`:
- Around line 752-757: Preserve integrationGuidance.primaryAction through the
IntegrationDetail guidance prop in apps/microbridge-ui/src/surfaces/Settings.tsx
lines 752-757, and use it as the source of truth for the Enable/Open App/Pair
actions where applicable. In
apps/microbridge-ui/src/components/IntegrationCard.tsx lines 182-197, add
primaryAction to the guidance prop type and vary callout emphasis based on it,
using neutral styling for "none" and actionable styling otherwise.
- Line 214: Update the MeshBackground usage and implementation to avoid an
unconditional requestAnimationFrame loop: pause or throttle redraws when there
is no pointer activity, and detect prefers-reduced-motion to disable or
substantially reduce animation. Preserve the existing dark and active behavior
while ensuring reduced-motion users receive a static or minimal-motion
background.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 37b11a83-1eb5-41bb-90e4-028244160e58
📒 Files selected for processing (7)
apps/microbridge-ui/src/components/IntegrationCard.tsxapps/microbridge-ui/src/components/MeshBackground.tsxapps/microbridge-ui/src/lib/hosts.test.tsapps/microbridge-ui/src/lib/hosts.tsapps/microbridge-ui/src/lib/integrationSetup.test.tsapps/microbridge-ui/src/lib/integrationSetup.tsapps/microbridge-ui/src/surfaces/Settings.tsx
| const resize = () => { | ||
| const rect = parent.getBoundingClientRect(); | ||
| const dpr = Math.min(window.devicePixelRatio || 1, 2); | ||
| canvas.width = Math.max(1, Math.floor(rect.width * dpr)); | ||
| canvas.height = Math.max(1, Math.floor(rect.height * dpr)); | ||
| canvas.style.width = `${rect.width}px`; | ||
| canvas.style.height = `${rect.height}px`; | ||
| ctx.setTransform(dpr, 0, 0, dpr, 0, 0); | ||
| }; |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
Cache the canvas rect instead of recomputing it on every mousemove.
onMove calls canvas.getBoundingClientRect() on every mousemove event, which forces a synchronous layout read on a very hot path. Since the canvas is absolutely positioned to inset-0 of parent, its rect only actually changes on resize — resize() already has the up-to-date rect. Cache it in a ref there and reuse it in onMove.
⚡ Proposed fix
+ const rectRef = { current: canvas.getBoundingClientRect() };
+
const resize = () => {
const rect = parent.getBoundingClientRect();
const dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = Math.max(1, Math.floor(rect.width * dpr));
canvas.height = Math.max(1, Math.floor(rect.height * dpr));
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ rectRef.current = canvas.getBoundingClientRect();
};
...
const onMove = (event: MouseEvent) => {
- const rect = canvas.getBoundingClientRect();
mouseRef.current = {
- x: event.clientX - rect.left,
- y: event.clientY - rect.top,
+ x: event.clientX - rectRef.current.left,
+ y: event.clientY - rectRef.current.top,
};
};Also applies to: 69-81
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/microbridge-ui/src/components/MeshBackground.tsx` around lines 55 - 63,
Cache the canvas bounding rectangle in a ref within the resize function near the
existing rect calculation, then update onMove to reuse that cached rect instead
of calling canvas.getBoundingClientRect() for each mousemove. Preserve the
existing coordinate calculations and ensure the cache is refreshed whenever
resize runs.
| case "synara": | ||
| case "chatgpt": | ||
| case "claude_desktop": | ||
| case "conductor": { | ||
| const hostName = | ||
| adapterId === "synara" | ||
| ? "Synara" | ||
| : adapterId === "chatgpt" | ||
| ? "ChatGPT" | ||
| : adapterId === "claude_desktop" | ||
| ? "Claude Desktop" | ||
| : "Conductor"; | ||
| if (label === "Idle" || state === "connected" || state === "limited") { | ||
| return { | ||
| title: `${hostName} via journals`, | ||
| steps: [ | ||
| `Idle is normal — start ${hostName} (or use Claude/Codex through it).`, | ||
| "Sessions appear automatically from Claude & Codex journals; no separate adapter or pairing.", | ||
| ], | ||
| primaryAction: "none", | ||
| }; | ||
| } | ||
| if (state === "disabled") { | ||
| return { | ||
| title: "Disabled in config", | ||
| steps: [ | ||
| `${hostName} attribution is disabled in ~/.microbridge/config.toml.`, | ||
| "Re-enable it there and restart Microbridge if you want these sessions listed.", | ||
| ], | ||
| primaryAction: "none", | ||
| }; | ||
| } | ||
| return null; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Guidance logic keyed off a UI label string ("Idle") instead of a typed signal.
label === "Idle" couples this business logic to the exact literal produced in hosts.ts (label: "Idle", hosts.ts L140). There's no shared constant or type enforcing this contract — if that label text is ever reworded (copy tweak, localization), this branch silently stops matching and the guidance panel disappears for idle host-attributed adapters with no compile-time signal.
🔧 Suggested fix: pass a typed signal instead of a label string
-export function integrationGuidance(
- adapterId: string,
- state: AdapterConnectionState,
- options?: { enabled?: boolean; label?: string },
-): IntegrationGuidance | null {
+export function integrationGuidance(
+ adapterId: string,
+ state: AdapterConnectionState,
+ options?: { enabled?: boolean; isIdle?: boolean },
+): IntegrationGuidance | null {
const enabled = options?.enabled;
- const label = options?.label;
+ const isIdle = options?.isIdle;And have callers pass isIdle: presence.count === 0 (or equivalent) rather than the rendered label text.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/microbridge-ui/src/lib/integrationSetup.ts` around lines 244 - 277,
Update the guidance logic for the synara/chatgpt/claude_desktop/conductor branch
to use a typed idle signal rather than comparing the rendered label to `"Idle"`.
Add or propagate an `isIdle` boolean from the caller, derived from the presence
state such as `presence.count === 0`, and use it alongside the connected and
limited states while preserving the existing guidance behavior.
Summary
pointer-events-none) to fill whitespaceTest plan
npm testinapps/microbridge-ui(hosts, integrationSetup, surfaces)Summary by CodeRabbit
New Features
Improvements