Character selecfeat(character-selection): Add first-launch character selection & tutorial robustness improvementstion guide#527
Conversation
…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
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (4)
Walkthrough新增首页注入的多阶段“角色甄选引导”功能:包含 overlay 模板、样式、前端控制类(window.CharacterSelection)、BGM/粒子/打字机效果、i18n 文案扩展、与后端角色 API 的创建/更新交互及 localStorage 标记与教程重置集成喵。 Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 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
⛔ Files ignored due to path filters (1)
static/default/Y-1.mp3is excluded by!**/*.mp3
📒 Files selected for processing (14)
docs/character-guide.mdstatic/css/character-selection.cssstatic/i18n-i18next.jsstatic/js/chara_manager.jsstatic/js/character-selection.jsstatic/locales/en.jsonstatic/locales/ja.jsonstatic/locales/ko.jsonstatic/locales/ru.jsonstatic/locales/zh-CN.jsonstatic/locales/zh-TW.jsonstatic/universal-tutorial-manager.jstemplates/index.htmltemplates/memory_browser.html
There was a problem hiding this comment.
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的 computedanimation-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
📒 Files selected for processing (4)
docs/character-guide.mdstatic/css/character-selection.cssstatic/js/character-selection.jstemplates/index.html
✅ Files skipped from review due to trivial changes (2)
- docs/character-guide.md
- templates/index.html
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
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.
There was a problem hiding this comment.
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
📒 Files selected for processing (8)
static/css/character-selection.cssstatic/js/character-selection.jsstatic/locales/en.jsonstatic/locales/ja.jsonstatic/locales/ko.jsonstatic/locales/ru.jsonstatic/locales/zh-CN.jsonstatic/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
feat(locales): 添加表情包分享功能的本地化支持
There was a problem hiding this comment.
♻️ Duplicate comments (1)
static/js/character-selection.js (1)
625-647:⚠️ Potential issue | 🟠 Major别再按展示文案拆
性格了,不然重跑引导会越写越长喵。Line 25、Line 40、Line 55 和 Line 70 这四条
personalityfallback 自己就都带,,这里再用/[,,、]/去拆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
📒 Files selected for processing (4)
static/css/character-selection.cssstatic/js/character-selection.jsstatic/locales/ru.jsonstatic/locales/zh-CN.json
🚧 Files skipped from review as they are similar to previous changes (1)
- static/locales/ru.json
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/locales/en.json (1)
1489-1504:⚠️ Potential issue | 🟠 Major请补回仍被代码引用的 meme i18n 键,当前会触发缺失翻译喵
static/avatar-ui-drag.js和static/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
📒 Files selected for processing (8)
static/css/character-selection.cssstatic/js/character-selection.jsstatic/locales/en.jsonstatic/locales/ja.jsonstatic/locales/ko.jsonstatic/locales/ru.jsonstatic/locales/zh-CN.jsonstatic/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
There was a problem hiding this comment.
🧹 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
📒 Files selected for processing (4)
static/locales/ja.jsonstatic/locales/ko.jsonstatic/locales/ru.jsonstatic/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
新功能
新增多阶段角色选择向导:引导、角色卡选择、问候互动(打字效果)、最终确认与跳过,选择可保存并完成同步流程。
样式
全屏星光背景与分层动画、卡片入场/悬停/选中动效、响应式布局、交互星点特效、背景音及减弱动画支持。
本地化
补充多语种角色选择文案(en/ja/ko/ru/zh-CN/zh-TW)。
文档
新增角色选择设计与使用指南。
修复 / 强化
教程管理、本地存储与视口校验更健壮;初始化、清理与加载回退流程改进。
Summary by CodeRabbit
新功能
界面与交互
多语言
文档
改进