Refactor the item shortcut bar and add a new item warehouse management panel.#1600
Refactor the item shortcut bar and add a new item warehouse management panel.#1600ErrorAP717 wants to merge 28 commits into
Conversation
- 解除道具菜单图标在暗色模式下被全局滤镜压暗的问题 - 将 PNG 图标置于气泡高光层之上,并保留原始图像透明度 - 移除额外白色光晕,仅保留暗色气泡上的原始 PNG 呈现
新增头像道具快捷栏与管理器组件,抽离道具配置和图片解析逻辑。\n\n接入紧凑聊天输入轮盘,支持快捷道具选择、取消、保存到本地存储,并在已选道具被移除时清理当前光标状态。
补齐头像快捷栏和道具管理弹窗的明暗主题、移动端与低动效样式。\n\n同步新增 8 个语言包中的头像道具管理文案,保证可访问标签和界面文字都有 locale key。
更新紧凑聊天头像道具相关断言,适配快捷栏替代旧工具气泡的交互。\n\n新增管理弹窗定位和拖动测试,并补充选择、取消与恢复鼠标状态的回归覆盖。
新增项目级交接说明,沉淀长期约束、验证方式、提交边界和常见任务处理原则。\n\n文档与头像道具功能提交分离,方便后续审阅或单独回退。
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
💤 Files with no reviewable changes (1)
Walkthrough在聊天界面新增 Avatar Tools:类型与资源解析、Quickbar、Manager 对话框、App 集成与持久化、样式、八语种 i18n、模板版本化与前端/回归测试覆盖喵。 Changes头像工具快捷管理系统
Estimated code review effort: Possibly related PRs:
Sequence Diagram (high-level flow): sequenceDiagram
participant User
participant App
participant AvatarToolQuickbar
participant AvatarToolItemManager
participant LocalStorage
User->>App: 点击 "Avatar tools" 打开 quickbar / 管理器入口
App->>AvatarToolQuickbar: 渲染 activeToolIds 与按钮交互绑定
AvatarToolQuickbar->>App: onEditClick -> 提供 anchorRect 并 open 管理器
App->>AvatarToolItemManager: open with anchorRect / activeToolIds
AvatarToolItemManager->>LocalStorage: onSave -> compactSlots -> persistActiveAvatarToolIds()
AvatarToolItemManager->>App: onSave 回传已保存 ids (关闭 dialog)
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.agent/project-handoff-notes.md:
- Around line 215-216: 当前文档中关于“无需确认直接执行”与“高风险场景需确认/补证据”存在冲突;请把执行策略统一为“默认直执行 +
风险场景强制确认”,并在本段明确添加一条例外白名单和一条优先级说明(例如“当触及高风险场景时,必须遵循强制确认规则并覆盖默认直执行”),同时在文档其他出现同类表述的位置(与本段语义等价的段落)同步替换为同一表述以避免分叉。确保描述包含如何判定“高风险场景”(简短示例或引用标准)和确认流程的最小步骤,以便实现一致行为。
- Around line 224-230: The rule that ".agent 内的文件都不提交" conflicts with this PR;
update .agent/project-handoff-notes.md to add a clear exception: introduce a new
clause (e.g., after rule 1) stating
"项目级基线文档可在明确批准下入库,需单独提交并在提交信息中注明审批单号/批准人",and modify rules 1-3 to reference this
exception (keep default: all other .agent/* not committed). Ensure the exception
requires explicit approval, a separate commit with clear title, and confirmation
that .agent/notes/ remains unstaged before pushing.
In `@frontend/react-neko-chat/src/App.test.tsx`:
- Around line 3219-3298: The tests mutate avatar-tool persistence in
localStorage but the shared beforeEach only clears the export-history key;
update the shared test setup beforeEach to also clear the avatar tool storage
keys by importing the exported key constants from avatarTools.ts (e.g. the
module's exported storage key constants) and calling localStorage.removeItem for
each (and/or localStorage.clear if appropriate) so tests like the default slot
assertions and initial aria-pressed checks are isolated and deterministic.
In `@frontend/react-neko-chat/src/App.tsx`:
- Around line 4468-4475: The AvatarToolItemManager can reopen unexpectedly
because you only gate its open prop with isCompactSurface &&
avatarToolManagerOpen but never reset avatarToolManagerOpen when the host leaves
compact surface; add logic to close/reset the manager whenever isCompactSurface
becomes false (for example in a useEffect that watches isCompactSurface) by
calling setAvatarToolManagerOpen(false) so avatarToolManagerOpen is cleared when
the surface is not compact; update references to AvatarToolItemManager,
isCompactSurface, avatarToolManagerOpen, and setAvatarToolManagerOpen
accordingly.
In `@frontend/react-neko-chat/src/AvatarToolItemManager.tsx`:
- Around line 199-247: When the AvatarToolItemManager dialog opens it does not
take focus and on close it does not restore focus to the trigger, causing
keyboard users to tab into background content; fix this by adding focus
management: inside AvatarToolItemManager create a ref to store the previously
focused element (e.g. prevActiveElementRef), then in an effect that watches open
save document.activeElement into that ref when opening, move focus into the
dialog (use dialogRef.current and focus the first focusable element or the
dialog container), and when open becomes false restore focus to
prevActiveElementRef.current; also add a keydown handler on the dialog (attached
to dialogRef) to trap Tab cycling within dialogRef when open so Tab/Shift+Tab do
not escape to the page. Ensure cleanup removes the keydown listener and does not
run when typeof window === 'undefined'.
In `@frontend/react-neko-chat/src/styles.css`:
- Around line 3855-3864: The CSS sets touch-action: none on the whole draggable
header (.avatar-tool-manager-header) which prevents native vertical scrolling
inside the scrollable panel (.avatar-tool-manager-body); change the approach so
the default CSS uses touch-action: pan-y (or remove touch-action) on the
header/cards and only set touch-action: none when a real drag session starts (or
restrict the drag to a smaller dedicated handle class instead of the entire
header/card); update the styles for .avatar-tool-manager-header and any card
classes and add a short JS toggle in the drag start/stop handlers to
apply/remove a "dragging" class that flips touch-action to none during active
dragging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: db97845e-93af-4daf-8735-bed48daabaca
⛔ Files ignored due to path filters (1)
static/icons/edit_tool_unified.pngis excluded by!**/*.png
📒 Files selected for processing (15)
.agent/project-handoff-notes.mdfrontend/react-neko-chat/src/App.test.tsxfrontend/react-neko-chat/src/App.tsxfrontend/react-neko-chat/src/AvatarToolItemManager.tsxfrontend/react-neko-chat/src/AvatarToolQuickbar.tsxfrontend/react-neko-chat/src/avatarTools.tsfrontend/react-neko-chat/src/styles.cssstatic/locales/en.jsonstatic/locales/es.jsonstatic/locales/ja.jsonstatic/locales/ko.jsonstatic/locales/pt.jsonstatic/locales/ru.jsonstatic/locales/zh-CN.jsonstatic/locales/zh-TW.json
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2c4410e6f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| ## 提交与工作区 | ||
|
|
||
| 1. 用户明确要求:`.agent` 内的文件都不提交。 |
There was a problem hiding this comment.
Remove committed .agent handoff notes
This commit adds .agent/project-handoff-notes.md to version control even though the added project instructions state that .agent files are working memory and must not be committed. Keeping this file in the feature commit leaks transient workflow notes and violates the repo's own submission boundary, so it should be removed from the commit and left untracked unless explicitly requested separately.
Useful? React with 👍 / 👎.
| <img | ||
| className="avatar-tool-quickbar-edit-image" | ||
| src="/static/icons/edit_tool_unified.png" |
There was a problem hiding this comment.
Downscale oversized quickbar edit icon
This quickbar button loads /static/icons/edit_tool_unified.png; I checked the committed PNG and it is 2,282×1,856 pixels and about 3.25 MB, while the CSS displays it at only 52×43 px. Any user who opens the avatar tools pays the download and decode cost for a multi-megabyte image on a tiny button, which is avoidable latency and memory pressure, especially on mobile or slower connections; please replace it with a resized/compressed asset.
Useful? React with 👍 / 👎.
… test isolation, docs) 包含焦点陷阱、touch-action、测试隔离和项目基线文档规则修复。批准人:用户本轮明确要求提交。
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.agent/project-handoff-notes.md:
- Line 208: 将高风险判定条目中语句“非代码任务要把产物保存到仓库路径”替换为更通顺的“会把产物保存到仓库路径”;定位该更改在
.agent/project-handoff-notes.md
文件中包含“高风险场景判定:任一命中即视为高风险……”的那一行,直接将原短语替换为“会把产物保存到仓库路径”以消除歧义并保持句子结构一致。
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: c5c16f24-6a0b-49d8-967c-d0c19d1d5b2c
📒 Files selected for processing (5)
.agent/project-handoff-notes.mdfrontend/react-neko-chat/src/App.test.tsxfrontend/react-neko-chat/src/App.tsxfrontend/react-neko-chat/src/AvatarToolItemManager.tsxfrontend/react-neko-chat/src/styles.css
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend/react-neko-chat/src/styles.css
- frontend/react-neko-chat/src/AvatarToolItemManager.tsx
- frontend/react-neko-chat/src/App.tsx
- frontend/react-neko-chat/src/App.test.tsx
解决 App.tsx 清理逻辑冲突,同时保留 setToolMenuOpen(false) 与 compact fan dispatch 清理逻辑。
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 41791ba16f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| return createPortal( | ||
| <div | ||
| className="avatar-tool-manager-overlay" |
There was a problem hiding this comment.
Stop manager clicks from unmounting the opener
Because this manager is portaled to document.body, a real mouse interaction inside the dialog fires mousedown outside both toolMenuRef and compactInputToolFanRef, so App.tsx's outside-click listener closes toolMenuOpen before the dialog's button click runs. That unmounts the quickbar/edit opener, so the cleanup that tries to restore focus to the previous element after Save/Close has no element to focus and leaves focus on the document body; the existing focus-restore test uses fireEvent.click only, so it misses the browser mousedown ordering. Exclude the manager overlay from that outside-click close path or stop the overlay/dialog mousedown from reaching it.
Useful? React with 👍 / 👎.
将高风险判定中的产物保存描述改为更通顺的一致句式。批准人:用户本轮明确要求提交并推送。
Integrate upstream changes: compact preview grapheme animation, tool popover CSS class refactor, and active-state attribute ordering. Keep avatar-tool-quickbar IDs from tools branch.
…ement - Add react_chat_asset_version to template script/css tags for cache-busting avatar tool icons independently of static_asset_version - Register icon assets in _REACT_CHAT_ASSET_VERSION_PATHS - Expose withAvatarToolAssetVersion() and wire it into active tool image path resolution - Refactor AvatarToolItemManager drag/drop layout and positioning - Expand test coverage for quickbar and manager interactions
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
static/app-react-chat-window.js (1)
1274-1279:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
avatar-tool-manager-dialog被反向放行进 compact 几何收集了喵这里的条件是“根外元素白名单”,当前把
.avatar-tool-manager-dialog加进了白名单,结果是它会被继续收集,而不是被排除;这和本 PR 的目标(manager dialog 不参与 compact geometry)相反喵。建议显式早退排除它喵。建议修复(最小改动)
return elements.reduce(function (items, element) { if ( !root.contains(element) && !element.classList.contains('compact-input-tool-fan') - && !element.classList.contains('compact-chat-choice-anchor') - && !element.classList.contains('avatar-tool-manager-dialog') + && !element.classList.contains('compact-chat-choice-anchor') ) return items; + if (element.classList.contains('avatar-tool-manager-dialog')) return items; if (!shouldIncludeCompactGeometryElement(element)) return items;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@static/app-react-chat-window.js` around lines 1274 - 1279, The current whitelist check wrongly allows elements with class 'avatar-tool-manager-dialog' into the compact geometry collection (the conditional using root.contains(element) and element.classList.contains(...)), so change the logic to explicitly exclude that dialog: add an early-return when element.classList.contains('avatar-tool-manager-dialog') so it returns items (skipping collection) before the whitelist check; keep other class checks and the root.contains(element) behavior intact.
🧹 Nitpick comments (4)
frontend/react-neko-chat/src/App.test.tsx (1)
3-7: ⚡ Quick win让这组回归断言再“脱离实现”一点喵
Line 632-639 和 Line 680-690 直接复用了
computeCompactHistoryEnterDelay/ExitDelay,一旦 helper 自己算错,组件和测试会一起假绿;同时 Line 4158-4175 现在只钉住了 quickbar/manager 的静态图,avatarTools.ts里同样会走版本包装的激活态 cursor/overlay 路径还没被覆盖到。这里更稳的是:delay 断言改成固定期望值,版本号用例里再激活一个工具,顺手把 overlay 或tool.cursorImagePath的?v=也一起钉住喵。Also applies to: 631-639, 680-690, 4158-4175
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/react-neko-chat/src/App.test.tsx` around lines 3 - 7, 当前测试复用 computeCompactHistoryEnterDelay / computeCompactHistoryExitDelay 导致实现与测试耦合且漏掉 avatarTools.ts 的版本化路径;请将对延迟的断言改为使用固定期望值(直接断言组件中使用的毫秒常量,而不是调用 computeCompactHistoryEnterDelay/computeCompactHistoryExitDelay),并在版本号相关用例中激活一个额外工具(通过 quickbar/manager 激活路径)以覆盖 avatarTools.ts 的版本化激活分支,顺便将 overlay 或 tool.cursorImagePath 的 URL 中的 ?v= 查询参数也固定断言(钉住具体期望的版本后缀)以防止假绿。frontend/react-neko-chat/src/styles.css (3)
3556-3566: ⚖️ Poor tradeoffemoji 按钮样式覆盖有点多喵?
这里用了三条规则把
.compact-input-tool-fan里的 emoji 按钮背景都设成透明喵。虽然功能上没问题,但感觉像是在和上游的.composer-emoji-btn样式"打架"喵~可以考虑:
- 要么在 avatar item 上加个更具体的 class
- 要么调整一下选择器优先级
不过现在这样写确实很明确,维护起来也清楚喵。就当我随口提一嘴啦~ 😺
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/react-neko-chat/src/styles.css` around lines 3556 - 3566, The CSS rule overly overrides upstream .composer-emoji-btn styles by blanket-targeting .compact-input-tool-fan .compact-input-tool-item-avatar > .composer-emoji-btn; to fix, narrow the scope—either add a dedicated modifier class to the avatar wrapper (e.g., .compact-input-tool-item-avatar--avatar-override) and change the rule to target that modifier together with .composer-emoji-btn, or reduce selector specificity by targeting only the exact state you need (e.g., the avatar modifier or a more specific descendant) so you stop clobbering global .composer-emoji-btn styles; update the stylesheet selectors (.compact-input-tool-fan, .compact-input-tool-item-avatar, .composer-emoji-btn) accordingly.
2899-2927: ⚖️ Poor tradeoff呼吸动画滤镜开销可能较高喵~
这里的
compact-history-handle-breathe和compact-history-handle-glow-breathe关键帧用了drop-shadow()和blur()滤镜配合 infinite 动画循环喵。在一些低端设备上连续跑滤镜动画可能会有点卡顿喵。如果追求极致性能,可以考虑把滤镜效果烘焙到静态图片里,然后只用
opacity做呼吸动画喵。不过现在这个视觉效果确实很精致,就看主人你更看重哪边啦喵~ ✨🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/react-neko-chat/src/styles.css` around lines 2899 - 2927, The keyframe animations compact-history-handle-breathe and compact-history-handle-glow-breathe animate expensive CSS filters (drop-shadow) which can cause jank on low-end devices; to fix, remove animated filter usage and instead bake the glow/shadow into a static asset or a non-animated pseudo-element (e.g., .compact-history-handle::before) and only animate cheap properties like opacity and transform in those keyframes, or if you must keep a dynamic glow, move the heavy filter to a static element and animate an overlaid element's opacity/scale; also add will-change: opacity, transform to the animated element and respect prefers-reduced-motion media query to disable the breathing animation for reduced-motion users.
5145-5152: 💤 Low value渐变定义可以考虑复用喵?
这里暗色模式的 visibility handle 光晕渐变(Lines 5145-5152)和浅色模式(Lines 2879-2886)的梯度停止点位置完全一样喵(都是 0% / 22% / 50% / 78% / 100%),只是颜色不同喵。
如果想减少重复,可以把这些百分比提取成 CSS 自定义属性喵:
--handle-glow-stops: 0%, 22%, 50%, 78%, 100%;不过现在这样写也挺清楚的,除非以后要统一调整这些 stop 值,不然改不改都行喵~ 🎨
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/react-neko-chat/src/styles.css` around lines 5145 - 5152, The gradient stop positions for the visibility-handle glow are duplicated between dark and light mode; extract the shared stop percentages into a CSS custom property (e.g., --handle-glow-stops: 0%, 22%, 50%, 78%, 100%) and then use that variable in both gradient declarations (the current linear-gradient blocks for the visibility handle in dark and light modes) so only the color RGBA values differ while the stop positions are reused; update the selectors that define the glow gradients to reference var(--handle-glow-stops).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@static/app-react-chat-window.js`:
- Around line 1274-1279: The current whitelist check wrongly allows elements
with class 'avatar-tool-manager-dialog' into the compact geometry collection
(the conditional using root.contains(element) and
element.classList.contains(...)), so change the logic to explicitly exclude that
dialog: add an early-return when
element.classList.contains('avatar-tool-manager-dialog') so it returns items
(skipping collection) before the whitelist check; keep other class checks and
the root.contains(element) behavior intact.
---
Nitpick comments:
In `@frontend/react-neko-chat/src/App.test.tsx`:
- Around line 3-7: 当前测试复用 computeCompactHistoryEnterDelay /
computeCompactHistoryExitDelay 导致实现与测试耦合且漏掉 avatarTools.ts
的版本化路径;请将对延迟的断言改为使用固定期望值(直接断言组件中使用的毫秒常量,而不是调用
computeCompactHistoryEnterDelay/computeCompactHistoryExitDelay),并在版本号相关用例中激活一个额外工具(通过
quickbar/manager 激活路径)以覆盖 avatarTools.ts 的版本化激活分支,顺便将 overlay 或
tool.cursorImagePath 的 URL 中的 ?v= 查询参数也固定断言(钉住具体期望的版本后缀)以防止假绿。
In `@frontend/react-neko-chat/src/styles.css`:
- Around line 3556-3566: The CSS rule overly overrides upstream
.composer-emoji-btn styles by blanket-targeting .compact-input-tool-fan
.compact-input-tool-item-avatar > .composer-emoji-btn; to fix, narrow the
scope—either add a dedicated modifier class to the avatar wrapper (e.g.,
.compact-input-tool-item-avatar--avatar-override) and change the rule to target
that modifier together with .composer-emoji-btn, or reduce selector specificity
by targeting only the exact state you need (e.g., the avatar modifier or a more
specific descendant) so you stop clobbering global .composer-emoji-btn styles;
update the stylesheet selectors (.compact-input-tool-fan,
.compact-input-tool-item-avatar, .composer-emoji-btn) accordingly.
- Around line 2899-2927: The keyframe animations compact-history-handle-breathe
and compact-history-handle-glow-breathe animate expensive CSS filters
(drop-shadow) which can cause jank on low-end devices; to fix, remove animated
filter usage and instead bake the glow/shadow into a static asset or a
non-animated pseudo-element (e.g., .compact-history-handle::before) and only
animate cheap properties like opacity and transform in those keyframes, or if
you must keep a dynamic glow, move the heavy filter to a static element and
animate an overlaid element's opacity/scale; also add will-change: opacity,
transform to the animated element and respect prefers-reduced-motion media query
to disable the breathing animation for reduced-motion users.
- Around line 5145-5152: The gradient stop positions for the visibility-handle
glow are duplicated between dark and light mode; extract the shared stop
percentages into a CSS custom property (e.g., --handle-glow-stops: 0%, 22%, 50%,
78%, 100%) and then use that variable in both gradient declarations (the current
linear-gradient blocks for the visibility handle in dark and light modes) so
only the color RGBA values differ while the stop positions are reused; update
the selectors that define the glow gradients to reference
var(--handle-glow-stops).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2ab1b700-9b13-4ce9-b88d-60147989619e
⛔ Files ignored due to path filters (1)
.agent/image/project-handoff-notes/1780475178015.pngis excluded by!**/*.png
📒 Files selected for processing (12)
.agent/project-handoff-notes.mdfrontend/react-neko-chat/src/App.test.tsxfrontend/react-neko-chat/src/App.tsxfrontend/react-neko-chat/src/AvatarToolItemManager.tsxfrontend/react-neko-chat/src/AvatarToolQuickbar.tsxfrontend/react-neko-chat/src/avatarTools.tsfrontend/react-neko-chat/src/styles.cssmain_routers/pages_router.pystatic/app-react-chat-window.jstemplates/chat.htmltemplates/index.htmltests/test_agent_rewrite_regression.py
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend/react-neko-chat/src/AvatarToolQuickbar.tsx
- frontend/react-neko-chat/src/avatarTools.ts
- frontend/react-neko-chat/src/AvatarToolItemManager.tsx
- frontend/react-neko-chat/src/App.tsx
…ollection The dialog was mistakenly added to the root-external whitelist, causing it to be included in compact geometry. Move it to an explicit early-return so it is always skipped regardless of containment.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f547c120f1
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| '.avatar-tool-quickbar', | ||
| '.avatar-tool-manager-overlay', |
There was a problem hiding this comment.
Treat the manager dialog as a compact cursor zone
When an avatar cursor tool is active and the manager opens in the anchored desktop mode, clicks and pointer moves inside the dialog are not recognized as compact UI: the only new manager selector here is the overlay, but .avatar-tool-manager-overlay.is-desktop has pointer-events: none, so elementsFromPoint/event.target.closest(...) see the dialog/buttons instead. The global cursor-tool handlers then treat manager clicks as avatar-area clicks, which can change variants or emit tool interactions while the user is just saving/removing tools; include .avatar-tool-manager-dialog in this selector as well.
Useful? React with 👍 / 👎.
…guard outside-click - Remove .agent/project-handoff-notes.md and its image from version control (working memory, not meant for commits) - Downscale edit_tool_unified.png from 2282×1856 / 3.1 MB to 156×128 / 15 KB (3× retina of the 52×43 display size) - Exclude avatar-tool-manager overlay/dialog from the compact tool-menu outside-click listener so portal interactions no longer close the opener and unmount the quickbar
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c622900157
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if ((window as any).nekoChatWindow?.enableInteraction) { | ||
| (window as any).nekoChatWindow.enableInteraction(); |
There was a problem hiding this comment.
Call the actual chat host when restoring interaction
When the manager opens in centered/mobile mode after the host has set document.body.style.pointerEvents = 'none', this branch never restores interaction because the repo exposes the bridge as window.reactChatWindowHost and there is no window.nekoChatWindow/enableInteraction implementation (repo-wide search only finds this reference). As a result the modal can remain non-interactive in that context despite setting the flag; use the real host API or explicitly restore the previous pointer-events value while the dialog is open.
Useful? React with 👍 / 👎.
…ents - Add .avatar-tool-manager-dialog to compactCursorZoneSelector so clicks inside the manager are not misinterpreted as avatar-area tool interactions when a cursor tool is active - Replace dead nekoChatWindow.enableInteraction call with direct document.body.style.pointerEvents restore/cleanup for centered mobile mode
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5dee50a72b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (eventTarget instanceof Element) { | ||
| const eventTargetSlotIndex = getDropSlotIndexFromElement(eventTarget); | ||
| if (eventTargetSlotIndex !== null) return eventTargetSlotIndex; |
There was a problem hiding this comment.
Hit-test captured slot drags by coordinates first
When dragging an equipped tool from one slot to another, startDrag captures the pointer on the source slot button, so the pointerup event target remains that source button even if the cursor is over a different slot. This early return therefore resolves the drop back to the original slot before elementsFromPoint() can inspect the actual coordinates, making slot-to-slot reordering a no-op in real pointer-capture interactions; skip the event-target shortcut for captured drags or prefer coordinate hit-testing.
Useful? React with 👍 / 👎.
Pointer capture keeps the pointerup target pinned to the source slot button, so the event-target shortcut resolved every slot drag back to its origin. Reorder to try elementsFromPoint first, falling back to event target only when coordinate hit-test finds nothing.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c546423b3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| useEffect(() => { | ||
| if (!open || typeof window === 'undefined') return undefined; | ||
| if (!isPositioned) { |
There was a problem hiding this comment.
Restore pointer events for anchored manager too
When the manager opens in anchored/desktop mode (dialogPosition !== null, which is the default at desktop widths and in Electron), this branch skips the body pointer-events restore entirely and only dispatches a resize event. If the host has already set document.body.style.pointerEvents = 'none', the portaled dialog remains under a non-interactive body and cannot be clicked; fresh evidence is that the current code only clears/restores pointerEvents inside the !isPositioned branch, while the new test setup in App.test.tsx opens at desktop width with body pointer events disabled.
Useful? React with 👍 / 👎.
| <div | ||
| id="composer-avatar-tool-quickbar" | ||
| className="avatar-tool-quickbar" |
There was a problem hiding this comment.
Include the quickbar in compact host geometry
In the Electron compact/minimize-ball host, static/app-react-chat-window.js builds native/hit rects for the tool fan from .compact-input-tool-item and the old .composer-icon-popover .composer-icon-button selector. This new quickbar is absolutely positioned outside the fan’s own rect but uses only .avatar-tool-quickbar, so the quick tool and edit buttons no longer contribute native hit regions; in that compact desktop context, the visible controls outside the base fan can be missed by click-through/hit testing unless the host geometry selector is updated or the quickbar keeps a collected selector.
Useful? React with 👍 / 👎.
…ometry - Move pointer-events save/restore out of the !isPositioned branch so the anchored desktop manager also clears body pointer-events block while open - Add .avatar-tool-quickbar .composer-icon-button to compact tool fan geometry selector so quickbar buttons contribute native hit regions in Electron compact host
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 33c0ca74e4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| }); | ||
| } | ||
| return items.concat(Array.prototype.slice.call(element.querySelectorAll('.compact-input-tool-item, .composer-icon-popover .composer-icon-button')) | ||
| return items.concat(Array.prototype.slice.call(element.querySelectorAll('.compact-input-tool-item, .composer-icon-popover .composer-icon-button, .avatar-tool-quickbar .composer-icon-button')) |
There was a problem hiding this comment.
Add the edit button to compact hit geometry
In the compact Electron/minimize-ball host, I checked collectCompactToolFanGeometryItems and this selector now adds quickbar tool buttons, but it still misses the visible edit button because AvatarToolQuickbar.tsx renders it as .avatar-tool-quickbar-edit without the composer-icon-button class. The edit control is absolutely positioned with the quickbar outside the fan's base rect, so its native/hit region is not reported and clicks on the edit button can still fall through or be ignored in that compact host context; include the edit button selector here as well.
Useful? React with 👍 / 👎.
The edit button uses .avatar-tool-quickbar-edit without the composer-icon-button class, so the previous selector missed it.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 20354616d4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| }); | ||
| } | ||
| return items.concat(Array.prototype.slice.call(element.querySelectorAll('.compact-input-tool-item, .composer-icon-popover .composer-icon-button')) | ||
| return items.concat(Array.prototype.slice.call(element.querySelectorAll('.compact-input-tool-item, .composer-icon-popover .composer-icon-button, .avatar-tool-quickbar .composer-icon-button, .avatar-tool-quickbar-edit')) |
There was a problem hiding this comment.
Keep the edit button from being filtered out
Fresh evidence: although this selector now includes .avatar-tool-quickbar-edit, the code immediately below still sets isAvatarToolChoice only for .composer-icon-button and then drops every non-choice without a data-compact-tool-wheel-slot, so the edit button is still filtered out before a rect is emitted. In the compact Electron/minimize-ball host, clicks on the visible edit control can therefore remain outside the reported hit/native geometry; either mark the edit button as an avatar tool choice or exempt it from the slot filter.
Useful? React with 👍 / 👎.
| }); | ||
| } | ||
| return elements.reduce(function (items, element) { | ||
| if (element.classList.contains('avatar-tool-manager-dialog')) return items; |
There was a problem hiding this comment.
Report the manager dialog in compact geometry
When the avatar tool manager opens in the compact minimize-ball/Electron flow, the React dialog is portaled to document.body with data-compact-geometry-owner="surface", but this early return discards it before its rect can be added to surfaceNativeRects/extraIslandNativeRects. That means the native hit/shape geometry stays tied to the old compact surface and does not include the visible manager, so the dialog can be clipped or non-interactive in the host context that consumes this geometry; include the manager as an extra island instead of skipping it.
Useful? React with 👍 / 👎.
…g as extra island - Treat .avatar-tool-quickbar-edit as an avatar tool choice so the slot filter does not discard it from compact hit geometry - Report the manager dialog as an extraIsland geometry item instead of skipping it entirely, so the compact host includes the dialog in its native/hit region
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3e9b2d6d70
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| fireEvent.click(editButton); | ||
|
|
||
| expect(await screen.findByRole('dialog', { name: 'Manage tools' })).toBeInTheDocument(); | ||
| expect(document.body.style.pointerEvents).toBe('auto'); |
There was a problem hiding this comment.
Fix the pointer-events assertion
This assertion cannot pass against the implementation in AvatarToolItemManager: when the dialog opens with document.body.style.pointerEvents === 'none', it clears the inline style to '', not 'auto'. In a CI run with the React test dependencies installed, this newly added test will fail even though the intended browser behavior may be to restore computed pointer events; either set the inline style to auto while open or assert the computed style instead.
Useful? React with 👍 / 👎.
…interactive flag - Fix test expecting 'auto' when implementation clears to '' (empty string) - Remove unused __nekoForceInteractive window flag that no code reads
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b31a2337f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const emojiButtonAriaLabel = i18n('chat.emojiButtonAriaLabel', 'Emoji'); | ||
| const toolIconsAriaLabel = i18n('chat.toolIconsAriaLabel', 'Tool icons'); |
There was a problem hiding this comment.
Remove the unused avatar label constants
With noUnusedLocals enabled in frontend/react-neko-chat/tsconfig.app.json, these two constants are now unreferenced after the quickbar refactor removed selectedEmojiButtonAriaLabel and the old tool-icons group label. In any build/typecheck run, TypeScript reports TS6133 for them and blocks the React chat bundle; either delete the constants or wire them into the new UI.
Useful? React with 👍 / 👎.
emojiButtonAriaLabel and toolIconsAriaLabel were left over from the quickbar refactor and never referenced — TS6133 blocked the build.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 58face790d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| .avatar-tool-manager-dialog.is-positioned { | ||
| position: relative; | ||
| left: auto; | ||
| top: auto; |
There was a problem hiding this comment.
Preserve fixed positioning for Electron tool manager
In the Electron compact chat context with a viewport at or below 640px, resolveAnchoredDialogPosition still returns a non-null position because isElectronDesktopEnvironment() bypasses the mobile fallback, so the dialog gets is-positioned. This media rule then changes that same positioned dialog to normal-flow position: relative and discards the computed left/top; because the portal is appended after the app root, the manager can render below/off the compact window instead of anchored to the edit button. Scope this override to non-Electron/mobile mode or keep the fixed coordinates when is-positioned is used.
Useful? React with 👍 / 👎.
…query The @media (max-width: 640px) rule forced .is-positioned dialogs to position:relative, which breaks Electron anchoring at narrow viewports. Non-Electron already returns null (no is-positioned class) so the rule never matched there — it was dead code outside Electron and harmful inside it.
优化 N.E.K.O 聊天窗口中的道具交互体验。将原有的放射状轮盘布局重构为更清爽的“水平胶囊快捷栏”,并引入了独立的“道具仓库管理面板”,实现了快捷释放与道具配置的逻辑解耦,为后续扩充更多交互道具奠定基础。
Summary by CodeRabbit
新功能
可访问性
本地化
样式
测试
维护