fix(onboarding): give the runtime step the frame measure and cut its copy

Nine runtimes in a 620px column meant truncated names, five rows, and
scrolling to reach the last four, with ~330px of dead space on each side.
This is the case step-shell's wider measure exists for, so the step takes
STEP_FRAME — which also puts it on exactly the header's measure — and the
card grid gains a third column at lg. Nine runtimes now land in three rows.

Copy went with it. The headline was two sentences over two lines; the first
one, "This computer is connected", is already said louder by the "9 agent
runtimes · all online" row directly beneath it, so only the instruction
remains. The lede was 44 words and five lines — I had grown it myself adding
Mika's introduction — and is 20 now, still naming the role. The
remote-computer note drops from 25 words to 15.

Prose stays capped at 620px inside the wider frame; a 920px measure is for
the card grid, not for reading.

The found-phase test keyed on the headline copy, so a copy edit read as a
behaviour regression. It now asserts the runtime count row, which is the
signal the test is actually about.

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Lambda
2026-07-30 23:57:30 +08:00
parent e954795615
commit fc94f60897
6 changed files with 26 additions and 25 deletions

View File

@@ -185,9 +185,9 @@
"scanning_headline": "Connecting this computer…",
"scanning_lede_prefix": "Your agent works through an AI coding tool you already have — like ",
"scanning_lede_suffix": ", and others. We're checking this computer for ones you've installed.",
"found_headline": "This computer is connected. Pick an agent runtime.",
"found_lede": "These are the agent runtimes we found on this computer. Pick one for Mika, your workspace's Chief of Staff, who turns what you want done into issues and brings in the right agent to run them. You can switch runtimes or add more later.",
"remote_computer_later_note": "Prefer to run agents on a remote computer, or don't have a local runtime? You can skip this step and connect a remote computer later.",
"found_headline": "Pick an agent runtime.",
"found_lede": "Mika, your workspace's Chief of Staff, runs on the one you pick. You can switch it or add more later.",
"remote_computer_later_note": "No local runtime, or prefer a remote computer? Skip this step and connect one later.",
"runtime_count_one": "{{count}} agent runtime",
"runtime_count_other": "{{count}} agent runtimes",
"status_all_online": "all online",

View File

@@ -185,9 +185,9 @@
"scanning_headline": "このコンピュータを接続中…",
"scanning_lede_prefix": "エージェントは、あなたがすでに持っている AI コーディングツールを通じて作業します — たとえば ",
"scanning_lede_suffix": " など。このコンピュータにインストール済みのものを確認しています。",
"found_headline": "このコンピュータが接続されました。エージェントランタイムを選んでください。",
"found_lede": "このコンピュータで見つかったエージェントランタイムです。Mika が使うものを 1 つ選んでください。Mika はこのワークスペースの参謀長で、やりたいことを issue に変え、適したエージェントを呼びます。ランタイムはあとで切り替えたり追加したりできます。",
"remote_computer_later_note": "エージェントをリモートコンピュータで実行したい場合や、ローカルに利用できるランタイムがない場合は、ここをスキップしてあとからリモートコンピュータを接続できます。",
"found_headline": "エージェントランタイムを選んでください。",
"found_lede": "このワークスペースの参謀長である Mika は、選んだランタイムで動きます。あとで切り替えたり追加したりできます。",
"remote_computer_later_note": "ローカルにランタイムがない、またはリモートコンピュータを使いたい場合は、このステップを飛ばしてあとで接続できます。",
"runtime_count_other": "エージェントランタイム {{count}} 個",
"status_all_online": "すべてオンライン",
"status_none_online": "オンラインなし",

View File

@@ -185,9 +185,9 @@
"scanning_headline": "이 컴퓨터 연결 중...",
"scanning_lede_prefix": "에이전트는 이미 사용하는 AI 코딩 도구를 통해 작업합니다. 예: ",
"scanning_lede_suffix": " 등. 이 컴퓨터에 설치된 도구를 확인하는 중입니다.",
"found_headline": "이 컴퓨터가 연결되었습니다. 에이전트 런타임을 선택하세요.",
"found_lede": "이 컴퓨터에서 찾은 에이전트 런타임입니다. Mika가 사용할 런타임을 선택하세요. Mika는 이 워크스페이스의 참모장으로, 하고 싶은 일을 이슈로 만들고 알맞은 에이전트를 불러옵니다. 런타임은 나중에 바꾸거나 더 추가할 수 있습니다.",
"remote_computer_later_note": "에이전트를 원격 컴퓨터에서 실행하려 하거나 로컬 런타임이 없다면 지금 건너뛰고 나중에 원격 컴퓨터를 연결할 수 있습니다.",
"found_headline": "에이전트 런타임을 선택하세요.",
"found_lede": "이 워크스페이스의 참모장인 Mika는 선택한 런타임에서 실행됩니다. 나중에 바꾸거나 더 추가할 수 있습니다.",
"remote_computer_later_note": "로컬 런타임이 없거나 원격 컴퓨터를 쓰고 싶다면 이 단계를 건너뛰고 나중에 연결하세요.",
"runtime_count_other": "에이전트 런타임 {{count}}개",
"status_all_online": "모두 온라인",
"status_none_online": "온라인 없음",

View File

@@ -185,9 +185,9 @@
"scanning_headline": "正在连接这台电脑…",
"scanning_lede_prefix": "你的智能体借助本机已安装的 AI 编程工具运行,例如 ",
"scanning_lede_suffix": " 等。我们正在检测这台电脑上装了哪些。",
"found_headline": "这台电脑已连接。选一个 agent 运行时。",
"found_lede": "以下是在这台电脑上找到的 agent 运行时。挑一个给 Mika —— 这个工作区的参谋长,负责把你想做的事变成 issue并找合适的智能体来做。运行时之后可以随时切换或再加。",
"remote_computer_later_note": "想让智能体在远程电脑上运行,或本机没有可用的运行时?可以跳过,之后再连接远程电脑。",
"found_headline": "一个 agent 运行时。",
"found_lede": "Mika 这个工作区的参谋长,会跑在你挑的这个运行时上。之后可以随时切换或再加。",
"remote_computer_later_note": "没有本地运行时,或者想用远程电脑?可以跳过这一步,之后再连。",
"runtime_count_other": "{{count}} 个 agent 运行时",
"status_all_online": "全部在线",
"status_none_online": "全部离线",

View File

@@ -123,9 +123,10 @@ describe("StepRuntimeConnect", () => {
renderStep();
act(() => vi.advanceTimersByTime(25000));
expect(
screen.getByText(/this computer is connected/i),
).toBeInTheDocument();
// Assert on the runtime count row rather than headline copy: the phase is
// what this test is about, and keying on the headline made a copy edit
// look like a behaviour regression.
expect(screen.getByText(/1 agent runtime/i)).toBeInTheDocument();
expect(
screen.queryByText(/no agent runtime found/i),
).not.toBeInTheDocument();

View File

@@ -20,7 +20,7 @@ import type { AgentRuntime } from "@multica/core/types";
import { DragStrip } from "@multica/views/platform";
import {
STEP_BLOCK_PADDING,
STEP_COLUMN,
STEP_FRAME,
STEP_GUTTER,
StepShellHeader,
} from "../components/step-shell";
@@ -234,8 +234,8 @@ function FancyView({
<StepShellHeader currentStep="runtime" onBack={onBack} />
{/* Scrollable middle — content changes by phase but always wraps
at STEP_COLUMN so the 2-column runtime grid has room to
breathe without stretching into readability territory.
at STEP_FRAME — the same measure as the header — so nine
runtimes fit without truncating their names or scrolling.
Skip + Continue sit inline directly below the phase view
(not in a sticky bottom footer) so the action bar stays
@@ -252,7 +252,7 @@ function FancyView({
hard cut. */}
<div
key={phase}
className={cn("animate-onboarding-enter", STEP_COLUMN, STEP_BLOCK_PADDING)}
className={cn("animate-onboarding-enter", STEP_FRAME, STEP_BLOCK_PADDING)}
>
{phase === "scanning" && <ScanningView />}
{phase === "found" && (
@@ -331,7 +331,7 @@ function ScanningView() {
<h1 className="text-balance font-serif text-display font-medium leading-[1.1] tracking-tight text-foreground">
{t(($) => $.step_runtime.scanning_headline)}
</h1>
<p className="mt-4 max-w-[560px] text-body-lg leading-[1.55] text-muted-foreground">
<p className="mt-4 max-w-[620px] text-body-lg leading-[1.55] text-muted-foreground">
{t(($) => $.step_runtime.scanning_lede_prefix)}
<span className="font-medium text-foreground">{"Claude Code"}</span>
{", "}
@@ -341,7 +341,7 @@ function ScanningView() {
{t(($) => $.step_runtime.scanning_lede_suffix)}
</p>
<RemoteComputerNote />
<div className="mt-10 grid grid-cols-1 gap-2.5 sm:grid-cols-2">
<div className="mt-10 grid grid-cols-1 gap-2.5 sm:grid-cols-2 lg:grid-cols-3">
<SkeletonRuntimeCard />
<SkeletonRuntimeCard />
</div>
@@ -380,7 +380,7 @@ function FoundView({
<h1 className="text-balance font-serif text-display font-medium leading-[1.1] tracking-tight text-foreground">
{t(($) => $.step_runtime.found_headline)}
</h1>
<p className="mt-4 max-w-[560px] text-body-lg leading-[1.55] text-muted-foreground">
<p className="mt-4 max-w-[620px] text-body-lg leading-[1.55] text-muted-foreground">
{t(($) => $.step_runtime.found_lede)}
</p>
<RemoteComputerNote />
@@ -407,7 +407,7 @@ function FoundView({
/>
</div>
<div className="mt-6 grid grid-cols-1 gap-2.5 sm:grid-cols-2">
<div className="mt-6 grid grid-cols-1 gap-2.5 sm:grid-cols-2 lg:grid-cols-3">
{runtimes.map((rt) => (
<RuntimeCard
key={rt.id}
@@ -444,7 +444,7 @@ function EmptyView({
className="mt-2 shrink-0"
/>
</div>
<p className="mt-4 max-w-[560px] text-body-lg leading-[1.55] text-muted-foreground">
<p className="mt-4 max-w-[620px] text-body-lg leading-[1.55] text-muted-foreground">
{t(($) => $.step_runtime.empty_lede_prefix)}
<span className="font-medium text-foreground">{"Claude Code"}</span>
{", "}
@@ -477,7 +477,7 @@ function RemoteComputerNote() {
const { t } = useT("onboarding");
return (
<div className="mt-5 flex max-w-[560px] items-start gap-3 rounded-lg border bg-muted/30 px-4 py-3">
<div className="mt-5 flex max-w-[620px] items-start gap-3 rounded-lg border bg-muted/30 px-4 py-3">
<MonitorUp
aria-hidden
className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground"