From c14d996c054d1b5b8596b5d2af7c476e271d1540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=A8=E9=B1=BC=E8=BE=A3=E6=A4=92?= Date: Mon, 12 Oct 2026 17:05:24 +0800 Subject: [PATCH] Restore Agent workspace and add platform pixel avatars --- .../20261012-agent-ui-restore-58d7c941.md | 85 ++++ README.md | 2 +- electron/api/routes/cloud-agents.ts | 2 + .../services/cloud-agent-avatar-presets.ts | 20 + electron/services/cloud-agents.ts | 11 + src/lib/cloud-agents-api.ts | 3 + src/pages/CloudAgents/AgentRequirements.tsx | 33 +- src/pages/CloudAgents/CloudAvatar.tsx | 51 ++- src/pages/CloudAgents/ConfigurationFields.tsx | 4 - src/pages/CloudAgents/DraftEditor.tsx | 90 ++--- src/pages/CloudAgents/agent-home.css | 79 ++++ src/pages/CloudAgents/desktop-workspace.css | 1 + src/pages/CloudAgents/index.tsx | 182 +++++---- tests/e2e/cloud-agent-channels.spec.ts | 2 +- tests/e2e/cloud-agent-home.spec.ts | 182 +++++++++ .../e2e/cloud-agent-platform-avatars.spec.ts | 236 +++++++++++ tests/e2e/cloud-agents.spec.ts | 355 ++++++++++------- tests/unit/cloud-agent-avatar-routes.test.ts | 37 ++ tests/unit/cloud-agent-avatar.test.tsx | 44 ++- tests/unit/cloud-agents-guidance.test.tsx | 22 +- tests/unit/cloud-agents-main.test.ts | 39 +- tests/unit/cloud-agents-page.test.tsx | 369 +++++++++++------- 22 files changed, 1387 insertions(+), 462 deletions(-) create mode 100644 .project-docs/30-worklog/tasks/20261012-agent-ui-restore-58d7c941.md create mode 100644 electron/services/cloud-agent-avatar-presets.ts create mode 100644 src/pages/CloudAgents/agent-home.css create mode 100644 tests/e2e/cloud-agent-home.spec.ts create mode 100644 tests/e2e/cloud-agent-platform-avatars.spec.ts create mode 100644 tests/unit/cloud-agent-avatar-routes.test.ts diff --git a/.project-docs/30-worklog/tasks/20261012-agent-ui-restore-58d7c941.md b/.project-docs/30-worklog/tasks/20261012-agent-ui-restore-58d7c941.md new file mode 100644 index 00000000..42a4152d --- /dev/null +++ b/.project-docs/30-worklog/tasks/20261012-agent-ui-restore-58d7c941.md @@ -0,0 +1,85 @@ +# Task: Restore accepted Agent homepage and add platform pixel avatars + +## Identity + +- Task ID: 20261012-agent-ui-restore-58d7c941 +- Mode: Feature +- Branch: codex/20261012-agent-ui-restore-58d7c941-agent-ui-restore +- Worktree: /Users/chillishark/Makelore 麦洛/.codex-worktrees/makelore/20261012-agent-ui-restore-58d7c941 +- Base commit: 7a7d1f7fd89fff3b77f2fd8f1986da65fb6223ea +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Restore the accepted personal-Agent homepage and creation/configuration refinements from committed source 01fc7d0 on latest remote baseline 7a7d1f7, and add platform pixel-avatar selection alongside personal uploads. + +## Intent And Constraints + +- User explicitly requests the referenced earlier conversation's UI and platform pixel avatars. Preserve all peer worktrees, remote/primary branches, Main-owned credentials, existing avatar storage contract and explicit save/apply policy. No paid model calls or real cloud mutations during verification. +- Preserve desktop focus: no UI automation, window activation or foreground switching. Build and screenshots use a headless browser. Any temporary-app update must account for the concurrently active trial-update peer before process replacement; installed app and profile data remain untouched. + +## Outcome + +- Restored the accepted warm-white homepage, blue/peach/lilac cards, lifecycle badges, direct chat/adjust/continue actions and More menu from committed01fc7d0. Retained its current name/avatar → model/full-prompt flow and preserved the latest shared-conversation avatar header. +- Reused all16 existing pixel portraits. Avatar fields expose selectable thumbnails; Main accepts only allowlisted preset IDs and uploads bundled PNG bytes through the existing authenticated Yuxi avatar contract, with no file dialog or new server schema. Canonical icon persistence, explicit save/application, upload cancellation, failure and account isolation remain. +- Unset/broken images use a deterministic local pixel fallback consistently across the homepage/editor/share/version components. Narrow selection uses4 columns; desktop uses8, with keyboard focus and selected/loading states. +- The other trial-update task had already produced source872d4b2 and replaced PID1748 with11944. Compared that committed product source, preserving its refinements; took a fresh current-runtime snapshot before replacement and retained it as rollback metadata. +- Updated the running temporary app from this tested owned checkout: PID17709, Host API52733. Stopped only previous trial11944 and its process tree. Reused the existing trial profile and direct-network preference; installed/primary/peer source and remote branches remain untouched. +- Temporary background entry uses open -g, showInactive during startup and suppresses automatic focus only for startup; then restores ordinary user-driven window controls. It contains literal UTF-8 profile/artifact paths, since LaunchServices mangled non-ASCII environment path values in the isolated probe. No native UI automation or foreground activation was used. + +## Verification + +- Required pnpm10.33.4 typecheck passed;5 focused unit files/108 tests passed. Scoped ESLint and git diff --check passed after correcting test-formatting lint. Dependencies reused only after byte-identical package/lockfile comparison. +- Production Renderer/Main/Preload/utility build passed. Verified all16 original PNG SHA-256 values are embedded in built Main, supporting packaged preset selection without source-asset filesystem paths. +-5 headless browser cases passed using the actual CloudAgents components and typed Host API through an IPC fixture. Covered both375px/1366px, exact three card colors/actions, all16 assets loading, selected preview, create/save/reopen, and failed selection/save preserving prior values. Screenshots inspected in background; no Electron test windows were opened. +- Native replacement independently reported a ready, visible but unfocused window loading this checkout's built index.html. Foreground ASN remained unchanged across replacement and the startup interval. Actual Electron public health was DIRECT/HTTP200; local Host API returned expected401. No real cloud avatar upload or paid model request was performed during verification. +- Build manifest, previous-runtime snapshot, source-aware background entry and launch logs retained under .trial-runtime/agent-ui-pixels-20261012-58d7c941. New pointer refers to this source; all compiled artifact hashes are recorded. +- The optional broad tsconfig.node check reviewed by the avatar agent still reports unrelated pre-existing Main/composite diagnostics; standard required typecheck and production build passed. No unrelated config/type workaround was added. + +## Follow-ups + +- Temporary app remains running. Main/remote integration and real cloud-storage acceptance are not performed by this feature task. + +## Promotion Candidates + +- Targets: personal-cloud-Agent ADR and integrated current state during serialized integration. Proposal: promote the user-approved personal homepage/name-avatar/full-prompt refinement and16 platform pixel portraits, preserving canonical cloudicon and explicit save/apply. Evidence: user request/reference, source01fc7d0,108 unit checks,5 headless cases and the built/native trial. Future impact: creation/editing can use platform portraits without personal file selection. Supersedes older purpose-starter/preview UX only; user authorization already supplied. +- Target: temporary-launch workflow. Proposal: explicitly include the requested committed feature branches in trial source selection and preserve desktop focus via background launch, startup showInactive and literal UTF-8 paths. Evidence: initial remote-only omission, source comparison, non-ASCII probe correction and unchanged foreground during successful replacement. No canonical workflow or primary source was edited. + +## Project Context Loaded + +Task context: +- Task ID: 20261012-agent-ui-restore-58d7c941 +- Mode: feature +- Branch: codex/20261012-agent-ui-restore-58d7c941-agent-ui-restore +- Worktree: /Users/chillishark/Makelore 麦洛/.codex-worktrees/makelore/20261012-agent-ui-restore-58d7c941 +- Base commit: 7a7d1f7fd89fff3b77f2fd8f1986da65fb6223ea +- Ownership: bundled start exited 0; status JSON confirms task, mode, branch, worktree and reservation. +- Other active local tasks: every registry peer's Scope, Intent And Constraints and Promotion Candidates reviewed read-only. Relevant peers are Agent design review (committed 01fc7d0), purpose-page removal (85030a8), latest-source trial and active combined-trial update. Older teacher tasks are unrelated; child-language scope remains undefined. +- Overlap assessment: import only committed Agent product source. The user explicitly requested reviewing/restoring the named source conversation; no arbitrary dirty peer files are adopted. Active combined-trial peer shares the launcher target, so defer replacement until its run has settled; it does not prevent independent feature implementation here. + +Read: +- AGENTS.md; planning entry, memory-index and planning-gate; this task record. +- Project positioning and integrated current-state (including merged-avatar entry); decision index; system-overview (Main/cloud/Agents boundaries). +- Personal-cloud-Agent ADR; relevant domain, success criteria, evidence/reflection indexes and commitments. +- Registry peer sections and referenced conversation user/assistant messages. + +Relevant understanding: +- Goal: creator/student desktop product with Main-owned auth, transport and runtime; personal Agents use cloud execution and creator billing. +- Integrated focus: remote7a includes Agent icon uploading and display but excludes local01fc homepage/configuration refinement. +- Scope: retain warm-white light styling and blue/peach/lilac cards, direct chat/adjust actions and current identity/model/full-prompt creation flow; add selectable existing platform pixel portraits. +- Constraints: use typed Host API, persist cloud icon through existing Yuxi storage, keep upload cancellation/errors/unsaved edits and published snapshot semantics. +- Decisions: referenced user approval supersedes older starter-selection/preview descriptions. No teacher positioning or canonical documentation change. +- Evidence: earlier source/test commit preserved; all16 existing pixel assets are available. Old trial build matched remote exactly, explaining the omission. +- Modules: CloudAgents UI/CSS, existing avatar helper/assets, typed Host API and Main avatar transport, focused tests and README. +- Unknowns: real remote upload/storage not exercised; current user's visible UI not inspected. Concurrent trial replacement must be sequenced after the peer. + +Gate result: +- Passed for isolated implementation and selective committed-source import; preserve peers and defer any concurrent trial mutation. + +## Implementation Plan + +- Import only the six committed Agent UI/CSS source files and related focused tests from01fc; preserve latest backend/avatar contracts and canonical docs. +- Expose16 bundled pixel portraits in avatar selection; Main uploads an allowlisted bundled preset through the existing avatar service without a file dialog. Use a local pixel fallback for missing/broken icons. +- Verify typecheck, focused unit tests, scoped lint, production build and headless UI interactions/screenshots; update README and this record. +- After the peer trial update settles, prepare and verify the new temporary bundle from this exact tested source, preserving direct-network configuration/profile and desktop focus. diff --git a/README.md b/README.md index ae86d761..8cef7387 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Makelore 是一个面向软件、视觉创作、智能机器人与个人云智 - `Makelore Code|AI 编程`:管理本地项目、项目智能体、对话、文件上下文、代码变更和运行时。 会话工作台采用浅蓝项目栏、彩色通用手柄图标与卡片选中态;中间对话/作品使用圆角标签,作品预览的后退、前进、刷新与网址输入统一放在标签右侧;只在作品页显示,窄窗口自动换行。右侧智能体咨询使用奶油色标题区。沿用真实项目和会话入口、侧栏伸缩与账户菜单,样式只作用于 Code 会话工作台。可编辑的空对话在输入区上方的可用区域中央显示童趣欢迎语;「去作品广场逛逛」入口紧贴输入框上方,两者一起位于底部。老师介绍以固定宽度浮在右上角,不占用中间布局;欢迎语和底部输入区始终相对项目栏右侧的整个内容区域居中;小窗口的欢迎内容独立滚动;发送首条消息后收起欢迎区。侧栏保留「作品广场」入口,只有点击后才在系统浏览器打开公共广场,不请求、摘取或内嵌展示广场作品。打开广场保留当前项目、对话、草稿和附件,不自动发送消息;老师求助按钮保留临时应用的最新文案「帮我整体看🧠」。 -- `Makelore Agents|AI 智能体`:配置个人云智能体并试聊,发布后正式对话、接入个人微信或创建应用 API Key。支持流式回复、审批、排队请求、附件与产物、自动任务和活动历史;重新进入时恢复最近智能体与对话,未确认操作可按原输入重试。聊天可提出日程建议,由创建者核对能力、时间和费用后启用。知识库上传、替换及导入本人会话附件后自动解析并建立索引;文档卡片区分排队、解析、索引、可用和失败,持续刷新已加载文档,失败可原地重试;零文本内容不会显示为可用,新文档可用后才移除被替换的旧文档。支持删除知识内容;个人 MCP 凭据、Skills、子智能体可自助管理。支持智能体和会话归档、发布版本比较及恢复为草稿。除知识库 embedding 向量化外,模型调用(包括知识处理触发的对话模型调用)仍按原规则由创建者个人词元点数支付,可按智能体设置每次任务和每日上限,费用按时间、来源、应用分页统计;embedding 向量化由平台承担,不计入用户用量、词元点数或智能体费用上限;应用调用者保有自己的内容空间。Main 管理云会话和本机文件,Renderer 通过 Host API 操作。配套服务接入见 [Yuxi MakeLore 说明](https://xerrors.github.io/Yuxi/advanced/makelore-agents.html)。 编辑采用全高桌面分栏,支持拖动调整与展开、独立滚动、小窗口配置/试用切换。首次创建按选用途、说要求、试一试引导,提供科学问答、故事、英语和笔记整理起点;回答方式可多选,特别要求直接编辑,仍以草稿的 system_prompt 保存。新助手预选云端目录的第一个可用模型并明确显示,可自行更换;已有助手不自动换模型。编辑区以“我的要求”和“高级设置”两个 Tab 切换,默认显示基础要求;高级设置包含能力、知识与限制,返回时保留上次分类,切换保留未保存的输入及当前试用。已开始使用的助手在顶部显示“修改已保存,尚未应用”或“已应用最新修改”,并提供显眼的“应用最新修改”按钮;保存只更新草稿,应用沿用同一发布操作,失败重试保留原操作身份。草稿明确保存(Ctrl/⌘+S),保存与切换页面保留当前试用及未发送文字;“保存并重新试用”使用最新保存的要求,保留上次试用供对比,并可复用上次问题,不自动发送。示例问题仅填入输入框;有运行、排队、审批或未确认操作时先处理当前试用。工具确认先展示实际操作说明与参数,完整技术详情可展开。确认“开始使用”会创建仅自己可用的发布版本并进入独立对话,微信渠道与应用接入仍单独配置。费用上限在限制中独立保存。Enter 发送、Shift+Enter 换行,F6 切换面板焦点、Esc 恢复分栏。 +- `Makelore Agents|AI 智能体`:首页以“我的智能体”和创建入口为核心;空状态引导创建第一个助手,不在首页展示推荐起点。浅色卡片展示自己的助手、已保存头像、用途及状态;可用助手直接“和它聊聊”或“调整”,草稿“继续制作”,停用/归档助手“查看与调整”。收到的分享、活动和包含已归档的筛选收进“更多”,保留原数据和入口。创建第一步填写名称,并可选择平台16张像素头像或上传自己的头像,点击“下一步”进入配置;创建结果不确定时保留原操作和头像,明确重试。独立配置页顶部显示当前名称和头像,基础页选择模型并直接编辑完整提示词;不包含用途选择、回答方式预设、模型介绍、模型价格提示或并排试聊。新助手预选真实目录中的首个可用模型,可自行更换;已有助手不会自动换模型。高级设置保留能力、知识与限制,切换保留未保存输入;微信与应用接入仍单独配置。未设置头像或图片加载失败时显示内置像素伙伴。平台头像支持预览与更换,保存后生效;选择失败保留原头像。头像复用 Yuxi 上传合同,Main 持有文件选择、固定平台头像资源与上传,保存 canonical icon,公开 icon_url 只用于显示。草稿明确保存(Ctrl/⌘+S);保存只更新草稿,顶部“应用最新修改”使新配置生效,失败重试保留原操作身份。确认“开始使用”创建仅自己可用的发布版本并打开独立聊天,配置页不会自动发送消息。聊天支持流式回复、审批、排队、附件、产物和历史;聊天与配置切换保留未发送内容。历史试用记录仍可从活动查看,不自动删除。自动任务建议须创建者核对后启用。知识上传、替换及本人附件导入后自动解析索引,持续显示处理/可用/失败状态,零内容不会显示可用,新内容可用后才移除旧资料。费用上限独立保存;收费模型调用仍由创建者个人词元点数支付,知识 embedding 由平台承担。访问隔离、版本恢复、原输入重试及 Main-owned Host API 边界保持。配套服务见 [Yuxi MakeLore 说明](https://xerrors.github.io/Yuxi/advanced/makelore-agents.html)。 云知识库的 embedding 模型只由平台管理员在 Yuxi 后台维护:在「智能体管理 → 模型供应商」启用供应商,按供应商要求填写有效的服务地址和 API Key,添加并启用 embedding 模型并填写实际向量维度;embedding 向量化费用由平台承担,不计入用户用量、词元点数或智能体费用上限;one-api 不参与 embedding 配置,Works Square 只负责其他应计费模型调用的创建者词元点数账本。客户端区分目录加载中、加载失败与无可用模型,空状态提供管理员配置说明;「刷新知识库与模型」保留未提交名称,未确认的创建请求沿用原操作身份。 diff --git a/electron/api/routes/cloud-agents.ts b/electron/api/routes/cloud-agents.ts index aee2ddae..b703e62b 100644 --- a/electron/api/routes/cloud-agents.ts +++ b/electron/api/routes/cloud-agents.ts @@ -43,6 +43,8 @@ export async function handleCloudAgentsRoutes(req: IncomingMessage, res: ServerR data = await cloudAgents.resolvePending(await parseJsonBody(req)); } else if (path === '/avatars/pick' && req.method === 'POST') { data = await cloudAgents.uploadAvatar(); + } else if (path === '/avatars/preset' && req.method === 'POST') { + data = await cloudAgents.useAvatarPreset(await parseJsonBody(req)); } else if (path === '/attachments/pick' && req.method === 'POST') { data = await cloudAgents.upload(); } else if (path === '/knowledge/pick' && req.method === 'POST') { diff --git a/electron/services/cloud-agent-avatar-presets.ts b/electron/services/cloud-agent-avatar-presets.ts new file mode 100644 index 00000000..1f302079 --- /dev/null +++ b/electron/services/cloud-agent-avatar-presets.ts @@ -0,0 +1,20 @@ +/// + +// Inline assets make the same allowlisted portraits available in development +// and packaged Main without accepting a path or image bytes from Renderer. +const portraits = import.meta.glob('../../src/assets/agent-avatars/avatar-*.png', { + eager: true, + import: 'default', + query: '?inline', +}) as Record; + +const presets = new Map(Array.from({ length: 16 }, (_, index) => { + const id = `avatar-${String(index + 1).padStart(2, '0')}`; + return [id, portraits[`../../src/assets/agent-avatars/${id}.png`]]; +})); + +export function cloudAgentAvatarPreset(id: string): { bytes: Uint8Array; fileName: string } | null { + const image = presets.get(id); + if (!image?.startsWith('data:image/png;base64,')) return null; + return { bytes: new Uint8Array(Buffer.from(image.slice('data:image/png;base64,'.length), 'base64')), fileName: `${id}.png` }; +} diff --git a/electron/services/cloud-agents.ts b/electron/services/cloud-agents.ts index 25533ab5..38517709 100644 --- a/electron/services/cloud-agents.ts +++ b/electron/services/cloud-agents.ts @@ -1,5 +1,6 @@ import { EMPTY_CLOUD_CONFIGURATION, type CloudAgentConfiguration, type CloudAgentDraft, type CloudAgentPage, type CreateCloudAgent, type SaveCloudAgentDraft, type CloudUpload } from '../../shared/cloud-agents'; import { operationPlan } from './cloud-agent-operations'; +import { cloudAgentAvatarPreset } from './cloud-agent-avatar-presets'; const retiredChannelOperations = new Set([ 'channelPolicy', @@ -397,6 +398,16 @@ export class CloudAgentsModule { return this.withAvatar({ icon: textField(result.icon, 255) }); } + async useAvatarPreset(value: unknown): Promise<{ icon: string; icon_url: string | null }> { + const id = textField(record(value).id, 32); + const preset = cloudAgentAvatarPreset(id); + if (!preset) throw new CloudAgentsError(422, 'invalid_input'); + const body = new FormData(); + body.append('file', new Blob([preset.bytes], { type: 'image/png' }), preset.fileName); + const result = record(await this.request('/api/makelore/avatars', 'POST', body)); + return this.withAvatar({ icon: textField(result.icon, 255) }); + } + async upload(): Promise { const body = await this.pickUpload(); if (!body) return null; diff --git a/src/lib/cloud-agents-api.ts b/src/lib/cloud-agents-api.ts index 5c6f78bc..db095d69 100644 --- a/src/lib/cloud-agents-api.ts +++ b/src/lib/cloud-agents-api.ts @@ -4,6 +4,9 @@ import { CLOUD_AGENTS_PATH, type CloudAgentDraft, type CloudAgentPage, type Crea export const cloudAgentsApi = { uploadAvatar: () => hostApiFetch<{ icon: string; icon_url: string | null } | null>(CLOUD_AGENTS_PATH + '/avatars/pick', { method: 'POST' }), + useAvatarPreset: (id: string) => hostApiFetch<{ icon: string; icon_url: string | null }>(CLOUD_AGENTS_PATH + '/avatars/preset', { + method: 'POST', body: JSON.stringify({ id }), + }), recovery: () => hostApiFetch(CLOUD_AGENTS_PATH + '/recovery'), remember: (recent: CloudRecent | null) => hostApiFetch(CLOUD_AGENTS_PATH + '/recovery/recent', { method: 'PUT', body: JSON.stringify(recent), diff --git a/src/pages/CloudAgents/AgentRequirements.tsx b/src/pages/CloudAgents/AgentRequirements.tsx index 992f0db1..fd6888ee 100644 --- a/src/pages/CloudAgents/AgentRequirements.tsx +++ b/src/pages/CloudAgents/AgentRequirements.tsx @@ -1,32 +1,9 @@ -import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; -const styles = [ - { label: '简单易懂', instruction: '使用简单易懂的语言,遇到新词先解释。' }, - { label: '一步一步来', instruction: '把任务拆成小步骤,一次引导我完成一步。' }, - { label: '先给我提示', instruction: '先给我提示和思考问题,让我尝试后再补充答案。' }, -] as const; - -/** Style choices are ordinary instruction lines in the existing system_prompt. */ export function AgentRequirements({ value, onChange }: { value: string; onChange: (value: string) => void }) { - const selected = styles.filter(style => value.split('\n').includes(style.instruction)); - const custom = value.split('\n').filter(line => !selected.some(style => style.instruction === line)).join('\n'); - return
-
- 希望它怎样回答?可多选 -
{styles.map(style => { - const checked = selected.includes(style); - return ; - })}
-
-