refine xworkmate assistant layout density
This commit is contained in:
parent
2ebecc8b43
commit
593adb0452
@ -694,7 +694,7 @@ export function OpenClawAssistantPane({
|
||||
}
|
||||
|
||||
const containerClassName = cn(
|
||||
"flex h-full min-h-0 flex-col overflow-hidden rounded-[var(--radius-2xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] shadow-[var(--shadow-md)]",
|
||||
"flex h-full min-h-0 flex-col overflow-hidden rounded-[var(--radius-xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] shadow-[var(--shadow-md)]",
|
||||
compact ? "rounded-none border-0 shadow-none" : "",
|
||||
);
|
||||
|
||||
@ -711,8 +711,8 @@ export function OpenClawAssistantPane({
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-3 border-b border-[color:var(--color-surface-border)] px-4 py-3">
|
||||
<div className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)] px-3 py-1 text-xs font-medium text-[var(--color-text-subtle)]">
|
||||
<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",
|
||||
@ -734,7 +734,7 @@ export function OpenClawAssistantPane({
|
||||
: copy.noToken}
|
||||
</div>
|
||||
|
||||
<div className="min-w-[180px] flex-1">
|
||||
<div className="min-w-[164px] flex-1">
|
||||
<select
|
||||
value={selectedAgentId}
|
||||
onChange={(event) => {
|
||||
@ -742,7 +742,7 @@ export function OpenClawAssistantPane({
|
||||
setSelectedSessionKey("");
|
||||
void connectGateway("", event.target.value);
|
||||
}}
|
||||
className="w-full rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-3 py-2 text-sm text-[var(--color-text)] outline-none transition focus:border-[color:var(--color-primary)]"
|
||||
className="w-full rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-3 py-1.5 text-sm text-[var(--color-text)] outline-none transition focus:border-[color:var(--color-primary)]"
|
||||
>
|
||||
<option value="">{copy.mainAgent}</option>
|
||||
{agents.map((agent) => (
|
||||
@ -759,7 +759,7 @@ export function OpenClawAssistantPane({
|
||||
onClick={() => {
|
||||
void connectGateway();
|
||||
}}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-3 py-2 text-xs font-semibold text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
|
||||
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)]"
|
||||
>
|
||||
{connectionState === "connecting" ? (
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
@ -772,7 +772,7 @@ export function OpenClawAssistantPane({
|
||||
<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-2 text-xs font-semibold text-[var(--color-primary)] transition hover:opacity-90"
|
||||
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"
|
||||
>
|
||||
<Settings2 className="h-3.5 w-3.5" />
|
||||
{copy.integrations}
|
||||
@ -780,7 +780,7 @@ export function OpenClawAssistantPane({
|
||||
</div>
|
||||
|
||||
<div className="flex min-h-0 flex-1 flex-col">
|
||||
<div className="border-b border-[color:var(--color-surface-border)] px-4 py-3">
|
||||
<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, compact ? 4 : 8).map((session) => (
|
||||
<button
|
||||
@ -791,7 +791,7 @@ export function OpenClawAssistantPane({
|
||||
void connectGateway(session.key);
|
||||
}}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-xs transition",
|
||||
"inline-flex items-center gap-2 rounded-full border px-2.5 py-1 text-[11px] transition",
|
||||
session.key === selectedSessionKey
|
||||
? "border-[color:var(--color-primary)] bg-[var(--color-primary-muted)] text-[var(--color-primary)]"
|
||||
: "border-[color:var(--color-surface-border)] bg-[var(--color-surface)] text-[var(--color-text-subtle)] hover:border-[color:var(--color-primary-border)]",
|
||||
@ -806,10 +806,10 @@ export function OpenClawAssistantPane({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto px-4 py-4">
|
||||
<div className="flex-1 overflow-y-auto px-3 py-3">
|
||||
{!openclawUrl.trim() ? (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-4 rounded-[var(--radius-2xl)] border border-dashed border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)]/40 px-6 text-center">
|
||||
<Sparkles className="h-8 w-8 text-[var(--color-primary)]" />
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 rounded-[var(--radius-xl)] border border-dashed border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)]/40 px-5 text-center">
|
||||
<Sparkles className="h-7 w-7 text-[var(--color-primary)]" />
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-base font-semibold text-[var(--color-heading)]">
|
||||
{copy.configureGateway}
|
||||
@ -821,16 +821,16 @@ export function OpenClawAssistantPane({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push("/panel/api")}
|
||||
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)]"
|
||||
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)]"
|
||||
>
|
||||
{copy.openIntegrations}
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
) : renderedMessages.length === 0 && !streamingText ? (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-5 rounded-[var(--radius-2xl)] border border-dashed border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)]/40 px-6 text-center">
|
||||
<div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-[var(--color-primary-muted)] text-[var(--color-primary)]">
|
||||
<Sparkles className="h-7 w-7" />
|
||||
<div className="flex h-full flex-col items-center justify-center gap-4 rounded-[var(--radius-xl)] border border-dashed border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)]/40 px-5 text-center">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-[var(--radius-lg)] bg-[var(--color-primary-muted)] text-[var(--color-primary)]">
|
||||
<Sparkles className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-base font-semibold text-[var(--color-heading)]">
|
||||
@ -857,7 +857,7 @@ export function OpenClawAssistantPane({
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-3">
|
||||
{renderedMessages.map((message) => {
|
||||
const isUser = message.role === "user";
|
||||
return (
|
||||
@ -870,7 +870,7 @@ export function OpenClawAssistantPane({
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"max-w-[88%] rounded-[var(--radius-xl)] px-4 py-3 shadow-[var(--shadow-sm)]",
|
||||
"max-w-[84%] rounded-[var(--radius-lg)] px-3 py-2.5 shadow-[var(--shadow-sm)]",
|
||||
isUser
|
||||
? "bg-[var(--color-primary)] text-[var(--color-primary-foreground)]"
|
||||
: "border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] text-[var(--color-text)]",
|
||||
@ -902,7 +902,7 @@ export function OpenClawAssistantPane({
|
||||
|
||||
{streamingText ? (
|
||||
<div className="flex justify-start">
|
||||
<div className="max-w-[88%] rounded-[var(--radius-xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-4 py-3 text-[var(--color-text)] shadow-[var(--shadow-sm)]">
|
||||
<div className="max-w-[84%] rounded-[var(--radius-lg)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-3 py-2.5 text-[var(--color-text)] shadow-[var(--shadow-sm)]">
|
||||
<div
|
||||
className="prose prose-sm max-w-none break-words whitespace-pre-wrap"
|
||||
dangerouslySetInnerHTML={{
|
||||
@ -916,7 +916,7 @@ export function OpenClawAssistantPane({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="border-t border-[color:var(--color-surface-border)] px-4 py-4">
|
||||
<div className="border-t border-[color:var(--color-surface-border)] px-3 py-3">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{modeOptions.map((option) => (
|
||||
<button
|
||||
@ -924,7 +924,7 @@ export function OpenClawAssistantPane({
|
||||
type="button"
|
||||
onClick={() => setAssistantMode(option.value)}
|
||||
className={cn(
|
||||
"rounded-full px-3 py-1.5 text-xs font-semibold transition",
|
||||
"rounded-full px-2.5 py-1 text-[11px] font-semibold transition",
|
||||
assistantMode === option.value
|
||||
? "bg-[var(--color-primary)] text-[var(--color-primary-foreground)]"
|
||||
: "border border-[color:var(--color-surface-border)] text-[var(--color-text-subtle)] hover:border-[color:var(--color-primary-border)]",
|
||||
@ -934,7 +934,7 @@ export function OpenClawAssistantPane({
|
||||
</button>
|
||||
))}
|
||||
|
||||
<div className="ml-auto flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-3 py-1.5 text-xs text-[var(--color-text-subtle)]">
|
||||
<div className="ml-auto flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-2.5 py-1 text-[11px] text-[var(--color-text-subtle)]">
|
||||
<BrainCircuit className="h-3.5 w-3.5" />
|
||||
<select
|
||||
value={thinking}
|
||||
@ -953,11 +953,11 @@ export function OpenClawAssistantPane({
|
||||
</div>
|
||||
|
||||
{attachments.length > 0 ? (
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<div className="mt-2.5 flex flex-wrap gap-2">
|
||||
{attachments.map((attachment) => (
|
||||
<div
|
||||
key={attachment.id}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)] px-3 py-1.5 text-xs text-[var(--color-text)]"
|
||||
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] text-[var(--color-text)]"
|
||||
>
|
||||
{attachment.type === "image" ? (
|
||||
<Camera className="h-3.5 w-3.5" />
|
||||
@ -982,15 +982,15 @@ export function OpenClawAssistantPane({
|
||||
) : null}
|
||||
|
||||
{errorMessage ? (
|
||||
<div className="mt-3 rounded-[var(--radius-xl)] border border-[color:var(--color-danger-border)] bg-[var(--color-danger-muted)]/40 px-3 py-2 text-sm text-[var(--color-danger-foreground)]">
|
||||
<div className="mt-2.5 rounded-[var(--radius-lg)] border border-[color:var(--color-danger-border)] bg-[var(--color-danger-muted)]/40 px-3 py-2 text-sm text-[var(--color-danger-foreground)]">
|
||||
{errorMessage}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="mt-3 rounded-[var(--radius-2xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] p-3 shadow-[var(--shadow-sm)]">
|
||||
<div className="mt-2.5 rounded-[var(--radius-xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] p-2.5 shadow-[var(--shadow-sm)]">
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
rows={compact ? 4 : 5}
|
||||
rows={compact ? 3 : 4}
|
||||
value={composerValue}
|
||||
placeholder={copy.placeholder}
|
||||
onChange={(event) => setComposerValue(event.target.value)}
|
||||
@ -1005,11 +1005,11 @@ export function OpenClawAssistantPane({
|
||||
className="w-full resize-none bg-transparent text-sm leading-6 text-[var(--color-text)] outline-none placeholder:text-[var(--color-text-subtle)]/70"
|
||||
/>
|
||||
|
||||
<div className="mt-3 flex flex-wrap items-center gap-2">
|
||||
<div className="mt-2.5 flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-3 py-1.5 text-xs font-semibold text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-2.5 py-1 text-[11px] font-semibold text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)]"
|
||||
>
|
||||
<Paperclip className="h-3.5 w-3.5" />
|
||||
{copy.attachment}
|
||||
@ -1021,7 +1021,7 @@ export function OpenClawAssistantPane({
|
||||
void capturePage();
|
||||
}}
|
||||
disabled={isCapturing}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-3 py-1.5 text-xs font-semibold text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)] disabled:cursor-not-allowed disabled:opacity-60"
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] px-2.5 py-1 text-[11px] font-semibold text-[var(--color-text)] transition hover:border-[color:var(--color-primary-border)] hover:bg-[var(--color-surface-muted)] disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
{isCapturing ? (
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
@ -1031,7 +1031,7 @@ export function OpenClawAssistantPane({
|
||||
{copy.capturePage}
|
||||
</button>
|
||||
|
||||
<div className="ml-auto flex items-center gap-2 text-xs text-[var(--color-text-subtle)]">
|
||||
<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>
|
||||
{activeSession?.derivedTitle ||
|
||||
@ -1050,7 +1050,7 @@ export function OpenClawAssistantPane({
|
||||
isSending ||
|
||||
(!composerValue.trim() && attachments.length === 0)
|
||||
}
|
||||
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)] transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
className="inline-flex items-center gap-2 rounded-full bg-[var(--color-primary)] px-3.5 py-1.5 text-sm font-semibold text-[var(--color-primary-foreground)] transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
>
|
||||
{isSending ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
|
||||
@ -259,7 +259,7 @@ function SidebarSectionButton({
|
||||
onClick={onClick}
|
||||
title={section.label}
|
||||
className={cn(
|
||||
"group flex w-full items-center gap-3 rounded-[var(--radius-xl)] border px-3 py-3 text-left transition",
|
||||
"group flex w-full items-center gap-2.5 rounded-[var(--radius-lg)] border px-2.5 py-2.5 text-left transition",
|
||||
active
|
||||
? "border-[color:var(--color-primary-border)] bg-[var(--color-primary-muted)] text-[var(--color-primary)] shadow-[var(--shadow-sm)]"
|
||||
: "border-transparent text-[var(--color-text-subtle)] hover:border-[color:var(--color-surface-border)] hover:bg-[var(--color-surface)] hover:text-[var(--color-text)]",
|
||||
@ -268,19 +268,19 @@ function SidebarSectionButton({
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl border transition",
|
||||
"flex h-9 w-9 shrink-0 items-center justify-center rounded-[var(--radius-lg)] border transition",
|
||||
active
|
||||
? "border-[color:var(--color-primary-border)] bg-white/80 text-[var(--color-primary)]"
|
||||
: "border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)] text-[var(--color-text-subtle)] group-hover:text-[var(--color-text)]",
|
||||
)}
|
||||
>
|
||||
<Icon className="h-5 w-5" />
|
||||
<Icon className="h-4.5 w-4.5" />
|
||||
</div>
|
||||
|
||||
{!collapsed ? (
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-semibold">{section.label}</p>
|
||||
<p className="mt-1 line-clamp-2 text-xs text-[var(--color-text-subtle)]">
|
||||
<p className="mt-0.5 line-clamp-2 text-[11px] text-[var(--color-text-subtle)]">
|
||||
{section.description}
|
||||
</p>
|
||||
</div>
|
||||
@ -302,7 +302,7 @@ function UtilityButton({
|
||||
onClick={onClick}
|
||||
title={label}
|
||||
className={cn(
|
||||
"flex w-full items-center gap-3 rounded-[var(--radius-xl)] border px-3 py-2.5 text-sm font-medium transition",
|
||||
"flex w-full items-center gap-2.5 rounded-[var(--radius-lg)] border px-2.5 py-2 text-sm font-medium transition",
|
||||
active
|
||||
? "border-[color:var(--color-primary-border)] bg-[var(--color-primary-muted)] text-[var(--color-primary)]"
|
||||
: "border-transparent text-[var(--color-text-subtle)] hover:border-[color:var(--color-surface-border)] hover:bg-[var(--color-surface)] hover:text-[var(--color-text)]",
|
||||
@ -323,22 +323,22 @@ function SurfaceCard({
|
||||
action,
|
||||
}: SurfaceCardProps) {
|
||||
return (
|
||||
<Card className="space-y-4 bg-[var(--color-surface)]">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl bg-[var(--color-primary-muted)] text-[var(--color-primary)]">
|
||||
<Icon className="h-5 w-5" />
|
||||
<Card className="space-y-3 bg-[var(--color-surface)] p-4">
|
||||
<div className="flex items-start gap-2.5">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-[var(--radius-lg)] bg-[var(--color-primary-muted)] text-[var(--color-primary)]">
|
||||
<Icon className="h-4.5 w-4.5" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<h3 className="text-base font-semibold text-[var(--color-heading)]">
|
||||
<h3 className="text-sm font-semibold text-[var(--color-heading)]">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-sm text-[var(--color-text-subtle)]">
|
||||
<p className="text-xs text-[var(--color-text-subtle)]">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{body ? (
|
||||
<p className="text-sm leading-6 text-[var(--color-text)]">{body}</p>
|
||||
<p className="text-sm leading-5 text-[var(--color-text)]">{body}</p>
|
||||
) : null}
|
||||
{action ? <div>{action}</div> : null}
|
||||
</Card>
|
||||
@ -347,14 +347,14 @@ function SurfaceCard({
|
||||
|
||||
function StatusRow({ label, value, ok }: StatusRowProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-3 rounded-[var(--radius-xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-4 py-3">
|
||||
<div className="flex items-center justify-between gap-3 rounded-[var(--radius-lg)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-3 py-2.5">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[var(--color-text)]">{label}</p>
|
||||
<p className="mt-1 text-xs text-[var(--color-text-subtle)]">{value}</p>
|
||||
<p className="text-xs font-medium text-[var(--color-text)]">{label}</p>
|
||||
<p className="mt-0.5 text-[11px] text-[var(--color-text-subtle)]">{value}</p>
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold",
|
||||
"inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-semibold",
|
||||
ok
|
||||
? "bg-emerald-500/10 text-emerald-600"
|
||||
: "bg-[var(--color-surface-muted)] text-[var(--color-text-subtle)]",
|
||||
@ -374,29 +374,29 @@ function StatusRow({ label, value, ok }: StatusRowProps) {
|
||||
|
||||
function OverviewMetrics({ items }: { items: OverviewMetric[] }) {
|
||||
return (
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
|
||||
{items.map((item) => {
|
||||
const Icon = item.icon;
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={`${item.label}-${item.value}`}
|
||||
className="space-y-3 border-[color:var(--color-surface-border)] bg-[var(--color-surface)] p-5"
|
||||
className="space-y-2.5 border-[color:var(--color-surface-border)] bg-[var(--color-surface)] p-4"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-11 w-11 items-center justify-center rounded-2xl bg-[var(--color-primary-muted)] text-[var(--color-primary)]">
|
||||
<Icon className="h-5 w-5" />
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-[var(--radius-lg)] bg-[var(--color-primary-muted)] text-[var(--color-primary)]">
|
||||
<Icon className="h-4.5 w-4.5" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-[var(--color-text-subtle)]">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[var(--color-text-subtle)]">
|
||||
{item.label}
|
||||
</p>
|
||||
<p className="mt-1 text-lg font-semibold text-[var(--color-heading)]">
|
||||
<p className="mt-0.5 text-base font-semibold text-[var(--color-heading)]">
|
||||
{item.value}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-[var(--color-text-subtle)]">
|
||||
<p className="text-xs text-[var(--color-text-subtle)]">
|
||||
{item.caption}
|
||||
</p>
|
||||
</Card>
|
||||
@ -418,7 +418,7 @@ export function XWorkmateWorkspacePage({
|
||||
useState<WorkspaceDestination>("assistant");
|
||||
const [activeTabs, setActiveTabs] =
|
||||
useState<Record<WorkspaceDestination, string>>(INITIAL_TABS);
|
||||
const [sidebarState, setSidebarState] = useState<SidebarState>("expanded");
|
||||
const [sidebarState, setSidebarState] = useState<SidebarState>("collapsed");
|
||||
const { resolvedTheme, toggleTheme, isDark } = useTheme();
|
||||
const user = useUserStore((state) => state.user);
|
||||
|
||||
@ -785,142 +785,140 @@ export function XWorkmateWorkspacePage({
|
||||
}
|
||||
|
||||
function renderAssistantSection(): ReactNode {
|
||||
return <OpenClawAssistantPane defaults={defaults} variant="page" />;
|
||||
}
|
||||
|
||||
function renderAssistantSidebarContent(): ReactNode {
|
||||
return (
|
||||
<div className="grid h-full min-h-0 gap-6 xl:grid-cols-[minmax(0,1.35fr)_360px]">
|
||||
<div className="min-h-0">
|
||||
<OpenClawAssistantPane defaults={defaults} variant="page" />
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<div className="space-y-4 overflow-y-auto">
|
||||
<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={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")}
|
||||
<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)]"
|
||||
>
|
||||
{pickCopy(isChinese, "查看任务视图", "Open tasks view")}
|
||||
API
|
||||
<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)]"
|
||||
</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)]"
|
||||
>
|
||||
{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>
|
||||
Console
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1868,7 +1866,7 @@ export function XWorkmateWorkspacePage({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative h-full min-h-0 overflow-hidden rounded-[var(--radius-2xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] shadow-[var(--shadow-md)]">
|
||||
<div className="relative h-full min-h-0 overflow-hidden rounded-[var(--radius-xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] shadow-[var(--shadow-md)]">
|
||||
{sidebarState === "hidden" ? (
|
||||
<div className="pointer-events-none absolute inset-y-0 left-0 z-10 flex items-start p-4">
|
||||
<button
|
||||
@ -1886,25 +1884,25 @@ export function XWorkmateWorkspacePage({
|
||||
{sidebarState !== "hidden" ? (
|
||||
<aside
|
||||
className={cn(
|
||||
"flex h-full shrink-0 flex-col border-r border-[color:var(--color-surface-border)] bg-[var(--color-background)]/90 px-3 py-4 backdrop-blur transition-[width] duration-200",
|
||||
collapsed ? "w-[96px]" : "w-[310px]",
|
||||
"flex h-full shrink-0 flex-col border-r border-[color:var(--color-surface-border)] bg-[var(--color-background)]/90 px-2.5 py-3 backdrop-blur transition-[width] duration-200",
|
||||
collapsed ? "w-[88px]" : "w-[292px]",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-[var(--radius-xl)] px-2 py-2",
|
||||
"flex items-center gap-2.5 rounded-[var(--radius-lg)] px-2 py-1.5",
|
||||
collapsed ? "justify-center" : "",
|
||||
)}
|
||||
>
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-[var(--color-primary-muted)] text-[var(--color-primary)]">
|
||||
<Sparkles className="h-6 w-6" />
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-[var(--radius-lg)] bg-[var(--color-primary-muted)] text-[var(--color-primary)]">
|
||||
<Sparkles className="h-5 w-5" />
|
||||
</div>
|
||||
{!collapsed ? (
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-lg font-semibold text-[var(--color-heading)]">
|
||||
<p className="truncate text-base font-semibold text-[var(--color-heading)]">
|
||||
XWorkmate
|
||||
</p>
|
||||
<p className="mt-0.5 truncate text-xs text-[var(--color-text-subtle)]">
|
||||
<p className="mt-0.5 truncate text-[11px] text-[var(--color-text-subtle)]">
|
||||
{pickCopy(
|
||||
isChinese,
|
||||
"Online Workspace",
|
||||
@ -1925,6 +1923,12 @@ export function XWorkmateWorkspacePage({
|
||||
onClick={() => setActiveSection(section.key)}
|
||||
/>
|
||||
))}
|
||||
|
||||
{!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">
|
||||
@ -1967,11 +1971,11 @@ export function XWorkmateWorkspacePage({
|
||||
onClick={() => setActiveSection("account")}
|
||||
title={accountName}
|
||||
className={cn(
|
||||
"mt-2 flex w-full items-center gap-3 rounded-[var(--radius-xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-3 py-3 text-left transition hover:border-[color:var(--color-primary-border)]",
|
||||
"mt-2 flex w-full items-center gap-2.5 rounded-[var(--radius-lg)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface)] px-2.5 py-2.5 text-left transition hover:border-[color:var(--color-primary-border)]",
|
||||
collapsed ? "justify-center px-2" : "",
|
||||
)}
|
||||
>
|
||||
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl bg-[var(--color-primary-muted)] text-sm font-semibold text-[var(--color-primary)]">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-[var(--radius-lg)] bg-[var(--color-primary-muted)] text-sm font-semibold text-[var(--color-primary)]">
|
||||
{(
|
||||
user?.username?.charAt(0) ||
|
||||
accountName.charAt(0) ||
|
||||
@ -1994,19 +1998,26 @@ export function XWorkmateWorkspacePage({
|
||||
) : null}
|
||||
|
||||
<section className="flex min-w-0 flex-1 flex-col">
|
||||
<header className="border-b border-[color:var(--color-surface-border)] px-5 py-4">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="space-y-2">
|
||||
<div className="inline-flex items-center gap-2 rounded-full border border-[color:var(--color-surface-border)] bg-[var(--color-surface-muted)] px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] text-[var(--color-text-subtle)]">
|
||||
<header
|
||||
className={cn(
|
||||
"border-b border-[color:var(--color-surface-border)] px-4",
|
||||
activeSection === "assistant" ? "py-3" : "py-4",
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className={cn("space-y-2", activeSection === "assistant" ? "space-y-1.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-semibold uppercase tracking-[0.16em] text-[var(--color-text-subtle)]">
|
||||
{pickCopy(isChinese, "XWorkmate 在线版", "XWorkmate Online")}
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold text-[var(--color-heading)]">
|
||||
<h1 className={cn("font-semibold text-[var(--color-heading)]", activeSection === "assistant" ? "text-xl" : "text-2xl")}>
|
||||
{activeDefinition.label}
|
||||
</h1>
|
||||
<p className="mt-2 max-w-3xl text-sm text-[var(--color-text-subtle)]">
|
||||
{activeDefinition.description}
|
||||
</p>
|
||||
{activeSection !== "assistant" ? (
|
||||
<p className="mt-2 max-w-3xl text-sm text-[var(--color-text-subtle)]">
|
||||
{activeDefinition.description}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2019,7 +2030,7 @@ export function XWorkmateWorkspacePage({
|
||||
current === "expanded" ? "collapsed" : "expanded",
|
||||
)
|
||||
}
|
||||
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)]"
|
||||
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)]"
|
||||
>
|
||||
{collapsed ? (
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
@ -2031,7 +2042,7 @@ export function XWorkmateWorkspacePage({
|
||||
) : null}
|
||||
<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)]"
|
||||
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" />
|
||||
@ -2045,7 +2056,7 @@ export function XWorkmateWorkspacePage({
|
||||
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)]"
|
||||
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" />
|
||||
@ -2074,8 +2085,13 @@ export function XWorkmateWorkspacePage({
|
||||
) : null}
|
||||
</header>
|
||||
|
||||
<div className="min-h-0 flex-1 overflow-auto p-5">
|
||||
<div className="space-y-5">
|
||||
<div
|
||||
className={cn(
|
||||
"min-h-0 flex-1 p-4",
|
||||
activeSection === "assistant" ? "overflow-hidden" : "overflow-auto",
|
||||
)}
|
||||
>
|
||||
<div className={cn("space-y-4", activeSection === "assistant" ? "h-full min-h-0" : "")}>
|
||||
{renderSectionOverview()}
|
||||
{renderMainContent()}
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user