Simplify XWorkmate assistant workspace chrome

This commit is contained in:
Haitao Pan 2026-03-12 19:43:08 +08:00
parent 4d0534dd47
commit 022478a11b
2 changed files with 117 additions and 227 deletions

View File

@ -263,6 +263,7 @@ export function OpenClawAssistantPane({
);
const compact = variant === "sidebar";
const minimalPage = variant === "page";
const locale = isChinese ? "zh-CN" : "en-US";
const compactConnected = compact && connectionState === "ready";
@ -416,11 +417,17 @@ export function OpenClawAssistantPane({
const renderedMessages = useMemo(
() =>
messages.map((message) => ({
...message,
html: renderMarkdown(message.text || ""),
})),
[messages],
messages
.filter((message) =>
minimalPage
? message.role === "user" || message.role === "assistant"
: true,
)
.map((message) => ({
...message,
html: renderMarkdown(message.text || ""),
})),
[messages, minimalPage],
);
const connectGateway = useCallback(
@ -757,35 +764,37 @@ export function OpenClawAssistantPane({
/>
<div className="flex flex-wrap items-center gap-2.5 border-b border-[color:var(--color-surface-border)] px-3 py-2.5">
<div className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)] px-2.5 py-1 text-[11px] font-medium text-[var(--color-text-subtle)]">
<span
className={cn(
"h-2.5 w-2.5 rounded-full",
connectionState === "ready"
? "bg-emerald-500"
: connectionState === "connecting"
? "bg-amber-400"
: connectionState === "error"
? "bg-rose-500"
: "bg-[var(--color-text-subtle)]/40",
)}
/>
{healthBadge}
{!compactConnected ? (
<>
<span className="text-[var(--color-text-subtle)]/60">·</span>
{gatewayTokenSource === "env"
? copy.envToken
: gatewayTokenSource === "vault"
? copy.vaultToken
: gatewayTokenSource === "request"
? copy.sessionToken
: copy.noToken}
</>
) : null}
</div>
{!minimalPage ? (
<div className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)] px-2.5 py-1 text-[11px] font-medium text-[var(--color-text-subtle)]">
<span
className={cn(
"h-2.5 w-2.5 rounded-full",
connectionState === "ready"
? "bg-emerald-500"
: connectionState === "connecting"
? "bg-amber-400"
: connectionState === "error"
? "bg-rose-500"
: "bg-[var(--color-text-subtle)]/40",
)}
/>
{healthBadge}
{!compactConnected ? (
<>
<span className="text-[var(--color-text-subtle)]/60">·</span>
{gatewayTokenSource === "env"
? copy.envToken
: gatewayTokenSource === "vault"
? copy.vaultToken
: gatewayTokenSource === "request"
? copy.sessionToken
: copy.noToken}
</>
) : null}
</div>
) : null}
<div className="min-w-[164px] flex-1">
<div className={cn("min-w-[164px] flex-1", minimalPage ? "max-w-xl" : "")}>
<select
value={selectedAgentId}
onChange={(event) => {
@ -805,35 +814,55 @@ export function OpenClawAssistantPane({
</select>
</div>
<button
type="button"
onClick={() => {
void connectGateway();
}}
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-3 py-1.5 text-[11px] font-semibold text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
title={copy.reconnect}
>
{connectionState === "connecting" ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<RefreshCw className="h-3.5 w-3.5" />
)}
{!compactConnected ? copy.reconnect : null}
</button>
{minimalPage ? (
<div className="ml-auto inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-primary-muted)] px-3 py-1.5 text-xs font-semibold text-[var(--color-heading)]">
<span
className={cn(
"h-2.5 w-2.5 rounded-full",
connectionState === "ready"
? "bg-emerald-500"
: connectionState === "connecting"
? "bg-amber-400"
: connectionState === "error"
? "bg-rose-500"
: "bg-[var(--color-text-subtle)]/40",
)}
/>
{healthBadge}
</div>
) : (
<>
<button
type="button"
onClick={() => {
void connectGateway();
}}
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-3 py-1.5 text-[11px] font-semibold text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
title={copy.reconnect}
>
{connectionState === "connecting" ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<RefreshCw className="h-3.5 w-3.5" />
)}
{!compactConnected ? copy.reconnect : null}
</button>
<button
type="button"
onClick={() => router.push("/panel/api")}
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-primary-border)] bg-[var(--color-primary-muted)] px-3 py-1.5 text-[11px] font-semibold text-[var(--color-primary)] transition hover:opacity-90"
title={copy.integrations}
>
<Settings2 className="h-3.5 w-3.5" />
{!compactConnected ? copy.integrations : null}
</button>
<button
type="button"
onClick={() => router.push("/panel/api")}
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-primary-border)] bg-[var(--color-primary-muted)] px-3 py-1.5 text-[11px] font-semibold text-[var(--color-primary)] transition hover:opacity-90"
title={copy.integrations}
>
<Settings2 className="h-3.5 w-3.5" />
{!compactConnected ? copy.integrations : null}
</button>
</>
)}
</div>
<div className="flex min-h-0 flex-1 flex-col">
{!compact ? (
{!compact && !minimalPage ? (
<div className="border-b border-[color:var(--color-surface-border)] px-3 py-2.5">
<div className="flex flex-wrap gap-2">
{sessions.slice(0, 8).map((session) => (
@ -895,7 +924,7 @@ export function OpenClawAssistantPane({
{compact ? copy.compactHint : copy.assistantHint}
</p>
</div>
{!compact ? (
{!compact && !minimalPage ? (
<div className="flex flex-wrap justify-center gap-2">
{quickActions.map((action) => (
<button
@ -940,7 +969,7 @@ export function OpenClawAssistantPane({
)}
dangerouslySetInnerHTML={{ __html: message.html }}
/>
{message.timestampMs && !compact ? (
{message.timestampMs && !compact && !minimalPage ? (
<p
className={cn(
"mt-2 text-[11px]",
@ -1088,7 +1117,7 @@ export function OpenClawAssistantPane({
{copy.capturePage}
</button>
{!compact ? (
{!compact && !minimalPage ? (
<div className="ml-auto flex items-center gap-2 text-[11px] text-[var(--color-text-subtle)]">
<Link2 className="h-3.5 w-3.5" />
<span>

View File

@ -280,9 +280,6 @@ function SidebarSectionButton({
{!collapsed ? (
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-semibold">{section.label}</p>
<p className="mt-0.5 line-clamp-2 text-[11px] text-[var(--color-text-subtle)]">
{section.description}
</p>
</div>
) : null}
</button>
@ -418,7 +415,7 @@ export function XWorkmateWorkspacePage({
useState<WorkspaceDestination>("assistant");
const [activeTabs, setActiveTabs] =
useState<Record<WorkspaceDestination, string>>(INITIAL_TABS);
const [sidebarState, setSidebarState] = useState<SidebarState>("collapsed");
const [sidebarState, setSidebarState] = useState<SidebarState>("expanded");
const { resolvedTheme, toggleTheme, isDark } = useTheme();
const user = useUserStore((state) => state.user);
@ -813,141 +810,6 @@ export function XWorkmateWorkspacePage({
return <OpenClawAssistantPane defaults={defaults} variant="page" />;
}
function renderAssistantSidebarContent(): ReactNode {
return (
<div className="space-y-4">
<Card className="space-y-4">
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-[var(--color-text-subtle)]">
XWorkmate
</p>
<h2 className="text-xl font-semibold text-[var(--color-heading)]">
{pickCopy(
isChinese,
"在线助手工作台",
"Online assistant workspace",
)}
</h2>
<p className="text-sm text-[var(--color-text-subtle)]">
{pickCopy(
isChinese,
"和桌面端一样主屏负责对话、截图、附件和执行反馈OpenClaw 只保留为底层 gateway。",
"Like desktop, the main workspace owns chat, screenshots, attachments, and execution feedback while OpenClaw remains the underlying gateway.",
)}
</p>
</div>
<div className="grid gap-3">
{integrationRows.map((row) => (
<StatusRow
key={row.label}
label={row.label}
value={row.value}
ok={row.ok}
/>
))}
</div>
</Card>
<SurfaceCard
icon={Workflow}
title={pickCopy(
isChinese,
"截图仍然走聊天模式",
"Screenshots stay in assistant chat mode",
)}
description={pickCopy(
isChinese,
"不再套壳 browser automation/control UI。",
"No browser automation shell or separate control UI.",
)}
body={pickCopy(
isChinese,
"当前页截图会直接作为附件进入会话,和文本、日志、图片共用同一条 assistant 流程。",
"Current-page screenshots enter the same assistant flow as text, logs, and images.",
)}
action={
<button
type="button"
onClick={() => setActiveSection("tasks")}
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-4 py-2 text-sm font-medium text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
>
{pickCopy(isChinese, "查看任务视图", "Open tasks view")}
<ArrowRight className="h-4 w-4" />
</button>
}
/>
<SurfaceCard
icon={ShieldCheck}
title={pickCopy(
isChinese,
"Pairing 优先,其次 token",
"Pairing first, token second",
)}
description={pickCopy(
isChinese,
"已经接入 challenge / device token 模式。",
"The challenge / device token pairing flow is active.",
)}
body={pickCopy(
isChinese,
"首次连接可以使用 shared token 建链,后续回落到 device token。认证失配时会自动提示重新配对。",
"The first connection can use a shared token, then fall back to a stored device token. Pairing guidance appears automatically if auth drifts.",
)}
action={
<button
type="button"
onClick={() => {
setActiveSection("settings");
setActiveTabs((current) => ({
...current,
settings: "integrations",
}));
}}
className="inline-flex items-center gap-2 rounded-full bg-[var(--color-primary)] px-4 py-2 text-sm font-semibold text-[var(--color-primary-foreground)]"
>
{pickCopy(
isChinese,
"打开融合设置",
"Open integration settings",
)}
<ChevronRight className="h-4 w-4" />
</button>
}
/>
<SurfaceCard
icon={Waypoints}
title={pickCopy(isChinese, "工作区默认值", "Workspace defaults")}
description={pickCopy(
isChinese,
"当前会话状态",
"Current session state",
)}
body={`${pickCopy(isChinese, "模式", "Mode")}: ${assistantMode} · ${pickCopy(isChinese, "推理", "Reasoning")}: ${thinking} · ${pickCopy(isChinese, "会话", "Session")}: ${selectedSessionKey || "main"}`}
action={
<div className="flex flex-wrap gap-2">
<Link
href="/panel/api"
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-4 py-2 text-sm font-medium text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
>
API
<ArrowRight className="h-4 w-4" />
</Link>
<Link
href="/panel"
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-4 py-2 text-sm font-medium text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
>
Console
<ArrowRight className="h-4 w-4" />
</Link>
</div>
}
/>
</div>
);
}
function renderTasksSection(): ReactNode {
if (activeTab === "running") {
return (
@ -1951,11 +1813,6 @@ export function XWorkmateWorkspacePage({
/>
))}
{!collapsed && activeSection === "assistant" ? (
<div className="space-y-3 border-t border-[color:var(--color-surface-border)] pt-4">
{renderAssistantSidebarContent()}
</div>
) : null}
</div>
<div className="mt-4 space-y-2 border-t border-[color:var(--color-surface-border)] pt-4">
@ -2084,31 +1941,35 @@ export function XWorkmateWorkspacePage({
{pickCopy(isChinese, "收展侧栏", "Toggle sidebar")}
</button>
) : null}
<Link
href="/panel"
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-3.5 py-2 text-sm font-medium text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
>
Console
<ArrowRight className="h-4 w-4" />
</Link>
<button
type="button"
onClick={() => {
setActiveSection("settings");
setActiveTabs((current) => ({
...current,
settings: "integrations",
}));
}}
className="inline-flex items-center gap-2 rounded-full bg-[var(--color-primary)] px-3.5 py-2 text-sm font-semibold text-[var(--color-primary-foreground)]"
>
{pickCopy(isChinese, "融合设置", "Integration settings")}
<ArrowRight className="h-4 w-4" />
</button>
{activeSection !== "assistant" ? (
<>
<Link
href="/panel"
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-3.5 py-2 text-sm font-medium text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
>
Console
<ArrowRight className="h-4 w-4" />
</Link>
<button
type="button"
onClick={() => {
setActiveSection("settings");
setActiveTabs((current) => ({
...current,
settings: "integrations",
}));
}}
className="inline-flex items-center gap-2 rounded-full bg-[var(--color-primary)] px-3.5 py-2 text-sm font-semibold text-[var(--color-primary-foreground)]"
>
{pickCopy(isChinese, "融合设置", "Integration settings")}
<ArrowRight className="h-4 w-4" />
</button>
</>
) : null}
</div>
</div>
{activeDefinition.tabs.length > 0 ? (
{activeDefinition.tabs.length > 0 && activeSection !== "assistant" ? (
<div className="mt-3 flex flex-wrap gap-2">
{activeDefinition.tabs.map((tab) => (
<button