Skip to content

feat(ui): integration setup guidance and settings mesh background#88

Closed
DevVig wants to merge 1 commit into
mainfrom
feat/integrations-guidance-mesh
Closed

feat(ui): integration setup guidance and settings mesh background#88
DevVig wants to merge 1 commit into
mainfrom
feat/integrations-guidance-mesh

Conversation

@DevVig

@DevVig DevVig commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Expand per-adapter setup guidance (Cursor, Factory, OpenCode, T3, CNVS, natives, host-attributed) and show numbered checklists in the Integrations detail panel
  • Label auto-discovered-but-disabled adapters as “Detected — click to install” instead of bare “Setup needed”
  • Add a subtle mouse-reactive canvas mesh behind Settings chrome (pointer-events-none) to fill whitespace

Test plan

  • npm test in apps/microbridge-ui (hosts, integrationSetup, surfaces)
  • Open Settings → Integrations: select Cursor / T3 / Idle Synara and confirm checklists
  • Move mouse over empty Settings areas — faint mesh reaction; tiles/sidebar still clickable
  • Check light and dark appearance

Summary by CodeRabbit

  • New Features

    • Added tailored setup guidance to integration details, including step-by-step instructions and recommended actions.
    • Added clearer handling for detected integrations that require installation or enabling.
    • Added an interactive mesh background to the Settings interface.
    • Expanded integration enable flows for integrations requiring setup.
  • Improvements

    • Updated Integrations messaging to provide clearer connection and configuration guidance.
    • Added host-specific instructions for pairing, permissions, watchers, and connection states.

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.
Copilot AI review requested due to automatic review settings July 21, 2026 20:09
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

Integration settings experience

Layer / File(s) Summary
Structured integration guidance
apps/microbridge-ui/src/lib/integrationSetup.ts, apps/microbridge-ui/src/lib/integrationSetup.test.ts
Adds typed guidance actions and adapter-specific instructions across connection states, including detected, setup, pairing, watcher, and journal scenarios, with corresponding tests.
Detected adapter state handling
apps/microbridge-ui/src/lib/hosts.ts, apps/microbridge-ui/src/lib/hosts.test.ts
Passes adapter enablement into integrationView and renders disabled auto-discovered adapters as “Detected — click to install”.
Settings integration guidance wiring
apps/microbridge-ui/src/surfaces/Settings.tsx, apps/microbridge-ui/src/components/IntegrationCard.tsx
Computes and passes structured guidance to integration details, enables disabled needs_setup adapters on selection, updates explanatory text, and renders guidance steps in a yellow callout.
Animated settings mesh background
apps/microbridge-ui/src/components/MeshBackground.tsx, apps/microbridge-ui/src/surfaces/Settings.tsx
Adds an interactive canvas mesh with resize, pointer, animation, and cleanup handling, then layers it behind the settings content.

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
Loading

Suggested reviewers: copilot

Poem

A rabbit hops through setup bright,
With tidy steps in yellow light.
Mesh lines dance and adapters wake,
Pair, enable, or plans they make.
UI burrows now feel right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main changes: integration setup guidance and the new settings mesh background.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-discovered needs_setup adapters with config enabled=false as “Detected — click to install”.
  • Introduced a MeshBackground canvas 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.

Comment on lines +256 to +265
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",
};
}
Comment on lines +191 to +195
<ol className="mt-1.5 list-decimal space-y-1 pl-4">
{guidance.steps.map((step) => (
<li key={step}>{step}</li>
))}
</ol>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
apps/microbridge-ui/src/components/MeshBackground.tsx (1)

41-63: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Effect fully re-runs (listeners + ResizeObserver + rAF loop torn down/rebuilt) on every dark toggle.

dark only affects baseAlpha/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 tracking dark in a ref updated by a separate lightweight effect, and reading it inside draw(), so the main setup effect only depends on active.

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

primaryAction is computed by integrationGuidance but never reaches the UI. It's dropped when building the guidance prop, 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: include primaryAction in the guidance object passed to IntegrationDetail, 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: add primaryAction to the guidance prop 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 | 🔵 Trivial

Consider pausing/throttling the mesh animation and respecting reduced-motion.

Based on MeshBackground's implementation, the requestAnimationFrame loop redraws every frame indefinitely regardless of pointer activity, and there's no prefers-reduced-motion check. 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 win

Auto-discovered/"detected" condition is duplicated across two files with different adapter-id scope. hosts.ts labels any needs_setup && enabled===false adapter as "Detected — click to install," while integrationSetup.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 in integrationSetup.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

📥 Commits

Reviewing files that changed from the base of the PR and between 39065fb and 2238db1.

📒 Files selected for processing (7)
  • apps/microbridge-ui/src/components/IntegrationCard.tsx
  • apps/microbridge-ui/src/components/MeshBackground.tsx
  • apps/microbridge-ui/src/lib/hosts.test.ts
  • apps/microbridge-ui/src/lib/hosts.ts
  • apps/microbridge-ui/src/lib/integrationSetup.test.ts
  • apps/microbridge-ui/src/lib/integrationSetup.ts
  • apps/microbridge-ui/src/surfaces/Settings.tsx

Comment on lines +55 to +63
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);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 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.

Comment on lines +244 to +277
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;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants