diff --git a/desktop/src/features/mesh-compute/meshRowModel.test.mjs b/desktop/src/features/mesh-compute/meshRowModel.test.mjs index cc699bb77..870bdac55 100644 --- a/desktop/src/features/mesh-compute/meshRowModel.test.mjs +++ b/desktop/src/features/mesh-compute/meshRowModel.test.mjs @@ -211,3 +211,22 @@ test("an agent that uses the mesh leaves a busy sharer showing activity", () => }); assert.equal(model.pulse, "activity"); }); + +test("an empty mesh offers the action instead of sitting inert", () => { + // Nothing to show and nothing happening: without a prompt the row is inert + // and unexplained. The action is real -- you can be the first to share -- so + // the badge slot carries the word where a figure would go. + const model = derive({ snapshot: snapshot() }); + assert.equal(model.tone, "unknown"); + assert.equal(model.badge, "Share"); + assert.match(model.tooltip, /share this computer to start the mesh/); +}); + +test("an empty mesh does not throb, and an unfetched one offers nothing", () => { + // A community that never uses the mesh would otherwise animate forever; + // permanent motion earns nothing but annoyance. And a prompt derived from no + // data would appear for one poll then retract, reading as a glitch. + assert.equal(derive({ snapshot: snapshot() }).pulse, "none"); + assert.equal(derive({ snapshot: null }).badge, null); + assert.equal(derive({ snapshot: null }).pulse, "none"); +}); diff --git a/desktop/src/features/mesh-compute/meshRowModel.ts b/desktop/src/features/mesh-compute/meshRowModel.ts index c35c61f16..195b4f67c 100644 --- a/desktop/src/features/mesh-compute/meshRowModel.ts +++ b/desktop/src/features/mesh-compute/meshRowModel.ts @@ -73,6 +73,9 @@ export type MeshRowModel = { * machine is outside the mesh the figure *is* the invitation: a real number * argues for joining better than any exhortation, and unlike a nudge there is * nothing to dismiss. + * + * When the mesh is empty there is no figure, so the slot carries the word + * "Share" — the row would otherwise be inert with nothing explaining why. */ badge: string | null; /** @@ -179,16 +182,32 @@ export function deriveMeshRowModel({ // Not participating. The relay snapshot is the only view, and the only thing // that distinguishes "there is a mesh to join" from "there is nothing here". const count = snapshot?.sharingDeviceCount ?? 0; - if (!snapshot || count === 0) { + if (!snapshot) { + // Not asked yet. No figure, no prompt: a call to action based on no data + // would appear for one poll and retract, which reads as a glitch. return { tone: "unknown", - tooltip: snapshot - ? "No shared compute in this community yet" - : "Checking for shared compute…", + tooltip: "Checking for shared compute…", badge: null, pulse: "none", }; } + if (count === 0) { + // An empty mesh is the one state with nothing to show and nothing + // happening, so without a prompt the row is inert and unexplained. The + // action is real — you can be the first to share — so the badge slot + // carries the word instead of a figure. + // + // Static, not throbbing. A community that never uses the mesh would + // otherwise animate forever, and permanent motion earns nothing but + // annoyance. The invite throb stays reserved for capacity that exists. + return { + tone: "unknown", + tooltip: "No shared compute yet — share this computer to start the mesh", + badge: "Share", + pulse: "none", + }; + } const gb = snapshot.sharedCapacityGb; return { tone: "available", diff --git a/desktop/src/features/mesh-compute/ui/SidebarMeshComputeRow.tsx b/desktop/src/features/mesh-compute/ui/SidebarMeshComputeRow.tsx index a638caf32..86fef77f5 100644 --- a/desktop/src/features/mesh-compute/ui/SidebarMeshComputeRow.tsx +++ b/desktop/src/features/mesh-compute/ui/SidebarMeshComputeRow.tsx @@ -7,7 +7,7 @@ import type { MeshRowPulse, MeshRowTone } from "../meshRowModel"; import { MeshComputePopover } from "./MeshComputePopover"; /** - * The sidebar Community mesh row. + * The sidebar Buzz MeshLLM row. * * Replaces the ~120px footer card with one menu line beside Inbox and Agents, * because front-page sidebar space is premium. The row carries a single signal @@ -99,7 +99,7 @@ export function SidebarMeshComputeRow({ - Community mesh + Buzz MeshLLM