From fba588055717e113a654525e55257f5673171e2e Mon Sep 17 00:00:00 2001 From: brother7 <7brother7@gmail.com> Date: Sun, 20 Sep 2026 10:52:35 +0800 Subject: [PATCH] Hide redeemed reset cards from the account menu --- ...20260920-hide-used-reset-cards-7a391bd2.md | 50 +++++++++++++++++++ README.md | 2 +- src/components/layout/Sidebar.tsx | 22 ++++---- tests/e2e/reset-card-wallet.spec.ts | 7 ++- tests/unit/sidebar-token-points.test.tsx | 24 +++++++-- 5 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 .project-docs/30-worklog/tasks/20260920-hide-used-reset-cards-7a391bd2.md diff --git a/.project-docs/30-worklog/tasks/20260920-hide-used-reset-cards-7a391bd2.md b/.project-docs/30-worklog/tasks/20260920-hide-used-reset-cards-7a391bd2.md new file mode 100644 index 0000000..e4529a7 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260920-hide-used-reset-cards-7a391bd2.md @@ -0,0 +1,50 @@ +# Task: Hide used reset cards from account sidebar menu + +## Identity + +- Task ID: 20260920-hide-used-reset-cards-7a391bd2 +- Mode: Feature +- Branch: codex/20260920-hide-used-reset-cards-7a391bd2-hide-used-reset-cards +- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260920-hide-used-reset-cards-7a391bd2 +- Base commit: c953126284eb81a3ac446c2213441db24e23929c +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Hide redeemed reset cards from the lower-left account menu, including initial load and successful redemption, and show a suitable empty state. +- Update focused Sidebar tests, reset-card Electron fixture and README. + +## Intent And Constraints + +- User explicitly requested already-used cards no longer appear. Keep available/expired inventory behavior and server card/entitlement records unchanged. +- Retain explicit owner-authorized redemption, shared-wallet restrictions, confirmation semantics, and authoritative card/balance refresh. +- Concurrent Task Gate and Planning Gate passed. Isolated feature from c953126; prior sharing-removal and QR-completion commits included, no peer reset-card ownership conflict identified. + +## Outcome + +- Sidebar derives a visible list excluding cards with server status redeemed, used consistently for rows, loading/empty states, available count and shared-wallet notice. +- Removed the redeemed badge/date presentation. Existing available and expired card behavior stays unchanged. +- Confirmed redemption already updates card state before refreshing authoritative inventory and balance; the new projection removes the row immediately. When no visible cards remain, the drawer shows 暂无未使用的重置卡。 +- README describes the account-menu behavior. Reviewed the complete diff and the Electron screenshot of the empty drawer with successful redemption feedback. + +## Verification + +- Pinned pnpm 10.33.4 frozen install: passed, lockfile unchanged. +- Sidebar token-point unit suite: 9 passed, covering mixed inventory, all-redeemed empty state, removal after redemption, refreshed balance, expired cards and shared-wallet restrictions. +- Typecheck and scoped ESLint on Sidebar, its unit test and reset-card E2E: passed. +- pnpm run build:vite: passed. +- Electron reset-card-wallet.spec.ts: 1 passed. Confirms row and count disappear, empty state is visible, balance refreshes and exactly one redemption is submitted. +- Visually inspected test-results/reset-card-wallet-Account--4d6fc-authoritative-point-balance/used-reset-card-hidden.png. +- git diff --check and task-aware check_doc_drift.py: passed. Electron uses a local Host fixture; no actual account reset card was consumed. + +## Follow-ups + +- Based on c953126 (QR completion), which includes e047877 (sharing removal). This task does not merge, package or deploy; the updated client is required to expose these changes. + +## Promotion Candidates + +- Target: .project-docs/40-domain/business-rules.md and .project-docs/30-worklog/current-state.md. +- Proposal: the account-menu reset-card inventory omits redeemed cards and removes a card on confirmed redemption, retaining authoritative card/balance refresh and owner/shared-wallet restrictions. +- Evidence: explicit user request, 9 unit tests and passing Electron scenario. Future impact: keep used-card history out of this compact action menu. +- Semantic conflict: none; this narrows display only, without deleting server history or changing redemption. Human confirmation is supplied by the request. diff --git a/README.md b/README.md index 5334e0f..c6aaad0 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Makelore 是一个面向软件、视觉创作、智能机器人与个人云智 - 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`。尚未选择本地项目时,页面以麦洛 M 标识和居中引导语建立焦点,下方提供独立的“新增项目”主操作以及可横向浏览、直接进入的已有项目卡片;项目创建和进入仍复用侧栏现有流程。已有项目的新 Conversation 在可编辑 Composer 上方沿用同一品牌引导,第一条消息出现后切换为正常时间线。 - AI 编程历史与配额反馈:Pi 上下文压缩不删除用户可见的 active-branch 历史,压缩摘要保持 Main-private;Renderer 首次渲染最近 120 个节点,向上滚动时按 100 个节点追加更早内容并保持阅读位置。Main 精确识别 `token_point_balance_exhausted` / `词元点数余额不足`,只向 Renderer 投影不可重试的稳定配额错误和安全中文提示,重开会话后仍可见且不暴露上游原始错误或 request id。 -- 账号词元点数:左下角账号菜单只通过 Main-owned `/api/works/billing/points` 读取 Works Square V2 余额。可管理会员的账号看到套餐、本周剩余/额度、总可用点数与刷新时间;家庭成员和青少年自有权益只收到“可用/已用尽”等粗粒度状态,不向 Renderer 暴露套餐周期或精确余额。 +- 账号词元点数:左下角账号菜单只通过 Main-owned `/api/works/billing/points` 读取 Works Square V2 余额。可管理会员的账号看到套餐、本周剩余/额度、总可用点数与刷新时间;家庭成员和青少年自有权益只收到“可用/已用尽”等粗粒度状态,不向 Renderer 暴露套餐周期或精确余额。 账号菜单的重置卡列表隐藏已使用的卡;使用成功后立即移除该卡并刷新卡包和余额,没有剩余卡片时显示“暂无未使用的重置卡”。 - 桌面性能策略:应用窗口使用不透明浅色表面并默认保留硬件合成;仅在显式安全模式或短时间内重复 GPU 进程崩溃时启用软件渲染,并把故障原因保存在用户数据目录。启动关键路径只创建本地应用壳,认证、代理、同步、更新和遥测在首帧后延迟初始化;开发版可通过 `app:performance` 快照观察 GPU、进程、WebContents、事件循环与 Renderer Long Task 聚合指标。 - 后台生命周期:Main 统一维护模块活动状态与任务租约。隐藏窗口、离开模块和关闭开发浏览器会释放非必要连接;生成、Code 执行、下载与发布构建持有租约并在完成后释放。已写入 Pi 的 prompt 或上下文整理即使确认超过 10 秒,也会继续持有运行所有权与后台租约,直到迟到响应、权威事件、明确失败或用户清理使其收敛;此时同一 Conversation 不接受重叠 mutation,其他 Conversation 不受影响。各模块的后台连接、轮询和子进程必须通过同一生命周期入口登记。 - 共享开发浏览器:AI 编程右侧提供项目级浏览器,用户与 Agent 查看并调试同一实时页面、Console 和 Network,支持本地与公网开发地址。 diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 13444f7..1f9ea93 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -273,7 +273,8 @@ export function Sidebar({ tokenPointBalance && !tokenPointBalance.can_manage_membership, ); const nextRefreshLabel = getRefreshTimeLabel(tokenPointBalance?.next_refresh_at); - const availableResetCardCount = resetCardState.cards.filter( + const visibleResetCards = resetCardState.cards.filter((card) => card.status !== 'redeemed'); + const availableResetCardCount = visibleResetCards.filter( (card) => getEffectiveResetCardStatus(card) === 'available', ).length; const resetCardRedemptionBlocked = tokenPointBalance?.entitlement_source === 'shared_group'; @@ -1286,7 +1287,7 @@ export function Sidebar({ className="ml-5 rounded-lg bg-surface-subtle/70 p-1 text-xs font-medium text-muted-foreground shadow-soft" innerClassName="grid gap-1" > - {resetCardState.status === 'loading' && resetCardState.cards.length === 0 ? ( + {resetCardState.status === 'loading' && visibleResetCards.length === 0 ? (

正在加载重置卡…

) : null} {resetCardState.status === 'error' ? ( @@ -1301,20 +1302,19 @@ export function Sidebar({ ) : null} - {resetCardState.status === 'loaded' && resetCardState.cards.length === 0 ? ( -

暂无赠送的重置卡。

+ {resetCardState.status === 'loaded' && visibleResetCards.length === 0 ? ( +

暂无未使用的重置卡。

) : null} - {resetCardRedemptionBlocked && resetCardState.cards.length > 0 ? ( + {resetCardRedemptionBlocked && visibleResetCards.length > 0 ? (

当前使用家庭共享额度,请由家庭管理员使用自己的重置卡。

) : null} - {resetCardState.cards.length > 0 ? ( + {visibleResetCards.length > 0 ? (
- {resetCardState.cards.map((card) => { + {visibleResetCards.map((card) => { const status = getEffectiveResetCardStatus(card); const expiresAt = formatResetCardTime(card.expires_at) ?? '时间不可用'; - const redeemedAt = formatResetCardTime(card.redeemed_at); const redeeming = redeemingResetCardId === card.id; return (
- {status === 'available' ? '可使用' : status === 'expired' ? '已过期' : '已使用'} + {status === 'available' ? '可使用' : '已过期'}

有效期至 {expiresAt}

- {status === 'redeemed' && redeemedAt ? ( -

使用于 {redeemedAt}

- ) : null} {status === 'available' ? (