From 88dd9c538527b9468f1319d0cec5a712939c8c4c Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Mon, 19 Oct 2026 13:14:40 -0400 Subject: [PATCH 1/3] fix(app): say where tools run in each move item, and name this host as the app does --- apps/host/src/gateway.ts | 2 +- apps/host/src/move.ts | 6 +++--- .../session-item/session-item-actions.tsx | 13 +++++++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/host/src/gateway.ts b/apps/host/src/gateway.ts index c3b870f4..13ade3fc 100644 --- a/apps/host/src/gateway.ts +++ b/apps/host/src/gateway.ts @@ -576,7 +576,7 @@ async function handle( return archived.length; } case "move-targets": - return catalog.owns(request.channel) ? targets(request.channel) : []; + return catalog.owns(request.channel) ? targets(request.channel, name) : []; case "move": { if (!catalog.owns(request.channel)) { throw new Error("Move channels from the host that runs them"); diff --git a/apps/host/src/move.ts b/apps/host/src/move.ts index 3d417e7c..8bb618b0 100644 --- a/apps/host/src/move.ts +++ b/apps/host/src/move.ts @@ -4,7 +4,6 @@ * both copies from running while the move is under way, and an unknown activation keeps it. */ import { existsSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs"; -import { hostname } from "node:os"; import { join } from "node:path"; import { openNodeSqliteDatabase } from "@earendil-works/pi-durable/storage/sqlite/node"; @@ -65,7 +64,8 @@ function movable(record: catalog.Listing): void { /** The target that moves a local channel to a cloud workspace on the team's compute service. */ const COMPUTE = "compute"; -export async function targets(id: string): Promise { +/** `host` is this host's name as the app shows it, its tailnet machine name when it has one. */ +export async function targets(id: string, host: string): Promise { const record = catalog.read(id); try { movable(record); @@ -73,7 +73,7 @@ export async function targets(id: string): Promise { return []; } if (record.hosted) { - return [{ target: "local", label: `this host (${hostname()})`, place: "local" }]; + return [{ target: "local", label: `this host (${host})`, place: "local" }]; } const team = directory.read().hosted; const hosted = new Set([ diff --git a/packages/ui/src/components/session-item/session-item-actions.tsx b/packages/ui/src/components/session-item/session-item-actions.tsx index ed63896c..26a5d5d1 100644 --- a/packages/ui/src/components/session-item/session-item-actions.tsx +++ b/packages/ui/src/components/session-item/session-item-actions.tsx @@ -67,6 +67,12 @@ type RowMenuProps = ActionProps & { }; const PLACES = { local: Hash, hosted: Radar, cloud: Cloud }; +// "Cloud" and "cloud workspace" alone don't say what moves, so each item says where tools run. +const HINTS = { + local: "Chat and tools run on this Mac", + hosted: "Chat moves; tools stay on this Mac", + cloud: "Chat and tools move to a cloud container", +}; type Destinations = | { state: "loading" } @@ -190,8 +196,11 @@ function MoveSection( onMove(target); }} > - - Move to {target.label} + + + Move to {target.label} + {HINTS[target.place]} + {moving === target.target && ( Moving… )} From dddae6ec5da355e0595b854ff9496ff7e4b6c1f0 Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Mon, 19 Oct 2026 13:55:16 -0400 Subject: [PATCH 2/3] fix(app): hosted channels keep the channel icon and show a cloud in the header --- apps/app/src/app.tsx | 1 - apps/app/src/channel-access.tsx | 6 +++--- apps/app/src/channel.tsx | 8 ++------ apps/app/src/dashboard.tsx | 8 -------- apps/app/src/layout/layout.tsx | 2 +- .../src/components/session-item/session-item-actions.tsx | 3 +-- .../ui/src/components/session-item/session-item-glyph.tsx | 2 -- .../ui/src/components/session-item/session-item.types.ts | 2 -- packages/ui/src/icons.tsx | 2 -- 9 files changed, 7 insertions(+), 27 deletions(-) diff --git a/apps/app/src/app.tsx b/apps/app/src/app.tsx index 6a8518a4..93130e2c 100644 --- a/apps/app/src/app.tsx +++ b/apps/app/src/app.tsx @@ -134,7 +134,6 @@ function row( lifecycle: channel.state === "archived" ? "archived" : "live", private: false, cloud: place.kind === "cloud", - hosted: place.kind === "hosted", mine: channel.owner === user, member: true, capabilities: { diff --git a/apps/app/src/channel-access.tsx b/apps/app/src/channel-access.tsx index e1012202..2f7f8c79 100644 --- a/apps/app/src/channel-access.tsx +++ b/apps/app/src/channel-access.tsx @@ -44,14 +44,14 @@ function reach(channel: Listing): { agents: string; confirm: string; desktop: bo }; } -/** The machine the channel's tools run on: its host's, or a container on the compute service. */ +/** Where the channel runs: a cloud for a hosted or compute channel, otherwise its host. */ function Machine({ channel }: { channel: Listing }) { const place = placement(channel); const [Icon, name, detail] = place.kind === "cloud" ? [IconCloud, "Cloud", `Tools run in a container on ${place.service}.`] : place.kind === "hosted" ? [ - IconLaptop, + IconCloud, place.workspace, `Hosted on ${place.service}. Tools run on ${place.workspace}.`, ] @@ -60,7 +60,7 @@ function Machine({ channel }: { channel: Listing }) { + } > diff --git a/apps/app/src/channel.tsx b/apps/app/src/channel.tsx index 862bcbbd..dff28bb9 100644 --- a/apps/app/src/channel.tsx +++ b/apps/app/src/channel.tsx @@ -21,7 +21,7 @@ import { Sidebar, useLayoutRight, } from "@ace/ui"; -import { IconCloud, IconHash, IconRadar } from "@ace/ui/icons"; +import { IconCloud, IconHash } from "@ace/ui/icons"; import { ChannelAccess } from "./channel-access"; import { EFFORTS } from "./composer"; @@ -323,11 +323,7 @@ export function Channel( : remote ? `${channel.name} · ${channel.host}` : channel.name} - icon={place.kind === "cloud" - ? place.stopped ? StoppedCloud : IconCloud - : place.kind === "hosted" - ? IconRadar - : IconHash} + icon={place.kind === "cloud" ? place.stopped ? StoppedCloud : IconCloud : IconHash} chat={chat} changed={changed} opener={opener} diff --git a/apps/app/src/dashboard.tsx b/apps/app/src/dashboard.tsx index 4a39add6..f39c5885 100644 --- a/apps/app/src/dashboard.tsx +++ b/apps/app/src/dashboard.tsx @@ -22,7 +22,6 @@ import { IconLaptop, IconMessage, IconPlus, - IconRadar, IconRotate, } from "@ace/ui/icons"; import type { GithubList } from "@ace/channel/protocol"; @@ -541,13 +540,6 @@ export function Dashboard( aria-label="Cloud channel" /> ) - : channel.hosted - ? ( - - ) : } {channel.name} diff --git a/apps/app/src/layout/layout.tsx b/apps/app/src/layout/layout.tsx index 815e24c7..afdf2849 100644 --- a/apps/app/src/layout/layout.tsx +++ b/apps/app/src/layout/layout.tsx @@ -237,7 +237,7 @@ function Add({ onAdd }: { onAdd: (choice: Choice) => void }) { variant="ghost" size="icon-sm" aria-label="New tab" - className="mx-0.5 shrink-0 text-muted-foreground electrobun-webkit-app-region-no-drag" + className="mr-2 ml-0.5 shrink-0 text-muted-foreground electrobun-webkit-app-region-no-drag" /> } > diff --git a/packages/ui/src/components/session-item/session-item-actions.tsx b/packages/ui/src/components/session-item/session-item-actions.tsx index 26a5d5d1..df220b15 100644 --- a/packages/ui/src/components/session-item/session-item-actions.tsx +++ b/packages/ui/src/components/session-item/session-item-actions.tsx @@ -21,7 +21,6 @@ import { IconPin as Pin, IconPinOff as PinSlash, IconPlay as Play, - IconRadar as Radar, IconRotate as Rebuild, IconStop as Stop, IconTrash as Trash, @@ -66,7 +65,7 @@ type RowMenuProps = ActionProps & { children: ReactNode; }; -const PLACES = { local: Hash, hosted: Radar, cloud: Cloud }; +const PLACES = { local: Hash, hosted: Cloud, cloud: Cloud }; // "Cloud" and "cloud workspace" alone don't say what moves, so each item says where tools run. const HINTS = { local: "Chat and tools run on this Mac", diff --git a/packages/ui/src/components/session-item/session-item-glyph.tsx b/packages/ui/src/components/session-item/session-item-glyph.tsx index 42854522..fb2c4c23 100644 --- a/packages/ui/src/components/session-item/session-item-glyph.tsx +++ b/packages/ui/src/components/session-item/session-item-glyph.tsx @@ -16,7 +16,6 @@ import { IconMessage as Msg, IconPullRequest, IconPullRequestClosed, - IconRadar, } from "../../icons"; type BadgeState = ReturnType; @@ -106,7 +105,6 @@ function resolveIcon(data: SidebarRow) { } if (data.private) return ; if (data.cloud) return ; - if (data.hosted) return ; return ; } diff --git a/packages/ui/src/components/session-item/session-item.types.ts b/packages/ui/src/components/session-item/session-item.types.ts index 4dd4105f..b96a3100 100644 --- a/packages/ui/src/components/session-item/session-item.types.ts +++ b/packages/ui/src/components/session-item/session-item.types.ts @@ -25,8 +25,6 @@ export type SidebarRow = { private: boolean; /** Runs on the team's cloud compute rather than a teammate's machine. */ cloud?: boolean; - /** Lives on the team's hosting service while a host runs its tools. */ - hosted?: boolean; mine: boolean; member: boolean; /** Omitted by local/legacy callers; explicit peer policy controls administrative actions. */ diff --git a/packages/ui/src/icons.tsx b/packages/ui/src/icons.tsx index 65af5905..440e21f4 100644 --- a/packages/ui/src/icons.tsx +++ b/packages/ui/src/icons.tsx @@ -80,7 +80,6 @@ import { PinOff, Play, Plus, - Radar, RotateCw, Rows2, Search, @@ -194,7 +193,6 @@ export const IconPointer = outline(MousePointer2); export const IconPullRequest = outline(GitPullRequest); export const IconPullRequestClosed = outline(GitPullRequestClosed); export const IconPush = outline(ArrowUpFromLine); -export const IconRadar = outline(Radar); export const IconReact = outline(FaceSlightlySmilingPlus); export const IconRecord = outline(CircleDot); export const IconReview = outline(MessageSquareDiff); From 635e2c05d59fcc3d783f1b683d2a0b3011f42078 Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Mon, 19 Oct 2026 13:59:13 -0400 Subject: [PATCH 3/3] chore: confirm lane notes --- ...4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md | 6 +++--- ...Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md | 6 +++--- ...314FQ002GKKHTQ5G9Q4NZJ-the-directory-s-hosting-serv.md | 8 ++++---- ...39GS26X4Y12MHXZY5VZFRD-the-cloud-icon-means-tools-r.md | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md b/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md index 90da69e3..0eb4e3c3 100644 --- a/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md +++ b/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md @@ -4,9 +4,9 @@ anchor: function handle created: 2026-10-15T04:08:49Z norm: '1' sig: e597b5fbc8e664ce -body_hash: 4c0a5f447f3453d7 -raw_hash: 277527dea93aa68f -vouched: 2026-10-16T22:11:39Z +body_hash: aae4da1d5b13a0a7 +raw_hash: 887a83004e69e816 +vouched: 2026-10-19T17:59:13Z lines: 266-519 --- diff --git a/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md b/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md index 57c0b6c0..f4bc996b 100644 --- a/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md +++ b/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md @@ -4,9 +4,9 @@ anchor: function handle created: 2026-10-15T06:00:20Z norm: '1' sig: e597b5fbc8e664ce -body_hash: 4c0a5f447f3453d7 -raw_hash: 277527dea93aa68f -vouched: 2026-10-16T22:11:39Z +body_hash: aae4da1d5b13a0a7 +raw_hash: 887a83004e69e816 +vouched: 2026-10-19T17:59:13Z lines: 281-561 --- diff --git a/.lane/memory/apps/host/src/move.ts/01M5314FQ002GKKHTQ5G9Q4NZJ-the-directory-s-hosting-serv.md b/.lane/memory/apps/host/src/move.ts/01M5314FQ002GKKHTQ5G9Q4NZJ-the-directory-s-hosting-serv.md index e65e968d..f5625ef8 100644 --- a/.lane/memory/apps/host/src/move.ts/01M5314FQ002GKKHTQ5G9Q4NZJ-the-directory-s-hosting-serv.md +++ b/.lane/memory/apps/host/src/move.ts/01M5314FQ002GKKHTQ5G9Q4NZJ-the-directory-s-hosting-serv.md @@ -3,10 +3,10 @@ id: 01M5314FQ002GKKHTQ5G9Q4NZJ anchor: function targets created: 2026-10-16T18:56:03Z norm: '1' -sig: 7b9616ffd24e6d4f -body_hash: 601506fe4d4d38d9 -raw_hash: 0f2a5a6a1e5af55a -vouched: 2026-10-19T16:56:54Z +sig: 002f9c457b031ab4 +body_hash: 85d4201c6260eec4 +raw_hash: 8782a699c3cff424 +vouched: 2026-10-19T17:59:13Z lines: 64-79 --- diff --git a/.lane/memory/packages/ui/src/components/session-item/session-item-glyph.tsx/01M539GS26X4Y12MHXZY5VZFRD-the-cloud-icon-means-tools-r.md b/.lane/memory/packages/ui/src/components/session-item/session-item-glyph.tsx/01M539GS26X4Y12MHXZY5VZFRD-the-cloud-icon-means-tools-r.md index 8029bfe6..5c9308b3 100644 --- a/.lane/memory/packages/ui/src/components/session-item/session-item-glyph.tsx/01M539GS26X4Y12MHXZY5VZFRD-the-cloud-icon-means-tools-r.md +++ b/.lane/memory/packages/ui/src/components/session-item/session-item-glyph.tsx/01M539GS26X4Y12MHXZY5VZFRD-the-cloud-icon-means-tools-r.md @@ -4,9 +4,9 @@ anchor: function resolveIcon created: 2026-10-16T21:22:34Z norm: '1' sig: d6f0475e7b3b3b6a -body_hash: ac51fa4986c3d09c -raw_hash: f8c73fa082615d42 -vouched: 2026-10-19T16:56:54Z +body_hash: 9f404a3e4bee814d +raw_hash: cd2cb0d75cfe2527 +vouched: 2026-10-19T17:59:13Z lines: 64-109 ---