mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
feat(mesh): make the cold state name its action
With nothing shared anywhere, the row and popover were both status surfaces with no status: a hollow dot beside "Buzz MeshLLM", and a "Mesh capacity" heading over "0 sharing in this community". Nothing said what the switch does, which is the one thing a first-time reader needs and the one thing a zero cannot tell them. The row now drops the "Buzz" qualifier -- "Buzz MeshLLM" plus an invitation does not fit 256px, and the name is the part that can afford a word -- carries a Share2 glyph where the status dot would go, and offers "Share your compute" in the trailing slot. The dot returns the moment there is a real state to report, which is the more honest signal there. The popover's cold subtitle explains rather than reports: "Share your spare compute with the Buzz community to run models." Its heading names the thing instead of heading an absent number. Only the cold state invites. A permanent nudge beside a real figure becomes furniture, and a prompt derived from an unfetched snapshot would appear for one poll and retract, reading as a glitch. Also renames the popover's footer link from "Compute settings" to "Advanced settings": it named the destination, where the useful thing to name is what is there. The picker, custom model refs, and the memory cap are all advanced concerns; the popover already covers the everyday ones. Signed-off-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
@@ -327,3 +327,22 @@ test("the card hint distinguishes giving, taking, and neither", () => {
|
||||
"3 of 4 requests ran on shared compute",
|
||||
);
|
||||
});
|
||||
|
||||
test("the cold popover explains the switch instead of restating a zero", () => {
|
||||
// "0 sharing in this community" is a status line with no status. Someone
|
||||
// opening this for the first time needs to know what the switch beside it
|
||||
// does, which is the one thing a zero cannot tell them.
|
||||
const model = deriveMeshDetailModel({
|
||||
view: null,
|
||||
snapshot: snapshot({
|
||||
sharingDeviceCount: 0,
|
||||
sharedCapacityGb: null,
|
||||
devices: [],
|
||||
}),
|
||||
usage: null,
|
||||
isSharing: false,
|
||||
inboundWork: false,
|
||||
});
|
||||
assert.match(model.participationLabel, /Share your spare compute/);
|
||||
assert.doesNotMatch(model.participationLabel, /^0 /);
|
||||
});
|
||||
|
||||
@@ -31,6 +31,13 @@ import { describeRequestOrigin } from "./meshActivity";
|
||||
* while our own dispatch count stays flat, means the work is not ours. See
|
||||
* `inferInboundWork` in `meshActivity.ts`. It is sampled, so it can undercount
|
||||
* — but it never over-claims, which is the direction that matters.
|
||||
*
|
||||
* ## The cold state explains, it does not report
|
||||
*
|
||||
* With nothing shared anywhere there is no status worth stating, so the subtitle
|
||||
* says what the switch does rather than restating a zero. Every other state has
|
||||
* a real number, and an explanation sitting permanently beside those would be
|
||||
* furniture.
|
||||
*/
|
||||
|
||||
export type MeshDetailModel = {
|
||||
@@ -133,9 +140,12 @@ export function deriveMeshDetailModel({
|
||||
? peers.length === 0
|
||||
? "No other devices yet"
|
||||
: `${peers.length} ${peers.length === 1 ? "peer" : "peers"} connected`
|
||||
: // Not participating: the relay snapshot is all we have, and it is the
|
||||
// reason to consider joining.
|
||||
`${snapshot?.sharingDeviceCount ?? 0} sharing in this community`,
|
||||
: (snapshot?.sharingDeviceCount ?? 0) > 0
|
||||
? // Someone else is sharing: the count is the reason to join.
|
||||
`${snapshot?.sharingDeviceCount ?? 0} sharing in this community`
|
||||
: // Cold. There is no status worth reporting, so the line explains what
|
||||
// the switch beside it actually does instead of restating a zero.
|
||||
"Share your spare compute with the Buzz community to run models",
|
||||
busyNow: (usage?.inflight ?? 0) > 0,
|
||||
activityLabel: describeActivity({ usage, isSharing, inboundWork }),
|
||||
originLabel: describeRequestOrigin(usage),
|
||||
|
||||
@@ -215,13 +215,38 @@ test("an agent that uses the mesh leaves a busy sharer showing 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.
|
||||
// the trailing slot carries an invitation where a figure would go.
|
||||
const model = derive({ snapshot: snapshot() });
|
||||
assert.equal(model.tone, "unknown");
|
||||
assert.equal(model.badge, "Share");
|
||||
assert.equal(model.callToAction, "Share your compute");
|
||||
assert.equal(model.badge, null);
|
||||
assert.match(model.tooltip, /share this computer to start the mesh/);
|
||||
});
|
||||
|
||||
test("the cold state drops the Buzz qualifier to make room", () => {
|
||||
// "Buzz MeshLLM" plus "Share your compute" does not fit 256px. The name is
|
||||
// the part that can afford to lose a word.
|
||||
assert.equal(derive({ snapshot: snapshot() }).label, "MeshLLM");
|
||||
assert.equal(
|
||||
derive({
|
||||
snapshot: snapshot({ sharingDeviceCount: 1, sharedCapacityGb: 36 }),
|
||||
}).label,
|
||||
"Buzz MeshLLM",
|
||||
);
|
||||
});
|
||||
|
||||
test("only the cold state invites; every other state reports", () => {
|
||||
// A permanent nudge beside a real figure becomes furniture.
|
||||
for (const input of [
|
||||
{ snapshot: null },
|
||||
{ snapshot: snapshot({ sharingDeviceCount: 2, sharedCapacityGb: 64 }) },
|
||||
{ toggle: SHARING, view: view() },
|
||||
{ toggle: CONSUMING },
|
||||
]) {
|
||||
assert.equal(derive(input).callToAction, null);
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
|
||||
@@ -64,6 +64,11 @@ export type MeshRowPulse = "none" | "activity" | "invite";
|
||||
|
||||
export type MeshRowModel = {
|
||||
tone: MeshRowTone;
|
||||
/**
|
||||
* Row label. Drops the "Buzz" qualifier in the cold state, where a call to
|
||||
* action shares the same 256px and every character costs.
|
||||
*/
|
||||
label: string;
|
||||
/** Accessible state sentence, used as the row tooltip. */
|
||||
tooltip: string;
|
||||
/**
|
||||
@@ -78,6 +83,15 @@ export type MeshRowModel = {
|
||||
* "Share" — the row would otherwise be inert with nothing explaining why.
|
||||
*/
|
||||
badge: string | null;
|
||||
/**
|
||||
* Invitation text, or null.
|
||||
*
|
||||
* Only set in the cold state — no capacity anywhere, nothing running. There
|
||||
* is no number to show and no status to report, so without words the row is
|
||||
* inert and unexplained. Every other state has a figure or a tone that speaks
|
||||
* for itself, and a permanent nudge beside them would become furniture.
|
||||
*/
|
||||
callToAction: string | null;
|
||||
/**
|
||||
* Why the dot moves, if it does. Never decorative: a still dot is a real
|
||||
* statement that there is nothing to act on and nothing happening.
|
||||
@@ -85,6 +99,9 @@ export type MeshRowModel = {
|
||||
pulse: MeshRowPulse;
|
||||
};
|
||||
|
||||
/** Full name. Used everywhere except the cold state, which needs the room. */
|
||||
const LABEL = "Buzz MeshLLM";
|
||||
|
||||
/** Live pool capacity: this machine plus every peer that reports a figure. */
|
||||
function liveCapacityGb(view: MeshLiveView | null): number {
|
||||
if (!view?.connected) return 0;
|
||||
@@ -126,9 +143,11 @@ export function deriveMeshRowModel({
|
||||
) {
|
||||
return {
|
||||
tone: "starting",
|
||||
label: LABEL,
|
||||
tooltip:
|
||||
pendingAction === "stop" ? "Stopping sharing…" : "Starting to share…",
|
||||
badge: null,
|
||||
callToAction: null,
|
||||
pulse: "none",
|
||||
};
|
||||
}
|
||||
@@ -138,8 +157,10 @@ export function deriveMeshRowModel({
|
||||
if (health && health.status !== "ok") {
|
||||
return {
|
||||
tone: "failed",
|
||||
label: LABEL,
|
||||
tooltip: "Sharing failed — open for details",
|
||||
badge: null,
|
||||
callToAction: null,
|
||||
pulse: "none",
|
||||
};
|
||||
}
|
||||
@@ -153,18 +174,22 @@ export function deriveMeshRowModel({
|
||||
if (hasMeshAgent === false) {
|
||||
return {
|
||||
tone: "sharing",
|
||||
label: LABEL,
|
||||
tooltip: "Sharing compute · no agent is using it yet",
|
||||
badge,
|
||||
callToAction: null,
|
||||
pulse: "invite",
|
||||
};
|
||||
}
|
||||
return {
|
||||
tone: "sharing",
|
||||
label: LABEL,
|
||||
tooltip:
|
||||
peerCount > 0
|
||||
? `Sharing compute · ${peerCount} ${plural(peerCount, "peer")}`
|
||||
: "Sharing compute · waiting for another device",
|
||||
badge,
|
||||
callToAction: null,
|
||||
pulse: busyNow ? "activity" : "none",
|
||||
};
|
||||
}
|
||||
@@ -173,8 +198,10 @@ export function deriveMeshRowModel({
|
||||
const gb = liveCapacityGb(view);
|
||||
return {
|
||||
tone: "consuming",
|
||||
label: LABEL,
|
||||
tooltip: "Using shared compute from the mesh",
|
||||
badge: gb > 0 ? formatCapacityGb(gb) : null,
|
||||
callToAction: null,
|
||||
pulse: busyNow ? "activity" : "none",
|
||||
};
|
||||
}
|
||||
@@ -187,8 +214,12 @@ export function deriveMeshRowModel({
|
||||
// would appear for one poll and retract, which reads as a glitch.
|
||||
return {
|
||||
tone: "unknown",
|
||||
label: LABEL,
|
||||
tooltip: "Checking for shared compute…",
|
||||
badge: null,
|
||||
// Nothing offered from no data: a prompt that appears for one poll and
|
||||
// retracts reads as a glitch, not a suggestion.
|
||||
callToAction: null,
|
||||
pulse: "none",
|
||||
};
|
||||
}
|
||||
@@ -203,14 +234,18 @@ export function deriveMeshRowModel({
|
||||
// annoyance. The invite throb stays reserved for capacity that exists.
|
||||
return {
|
||||
tone: "unknown",
|
||||
// "Buzz" is dropped here so the invitation fits beside it.
|
||||
label: "MeshLLM",
|
||||
tooltip: "No shared compute yet — share this computer to start the mesh",
|
||||
badge: "Share",
|
||||
badge: null,
|
||||
callToAction: "Share your compute",
|
||||
pulse: "none",
|
||||
};
|
||||
}
|
||||
const gb = snapshot.sharedCapacityGb;
|
||||
return {
|
||||
tone: "available",
|
||||
label: LABEL,
|
||||
tooltip:
|
||||
gb === null
|
||||
? `Shared compute available · ${count} ${plural(count, "device")}`
|
||||
@@ -221,6 +256,7 @@ export function deriveMeshRowModel({
|
||||
gb === null
|
||||
? `${count} ${plural(count, "device")}`
|
||||
: formatCapacityGb(gb),
|
||||
callToAction: null,
|
||||
pulse: "invite",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export function MeshComputePopover({
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-semibold leading-tight">
|
||||
{model.capacityLabel ?? "Mesh capacity"}
|
||||
{model.capacityLabel ?? "Buzz MeshLLM"}
|
||||
</p>
|
||||
<p
|
||||
className="mt-0.5 text-2xs text-muted-foreground"
|
||||
@@ -226,7 +226,7 @@ export function MeshComputePopover({
|
||||
onClick={onOpenComputeSettings}
|
||||
type="button"
|
||||
>
|
||||
Compute settings
|
||||
Advanced settings
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Share2 } from "lucide-react";
|
||||
|
||||
import { SidebarMenuButton, SidebarMenuItem } from "@/shared/ui/sidebar";
|
||||
import { SidebarMenuLabel } from "@/shared/ui/sidebar-menu-label";
|
||||
import { cn } from "@/shared/lib/cn";
|
||||
@@ -81,31 +83,47 @@ export function SidebarMeshComputeRow({
|
||||
aria-hidden
|
||||
className="flex h-4 w-4 shrink-0 items-center justify-center"
|
||||
>
|
||||
<span className="relative flex h-2 w-2 items-center justify-center">
|
||||
{row.pulse === "none" ? null : (
|
||||
<span
|
||||
className={cn(
|
||||
"absolute h-2 w-2 rounded-full opacity-75 motion-reduce:animate-none",
|
||||
PULSE_CLASS[row.pulse],
|
||||
TONE_PING[row.tone],
|
||||
)}
|
||||
data-mesh-pulse={row.pulse}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={cn("h-2 w-2 rounded-full", TONE_DOT[row.tone])}
|
||||
data-testid="mesh-row-dot"
|
||||
{/*
|
||||
In the cold state a status dot has nothing to say — there is no
|
||||
capacity to report and nothing running — so the slot carries a
|
||||
Share2 glyph, which names the action instead. Every other state has
|
||||
a real status, and the dot is the more honest signal there.
|
||||
*/}
|
||||
{row.callToAction ? (
|
||||
<Share2
|
||||
className="h-3.5 w-3.5 text-muted-foreground"
|
||||
data-testid="mesh-row-share-icon"
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<span className="relative flex h-2 w-2 items-center justify-center">
|
||||
{row.pulse === "none" ? null : (
|
||||
<span
|
||||
className={cn(
|
||||
"absolute h-2 w-2 rounded-full opacity-75 motion-reduce:animate-none",
|
||||
PULSE_CLASS[row.pulse],
|
||||
TONE_PING[row.tone],
|
||||
)}
|
||||
data-mesh-pulse={row.pulse}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={cn("h-2 w-2 rounded-full", TONE_DOT[row.tone])}
|
||||
data-testid="mesh-row-dot"
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<SidebarMenuLabel className="opacity-80">
|
||||
Buzz MeshLLM
|
||||
{row.label}
|
||||
</SidebarMenuLabel>
|
||||
</SidebarMenuButton>
|
||||
</MeshComputePopover>
|
||||
|
||||
{/*
|
||||
Capacity, in every state that has a number — not just while sharing.
|
||||
The trailing slot: an invitation when cold, capacity otherwise.
|
||||
|
||||
Capacity shows in every state that has a number — not just while
|
||||
sharing.
|
||||
|
||||
This slot used to hold a 70%-scaled unlabelled Switch, which was hard to
|
||||
see and ambiguous about what it would do. The control moved wholesale to
|
||||
@@ -114,7 +132,14 @@ export function SidebarMeshComputeRow({
|
||||
joining better than any exhortation, and unlike a nudge there is nothing
|
||||
to dismiss. Pointer-events off so the whole row stays one click target.
|
||||
*/}
|
||||
{row.badge ? (
|
||||
{row.callToAction ? (
|
||||
<span
|
||||
className="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-2xs font-medium text-muted-foreground group-data-[collapsible=icon]:hidden"
|
||||
data-testid="mesh-row-cta"
|
||||
>
|
||||
{row.callToAction}
|
||||
</span>
|
||||
) : row.badge ? (
|
||||
<span
|
||||
className="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-2xs tabular-nums text-muted-foreground group-data-[collapsible=icon]:hidden"
|
||||
data-testid="mesh-row-badge"
|
||||
|
||||
Reference in New Issue
Block a user