Distinguish home and parent navigation icons
This commit is contained in:
1 parent
af63926719
commit
d3b0d411ca
10 files changed
+72
-10
No files matched your search
@@ -0,0 +1,53 @@
|
||||
# Task: Distinguish home and parent navigation in Agents and Code
|
||||
|
||||
## Identity
|
||||
|
||||
- Task ID: 20261014-navigation-icon-93c4a6e8
|
||||
- Mode: Feature
|
||||
- Branch: codex/20261014-navigation-icon-93c4a6e8-navigation-icon
|
||||
- Worktree: /Users/chillishark/Makelore 麦洛/.codex-worktrees/makelore/20261014-navigation-icon-93c4a6e8
|
||||
- Base commit: af639267198d908e43bcb0bd243e86be2a533427
|
||||
- Owner: codex-root
|
||||
- Status: In Progress
|
||||
|
||||
## Scope
|
||||
|
||||
- Distinguish global home from contextual back navigation in Agents and Code with the existing Lucide icon set. Global module return uses a house and “返回首页”; local configuration, chat, settings and plugin return arrows use hover text “返回上一级”.
|
||||
- Update the current temporary Renderer while retaining its previously prepared WeChat card-name edit.
|
||||
|
||||
## Intent And Constraints
|
||||
|
||||
- Explicit user decision: icons only; no visible destination text added. Existing routes, accessible destination labels, dimensions, keyboard handling and unsaved/pending operation guards remain intact.
|
||||
- Keep Main PID 35142, Host API port 58003, bundle, bootstrap and profile; no cloud mutations or profile/credential reads. No canonical documentation writes, main merge, push or deployment.
|
||||
|
||||
## Project Context Loaded
|
||||
|
||||
- Task ID: 20261014-navigation-icon-93c4a6e8; mode Feature; matching branch and absolute worktree above. Ownership start/status passed; base af639267 is the documentation successor of tested WeChat-name product f1dabdb.
|
||||
- Read: read-before-planning, memory-index, own task record, project-positioning, current-state, decision-index, system-overview, business-rules, success-criteria, personal-cloud-agents ADR, community-collaboration, commitments and stale-items. Required context was reviewed by root and the read-only coordination agent.
|
||||
- Project goal: project-centered creation with Main-owned authenticated APIs; Chinese light visual system. Integrated snapshot predates current trial and several accepted UI changes; runtime/source receipts take precedence for actual delivery state.
|
||||
- Other active local tasks: 79 peer records / 237 scope, intent and promotion sections read. Sidebar/layout/Code configuration peers overlap paths but preserve global return and contextual navigation; no semantic conflict. Child-language-skill placeholder has unknown scope but is not a navigation dependency. No foreign source changes adopted.
|
||||
- Decisions: retain manual Agent sidebar control and original navigation protection; current user explicitly chooses house/home and arrow/parent icons plus exact hover text. No architecture or identity changes.
|
||||
- Likely files: ModuleSwitcher, DraftEditor, ProjectConfiguration, Settings, Plugins and existing navigation fixtures.
|
||||
- Gate result: Passed. Root planning uses isolated owned source, never the product snapshots or peer worktrees.
|
||||
|
||||
## Plan
|
||||
|
||||
- Change house icon and local arrow hover titles, retain handlers and accessible target labels.
|
||||
- Update focused existing navigation assertions; run relevant checks and default full Vite build.
|
||||
- Prepare immutable file-only Renderer delivery preserving pending name edit, refresh and inspect actual UI before marking delivery verified.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Not completed.
|
||||
|
||||
## Verification
|
||||
|
||||
- Not run.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- None recorded.
|
||||
|
||||
## Promotion Candidates
|
||||
|
||||
- None recorded.
|
||||
@@ -41,7 +41,7 @@ Makelore 是一个面向软件、视觉创作、智能机器人与个人云智
|
||||
- macOS 窗口按钮:红色关闭当前窗口,全屏时先退出全屏再关闭;黄色最小化,绿色保留原生缩放/全屏行为。关窗后应用和进行中的后台任务继续运行,点击 Dock、托盘或菜单可重新打开窗口;`Cmd+Q` 退出应用。
|
||||
- 桌面文本右键菜单:主 Renderer 窗口由 Electron Main 提供原生“剪切、复制、粘贴、全选”操作;菜单只在可编辑区域或已有文本选择时出现,普通空白区域不显示,也不向 Renderer 暴露剪贴板 IPC。
|
||||
- AI 编程核心对话运行时:Electron Main 按需启动一个长驻 Pi Agent Server,同一进程内为每条 Conversation 建立独立的 Runtime、Session、凭证视图、扩展上下文和 JSONL 通道,同时复用 Pi 模块与进程启动成本;Server 异常退出时所有旧通道统一失效,下一次恢复只重启一个 Server。正式包中的 Agent Server 从明确传入的 `resources/pi-runtime` 根定位依赖清单与包入口,不从脚本所在目录或应用 `node_modules` 猜测解析。Renderer 不直接启动或调用 Pi,也不读取其 wire 类型。项目与 Conversation 元数据先从本地读取,选中对话后才按需准备对应逻辑线程;输入框不等待运行时准备完成。同一助手工具批次中,会取得项目写租约的 `bash`、`edit`、`write` 与动态产品工具按顺序执行,避免批次在命令启动前自锁;纯只读工具仍可并行。子 Agent 仍使用独立的短生命周期进程,以保留并发配额、故障和写入边界;macOS 上两类后台进程都使用 Electron 自带的 Helper,不在 Dock 中创建额外应用图标。
|
||||
- AI 编程默认入口:进入 Code 后保持在 `/chat`。顶部返回箭头与侧栏收起图标同排,与右侧项目/会话名称对齐;折叠时保留返回和展开入口。macOS 为原生窗口按钮保留安全空间,窄侧栏仍可使用。左侧不再展示麦洛品牌与说明语,“新建项目”置于原品牌区,“我的项目”与列表上移到原新建项目区域;“作品广场”与“新建项目”沿用 Agent 的彩色图标徽章+文字样式,不再使用整行白色块与厚阴影,“作品广场”移至底部、紧邻账户按钮上方;“我的项目”和项目列表紧接新建项目,列表沿用图标与文字布局及浅色选中反馈;从项目菜单进入的配置页及插件资源页复用相同的左侧工作栏,返回与收起按钮、项目和会话列表、底部作品广场与账户的位置保持一致,并沿用当前宽度和收起状态;配置正文、顶部项目名称与配置页自身的返回操作保留。尚未选择本地项目时,页面以麦洛 M 标识和居中引导语建立焦点,下方提供独立的“新增项目”主操作以及可横向浏览、直接进入的已有项目卡片;项目创建和进入仍复用侧栏现有流程。已有项目的新 Conversation 在可编辑 Composer 上方沿用同一品牌引导,第一条消息出现后切换为正常时间线。
|
||||
- AI 编程默认入口:进入 Code 后保持在 `/chat`。顶部房子图标与侧栏收起图标同排,与右侧项目/会话名称对齐;折叠时保留返回和展开入口。macOS 为原生窗口按钮保留安全空间,窄侧栏仍可使用。左侧不再展示麦洛品牌与说明语,“新建项目”置于原品牌区,“我的项目”与列表上移到原新建项目区域;“作品广场”与“新建项目”沿用 Agent 的彩色图标徽章+文字样式,不再使用整行白色块与厚阴影,“作品广场”移至底部、紧邻账户按钮上方;“我的项目”和项目列表紧接新建项目,列表沿用图标与文字布局及浅色选中反馈;从项目菜单进入的配置页及插件资源页复用相同的左侧工作栏,返回与收起按钮、项目和会话列表、底部作品广场与账户的位置保持一致,并沿用当前宽度和收起状态;配置正文、顶部项目名称与配置页自身的返回操作保留。尚未选择本地项目时,页面以麦洛 M 标识和居中引导语建立焦点,下方提供独立的“新增项目”主操作以及可横向浏览、直接进入的已有项目卡片;项目创建和进入仍复用侧栏现有流程。已有项目的新 Conversation 在可编辑 Composer 上方沿用同一品牌引导,第一条消息出现后切换为正常时间线。
|
||||
- AI 编程历史与配额反馈:Pi 上下文压缩不删除用户可见的 active-branch 历史,压缩摘要保持 Main-private;Renderer 首次渲染最近 120 个节点,向上滚动时按 100 个节点追加更早内容并保持阅读位置。Main 精确识别 `token_point_balance_exhausted` / `词元点数余额不足`,只向 Renderer 投影不可重试的稳定配额错误和安全中文提示,重开会话后仍可见且不暴露上游原始错误或 request id。
|
||||
- 账号词元点数:左下角账户菜单显示本人永久余额和预占;“词元点数与充值”入口暂时隐藏,充值、充值订单与分页收支记录的既有实现保留。真正新注册赠送 100 点,1 元充值 50 点,永久有效;旧账户不补送,旧会员/订阅/周额度/重置卡入口已移除。家长为孩子充值时登录孩子的账号,页面显示当前昵称和账号,点数进入该账号自己的钱包;无需将孩子身份改为家长。当前有效账号可给本人充值,青少年可查看自己的精确余额;使用他人共享钱包时仅显示可用状态,不显示付款人的余额或记录。Main 持有凭据并转发充值请求;付款结果未知时沿用原请求并核查原订单,只有服务端确认才显示到账。扫码信息缺失时可到网页账户页继续原订单,付款来源也由网页账户页管理,充值不会自动切换共享/个人来源。需配套部署 Works Square 当前账号充值资格更新(本次无需新迁移,原永久点数基础为 0094);本地模拟验收不代表真实支付通过。
|
||||
- 桌面性能策略:应用窗口使用不透明浅色表面并默认保留硬件合成;仅在显式安全模式或短时间内重复 GPU 进程崩溃时启用软件渲染,并把故障原因保存在用户数据目录。启动关键路径只创建本地应用壳,认证、代理、同步、更新和遥测在首帧后延迟初始化;开发版可通过 `app:performance` 快照观察 GPU、进程、WebContents、事件循环与 Renderer Long Task 聚合指标。
|
||||
@@ -70,6 +70,8 @@ Makelore 是一个面向软件、视觉创作、智能机器人与个人云智
|
||||
|
||||
微信卡片名称可点击名称旁的铅笔修改,支持保存、取消、Enter 保存与 Escape 取消。备注名按当前账号与微信账号 ID 保存在本机,刷新或重新绑定后保留;不修改真实微信昵称或云端名称,不跨设备同步。保存失败保留原名称与编辑内容,可重试。
|
||||
|
||||
首页入口使用房子图标,悬停提示“返回首页”;智能体聊天/调整及 Code 配置、设置、插件页面的上级返回保留箭头,悬停提示“返回上一级”。既有目的地、键盘操作和未保存修改的离开保护保持。
|
||||
|
||||
## 安装与开发
|
||||
|
||||
项目使用 `package.json` 中固定版本的 pnpm。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
import { Home } from 'lucide-react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { AI_MODULE_SELECTION_PATH, aiModules, getAiModuleForPath } from '@/lib/ai-modules';
|
||||
@@ -34,10 +34,10 @@ export function ModuleSwitcher({ sidebarCollapsed, compact = false, toolbar = fa
|
||||
agentReturn && 'rounded-full bg-background/70 text-brand',
|
||||
)}
|
||||
>
|
||||
<ArrowLeft
|
||||
<Home
|
||||
data-testid="sidebar-module-return-icon"
|
||||
aria-hidden="true"
|
||||
className={cn('h-4 w-4 shrink-0 -translate-y-px transition-[color,transform] duration-150 group-hover:-translate-x-0.5 group-hover:text-brand', agentReturn ? 'text-brand' : 'text-muted-foreground')}
|
||||
className={cn('h-4 w-4 shrink-0 -translate-y-px transition-colors duration-150 group-hover:text-brand', agentReturn ? 'text-brand' : 'text-muted-foreground')}
|
||||
/>
|
||||
{!sidebarCollapsed && !iconOnly ? <span className={cn('min-w-0 whitespace-nowrap leading-4', toolbar && 'truncate')}>{activeModuleLabel}</span> : null}
|
||||
</button>
|
||||
|
||||
@@ -125,7 +125,7 @@ export function DraftEditor({ initial, recent, onBack, onSaved, startingPrompt,
|
||||
return <CloudPendingContext.Provider value={pending.report}><section className={`agent-desktop${tab === 'chat' ? ' agent-chat-editor' : ''}`} data-testid="cloud-agent-editor"
|
||||
data-configuration-settled={!dirty && !busy && !publication.pending && !publication.error && !remote && publication.appliedRevision === saved.draft_revision}>
|
||||
<header className={`agent-workspace-header${tab === 'chat' ? ' agent-chat-workspace-header' : ''}`}>
|
||||
<Button variant="ghost" size="icon" className="h-10 w-10 shrink-0" aria-label={tab === 'chat' ? '返回智能体列表' : '我的智能体'} disabled={busy}
|
||||
<Button variant="ghost" size="icon" className="h-10 w-10 shrink-0" aria-label={tab === 'chat' ? '返回智能体列表' : '我的智能体'} title="返回上一级" disabled={busy}
|
||||
onClick={() => dirty || hasPending ? setLeaving(true) : onBack()}><ArrowLeft className="h-4 w-4" /></Button>
|
||||
{tab === 'chat' ? <>
|
||||
<CloudAvatar src={saved.icon_url} name={saved.name} identity={saved.slug} className="agent-chat-header-avatar" />
|
||||
|
||||
@@ -213,7 +213,7 @@ export function PluginsView(props: PluginsViewProps) {
|
||||
size="icon"
|
||||
data-testid="project-plugins-back-button"
|
||||
aria-label="返回项目配置"
|
||||
title="返回项目配置"
|
||||
title="返回上一级"
|
||||
onClick={props.onBackToProjectSettings}
|
||||
className="mt-0.5 h-8 w-8 shrink-0 text-muted-foreground hover:bg-surface-subtle hover:text-foreground"
|
||||
>
|
||||
|
||||
@@ -34,7 +34,7 @@ function ResourceCard({ id, icon, title, subtitle, onClick }: { id: string; icon
|
||||
}
|
||||
|
||||
function DrawerFrame({ title, desc, onClose, children, closeLabel = '关闭', closeIcon = 'close' }: { title: string; desc: string; onClose: () => void; children: React.ReactNode; closeLabel?: string; closeIcon?: 'close' | 'back' }) {
|
||||
const closeButton = <Button type="button" variant="ghost" size="icon" onClick={onClose} aria-label={closeLabel} title={closeLabel} className="relative z-10 shrink-0">{closeIcon === 'back' ? <ArrowLeft className="h-4 w-4" /> : <X className="h-4 w-4" />}</Button>;
|
||||
const closeButton = <Button type="button" variant="ghost" size="icon" onClick={onClose} aria-label={closeLabel} title={closeIcon === 'back' ? '返回上一级' : closeLabel} className="relative z-10 shrink-0">{closeIcon === 'back' ? <ArrowLeft className="h-4 w-4" /> : <X className="h-4 w-4" />}</Button>;
|
||||
return <div className="flex min-h-0 flex-1 flex-col"><div className="glass-surface drawer-motion-header flex items-center justify-between gap-3 border-b border-border/70 bg-background px-5 py-4"><div className="min-w-0 flex-1"><SheetTitle className="text-lg font-semibold">{title}</SheetTitle><SheetDescription className="mt-1 text-sm font-medium text-muted-foreground">{desc}</SheetDescription></div>{closeIcon === 'back' ? closeButton : <SheetClose asChild>{closeButton}</SheetClose>}</div><div className="drawer-motion-body min-h-0 flex-1 overflow-auto p-5">{children}</div></div>;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ export function ProjectConfiguration() {
|
||||
return <main className="-m-5 flex min-h-[calc(100%+2.5rem)] min-h-0 flex-col overflow-auto bg-background p-4 text-foreground sm:-m-6 sm:min-h-[calc(100%+3rem)]" data-testid="project-configuration-page">
|
||||
<div className="mx-auto flex min-h-0 w-full max-w-7xl flex-1 flex-col gap-3">
|
||||
<div className="config-motion-title flex items-center gap-2">
|
||||
<Button type="button" variant="ghost" size="icon" data-testid="project-configuration-back-button" aria-label="返回" title="返回" onClick={handleBack} className="h-8 w-8 shrink-0 text-muted-foreground hover:bg-surface-subtle hover:text-foreground">
|
||||
<Button type="button" variant="ghost" size="icon" data-testid="project-configuration-back-button" aria-label="返回" title="返回上一级" onClick={handleBack} className="h-8 w-8 shrink-0 text-muted-foreground hover:bg-surface-subtle hover:text-foreground">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<h1 className="text-xl font-semibold">项目配置</h1>
|
||||
|
||||
@@ -257,7 +257,7 @@ export function Settings() {
|
||||
<main data-testid="settings-page" data-settings-dirty={proxySettingsDirty || savingProxy} className="min-h-full bg-background">
|
||||
<div className="mx-auto flex max-w-4xl flex-col gap-8 px-8 py-8">
|
||||
<div className="flex items-center gap-3">
|
||||
<Button type="button" variant="ghost" aria-label="返回上一页" data-testid="settings-back-button"
|
||||
<Button type="button" variant="ghost" aria-label="返回上一页" title="返回上一级" data-testid="settings-back-button"
|
||||
className="shrink-0 gap-2" onClick={() => {
|
||||
if (!canLeaveCurrentPage()) { toast.info('请先保存或还原当前修改,再返回。'); return; }
|
||||
navigate(getSettingsReturnPath(location.state));
|
||||
|
||||
@@ -150,10 +150,14 @@ test('Agent cards open conversation, adjustment and unfinished creation explicit
|
||||
|
||||
await ready.getByRole('button', { name: '和它聊聊', exact: true }).click();
|
||||
await expect(page.getByRole('region', { name: '智能体对话', exact: true })).toBeVisible();
|
||||
await expect(page.getByTestId('sidebar-module-return-icon')).toHaveClass(/lucide-house/);
|
||||
await expect(page.getByRole('button', { name: '返回首页', exact: true })).toHaveAttribute('title', '返回首页');
|
||||
await expect(page.getByTestId('cloud-agent-editor').getByRole('button', { name: '返回智能体列表', exact: true })).toHaveAttribute('title', '返回上一级');
|
||||
await expect(page.getByTestId('cloud-agent-editor').getByRole('button', { name: '聊天', exact: true })).toHaveCount(0);
|
||||
await page.getByTestId('cloud-agent-editor').getByRole('button', { name: '返回智能体列表', exact: true }).click();
|
||||
await expect(ready).toBeVisible();
|
||||
await ready.getByRole('button', { name: '调整', exact: true }).click();
|
||||
await expect(page.getByTestId('cloud-agent-editor').getByRole('button', { name: '我的智能体', exact: true })).toHaveAttribute('title', '返回上一级');
|
||||
await expect(page.getByRole('tab', { name: '介绍与提示词', exact: true })).toHaveAttribute('aria-selected', 'true');
|
||||
await expect(page.getByTestId('cloud-agent-editor').getByRole('button', { name: /^(聊天|自动任务|使用与接入)$/ })).toHaveCount(0);
|
||||
await expect(page.getByTestId('cloud-agent-editor').getByRole('button', { name: '修改头像', exact: true })).toBeVisible();
|
||||
|
||||
@@ -406,6 +406,7 @@ test.describe('Makelore module navigation without setup flow', () => {
|
||||
await expect(moduleReturn).toHaveAttribute('aria-label', '返回首页');
|
||||
await expect(moduleReturn).toHaveAttribute('title', '返回首页');
|
||||
await expect(page.getByTestId('sidebar-module-return-icon')).toBeVisible();
|
||||
await expect(page.getByTestId('sidebar-module-return-icon')).toHaveClass(/lucide-house/);
|
||||
await expect(sidebar.locator('.classroom-brand')).toHaveCount(0);
|
||||
await expect(sidebar).not.toContainText('把脑洞变成作品');
|
||||
const returnBox = await moduleReturn.boundingBox();
|
||||
@@ -528,6 +529,8 @@ test.describe('Makelore module navigation without setup flow', () => {
|
||||
await expect(moduleReturn).toBeVisible();
|
||||
const projectBack = page.getByTestId('project-configuration-back-button');
|
||||
await expect(projectBack).toBeVisible();
|
||||
await expect(projectBack).toHaveAttribute('title', '返回上一级');
|
||||
await expect(projectBack.locator('svg')).toHaveClass(/lucide-arrow-left/);
|
||||
await page.mouse.move(1000, 500);
|
||||
await expect.poll(readCodeChrome).toEqual(chatChrome);
|
||||
await expect(sidebar).toHaveCSS('background-color', 'rgb(243, 247, 251)');
|
||||
|
||||
@@ -240,7 +240,7 @@ describe('AI module navigation', () => {
|
||||
expect(screen.getByTestId('sidebar-module-switcher-trigger')).toHaveTextContent('绘画 Canvas');
|
||||
expect(screen.getByTestId('sidebar-module-switcher-trigger')).toHaveAttribute('aria-label', '返回首页');
|
||||
expect(screen.getByTestId('sidebar-module-switcher-trigger')).toHaveClass('p-3');
|
||||
expect(screen.getByTestId('sidebar-module-return-icon')).toHaveClass('lucide-arrow-left', '-translate-y-px');
|
||||
expect(screen.getByTestId('sidebar-module-return-icon')).toHaveClass('lucide-house', '-translate-y-px');
|
||||
expect(screen.queryByTestId('sidebar-module-switcher-menu')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByTestId('sidebar-module-switcher-trigger'));
|
||||
|
||||
Reference in new issue
Block a user