mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-13 05:16:29 +02:00
fix(runtimes): simplify local machine list row (#5298)
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import { render } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const runtimesPage = vi.fn<(props: Record<string, unknown>) => null>(() => null);
|
||||
const useDesktopRuntimeContext = vi.fn();
|
||||
|
||||
vi.mock("@multica/views/runtimes", () => ({
|
||||
RuntimesPage: (props: Record<string, unknown>) => runtimesPage(props),
|
||||
}));
|
||||
|
||||
vi.mock("./use-desktop-runtime-context", () => ({
|
||||
useDesktopRuntimeContext: () => useDesktopRuntimeContext(),
|
||||
}));
|
||||
|
||||
import { DesktopRuntimesPage } from "./desktop-runtimes-page";
|
||||
|
||||
describe("DesktopRuntimesPage", () => {
|
||||
beforeEach(() => {
|
||||
runtimesPage.mockClear();
|
||||
useDesktopRuntimeContext.mockReturnValue({
|
||||
localDaemonId: "daemon-local",
|
||||
localMachineName: "Jiayuan's MacBook",
|
||||
bootstrapping: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps daemon controls out of the machine collection", () => {
|
||||
render(<DesktopRuntimesPage />);
|
||||
|
||||
expect(runtimesPage).toHaveBeenCalledWith({
|
||||
localDaemonId: "daemon-local",
|
||||
localMachineName: "Jiayuan's MacBook",
|
||||
hasLocalMachine: true,
|
||||
bootstrapping: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
import { RuntimesPage } from "@multica/views/runtimes";
|
||||
import { DaemonRuntimeActions } from "./daemon-runtime-card";
|
||||
import { useDesktopRuntimeContext } from "./use-desktop-runtime-context";
|
||||
|
||||
/**
|
||||
@@ -23,11 +22,10 @@ export function DesktopRuntimesPage() {
|
||||
<RuntimesPage
|
||||
localDaemonId={context.localDaemonId}
|
||||
localMachineName={context.localMachineName}
|
||||
localMachineActions={<DaemonRuntimeActions />}
|
||||
// Desktop owns a local machine for the lifetime of the app, even
|
||||
// while the daemon is stopped or hasn't registered yet. The shared
|
||||
// page synthesizes a placeholder local row when no real runtime
|
||||
// matches, so the Start button is always reachable.
|
||||
// while the daemon is stopped or hasn't registered yet. Lifecycle
|
||||
// controls live on the machine detail page so this collection stays
|
||||
// consistent with every other machine row.
|
||||
hasLocalMachine
|
||||
bootstrapping={context.bootstrapping}
|
||||
/>
|
||||
|
||||
@@ -41,8 +41,6 @@ export interface RuntimesPageProps {
|
||||
localDaemonId?: string | null;
|
||||
/** Desktop-only friendly device name for the local daemon. */
|
||||
localMachineName?: string | null;
|
||||
/** Desktop-only daemon controls attached to the local machine row. */
|
||||
localMachineActions?: React.ReactNode;
|
||||
/** Keep the local device visible even before its first runtime registers. */
|
||||
hasLocalMachine?: boolean;
|
||||
/** The bundled daemon is starting but has not registered yet. */
|
||||
@@ -63,7 +61,6 @@ function useNowTick(intervalMs = 30_000): number {
|
||||
export function RuntimesPage({
|
||||
localDaemonId,
|
||||
localMachineName,
|
||||
localMachineActions,
|
||||
hasLocalMachine,
|
||||
bootstrapping,
|
||||
cloudRuntimeEnabled = false,
|
||||
@@ -156,7 +153,6 @@ export function RuntimesPage({
|
||||
<MachineList
|
||||
machines={machines}
|
||||
bootstrapping={bootstrapping}
|
||||
localMachineActions={localMachineActions}
|
||||
/>
|
||||
)}
|
||||
{orphanProfileRuntimes.length > 0 && (
|
||||
@@ -252,11 +248,9 @@ function PageHeaderBar({
|
||||
function MachineList({
|
||||
machines,
|
||||
bootstrapping,
|
||||
localMachineActions,
|
||||
}: {
|
||||
machines: RuntimeMachine[];
|
||||
bootstrapping?: boolean;
|
||||
localMachineActions?: React.ReactNode;
|
||||
}) {
|
||||
const { t } = useT("runtimes");
|
||||
if (machines.length === 0) {
|
||||
@@ -281,24 +275,14 @@ function MachineList({
|
||||
<div className="overflow-hidden rounded-lg border bg-card">
|
||||
<div className="divide-y">
|
||||
{machines.map((machine) => (
|
||||
<MachineRow
|
||||
key={machine.id}
|
||||
machine={machine}
|
||||
actions={machine.isCurrent ? localMachineActions : undefined}
|
||||
/>
|
||||
<MachineRow key={machine.id} machine={machine} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MachineRow({
|
||||
machine,
|
||||
actions,
|
||||
}: {
|
||||
machine: RuntimeMachine;
|
||||
actions?: React.ReactNode;
|
||||
}) {
|
||||
function MachineRow({ machine }: { machine: RuntimeMachine }) {
|
||||
const { t } = useT("runtimes");
|
||||
const healthLabel = useHealthLabel();
|
||||
const timeAgo = useTimeAgo();
|
||||
@@ -316,20 +300,22 @@ function MachineRow({
|
||||
/>
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
<span className="truncate text-sm font-medium">{machine.title}</span>
|
||||
<span className="block truncate text-sm font-medium">
|
||||
{machine.title}
|
||||
</span>
|
||||
<span className="mt-1 flex min-w-0 items-center gap-2 text-xs text-muted-foreground">
|
||||
<span className="truncate">
|
||||
{machine.subtitle ??
|
||||
(machine.section === "cloud"
|
||||
? t(($) => $.machine.metrics.cloud_worker)
|
||||
: t(($) => $.machine.metrics.local_daemon))}
|
||||
</span>
|
||||
{machine.isCurrent && (
|
||||
<span className="shrink-0 rounded bg-foreground px-1.5 py-0.5 text-[10px] font-medium text-background">
|
||||
<span className="shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
|
||||
{t(($) => $.machine.this_machine)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="mt-1 block truncate text-xs text-muted-foreground">
|
||||
{machine.subtitle ??
|
||||
(machine.section === "cloud"
|
||||
? t(($) => $.machine.metrics.cloud_worker)
|
||||
: t(($) => $.machine.metrics.local_daemon))}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="hidden w-36 shrink-0 items-center gap-1.5 text-xs md:flex">
|
||||
@@ -365,15 +351,12 @@ function MachineRow({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex min-w-0 items-center">
|
||||
<AppLink
|
||||
href={paths.runtimeDetail(locator)}
|
||||
className="group flex min-w-0 flex-1 items-center gap-3 px-4 py-3.5 transition-colors hover:bg-accent/40 focus-visible:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
|
||||
>
|
||||
{body}
|
||||
</AppLink>
|
||||
{actions && <div className="shrink-0 pr-4">{actions}</div>}
|
||||
</div>
|
||||
<AppLink
|
||||
href={paths.runtimeDetail(locator)}
|
||||
className="group flex min-w-0 items-center gap-3 px-4 py-3.5 transition-colors hover:bg-accent/40 focus-visible:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
|
||||
>
|
||||
{body}
|
||||
</AppLink>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user