Simplify XWorkmate assistant workspace chrome
This commit is contained in:
parent
4d0534dd47
commit
022478a11b
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user