Skip to content

Character selecfeat(character-selection): Add first-launch character selection & tutorial robustness improvementstion guide#527

Open
yiyiyiyiGKY wants to merge 39 commits intoProject-N-E-K-O:mainfrom
yiyiyiyiGKY:character_selection_guide
Open

Character selecfeat(character-selection): Add first-launch character selection & tutorial robustness improvementstion guide#527
yiyiyiyiGKY wants to merge 39 commits intoProject-N-E-K-O:mainfrom
yiyiyiyiGKY:character_selection_guide

Conversation

@yiyiyiyiGKY
Copy link
Copy Markdown
Contributor

@yiyiyiyiGKY yiyiyiyiGKY commented Mar 22, 2026

新功能

新增多阶段角色选择向导:引导、角色卡选择、问候互动(打字效果)、最终确认与跳过,选择可保存并完成同步流程。
样式

全屏星光背景与分层动画、卡片入场/悬停/选中动效、响应式布局、交互星点特效、背景音及减弱动画支持。
本地化

补充多语种角色选择文案(en/ja/ko/ru/zh-CN/zh-TW)。
文档

新增角色选择设计与使用指南。
修复 / 强化

教程管理、本地存储与视口校验更健壮;初始化、清理与加载回退流程改进。

Summary by CodeRabbit

  • 新功能

    • 新增四阶段角色甄选引导(启动→选卡→问候→最终确认),支持跳过、重启、完成标记并更新默认角色设置。
  • 界面与交互

    • 全屏视觉与动效(星空、卡片入场、打字机、点击粒子)、响应式与键盘无障碍、降低运动模式兼容、背景音乐与交互降级/出错处理。
  • 多语言

    • 扩展多语文案与四种角色预设,更新本地化资源版本以刷新缓存。
  • 文档

    • 新增详尽角色甄选指南(流程、存储与 API 列表、示例)。
  • 改进

    • 教程重置加入角色甄选项、默认角色重命名同步与布局/存储容错增强。

yiyiyiyiGKY and others added 27 commits March 12, 2026 09:07
…h disabled state

引导系统新增跳过按钮及重置按钮禁用状态优化

## 新增功能 (New Features)

### 1. 引导系统跳过按钮
- 右上角添加跳过按钮,允许用户随时跳过引导
- 支持6种语言的本地化:
  - 简体中文: 跳过
  - 繁体中文: 跳過
  - English: Skip
  - 日本語: スキップ
  - 한국어: 건너뛰기
  - Русский: Пропустить
- 引导结束时自动移除按钮

### 2. 记忆浏览页面 - 重置按钮禁用状态
- 默认选择"--选择页面--"时,重置按钮自动禁用
- 选择实际页面后启用重置功能
- 禁用状态视觉反馈:
  - 浅色模式:灰色背景、降低透明度、禁止光标
  - 深色模式:深色背景、降低透明度、禁止光标
  - 禁用悬停和点击效果

## UI/UX 改进 (UI/UX Improvements)
- 跳过按钮:固定右上角、半透明背景、悬停高亮效果
- 重置按钮:清晰的禁用状态提示、无障碍光标反馈
- 主题适配:浅色/深色模式完整支持

## 技术实现 (Technical Implementation)
- **跳过按钮**:
  - JavaScript: 添加 `showSkipButton()` 和 `hideSkipButton()` 方法
  - CSS: `#neko-tutorial-skip-btn` 样式定义
  - i18n: 所有语言文件添加 `tutorial.buttons.skip` 翻译键

- **重置按钮**:
  - HTML: 添加 `disabled` 属性和唯一 ID
  - JavaScript: 下拉框 `change` 事件监听器
  - CSS: `:disabled` 伪类样式定义

## 影响文件 (Affected Files)
- `static/universal-tutorial-manager.js` - 跳过按钮逻辑 (+38行)
- `static/css/tutorial-styles.css` - 跳过按钮样式 (+28行)
- `static/css/dark-mode.css` - 深色模式重置按钮样式 (+18行)
- `static/css/memory_browser.css` - 浅色模式重置按钮样式 (+18行)
- `static/js/memory_browser.js` - 重置按钮状态管理 (+14行)
- `templates/memory_browser.html` - 重置按钮 HTML 结构 (+2行)
- `templates/*.html` (5个模板文件) - 添加 tutorial-styles.css 引用 (+5行)
- `static/locales/*.json` (6个语言文件) - 跳过按钮翻译 (+6行)

**总计:+135行,-7行**
- Remove obsolete @param from showSkipButton JSDoc
- Initialize reset button state from select value (supports browser form restoration)
Move `console` section after `tutorial` to align with en.json key ordering,
fixing line number correspondence between ru.json and other locale files.
…aunch

新增角色甄选功能,用户首次启动时通过三阶段引导选择 AI 伴侣性格(傲娇猫系、高冷机械、知性治愈、效率专家)。包含完整的 HTML 结构、CSS 动效、JavaScript 交互逻辑及六语种 i18n 支持。
修复引导弹窗在视口尺寸或位移量计算结果无效时(Infinity/NaN)的崩溃问题;对视口宽高增加最小值保护,并在最终定位前添加有效性校验,避免弹窗位置异常。
…letion screen

- Replace contract/soul-resonance wording with casual, first-person tone
- Rename character presets: 傲娇叛逆 / 绝对理智 / 极致温柔 / 优雅利落
- Add per-character readyTitle & readyDesc to all locale files (zh-CN/TW, en, ja, ko, ru)
- Connecting screen (stage 3) now shows character-specific readyTitle instead of generic text
- updateFinalInfo() looks up per-character readyTitle/readyDesc with generic fallback
- Fix: renameCatgirl now syncs neko_default_catgirl_name in localStorage
- Add keyboard support for character cards (Enter/Space, aria-pressed)
- Add :focus-visible focus ring for keyboard users
- Add clearTimeout debounce to prevent duplicate goToStage(3) calls
- Wrap CharacterSelection instantiation in try/catch via shared handler
- Add data-i18n to greeting-title, replace stale hardcoded fallback text
- Update character-guide.md: reorganise into design goals / implementation / backlog
- Add language markers to fenced code blocks (fix MD040 lint warnings)
- Clarify JSON quote escaping in translation files documentation
- Consolidate two PUT requests into single atomic operation
- Add prefers-reduced-motion support for continuous animations
- Add 100dvh fallback for better mobile viewport support
- Fix CSS viewport-height fallback order (100vh before 100dvh)
- Add instance-level _typeTimer tracking to prevent orphaned intervals
- Add clearTypeTimer() method for external timer cleanup
- Clear typeTimer in close() to prevent DOM updates after unmount
- Add defensive null checks for all DOM elements in playGreeting
- Add console.warn messages for easier debugging of missing elements
- Add mobile vertical scrolling support with overflow-y: auto
- Fix completion flag race condition: write immediately on success/skip
- Prevent localized field persistence by requesting canonical data (?language=zh-CN)
- Update Russian localization to gender-neutral form (Передумали?)
- Allow retry on updateDefaultCatgirl() failure
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 01bd7833-380f-4b15-a30b-3a9ee8e596da

📥 Commits

Reviewing files that changed from the base of the PR and between 6b0a67c and a93cfc5.

📒 Files selected for processing (8)
  • static/js/chara_manager.js
  • static/locales/en.json
  • static/locales/ja.json
  • static/locales/ko.json
  • static/locales/ru.json
  • static/locales/zh-CN.json
  • static/locales/zh-TW.json
  • templates/index.html
✅ Files skipped from review due to trivial changes (2)
  • static/js/chara_manager.js
  • static/locales/ru.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • templates/index.html
  • static/locales/ja.json
  • static/locales/en.json
  • static/locales/zh-CN.json

Walkthrough

新增首页注入的多阶段“角色甄选引导”功能:包含 overlay 模板、样式、前端控制类(window.CharacterSelection)、BGM/粒子/打字机效果、i18n 文案扩展、与后端角色 API 的创建/更新交互及 localStorage 标记与教程重置集成喵。

Changes

Cohort / File(s) Summary
前端交互与逻辑
static/js/character-selection.js
新增公开类 CharacterSelection(挂载到 window),实现 4 阶段选择流程、事件绑定、打字机(AbortController)、星星粒子、BGM 控制、跳过/确认逻辑,并在 finalize 时调用后端 API 更新默认猫娘与写入 neko_character_selection_completed 喵。
样式
static/css/character-selection.css
新增全屏 overlay 样式:径向星空背景与星场动画、卡片网格、选中/悬浮/聚焦样式、打字机光标、点击星星粒子、入口/弹性按钮动画与 prefers-reduced-motion 降级喵。
页面模板与入口
templates/index.html, templates/memory_browser.html
在首页注入 overlay DOM 与入口脚本(动态注入 CSS/JS、防止 FOUC、错误回退并触发 characterSelectionLoadFailed 事件);在 memory_browser 中新增重置选项 character_selection 喵。
国际化
static/locales/{en,ja,ko,ru,zh-CN,zh-TW}.json
为角色甄选与四套角色档案(tsundere_nekocool_mechintellectual_healerefficiency_expert)新增 i18n 文案与展示文案,并调整若干设置提示文字与格式喵。
教程管理器与重置
static/universal-tutorial-manager.js
character_selection 添加专用重置分支与页面名映射,清除 neko_character_selection_completed,并对布局/视口计算加入 isFinite/兜底检查以避免异常值喵。
i18n 版本与加载
static/i18n-i18next.js
更新 LOCALE_VERSION2026-03-18-1,用于刷新 locale JSON 请求缓存喵。
猫娘档案同步
static/js/chara_manager.js
renameCatgirl 成功后若 localStorage.neko_default_catgirl_name === oldName 则同步写入 newName,并随后调用后端更新,修正默认档名一致性喵。
文档
docs/character-guide.md
新增系统化文档:阶段设计、已实现/待实现项、localStorage 键、API 列表、i18n 与无障碍细节、流程 mermaid 图与测试示例喵。

Sequence Diagram(s)

sequenceDiagram
    actor User as 用户
    participant UI as Overlay\n(CharacterSelection)
    participant i18n as i18n/前端文案
    participant API as 后端 API\n(/api/characters...)
    participant Storage as localStorage

    User->>UI: 页面加载,UI 检查 `neko_character_selection_completed` 喵
    alt 未完成
        UI->>i18n: 请求/渲染本地化文本 喵
        i18n-->>UI: 返回文本 喵
        UI->>User: 显示 Stage1 → Stage2(选择卡片) 喵
        User->>UI: 选择角色(点击/键盘) 喵
        UI->>UI: playGreeting(打字机动画,可中止) 喵
        User->>UI: 确认 -> Stage4(最终确认) 喵
        UI->>API: GET /api/characters?language=... 喵
        API-->>UI: 返回角色列表 喵
        UI->>API: POST/PUT 创建或更新默认猫娘(包含 personality/voice_id 等) 喵
        API-->>UI: 返回结果 喵
        UI->>Storage: 写入 `neko_character_selection_completed` 喵
        UI->>UI: 关闭并移除 Overlay 喵
    else 已完成
        UI->>UI: 立即移除 Overlay 喵
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

星空下的选择在招手喵,
四位角色轻声等待你挑喵,
打字机敲出初次的问候喵,
本地记录一个小小的约定喵,
完成后跟随它开启新冒险喵。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning 标题包含中英混杂文本和格式错误,"selecfeat" 明显是笔误,"feat(character-selection)" 与后续描述重复冗长,难以清晰理解主要改动。 建议修正为简洁的单句标题,例如:"feat(character-selection): Add first-launch character selection guide" 或 "feat: Implement character selection & tutorial improvements"。
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/character-guide.md`:
- Around line 382-383: 文档中的相对链接 `frontend/tutorial.md` 是死链(而
`api/rest/characters.md` 正常),请在 docs/character-guide.md 中找到并修复该链接:确认目标文件的正确位置并更新
`frontend/tutorial.md` 为正确的相对路径或替换为新的目标页面(或删除链接并保留说明),然后在同一文件中扫描并验证所有其它相对链接(例如
`api/rest/characters.md`)确保都能正确打开。

In `@static/css/character-selection.css`:
- Around line 353-358: The font-family declaration in the .card-icon rule uses
unnecessary quotes around "Monaco" which triggers stylelint; update the
.card-icon CSS rule by removing the quotes so the font-family reads Monaco,
"Courier New", monospace and ensure spacing/commas are preserved in the
font-family property to satisfy the linter.
- Around line 363-380: The prefers-reduced-motion media query is incomplete: add
rules to disable the remaining animations by overriding the animation on the
character card icon and click-star effects when users prefer reduced motion.
Specifically, inside your `@media` (prefers-reduced-motion: reduce) block set
animation: none (or transition: none) for selectors .character-card .card-icon
and the .click-star elements and remove any keyframe-driven transforms for the
colorPulseBlue/colorPulsePink/colorPulsePurple/colorPulseCyan animations (and
any related star-scatter classes) so those animations no longer run for
colorPulse* and .click-star when prefers-reduced-motion is active.

In `@static/js/character-selection.js`:
- Around line 468-482: finalizeSelection currently allows reentrant concurrent
runs which can trigger multiple updateDefaultCatgirl calls; add a boolean
reentrancy lock (e.g., this.isFinalizing or this.finalizeLock) at the top of
finalizeSelection to return early if already running, set the lock before
awaiting updateDefaultCatgirl, and clear it in a finally block so it is always
released; ensure the same pattern is applied to the other related handler(s)
that call updateDefaultCatgirl (the methods in the 489-556 range) so
creation/update requests cannot run concurrently.
- Around line 348-399: In playGreeting, the confirm button can remain visible
from a previous run causing premature confirmation; find the confirm button via
confirmBtn (element id 'confirm-greeting-btn') at the start of playGreeting and
explicitly hide/disable it (e.g., set style.display = 'none' and/or disabled =
true) before running the greeting/typeText sequence, then only show/enable it
after typeText completes; ensure any early-return branches also keep it hidden
so old state doesn't leak into the new run.

In `@templates/index.html`:
- Around line 283-286: The current check uses a truthy test on completed (from
localStorage.getItem('neko_character_selection_completed')) which treats any
non-empty string like "false" as completed; change the logic to explicitly
compare completed === 'true' before removing or hiding the element with id
'character-selection-overlay' so only the exact string 'true' written by the
writer marks completion (use the completed variable and the overlay element in
the comparison).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f54c3319-11ca-47a6-97f5-6ba491bad495

📥 Commits

Reviewing files that changed from the base of the PR and between 93f5ac6 and b78a190.

⛔ Files ignored due to path filters (1)
  • static/default/Y-1.mp3 is excluded by !**/*.mp3
📒 Files selected for processing (14)
  • docs/character-guide.md
  • static/css/character-selection.css
  • static/i18n-i18next.js
  • static/js/chara_manager.js
  • static/js/character-selection.js
  • static/locales/en.json
  • static/locales/ja.json
  • static/locales/ko.json
  • static/locales/ru.json
  • static/locales/zh-CN.json
  • static/locales/zh-TW.json
  • static/universal-tutorial-manager.js
  • templates/index.html
  • templates/memory_browser.html

Comment thread docs/character-guide.md Outdated
Comment thread static/css/character-selection.css
Comment thread static/css/character-selection.css
Comment thread static/js/character-selection.js
Comment thread static/js/character-selection.js
Comment thread templates/index.html Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
static/js/character-selection.js (2)

348-399: ⚠️ Potential issue | 🟡 Minor

重进第三阶段前先把确认按钮重置掉喵。

从第四阶段点“再想想”回到第二阶段再选一次时,confirm-greeting-btn 上一次写进去的 inline-block 不会被清掉,新的问候还没打完就能直接跳到下一步了喵。进入 playGreeting() 时先把按钮藏回去,再在打字结束后显示会更稳喵。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/js/character-selection.js` around lines 348 - 399, In playGreeting(),
the confirm button (`#confirm-greeting-btn`) isn't hidden when re-entering this
phase so a previous inline-block remains; at the start of playGreeting() set
confirmBtn.style.display = 'none' (or hide it) if confirmBtn exists, then
proceed with avatar/title/typing, and only after await this.typeText(...) set
confirmBtn.style.display = 'inline-block' to re-enable it; update the logic
around the existing confirmBtn variable in the playGreeting() method (and ensure
typeText is awaited before revealing the button).

468-495: ⚠️ Potential issue | 🟠 Major

保存进行中时还要把 Skip/再想想 一起锁住喵。

现在 _finalizing 只挡住了再次点最终确认,挡不住同一时刻去点 Skip 或“再想想”喵。用户先点“出发,同行!”再点 Skip 时,会先把 neko_character_selection_completed 写成 true 并关闭 overlay;如果后面的 updateDefaultCatgirl() 失败,这个引导就被永久跳过了喵。

🐾 可直接套的修正喵
 async finalizeSelection() {
     // 防重入锁,防止并发调用 updateDefaultCatgirl
     if (this._finalizing) return;
     this._finalizing = true;
+    this.overlay?.querySelectorAll('button').forEach(btn => {
+        btn.disabled = true;
+    });
     try {
         console.log('[CharacterSelection] 用户确认选择:', this.selectedCharacter);
         if (this.selectedCharacter) {
             const success = await this.updateDefaultCatgirl();
             if (success) {
@@
         }
         this.close();
     } finally {
+        if (this.isOpen) {
+            this.overlay?.querySelectorAll('button').forEach(btn => {
+                btn.disabled = false;
+            });
+        }
         this._finalizing = false;
     }
 }
+
+skip() {
+    if (this._finalizing) return;
+    console.log('[CharacterSelection] 用户跳过角色甄选');
+    localStorage.setItem('neko_character_selection_completed', 'true');
+    this.close();
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/js/character-selection.js` around lines 468 - 495, The race occurs
because finalizeSelection() uses this._finalizing to prevent reentry but skip()
(and any "reconsider"/similar handlers) can run concurrently; update skip() (and
any other finalizing handlers) to respect the same guard: at the top of skip()
return early if this._finalizing is true, otherwise set this._finalizing = true
before writing localStorage/closing, and clear the flag after close (or in a
shared finally block) so the overlay cannot be skipped while
updateDefaultCatgirl() is in-flight; reference finalizeSelection, skip,
this._finalizing, updateDefaultCatgirl, and close when making the change.
static/css/character-selection.css (1)

364-380: ⚠️ Potential issue | 🟠 Major

这条 reduced-motion 覆盖现在压不过前面的脉冲动画喵。

前面四条 [data-id="..."] .card-icon 的具体度比 @media 里的 .character-card .card-icon 更高,所以开启“减少动态效果”后,卡片图标的 colorPulse* 其实还是会继续跑喵。这里要么把媒体查询里的选择器写成同等具体度,要么直接把 animation: none 提升成强覆盖喵。用 DevTools 模拟 prefers-reduced-motion: reduce.card-icon 的 computed animation-name,现在还能看到 colorPulse* 喵。

🐾 可直接套的修正喵
 `@media` (prefers-reduced-motion: reduce) {
     /* 禁用角色卡片图标颜色脉冲动画 */
     .character-card .card-icon {
-        animation: none;
+        animation: none !important;
     }
 
     /* 禁用点击星星散射动画 */
     .click-star {
         animation: none;
     }
 }

Also applies to: 681-695

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/css/character-selection.css` around lines 364 - 380, Reduced-motion
media query isn't overriding the per-character animations because selectors like
.character-card[data-id="cool_mech"] .card-icon (e.g., colorPulseBlue,
colorPulsePink, colorPulsePurple, colorPulseCyan) are more specific; update the
prefers-reduced-motion block to use equal specificity or force the override by
setting animation: none !important on .character-card .card-icon (or better:
.character-card[data-id] .card-icon) inside the `@media` (prefers-reduced-motion:
reduce) so the computed animation-name no longer shows colorPulse*; apply the
same change to the other affected range mentioned (also applies to 681-695).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@static/js/character-selection.js`:
- Around line 183-193: The mouseup handler only listens on document, so if the
user releases the mouse outside the window the _isMousePressed flag and
_starIntervalId stay active; add additional cleanup triggers (e.g., window blur
and document visibilitychange or mouseleave/window.mouseout) that call the
existing _onMouseUp to clear _isMousePressed and clearInterval(_starIntervalId),
and ensure you register those handlers alongside
document.addEventListener('mouseup', this._handleMouseUp) and remove them in
close() (also explicitly clear _starIntervalId and set it to null in close()).
Ensure the same handler reference (this._handleMouseUp) is used for
add/removeEventListener to avoid leaks.

---

Duplicate comments:
In `@static/css/character-selection.css`:
- Around line 364-380: Reduced-motion media query isn't overriding the
per-character animations because selectors like
.character-card[data-id="cool_mech"] .card-icon (e.g., colorPulseBlue,
colorPulsePink, colorPulsePurple, colorPulseCyan) are more specific; update the
prefers-reduced-motion block to use equal specificity or force the override by
setting animation: none !important on .character-card .card-icon (or better:
.character-card[data-id] .card-icon) inside the `@media` (prefers-reduced-motion:
reduce) so the computed animation-name no longer shows colorPulse*; apply the
same change to the other affected range mentioned (also applies to 681-695).

In `@static/js/character-selection.js`:
- Around line 348-399: In playGreeting(), the confirm button
(`#confirm-greeting-btn`) isn't hidden when re-entering this phase so a previous
inline-block remains; at the start of playGreeting() set
confirmBtn.style.display = 'none' (or hide it) if confirmBtn exists, then
proceed with avatar/title/typing, and only after await this.typeText(...) set
confirmBtn.style.display = 'inline-block' to re-enable it; update the logic
around the existing confirmBtn variable in the playGreeting() method (and ensure
typeText is awaited before revealing the button).
- Around line 468-495: The race occurs because finalizeSelection() uses
this._finalizing to prevent reentry but skip() (and any "reconsider"/similar
handlers) can run concurrently; update skip() (and any other finalizing
handlers) to respect the same guard: at the top of skip() return early if
this._finalizing is true, otherwise set this._finalizing = true before writing
localStorage/closing, and clear the flag after close (or in a shared finally
block) so the overlay cannot be skipped while updateDefaultCatgirl() is
in-flight; reference finalizeSelection, skip, this._finalizing,
updateDefaultCatgirl, and close when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c11f29a5-0eed-4544-b94f-b74e2ce02951

📥 Commits

Reviewing files that changed from the base of the PR and between b78a190 and 33157e2.

📒 Files selected for processing (4)
  • docs/character-guide.md
  • static/css/character-selection.css
  • static/js/character-selection.js
  • templates/index.html
✅ Files skipped from review due to trivial changes (2)
  • docs/character-guide.md
  • templates/index.html

Comment thread static/js/character-selection.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
static/js/character-selection.js (1)

183-185: ⚠️ Potential issue | 🟠 Major

补上离窗清理,不然星屑定时器还会卡在按下态喵。

现在只监听了 document.mouseup。鼠标按住舞台拖出浏览器后在窗口外松开时,这个事件很容易收不到,_isMousePressed_starIntervalId 就会一直留着,直到用户回页面再补一次松手或 overlay 被关闭喵。

🐾 可直接套用的修正喵
 constructor() {
     ...
     this._handleMouseUp = () => this._onMouseUp();
+    this._handleWindowBlur = () => this._onMouseUp();
+    this._handleVisibilityChange = () => {
+        if (document.hidden) {
+            this._onMouseUp();
+        }
+    };
     ...
 }
 ...
 document.addEventListener('mouseup', this._handleMouseUp);
+window.addEventListener('blur', this._handleWindowBlur);
+document.addEventListener('visibilitychange', this._handleVisibilityChange);
 ...
 document.removeEventListener('mouseup', this._handleMouseUp);
+window.removeEventListener('blur', this._handleWindowBlur);
+document.removeEventListener('visibilitychange', this._handleVisibilityChange);

Also applies to: 595-602

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/js/character-selection.js` around lines 183 - 185, You're only
listening for document 'mouseup' so when the user presses the mouse inside the
stage and releases outside the browser the _isMousePressed flag and
_starIntervalId remain set; update the component to also call the same cleanup
handler on window-level exit/blur events (e.g., add
window.addEventListener('mouseleave', this._handleMouseUp) and
window.addEventListener('blur', this._handleMouseUp) or use 'visibilitychange'
to detect tab switch) and ensure these extra listeners are removed in the same
teardown/destroy path; reference the existing _handleMouseUp, _isMousePressed
and _starIntervalId so the same cleanup logic is reused and apply the same
change where the duplicate logic appears.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@static/js/character-selection.js`:
- Around line 356-360: The confirm button is hidden and disabled (confirmBtn /
"confirm-greeting-btn") but when making it visible the code only restores
confirmBtn.style.display and never sets confirmBtn.disabled = false, causing the
UI to hang; update the code paths that show the confirm button (the block
setting confirmBtn.style.display = 'block') to also set confirmBtn.disabled =
false, and apply the same fix to the similar block around the other occurrence
(the other confirm button handling at the second location noted) so the button
becomes clickable when shown.

---

Duplicate comments:
In `@static/js/character-selection.js`:
- Around line 183-185: You're only listening for document 'mouseup' so when the
user presses the mouse inside the stage and releases outside the browser the
_isMousePressed flag and _starIntervalId remain set; update the component to
also call the same cleanup handler on window-level exit/blur events (e.g., add
window.addEventListener('mouseleave', this._handleMouseUp) and
window.addEventListener('blur', this._handleMouseUp) or use 'visibilitychange'
to detect tab switch) and ensure these extra listeners are removed in the same
teardown/destroy path; reference the existing _handleMouseUp, _isMousePressed
and _starIntervalId so the same cleanup logic is reused and apply the same
change where the duplicate logic appears.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 57a2ba3b-6599-46e5-976a-424b347fcb98

📥 Commits

Reviewing files that changed from the base of the PR and between 33157e2 and 95f81e5.

📒 Files selected for processing (1)
  • static/js/character-selection.js

Comment thread static/js/character-selection.js
…n localization files

- Enhanced English, Japanese, Korean, Russian, Simplified Chinese, and Traditional Chinese localization files with new character attributes including personality, catchphrase, hobby, trigger, hidden settings, and one-liner quotes for various N.E.K.O. characters.
- Removed proactive meme chat options from the localization files.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@static/css/character-selection.css`:
- Around line 681-701: The current prefers-reduced-motion block only disables a
few animations; add broader overrides to fully honor reduced-motion by also
disabling entrance/keyframe animations (e.g., fadeIn, fadeInUp,
characterButtonEntrance) and neutralizing transitions/transforms: target
selectors used for cards, buttons, overlays and any elements using
transition/transform (e.g., .character-card, .character-list, .character-button,
.character-overlay, .greeting-text, .card-icon, .click-star) and set animation:
none !important; transition: none !important; transform: none !important; and
animation-duration: 0.001s where needed so entrance effects and transform-based
motion are suppressed for users who prefer reduced motion.

In `@static/js/character-selection.js`:
- Around line 607-623: The replacement logic is matching against localized
display text (voiceMapping.personality / getI18nOrFallback) which breaks in
non-zh environments; change the logic to match and replace using a stable
internal identifier instead: update CHARACTER_VOICE_MAPPING to expose a unique
key (e.g., personality_key or id) on each mapping, build oldPersonalityKeys =
Object.values(CHARACTER_VOICE_MAPPING).map(m => m.personality_key || m.id),
parse parts to extract that key from stored entries (or store entries as
"display_text|key"), use existingIdx = parts.findIndex(p =>
oldPersonalityKeys.includes(extractKey(p))) to replace the correct segment with
the new display text (keeping the key attached), and when appending ensure you
append both display text and its key so future matches use the stable identifier
rather than localized text (adjust getI18nOrFallback usage only for the
displayed label).
- Around line 455-499: typeText's Promise can remain pending when clearTypeTimer
clears the interval then aborts the controller; add an AbortSignal handler
inside typeText so the Promise always settles on abort: in typeText (referencing
this._typeAbort and this._typeTimer) attach
this._typeAbort.signal.addEventListener('abort', ...) that clears the interval
(if any) and calls reject(new Error('Typing cancelled')) (and remove the
listener after settle), and in clearTypeTimer keep abort() but do not rely on
interval callback to reject — ensure any stored listeners/refs are cleaned so
the Promise is always resolved/rejected when abort/clear happens.

In `@static/locales/ru.json`:
- Line 75: In the ru.json entry for the "trigger" key, fix the misspelling
"берёжёшь" to the correct "бережёшь" so the string reads "...Видеть, как ты
переутомляешься и не бережёшь своё здоровье..."; update the value for "trigger"
accordingly to preserve proper Russian spelling.

In `@static/locales/zh-CN.json`:
- Around line 47-85: The PR removed meme-related i18n keys from zh-CN.json while
code still references them: restore the removed locale keys (e.g., the keys used
by proactive.meme.loadError and the settings keys referenced as
settings.toggles.proactiveMemeChat and
settings.toggles.proactiveMemeChatTooltip) so the UI shows translated text, or
if you truly intend to remove the feature, delete all references to those keys
in static/avatar-ui-drag.js (the code that reads
settings.toggles.proactiveMemeChat and
settings.toggles.proactiveMemeChatTooltip) and static/app-proactive.js (the code
that reads proactive.meme.loadError); also re-add any other locale entries you
removed in the same commit to keep translations consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8fc9eb87-1abf-4520-883e-f83a57025973

📥 Commits

Reviewing files that changed from the base of the PR and between 95f81e5 and f0dbe86.

📒 Files selected for processing (8)
  • static/css/character-selection.css
  • static/js/character-selection.js
  • static/locales/en.json
  • static/locales/ja.json
  • static/locales/ko.json
  • static/locales/ru.json
  • static/locales/zh-CN.json
  • static/locales/zh-TW.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • static/locales/en.json
  • static/locales/ja.json
  • static/locales/ko.json

Comment thread static/css/character-selection.css
Comment thread static/js/character-selection.js
Comment thread static/js/character-selection.js
Comment thread static/locales/ru.json Outdated
Comment thread static/locales/zh-CN.json
feat(locales): 添加表情包分享功能的本地化支持
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
static/js/character-selection.js (1)

625-647: ⚠️ Potential issue | 🟠 Major

别再按展示文案拆 性格 了,不然重跑引导会越写越长喵。

Line 25、Line 40、Line 55 和 Line 70 这四条 personality fallback 自己就都带 ,这里再用 /[,,、]/ 去拆 targetData['性格'],同语言重跑一次都会把旧人设拆成碎片,existingIdx 命不中后继续 append;换语言时又只认当前 locale 的文案,重复堆叠会更明显喵。这里得改成稳定的内部标识/标记来替换,而不是拿本地化后的整段展示文本做匹配喵。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/js/character-selection.js` around lines 625 - 647, The code currently
splits targetData['性格'] by punctuation and tries to match localized display
texts (voiceMapping.personality/personality_i18n), which breaks when re-running
or switching locales; instead, add/use a stable internal identifier (e.g., a
property like id or personality_key on each CHARACTER_VOICE_MAPPING entry) and
match/replace that identifier in the stored value rather than the localized
label. Update the logic around CHARACTER_VOICE_MAPPING, oldPersonalityValues,
existingIdx, and where newPersonality is appended so you store/lookup the
internal id token (and only render the localized label for display), replacing
the internal token when a character voice is selected instead of splitting by
/[,,、]/ and matching localized text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@static/js/character-selection.js`:
- Around line 625-647: The code currently splits targetData['性格'] by punctuation
and tries to match localized display texts
(voiceMapping.personality/personality_i18n), which breaks when re-running or
switching locales; instead, add/use a stable internal identifier (e.g., a
property like id or personality_key on each CHARACTER_VOICE_MAPPING entry) and
match/replace that identifier in the stored value rather than the localized
label. Update the logic around CHARACTER_VOICE_MAPPING, oldPersonalityValues,
existingIdx, and where newPersonality is appended so you store/lookup the
internal id token (and only render the localized label for display), replacing
the internal token when a character voice is selected instead of splitting by
/[,,、]/ and matching localized text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 055aafb0-9c78-479b-a921-b79279f740e1

📥 Commits

Reviewing files that changed from the base of the PR and between f0dbe86 and a845edb.

📒 Files selected for processing (4)
  • static/css/character-selection.css
  • static/js/character-selection.js
  • static/locales/ru.json
  • static/locales/zh-CN.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • static/locales/ru.json

@Project-N-E-K-O Project-N-E-K-O deleted a comment from coderabbitai bot Mar 23, 2026
@Project-N-E-K-O Project-N-E-K-O deleted a comment from coderabbitai bot Mar 23, 2026
@Project-N-E-K-O Project-N-E-K-O deleted a comment from coderabbitai bot Mar 23, 2026
@Project-N-E-K-O Project-N-E-K-O deleted a comment from coderabbitai bot Mar 23, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
static/locales/en.json (1)

1489-1504: ⚠️ Potential issue | 🟠 Major

请补回仍被代码引用的 meme i18n 键,当前会触发缺失翻译喵

static/avatar-ui-drag.jsstatic/app-proactive.js 还在读这些键,locale 删除后会出现 missing key 或回退文案,英文界面会不一致喵。

💡 建议补丁(兼容现有调用)
@@
     "settings": {
@@
         "toggles": {
@@
             "proactiveMusicChat": "Music Recommendation",
             "proactiveMusicChatTooltip": "Use recommended music for proactive chat",
+            "proactiveMemeChat": "Meme Pack",
+            "proactiveMemeChatTooltip": "Use meme packs for proactive chat",
@@
         }
     },
@@
+    "proactive": {
+        "meme": {
+            "loadError": "Failed to load meme pack"
+        }
+    },
     "notice": {

Also applies to: 2546-2551

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/locales/en.json` around lines 1489 - 1504, Re-add the removed "meme"
i18n keys to static/locales/en.json so static/avatar-ui-drag.js and
static/app-proactive.js can find translations (restore the keys that were
deleted around the toggles block and the block at lines ~2546-2551); ensure the
keys exactly match the identifiers used by those files (the "meme" keys
referenced in avatar-ui-drag.js and app-proactive.js) and provide sensible
English values to avoid missing-key fallbacks and keep the UI consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@static/locales/en.json`:
- Around line 1489-1504: Re-add the removed "meme" i18n keys to
static/locales/en.json so static/avatar-ui-drag.js and static/app-proactive.js
can find translations (restore the keys that were deleted around the toggles
block and the block at lines ~2546-2551); ensure the keys exactly match the
identifiers used by those files (the "meme" keys referenced in avatar-ui-drag.js
and app-proactive.js) and provide sensible English values to avoid missing-key
fallbacks and keep the UI consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 70206fe7-53c2-4356-bcdc-6bcd89b86fe4

📥 Commits

Reviewing files that changed from the base of the PR and between f0dbe86 and 6ab6ff5.

📒 Files selected for processing (8)
  • static/css/character-selection.css
  • static/js/character-selection.js
  • static/locales/en.json
  • static/locales/ja.json
  • static/locales/ko.json
  • static/locales/ru.json
  • static/locales/zh-CN.json
  • static/locales/zh-TW.json
✅ Files skipped from review due to trivial changes (1)
  • static/locales/ja.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • static/locales/zh-CN.json
  • static/locales/ko.json
👮 Files not reviewed due to content moderation or server errors (3)
  • static/js/character-selection.js
  • static/locales/ru.json
  • static/css/character-selection.css

Comment thread static/locales/zh-CN.json
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
static/locales/ja.json (2)

2559-2559: 文件末尾缺少换行符喵~

大部分代码规范都建议文件以换行符结尾喵,这样在 git diff 时不会显示 "No newline at end of file" 的烦人提示喵。虽然不影响功能,但本喵觉得加上会更整洁喵~

🐱 建议的修改喵
 }
+
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/locales/ja.json` at line 2559, The file static/locales/ja.json
currently ends with the closing brace '}' but lacks a trailing newline; update
ja.json to add a single newline character after the final '}' so the file ends
with a newline (POSIX-style) to avoid "No newline at end of file" diffs and
satisfy style checks.

51-51: "雷点" 这个翻译可能对日语用户不太自然喵~

"雷点" 是中文网络用语,日语中通常不这么说喵。更自然的日语表达可以考虑用 "地雷"、"苦手なこと" 或者 "NGポイント" 喵~

🐱 建议的修改喵
-            "雷点": "雷点",
+            "雷点": "地雷",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/locales/ja.json` at line 51, The translation key "雷点" is a Chinese
internet term and sounds unnatural in Japanese; update the value for the "雷点"
key to a more natural Japanese phrase such as "地雷", "苦手なこと", or "NGポイント"
depending on context (prefer "地雷" for taboo items, "苦手なこと" for personal
weaknesses, or "NGポイント" for no-go points) and ensure the JSON entry keeps the
same key ("雷点") while replacing the value string with the chosen Japanese term.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@static/locales/ja.json`:
- Line 2559: The file static/locales/ja.json currently ends with the closing
brace '}' but lacks a trailing newline; update ja.json to add a single newline
character after the final '}' so the file ends with a newline (POSIX-style) to
avoid "No newline at end of file" diffs and satisfy style checks.
- Line 51: The translation key "雷点" is a Chinese internet term and sounds
unnatural in Japanese; update the value for the "雷点" key to a more natural
Japanese phrase such as "地雷", "苦手なこと", or "NGポイント" depending on context (prefer
"地雷" for taboo items, "苦手なこと" for personal weaknesses, or "NGポイント" for no-go
points) and ensure the JSON entry keeps the same key ("雷点") while replacing the
value string with the chosen Japanese term.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e96c9b2a-d17a-41bc-82a7-ee72ca438753

📥 Commits

Reviewing files that changed from the base of the PR and between 1d94d06 and 9426abc.

📒 Files selected for processing (4)
  • static/locales/ja.json
  • static/locales/ko.json
  • static/locales/ru.json
  • static/locales/zh-TW.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • static/locales/zh-TW.json
  • static/locales/ko.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants