Files
claude-code/packages/remote-control-server/web/src/index.css
claude-code-best 34154ee3f5 feat: 支持 acp-link 包进行 acp 通用的 remote-control (#292)
* fix: 修复超时问题

* feat: 添加 acp-link 代码

* refactor: 样式重构完成

* feat: RCS 添加 ACP 后端支持

- 新增 ACP WebSocket handler (agent 注册、EventBus 订阅)
- 新增 relay handler (前端 WS → acp-link 透传 + EventBus inbound 转发)
- 新增 SSE event stream 供外部消费者订阅 channel group 事件
- ACP REST 接口无鉴权 (agents、channel-groups)
- WebSocket 端点保留 token 鉴权
- SPA 路由 /acp/ 指向 acp.html

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: 添加 ACP 专属前端界面

- 新增 /acp/ SPA 页面 (agent 列表 + 实时交互)
- Agent 列表按 channel group 分组,显示在线状态
- 通过 RCS WebSocket relay 与 agent 通信
- Vite multi-page 构建 (index.html + acp.html)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: acp-link 支持 RCS relay 双向通信

- rcs-upstream 新增 messageHandler 转发非控制消息
- server.ts 新增虚拟 WS + relay client state 处理 relay ACP 消息
- newSession/loadSession 补充 mcpServers 参数
- 连接成功后显示 ACP Dashboard URL

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 移除 FileExplorer 及文件操作相关代码

- 删除 FileExplorer 组件
- ACPMain 移除 Files tab,仅保留 Chat 和 History
- client.ts 移除 listDir/readFile/onFileChanges 等方法
- types.ts 移除 FileItem/FileContent/FileChange 等类型

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复类型问题

* feat: RCS 后端统一 ACP/Bridge 注册逻辑

- store: EnvironmentRecord 增加 capabilities 字段、storeFindEnvironmentByMachineName 复用逻辑
- store: 新增 storeGetSessionOwners,支持未绑定 session 自动 claim
- environment: registerEnvironment 支持 ACP 复用已有记录,返回 session_id
- session: resolveOwnedWebSessionId 支持无 owner session 自动绑定
- acp-ws-handler: 新增 handleIdentify 支持 REST+WS 两步注册
- acp routes: /acp/relay 和 /acp/agents 支持 UUID 认证
- event-bus: 增加 error 类型 payload 日志

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: acp-link 改 REST 注册 + WS identify 两步流程

- rcs-upstream: 新增 registerViaRest() 通过 POST /v1/environments/bridge 注册
- rcs-upstream: WS 连接后发送 identify 替代 register,携带 agentId
- rcs-upstream: 入口链接改为 /code/?sid=${sessionId} 实现用户绑定
- server: 修复心跳跳过 relay 虚拟连接的 bug
- server: maxSessions 配置传入 RCS upstream

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: 前端统一 Chat 组件 + ACP 聊天界面重构

- 新增 chat/ 组件: ChatView, ChatInput, MessageBubble, ToolCallGroup, PermissionPanel, SessionSidebar, CommandMenu
- ACPMain: 重构支持完整 ACP 协议交互(session/prompt/permission)
- rcs-chat-adapter: 统一 bridge session SSE 适配器
- ACPClient: 增强 session 管理、permission 流程、streaming 支持
- index.css: 新增 chat 相关样式、动画、布局
- useCommands: 新增快捷命令 hook

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 删除 /acp/ 独立页面,ACP 聊天统一到 /code/:sessionId

- 删除 acp.html、acp-main.tsx 入口文件和 pages/acp/ 目录
- SessionDetail: ACP session 在同一页面渲染 ACPSessionDetail 组件
- App.tsx: ?sid= 参数自动调用 apiBind 绑定用户 UUID
- Dashboard: 统一 session 列表导航,ACP 显示紫色标签
- relay-client: 改用 UUID 认证替代 API token
- EnvironmentList: 显示 workerType 标签(ACP Agent / Claude Code)
- index.ts: 移除 /acp/ SPA 路由,vite.config 移除 acp 入口

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* build: 更新构建及测试修复

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 17:59:29 +08:00

221 lines
6.3 KiB
CSS

/* Font imports must precede @import "tailwindcss" per CSS spec */
@import url("https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Poppins:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap");
@import "tailwindcss";
/* ============================================================
Theme — Anthropic-inspired warm design tokens
============================================================ */
@theme {
/* Fonts — Lora (serif body) + Poppins (sans-serif UI) + JetBrains Mono */
--font-sans: "Lora", Georgia, serif;
--font-display: "Poppins", sans-serif;
--font-mono: "JetBrains Mono", monospace;
/* Brand — signature orange */
--color-brand: #D97757;
--color-brand-light: #C96A4D;
--color-brand-subtle: rgba(217, 119, 87, 0.1);
/* Surfaces — warm beige palette (not white) */
--color-surface-0: #ECE9E0;
--color-surface-1: #F5F3EC;
--color-surface-2: #FDFCF8;
--color-surface-3: #E8E6DC;
/* Text — warm dark tones */
--color-text-primary: #141413;
--color-text-secondary: #6B6860;
--color-text-muted: #9C9890;
/* Inverted — for user message bubbles */
--color-bg-inverted: #2D2B27;
--color-text-inverted: #F5F3EC;
/* Warning — warm yellow for permission panels */
--color-warning-bg: #FEF3C7;
--color-warning-border: #F59E0B;
--color-warning-text: #92400E;
/* Status */
--color-status-active: #6B8F47;
--color-status-running: #4A7FB5;
--color-status-idle: #7C3aed;
--color-status-error: #C0453A;
--color-status-warning: #C9943A;
/* Tool card */
--color-tool-card: #F5F3EC;
/* shadcn/ui tokens (oklch — warm hue ~85) */
--color-background: oklch(0.94 0.01 85);
--color-foreground: oklch(0.20 0.01 85);
--color-card: oklch(0.97 0.008 85);
--color-card-foreground: oklch(0.20 0.01 85);
--color-popover: oklch(0.97 0.008 85);
--color-popover-foreground: oklch(0.20 0.01 85);
--color-primary: oklch(0.20 0.01 85);
--color-primary-foreground: oklch(0.96 0.01 85);
--color-secondary: oklch(0.95 0.01 85);
--color-secondary-foreground: oklch(0.20 0.01 85);
--color-muted: oklch(0.93 0.01 85);
--color-muted-foreground: oklch(0.50 0.02 85);
--color-accent: oklch(0.95 0.01 85);
--color-accent-foreground: oklch(0.20 0.01 85);
--color-destructive: oklch(0.577 0.245 27.325);
/* Border / Input / Ring */
--color-border: oklch(0.88 0.015 85);
--color-border-light: #E8E6DC;
--color-input: oklch(0.88 0.015 85);
--color-ring: oklch(0.65 0.08 50);
/* Default utility values */
--default-border-color: var(--color-border);
--default-ring-color: var(--color-ring);
/* Radius */
--radius: 0.625rem;
}
/* ============================================================
Dark mode — warm dark palette
============================================================ */
.dark {
--color-surface-0: #1C1B18;
--color-surface-1: #242320;
--color-surface-2: #2D2B27;
--color-surface-3: #3A3832;
--color-text-primary: #ECE9E0;
--color-text-secondary: #9C9890;
--color-text-muted: #6B6860;
--color-bg-inverted: #F5F3EC;
--color-text-inverted: #2D2B27;
--color-border: #3A3832;
--color-border-light: #2D2B27;
--color-tool-card: #2D2B27;
--color-warning-bg: #45290A;
--color-warning-border: #B47818;
--color-warning-text: #FCD980;
}
/* ============================================================
Base styles — layered so they coexist with Tailwind preflight
============================================================ */
@layer base {
html,
body {
background-color: var(--color-surface-0);
color: var(--color-text-primary);
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
}
*,
::before,
::after {
border-color: var(--color-border);
}
}
/* ============================================================
Custom utilities (unlayered so they always win)
============================================================ */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--color-surface-3);
border-radius: 3px;
}
/* Glimmer sweep — reverse sweep highlight (same visual as TUI) */
.glimmer-text {
background: linear-gradient(
90deg,
var(--color-text-secondary) 0%,
var(--color-text-secondary) 40%,
var(--color-brand) 50%,
var(--color-text-secondary) 60%,
var(--color-text-secondary) 100%
);
background-size: 200% 100%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: glimmer-sweep 3s ease-in-out infinite;
}
@keyframes glimmer-sweep {
0% {
background-position: 100% 0;
}
100% {
background-position: -100% 0;
}
}
/* ============================================================
Chat UI — Anthropic style overrides
============================================================ */
/* Chat input — warm orange focus ring */
.chat-input-focus:focus-within {
box-shadow: 0 0 0 2px rgba(217, 119, 87, 0.25);
}
/* Markdown content in message bubbles */
.message-content pre {
background-color: var(--color-surface-1);
border-radius: 0.5rem;
padding: 0.75rem;
overflow-x: auto;
font-size: 0.8125rem;
line-height: 1.5;
}
.message-content code {
font-family: var(--font-mono);
font-size: 0.8125rem;
}
.message-content :not(pre) > code {
background-color: var(--color-surface-1);
padding: 0.125rem 0.375rem;
border-radius: 0.25rem;
}
/* ============================================================
Animations — Anthropic entrance effects
============================================================ */
@keyframes fadeUp {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes typing-bounce {
0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
30% { transform: translateY(-5px); opacity: 1; }
}
/* Typing indicator dots */
.chat-typing-indicator {
display: inline-flex;
gap: 4px;
align-items: center;
height: 20px;
}
.chat-typing-indicator span {
width: 6px;
height: 6px;
border-radius: 9999px;
background: var(--color-brand);
animation: typing-bounce 1.2s ease-in-out infinite;
}
.chat-typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
.chat-typing-indicator span:nth-child(3) { animation-delay: 0.4s; }