Commit 1aee875
feat(card-assist): 角色卡 AI 陪伴式捏人助手(clarify/generate/refine + chat 四端点 + 侧栏面板) (Project-N-E-K-O#1419)
* feat(frontend): 角色卡 AI 辅助生成(3 步式 clarify → generate → refine)
在「角色卡管理」里新增 AI 辅助生成入口,复用 assist API(沿用 memory/refine.py 的
create_chat_llm 路径),三步引导用户产出一张完整的猫娘设定:
- POST /api/card-assist/clarify —— 根据一句话描述返回 2~4 个 chip 式澄清问题
- POST /api/card-assist/generate —— 结合澄清答案产出完整字段字典(中文 key)
- POST /api/card-assist/refine —— 对单个字段重新生成(再随机/更傲娇/更温柔/自定义)
前端:在 character_card_manager.js 接入 3 步式 wizard,字段级 diff 预览、勾选应用、
inline 编辑、单字段 chip 微调;CSS 配套;en / zh-CN 双语 i18n 全量补齐。
返回字段都做了脱壳(去掉 \`\`\`json fence + 引号)、字段值统一 coerce 成非空字符串,
LLM 异常路径都按 502 / 400 区分。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ci): card-assist 改名 q→qd 绕开 ASYNC_BLOCK 误判 + 补 6 个 locale 同步
CI 两处失败:
1. ruff async-blocking 把 `q.get(...)` 启发式当成 queue.Queue.get() 报 ASYNC_BLOCK。
把 clarify() 里的循环变量 q 改名 qd(question dict),加注释提醒后续别再叫 q。
2. i18n-sync 要求 static/locales 8 个语言文件一起改。补齐 es / ja / ko / pt / ru / zh-TW
的 34 个 aiAssist* key,hunk 范围与 en.json / zh-CN.json 对齐。
本地跑 `python scripts/check_i18n_sync.py --base origin/main` 已通过。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): 处理 PR review 6 条 finding(field key 错位 / 保存失败误报 / 引号对 / Step2 旧答案)
回应 Codex + CodeRabbit 在 Project-N-E-K-O#1419 的 inline review:
1. [Codex P2 / prompts:142] LLM 强行返回中文 key(性别/年龄/...),但
英文 locale 模板用 Nickname / Gender / Age 等英文 key,Apply 时按
`textarea[name=...]` 精确匹配 → 平行插入新字段、旧字段不被覆盖。
- 前端用 `_cardAssistCollectFieldKeys` 把 form 上所有 textarea/input 的
name(包括空值)收集成 `target_field_keys` 一起发到 router。
- Router 加 `_resolve_target_keys` 三级回退:前端列表 → current_card keys
→ locale 默认(zh / en)。
- Prompt 模板的 generate zh / en 都改成「目标字段名 %s,必须 1:1 原样使用」,
不再硬编码 9 个中文 key。
2. [Codex P2 / js:10576 + CodeRabbit major / js:10241] saveCatgirlFromPanel
各个失败分支只是自己 showMessage 然后 return,不抛错,导致 Apply & Save
即便保存失败也会关向导 + 弹成功提示。
- saveCatgirlFromPanel 显式 return true/false。
- applyBtn 改成 `const saved = await save(...); if (!saved) return;`。
- `_cardAssistFetch` 的 `e.code` 改成 `body.code || body.error`,兼容
其他接口的 {code, error} 形状。
3. [CodeRabbit minor / router:321] refine 输出剥引号用 `text[0] == text[-1]`
挡不住 Unicode 配对引号(U+201C 左 vs U+201D 右)。改成显式 open→close
映射,多覆盖 `「」`/`『』`/`""`/`''`。
4. [Codex P2 / js:10418] Step3 用 Back 回到 Step2 时 state.answers 还留着
上一轮答案,但 chip / customInput 全渲染成空,导致用户以为已清空、
生成请求却仍带旧答案。从 state.answers 回填 chip selected / customInput
value。
本地 ruff + check_async_blocking + check_i18n_sync + JS 语法 全部通过。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(i18n): pt.json typo + 术语对齐(regerar→regenerar,API Key→chave de API)
回应 CodeRabbit 两条 minor finding:
- aiAssistStep3Hint 里 "regerar" 是拼写错误,应为 "regenerar"
- aiAssistApiMissing 里的 "Configurações de API Key" 改成 "Configurações de chave de API",
与同文件 line 851 (settings) / 906 (assistApiKeyQwen) 等的术语保持一致
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): 新建卡空档案名 warning 后不再叠 success toast
回应 Codex P2 Project-N-E-K-O#1419 (comment_id=3265601710):
当 isNew && 档案名为空时,apply 路径在 warning 之后会继续走
`_cardAssistClose` + 成功 toast,用户会被 warning + success 双弹窗
误导成「已落库」。其实这条路径只把字段写到了 form,没调
saveCatgirlFromPanel。
修法:warning 后立刻 `_cardAssistClose(shell.overlay) + return`,
跳过下面的 success toast(表单仍保留 AI 生成内容,用户填档案名后
正常点保存即可)。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(card-assist): 抽出 _RESERVED_CARD_FIELDS 让 format / target_keys 行为一致
回应 CodeRabbit nitpick Project-N-E-K-O#1419 (review 4318164227):
- `_format_card_for_prompt` 之前 filter 7 个保留字段
- `_resolve_target_keys` 之前 filter 同样 7 个 + "档案名"
两套独立写法导致逻辑漂移。把保留字段抽出 `_RESERVED_CARD_FIELDS` 常量
+ `_is_reserved_card_field()` helper,两处都走同一个判定("档案名"
现在两处都过滤)。注释里写明 `档案名` 是 form 元数据 input 的固定
中文 literal name,不是按 locale 翻译的字段。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): 兜底 target_field_keys 按 locale 读模板(ja/ko/pt/ru/es/zh-TW)
回应 Codex P2 Project-N-E-K-O#1419 (comment_id=3265669416):
之前 `_DEFAULT_TARGET_KEYS_BY_LANG` 只硬编码了 zh/en。当 UI locale 是
ja/ko/pt/ru/es 时,前端发的 locale 会被 `_resolve_language` 收敛成 "en",
然后兜底字段就成了英文的;zh-TW 会被吃成 zh-CN 简体字段。空白新建卡
(`_cardAssistCollectFieldKeys()` 返回空)就会让 /generate 用错 key set,
生成出的卡和当前 locale 的模板对不上。
修法:
- 新增 `_resolve_locale_code()` 把前端 locale 映射到
`config/characters/<x>.json` 的实际文件名(en / zh-CN / zh-TW / ja /
ko / pt / ru / es 八种 + primary-subtag 兜底)。
- 新增 `_load_template_keys_for_locale()` (lru_cache) 直接从该 locale
的模板文件读字段名顺序,比硬编码列表自动跟住模板演进。
- `_resolve_target_keys` 兜底链:payload → current_card keys →
locale 模板字段 → 硬编码 en(仅在文件读取彻底失败时兜底)。
- `_resolve_language` 仍然只走 zh/en(prompt 模板只有这两版),
注释里写明分工。
本地 smoke:8 个 locale + 各种变体(en-US / ja-JP / zh-Hant / pt-BR /
es-MX / 未知 locale)都解析正确。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): 处理 CodeRabbit full review 7 条 finding
回应 Project-N-E-K-O#1419 full review:
后端 (main_routers/card_assist_router.py):
- [Minor 3266209617] _invoke_assist 把 ainvoke/aclose 分开 try,aclose 抛错
不再误把成功的 resp 当 llm_call_failed 丢掉
- [Major 3266209632] /generate 输出清洗用 `_is_reserved_card_field`,
挡住 LLM 误吐 "档案名"/"voice_id"/"system_prompt" 把表单元数据污染
- [Major 3266209640] /refine 入口校验 field_key 不能在 _RESERVED_CARD_FIELDS,
防止客户端绕过来 refine 系统字段,返回 field_key_reserved (400)
前端 (static/js/character_card_manager.js):
- [Major 3266209652] state 加 pendingRefines Set;_cardAssistRefineField
把任务塞进去;applyBtn 在写表单前 `await Promise.allSettled(pending)`,
避免「点完更傲娇秒点应用并保存,落库还是旧值」的 UI/状态分叉
CSS (static/css/character_card_manager.css):
- [Minor 3266209645] `word-break: break-word` → `overflow-wrap: break-word`
(.card-assist-row-key / .card-assist-row-original 两处)
韩语 (static/locales/ko.json):
- [Minor 3266209659] aiAssistRefineRandom "재무작위" → "다시 랜덤"
- [Minor 3266209684] aiAssistApiMissing "API Key 설정" → "API 키 설정"
(与同文件其它位置 "API 키" 风格统一)
本地 ruff / async-blocking / JS 语法 / i18n-sync 全过。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(i18n): ko.json aiAssistRefineRandomInstr 漏改的 "재무작위화" → "무작위화"
回应 CodeRabbit (comment_id=新一轮):之前修 `aiAssistRefineRandom`
为 "다시 랜덤" 时漏改了同一组的 instruction 文案 ——
"이 필드를 완전히 재무작위화" → "이 필드를 완전히 무작위화"。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor: replace AI Assist with AI Companion in character card generation flow
- Updated English and Chinese localization files to reflect the new AI Companion terminology.
- Removed old AI Assist steps and descriptions, replacing them with a more conversational AI Companion interface.
- Enhanced user interaction prompts for a smoother character creation experience.
* fix(card-assist): 处理 PR review 5 条 finding(state sync / i18n parity / 其他)
阻塞:
- _companionEnsureLiveForm 换 state.form 时同步 state.isNew / state.originalName
到 liveForm._isNew / _catgirlName;isNew 翻 false 时清掉 _warnedNewCardSaveHint。
否则用户首次手动保存 blank card 之后 companion 会把已存在的卡当成新卡:
auto-save 永远 bail(save-hint 反复弹),万一走到 saveCatgirlFromPanel 会
effectiveIsNew=true 触发 POST 而不是 PUT,造成同名 catgirl 409。
- i18n 缺漏:6 个 locale(es/ja/ko/pt/ru/zh-TW)之前只补了旧 wizard 时代的
aiAssistStep* 那一套(实际已是死代码),完全没补 aiCompanion* 一套。
这 6 个 locale 用户切语言后 companion UI 整套 fallback 回中文/英文。
→ 翻译并补齐 aiCompanion* 29 个 key(含 2 个新增 seed key),同时把
32 个未使用的 aiAssist* 键删干净。en/zh-CN 只补了 2 个新增 seed key。
Nit:
- chatHistory seed 之前硬编码中文("基于这段描述生成猫娘卡:"),英文 locale
用户走完澄清问答后 LLM 会被 seed 镜像成中文回复。改成走 i18n key
aiCompanionSeedDescribe / aiCompanionSeedGenerated。
- prompts_card_assist.py 头部 docstring 还写「3-stage AI assistant」但实际
已有 chat 端点,改成 4-stage(追加 chat 一条)。
- companion avatar URL 去掉 ?t=Date.now() cache-bust:稳定静态图,让浏览器
HTTP cache 接管,多次开关 companion / 切换猫娘不用反复拉图。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): companion auto-save 失败时给系统气泡兜底(Codex P2 #2)
之前 _companionTryAutoSave 用 try{}catch{} 把 saveCatgirlFromPanel 的 return
值全吞了,但那函数 HTTP 非 2xx / success:false / 网络异常都是 toast + return
false 而不是抛错。结果:companion 系统气泡仍显示「✎ 已应用:xxx」,体感是
「字段改了 + 已保存」,但其实后端拒绝了。
现在读回 ok 值,false 时再补一条 system 错误气泡。`undefined` 表示 form
debounce 已经在跑了的早 return,不算失败,跳过提示避免误报。
新增 i18n key aiCompanionAutoSaveFailed,8 个 locale 全配齐。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): companion form-rebind 兜底(Codex round 2: P2 #A + P2 #B)
A) _companionEnsureLiveForm 在 state.originalName 为空(空白新卡开 companion)
时也能找回 form:
- 之前:blank card → state.originalName='' → `getElementById('catgirl-form-' + '')`
永远 null → 早 return false → 用户填档案名后手动 Save → 卡被建出来、form
id 从 catgirl-form-new 变成 catgirl-form-<actualName>,但 companion 永远
找不到,永久陷在 "form gone"。
- 现在:originalName 空时 fallback 到
`document.querySelector('.catgirl-panel-right form[id^="catgirl-form-"]')`,
panel 同时只能有一个 form,所以这条选择器命中唯一;再走原有的 isNew /
originalName 同步逻辑。
B) _companionRunClarify / _companionRunGenerate / _companionRunChat 都改为检查
_companionEnsureLiveForm 的返回值,false 时直接 typing.remove() + 系统气泡
"form gone" 然后 return,不再白白发起 LLM 调用。之前忽略返回值,导致:
即使 backend 把 reply + actions 算好返回,前端 apply 阶段也只能弹「form
gone」,钱白花、用户体验冲突。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): stale chip + closed-companion late-apply 双兜底(Codex round 3)
A) stale question chip 防 race(_companionRenderNextQuestion)
bubble 渲染时 capture 自己的 question idx + id 到 closure。chip onClick 触
发时若 state.currentQuestionIdx 已经因为用户走输入框 / 点了别的 chip 推进
到下一题,no-op 这次点击 —— 否则会:
- 把 stale 答案塞进 collectedAnswers (覆盖原本的答案)
- 再次 ++currentQuestionIdx (跳过下一题没被回答)
旧 bubble 在 thread 里持续可见可点,必须防一手。
B) closed companion 之后丢弃 in-flight LLM 结果
_companionTeardown 现在 set state.closed = true;3 个 runner(clarify /
generate / chat)在 `await _cardAssistFetch(...)` 拿到 response 之后立即
check state.closed,如果 true 就直接 return —— 不 typing.remove、不写
form、不 autoSave。
场景:用户点开 companion → AI 在写草稿 → 慢网络 → 用户耐心耗尽关掉 panel
→ /generate 终于回来 → 之前会静默把 9 个字段写进 form 并 POST 保存,把
用户「关掉=取消」的意图给覆盖了。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): stale inline custom input 防 race(Codex round 4)
round 3 给 chip onClick 加的 ownIdx 防 race 漏了一个对称路径:bubble 自定义
输入框的 Enter 也是一样的 race。用户走输入框 / 新 chip 推进进度后回头在老
bubble 的自定义框按 Enter,会:
- 把这次的输入塞进 collectedAnswers[当前题.id](不是 bubble 对应的题)
- 再次 ++ currentQuestionIdx 跳过下一题
修法:
- _companionAppendAssistant 接受新可选 opts.customSubmit 回调(提供时优先
用它,否则继续走通用 _companionHandleUserText 兜底)。
- _companionRenderNextQuestion 传一个施加 ownIdx 防 race 的 customSubmit —
跟 chip onClick 内的检查同形。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): 拒绝非 object JSON payload(Codex round 5)
`await request.json()` 接受任意合法 JSON 类型(list / str / int / null 都
过),4 个 endpoint 后面都假设 body 是 dict 然后 `body.get(...)`,碰到
`[]` / `"x"` / `123` / `null` 之类的合法但非 object 输入会 AttributeError
飙到 500。
四个 endpoint(clarify / generate / refine / chat)的 try/except 之后统一
加 isinstance(body, dict) 检查,不是 object 就 400 invalid_json + 明确
message "JSON body must be an object",符合 client error 语义。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): refine_field 找不到目标时 skip 而不是创建(Codex round 6)
_companionApplyActions 之前对 refine_field 和 add_field 一视同仁,都丢给
_cardAssistApplyToForm 兜底创建。问题:LLM 偶尔会在 refine action 里把目
标字段名打错 / 大小写漂移("Personality archetype" vs "Personality
Archetype"),ApplyToForm 找不到就新建一条 → 重复字段被 autoSave 持久化、
留下脏 schema。
修法:refine_field 进 ApplyToForm 之前先用 _findFieldTextareaByName 精确
(三层 fuzzy match)查一次,找不到直接 push 进 skippedTags 走「⤬ 未匹配/
已保留」反馈通路。add_field 不动 —— 那才是真的「需要找不到」的场景。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): companion auto-save 区分 debounce-skip 和真失败(Codex round 7)
上一轮(commit 3bf0b17)`_companionTryAutoSave` 读回 saveCatgirlFromPanel
的 `false` 当成失败、弹"自动保存失败"系统气泡。问题是 saveCatgirlFromPanel
对**两种**完全不同的情况都返回 false:
(a) form.dataset.submitting === 'true' 时的 debounce 跳过(用户手动点了
Save,companion auto-save 同时触发 → 这次跳过不算失败)
(b) HTTP 非 2xx / success:false / 网络异常等真·失败
两种情况都被当成失败 → 用户手动 Save 跟 companion auto-save 撞车时会看到
误报的"自动保存失败"气泡(实际上 save 还在飞、最终也会成功)。
修法:进 save 之前自己 check 一下 `state.form.dataset.submitting === 'true'`,
是的话静默 return;其它时候 false 才视为真失败。不动 saveCatgirlFromPanel
的返回契约,避免影响它的 9 个其他 caller。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): rename rebind + i18n 占位符 + 文档对齐(Codex round 8 + CodeRabbit)
10 个 file 改动覆盖一组 Codex + CodeRabbit findings:
1. **rename rebind**(Codex 3272690171 / CR 3272709503)
_companionEnsureLiveForm 之前 state.originalName 有值时只按
`catgirl-form-<originalName>` 精确查,没 fallback。用户在 companion 开
着时改了档案名 → saveCatgirlFromPanel 用新名 rebuild 表单 → 旧 id 找不到
→ companion 永久陷在 "form gone"。
修法:getElementById 失败时 fallback 到
`.catgirl-panel-right form[id^="catgirl-form-"]` 选择器(panel 同时只能
有一个 form 所以命中唯一),下游 sync 逻辑会把 state.originalName 回填
成新名字。这也顺带统一了空白新卡 first-save 的兜底路径。
2. **prompts_card_assist.py docstring 矫正**(CR 3272709496)
头部之前写"四个 prompt 都要求 STRICT JSON"。实际 /refine 单独要求 plain
string(无 JSON 包装),让 router 能直接拿到字段值灌进表单 textarea。改
成"三个 JSON / refine 是 plain string"。
3. **i18n 占位符 {n} → {{n}}**(CR 3272709508/536/547/562/570 + zh-CN/zh-TW
未被 CR flag 但同样有问题)
repo 里 60+ 处 i18n 都是 {{var}} 风格,{n} 是 outlier。即便我 JS 那边
用 .replace('{n}', ...) 兜住了渲染,对 reviewer / 未来维护是 noise。
修法:
- JS:删 .replace('{n}', ...),改成 _cardAssistT(key, fallback, { n: count })
—— vars 透传给 i18next 走标准 {{n}} 插值。fallback 字符串内联数字避免
i18next 没加载时漏出字面量。
- 8 个 locale:{n} → {{n}} 同步替换。
4. **es / pt aiCompanionSub 去 dev-jargon**(CR 3272709531)
"gato dev" 读起来像内部备注,改成 "gatito/gatinho desarrollador/
desenvolvedor" —— 仍保留 dev-cat 概念(这是产品未来角色的占位),但语感
是「开发小猫」而不是程序员行话。ja/ko/zh-* 的"开发猫/開発猫/개발 고양이"
作为产品概念保留,不动。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): companion auto-save 等 in-flight save + 重放丢失字段(Codex P1)
上一轮 (722ada8) 的简化"in-flight 就 return"会让用户的修改静默丢失:
T0: 用户手动 Save → saveCatgirlFromPanel 用 T0 的快照起 POST
T1: companion 把 AI 应用的新字段写进同一个 form 的 textarea
T2: companion tryAutoSave → 看到 dataset.submitting='true' → return
T3: 后端 success → buildCatgirlDetailForm() 用 server 返回的数据 (T0 快
照) 重建 form → companion 在 T1 写进去的字段被抹掉
T4: 既没存进后端 / 不在 form 里 / companion 也不知道要重试 —— 静默丢失
修法:
1. 等 in-flight save 收尾:轮询 dataset.submitting 翻 'false'(8s 超时兜底)
2. wait 完调一次 _companionEnsureLiveForm,可能 rebuild 出来的新 form 接上
3. 如果 form 实例换了,比对 state.formWatchSnapshot(tryAutoSave 调用前刚
refresh 过,代表 companion 期望状态)和新 form 的实际值,把丢失/被抹掉
的字段 replay 一遍(仅 value diff 才 apply 避免覆盖 server 端合法数据)
4. 再调 saveCatgirlFromPanel 把 companion 的修改真正落盘
debounce-skip 的误报问题保留之前的解决(companion 不再把 false 当真失败的
唯一情况就是它自己已经 wait 完的场景;如果 wait 8s 后还 submitting,给
console.warn 然后放弃,不再弹气泡)。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(card-assist): companion auto-save 两条 race fix(Codex P1 + CR Major)
刚才的 ff6b4b1 引入 wait+replay 链路时漏掉了两个 meta-bug,两个都会让在
manual-save 与 companion-apply 撞车场景下静默丢失数据:
1. CR Major: state.formWatchSnapshot 被 _companionAttachFormWatchers 在切
到新 form 时**重写**(line 10968),把"companion 期望状态"覆盖成"刚
rebuild 完的旧值"。结果下面那条 `state.form !== formBeforeWait` 分支里
的 diff 永远比不出差异,replay 哑火、数据照样丢。
修:进 wait loop 之前把 snapshot **defensive 拷贝**到本地 expectedSnapshot
变量,不再依赖会被覆写的 state slot。
2. Codex P1: snapshot 只记得到字段值,不记得到 "companion 故意删除了这个
key"。companion 的 remove_field action 删掉的字段不在 snapshot 里 →
server rebuild 后那些字段又冒出来 → 既不替换又不删除 → AI 删的字段被
静默复活并 PUT 回 server。
修:_companionApplyActions 把每次 apply 的 4 类结果(updated / created /
removed / skipped)挂到 state._lastApplyResult;_companionTryAutoSave 在
wait 前也 defensive 拷贝一份 removed 名单,rebind 后单独跑一轮删除
replay。字段值 replay 那一路顺手跳过 removed 集合,避免一删一写自己跟自
己打架。
最后 replay 完顺手 _companionRefreshFormSnapshot 一次,把新 baseline 同步
给后续 form-watch listener,防止把 replay 误判成"用户手改"狂刷系统气泡。
state._lastApplyResult 用完即清,避免下一次 tryAutoSave 误用过期记录。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Improve character card companion behavior
* fix(card-assist): companion 动画补 reduced-motion + remove_field 亮 Save(CR Minor + Codex P2)
- CSS(CodeRabbit #3328757121):现有 @media (prefers-reduced-motion: reduce)
没覆盖陪伴面板的新动画。补一段兜底,把窗口入场 cardCompanionWindowIn、最小化
呼吸光 cardCompanionBallGlow、最小化↔展开的收/展几何过渡(collapsing/expanding)、
气泡入场 cardCompanionBubbleIn、typing dots cardCompanionDot、字段闪烁
cardAssistRowFlash* 全部 animation/transition 静音。收/展终态切换由 JS
setTimeout(260) 收尾、不依赖 transitionend,故 transition:none 后是「瞬移就位」
不会卡住;各元素基础态可见、无需复位 opacity/transform。与文件里 @1744 / @6415
两处 reduced-motion 兜底同一套语义。
- JS(Codex #3272286467):remove_field 直接删 DOM 行,不走 _cardAssistApplyToForm
末尾那段「亮 Save/Cancel」。已保存卡这两个按钮默认 display:none,一旦 autosave 失败、
系统气泡提示「请手动点 Save 重试」时按钮还藏着 → 用户无从重试、删除在 reload 后复活。
改成:纯删除场景也把 Save/Cancel 显式亮出,让 fallback 提示可操作。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): add free API watermark
为 AI 辅助捏人的免费 Lanlan 文本调用补充通用安全水印,避免免费端误判为非 Lanlan 请求。
同时收敛捏人助手输入框滚动条隐藏逻辑,并用 transform 优化 YUI 头像的下移和放大效果。
验证:
- .\.venv\Scripts\python.exe -m py_compile main_routers\card_assist_router.py
- git diff --check
- /api/card-assist/clarify 免费端请求返回 200,未再出现 STOP ABUSE THE API
* fix(card-assist): companion 防误绑别的卡 + AI 字段删除亮 Save(Codex P2 ×2)
- #3328901017:_companionEnsureLiveForm 的选择器回退(path-2)会把 A 的聊天误绑到
另一张卡 B —— closeCatgirlPanel 不销毁 companion 侧栏,用户关掉 A 的详情面板、打开
B 的面板后在仍可见的 companion 里输入,回退就抓到 B 的 form,后续 action/autosave 改
错卡。改法:closeCatgirlPanel 时给 companion 打 _detailPanelClosed 标记,path-2 仅在
标记为 false 时允许;按确切档案名命中的 path-1(安全、确定同卡)清掉标记。合法的
in-place rebuild(新卡首存 popup 被拦走 rebuildSavedCatgirlPanel、改档案名字段后
saveCatgirlFromPanel 重建表单)都不经过 closeCatgirlPanel,回退照常生效;同卡关掉再开
由 path-1 兜住。
- #3328901018:_cardAssistApplyToForm 新建字段的删除按钮只 wrapper.remove(),不像普通
自定义字段删除路径(~5041)那样亮 Save/Cancel。已保存卡上删掉 AI 新建字段后既不触发
autosave、也没有可见的手动保存入口,reload 后字段复活。改成删除时一并亮出 Save/Cancel。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): autosave 不甩面板 + replay 删除亮 Save(Codex P2 ×2)
- #3328942156:_companionTryAutoSave 的 _autoCreated 卡走到 saveCatgirlFromPanel 时仍
传 state.isNew=true。helper 的请求方法本就按内部 effectiveIsNew(=isNew && !_autoCreated)
走 PUT,没问题;但它**保存成功后的 UI 分支**按原始 isNew 判定,会 closeCatgirlPanel /
开卡面制作弹窗,把正在进行的 companion 聊天打断。改成按"是否已落库"传 effectiveIsNew
(_autoCreated 视作已保存卡),post-save 走原地刷新、不甩面板;请求方法不变。
- #3328942158:in-flight save 的 replay 分支里,对 rebuilt 表单执行「删除」replay 后没
重新亮 Save/Cancel。若只 replay 了删除、没 replay 字段值,_cardAssistApplyToForm 不会
被调到、不顺带亮按钮;紧接着 autosave 一旦失败、提示「手动点 Save 重试」时按钮却藏着 →
删除丢失、reload 后字段复活。跟直连 remove_field / AI 字段删除两条路径一致,补上亮按钮。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 关面板即 teardown companion + 等手动保存真收尾(CR Major + Codex P1)
- 防误绑改用 teardown-on-close(取代上一版 _detailPanelClosed 一刀切):CodeRabbit 指出
flag 封死 path-2 会让 companion 在重命名 / 新卡首存后「同卡也回不来」永久失联。关键事实是
openCatgirlPanel 顶部有 _catgirlPanelOpen 互斥——打开/切换到别的卡**必须先 closeCatgirlPanel**。
所以改成 closeCatgirlPanel 时直接 _companionTeardown + _companionDestroy + 置 null:跨卡误绑
从根上消除,且不再有 stranded 的 form-gone 死局。合法 in-place rebuild(改档案名字段后重建、
新卡首存 popup 被拦走 rebuildSavedCatgirlPanel)不经过 closeCatgirlPanel,companion 照常跟随,
path-2 回退恢复无条件、安全(Codex #3328901017 + CodeRabbit Major @4845/11349)。
- #3328951294 P1:autosave 的 wait loop 之前盯 state.form.dataset.submitting,可手动保存 PUT
成功一触发重建,_companionEnsureLiveForm 就把 state.form 重绑到新 form(submitting 未置位)→
循环提前 break、抢在手动保存 finally(还要 loadCharacterData + 二次重建)收尾前 replay+自存 →
被后续重建覆盖、退回静默丢失。改成盯原始 formBeforeWait.dataset.submitting 清掉再继续。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): autosave 等待超时不再静默放弃(Codex P2 #3328963563)
wait loop 等手动保存收尾超过 8s 时,之前只 console.warn 后 return。那次慢保存收尾会用
较旧的请求快照重建表单、覆盖掉 companion 刚写进去的改动/删除,而用户只看到之前那条
「已应用」气泡、误以为存好了。改成:补一条 aiCompanionAutoSaveFailed 失败气泡讲清楚,
并尽量把 Save/Cancel 亮出来给手动兜底入口(那次保存最终失败、没重建表单时这俩按钮即重试
路径)。复用既有 i18n key,不新增 locale 项。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 抬高 chat action 上限避免「重写全部」被静默截断(Codex P2 #3328971304)
_CHAT_MAX_ACTIONS 原为 8,但默认模板就有 9 个可见字段(昵称/性别/年龄/种族/自称/核心特点/
行为特点/厌恶/一句话台词)。「重写全部」quick action 会一字段一个 refine_field 返回,卡在 8
会把第 9 个及之后静默丢掉、autosave 只落库半张卡。抬到 32:覆盖默认 9 字段 + 充裕自定义字段,
仍能拦住真正失控的超长 action 列表。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 给四个 LLM 端点加本地 Origin/CSRF 守卫(Codex P2 #3328998416)
card-assist 的 clarify/generate/refine/chat 都会真去打用户配置的对话/辅助 LLM、花
API/免费额度,却没走仓库里其它有副作用浏览器端点的统一本地请求校验。localhost 部署下
恶意网页可用 no-cors + text/plain body 伪造合法 JSON,request.json() 照样解析、白嫖配额
(攻击者读不到响应但能烧额度)。
- 后端:复用 system_router 的 _validate_local_mutation_request(issue Project-N-E-K-O#1479 统一守卫),
四个端点在 dict 校验后、调 LLM 前先过 Origin+CSRF;不通过返回 403 csrf_validation_failed。
system_router 不反向依赖 card_assist,无循环导入。
- 前端:_cardAssistFetch 带上 X-CSRF-Token。本页(character_card_manager.html 独立页)不
加载 app-prompt-shared.js → 没有 nekoLocalMutationSecurity,故自包含地从
/api/config/page_config 取 autostart_csrf_token 缓存(与本页 universal-tutorial-manager.js
同源);主 app 上下文里若有统一安全助手则优先用它。Origin 由浏览器同源 POST 自动带。
- 测试:tests/unit/test_card_assist_csrf.py канary——四个端点无 Origin/CSRF 必返 403
csrf_validation_failed,防止守卫被误删。本地实测 4 passed,且带合法 Origin+token 能穿过守卫。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 新卡「Save 与 /generate 竞态」保住 AI 字段(Codex P2 #3329022313)
_companionTryAutoSave 的新卡 guard 之前无条件 early-return,不等也不 replay。竞态:
用户在 /generate 还在飞时点了 Save → saveCatgirlFromPanel 已用「AI 写字段之前」的旧快照
序列化好 → AI 把字段写进同一张新卡表单 → 这里 guard 直接 return → 保存成功后用旧快照
rebuild/关面板,把用户已看到「已应用」的 AI 字段静默丢掉。
改法:
- 新卡 guard 增加 `dataset.submitting !== 'true'` 条件——已有手动 Save 在飞行中时不 return,
落到下面的 wait/replay:等那次 Save 收尾、把 AI 字段 replay 到 rebuild 出来的已保存卡表单
再存一遍(与已保存卡路径同一套机制)。
- saveCatgirlFromPanel 前加二次 guard:等待后若卡仍未落库(save 失败/没建成),绝不 POST 建卡
+ 关面板,AI 字段留在表单、提示手动 Save。
- 关面板竞态(首存 popup/有卡面 → closeCatgirlPanel)下 companion 已被 teardown,wait 循环里
ensureLiveForm 返回 false 优雅退出,无副作用。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 生成中禁掉新卡 Save,从源头堵住竞态丢字段(Codex P2 #3329817833)
承接 #3329022313 的后续:在「新卡首存成功后走 closeCatgirlPanel(开卡面制作 / 已有卡面)」
的分支里,面板关闭会 teardown companion,wait/replay 的 _companionEnsureLiveForm 返回 false
直接退出,AI 字段救不回来——这种「关面板」竞态没法在事后挽回。
按 Codex 给的「manual-save-disable during generation」方向从源头预防:_companionSetBusy 里,
未落库新卡(isNew && !_autoCreated)在 companion 打 LLM 期间禁掉详情表单 Save,用户就无法在
「/generate 还没把字段写进表单」的窗口里点 Save,竞态不再被触发。disabled = busy && 未落库新卡,
busy 退场或卡一旦落库都会自动放开,不会卡死;用 disabled 属性,与 saveCatgirlFromPanel 的
dataset.submitting 去抖互不干扰。已落库卡不禁(其并发由 wait/replay 安全兜住)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 关闭 companion 时恢复被禁的新卡 Save(Codex #3331627614 / CR Major #3331629488)
上一条(8feb2805)给未落库新卡在 LLM 飞行中禁 Save 的副作用:用户在请求还没回来时点 × 关掉
companion,_companionTeardown 只置 closed / 摘监听,迟到响应的 guard 又直接 return,于是
_companionSetBusy(false) 永远不会被调到,Save 一直灰着直到请求超时(最多 60s)——表单还在
页面上却存不了。在 _companionTeardown 开头无条件把 #save-button 的 disabled 放开即可。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 复用完整保留字段列表,避免保留 key 被静默丢弃(Codex P2 #3331668038)
card-assist 前后端各维护了一份不完整的保留字段拷贝,漏了 lighting / live3d_sub_type /
vrm_animation / live2d_idle_animation / live2d_item_id 等。这些 key 在 chat/add_field 里
被当普通 AI 字段渲染、autosave 报成功,但保存时被 collectCharacterFields /
_filter_mutable_catgirl_fields 丢掉 → 刷新后行消失、用户改动静默不持久化。
- 后端:_RESERVED_CARD_FIELDS 改成 frozenset(CHARACTER_RESERVED_FIELDS)(角色编辑器/保存
过滤同源)∪ {档案名, live3d},不再维护部分拷贝。
- 前端:去掉写死的 CARD_ASSIST_RESERVED_KEYS,新增 _cardAssistIsReservedKey(),复用角色编辑器
同一套 isCharacterReservedFieldName(后端实时配置 + ReservedFieldsUtils 兜底)+ '档案名',
6 处调用点统一切过去。
- 实测:lighting/live3d_sub_type/vrm_animation/live2d_idle_animation/档案名/live3d 均判为保留,
性别/Gender 等真设定字段仍可改;CSRF canary 4 passed。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 给 prompt 没专门版本的 locale 加输出语言指示(Codex P2 #3331696257)
prompt 只写了 zh/en 两版:ja/ko/ru/pt/es 落到 en、zh-TW 落到简中,且没有「用该语言回答」的
指示——于是这些 locale 下助手用英文/简中提问、把字段值也填成英文/简中,尽管字段 key 已按
locale 模板给定。
按 Codex 给的方向二落地:新增 _LOCALE_OUTPUT_LANGUAGE 映射 + _output_language_directive(),
对 ja/ko/ru/pt/es/zh-TW 在 prompt 末尾追加一条显式输出语言指示(要求 questions / field
values / 说明都用目标语言、保持 JSON 结构与 key 不变);en/zh-CN 与基础 prompt 一致返回空串。
clarify/generate/refine/chat 四端点都接上。
实测各 locale 指示正确,en/zh-CN 不加;CSRF canary 4 passed。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Improve free card assist action reliability
* 头像位置修正
* Unify card assist action recovery
* fix(card-assist): generate 草稿应用前重新 ensure live form(Codex P2 #3332998069)
_companionRunGenerate 在 /generate 的 await **之前** ensure 过 form,但返回后直接
_cardAssistApplyToForm(state.form, ...)。若期间表单被 rebuild(用户改名/保存先于模型返回
完成、旧 form detach),draft 就写进了 detached DOM;紧接着 _companionTryAutoSave rebind 到
新 form,但 generate 这条路没有 in-flight replay 通路,于是把不带 draft 的表单存下去,助手
却报「已应用」→ 字段静默丢失。
修法:apply 前再 _companionEnsureLiveForm(state) 一次,写进当前活着的 form;接不上就报
form-gone、不写 detached DOM。chat 路径的 _companionApplyActions 开头本就重新 ensure,不受影响。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 全量重写用 locale 无关 flag + 新卡问答阶段也禁 Save(Codex P2 ×2)
- #3333137718:「重写整张卡」靠后端正则匹配 latest_user 文本判定全量重写意图,只覆盖
简中/繁中/英文;es/ja/ko/pt/ru 的本地化文案匹配不到 → _complete_full_rewrite_actions
补全通路不触发,部分 action 被当部分重写存下。改成前端 quick action 透传 locale 无关的
full_rewrite flag,后端优先读 flag(保留文本启发式兼容手敲措辞)。
- #3333137733:上一版(8feb2805)只在 busy 时禁未落库新卡的 Save,但 clarify 返回后、
问答阶段 busy=false,Save 又放开;用户此时点 Save 再答完最后一题 → /generate 应用字段、
等 in-flight save,仍可能在首存成功关面板分支里丢字段。把 Save 禁用条件挪进
_companionUpdateQuickAvailability(busy + 每次 mode 切换都会同步),改成
未落库新卡 && (busy || mode !== 'chat'):整个问答/生成流程都禁,进 chat 模式(草稿已落
表单)才放开;已落库卡不禁;关 companion 时 teardown 仍无条件恢复。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): full_rewrite flag 也触发 action 恢复(Codex P2 #3333394174)
承接 #3333137718:_recover_actions_from_reply 的 gate 只看 edit_intent,而它走
_CHAT_EDIT_INTENT_RE 只覆盖中英。本地化「重写整张卡」quick chip(es/ja/ko/pt/ru/zh-TW)
文本匹配不到 → edit_intent=False;若首轮 LLM 又没吐可用 actions(纯文本 / actions:[]),
recovery 被跳过、只回一句话、卡没改,辜负了显式 full_rewrite flag(_complete_full_rewrite_actions
只补全已有 actions、空 actions 救不回)。
改成 `(edit_intent or full_rewrite_intent) and not actions` 触发恢复。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): full_rewrite flag 改成真正一次性消费(CodeRabbit Major #3333410664)
flag 之前在 form-gone early-return 之后才读+清,若点「重写整张卡」时撞上 form rebuild、
没接上 live form 而提前 return,标记会残留、被下一条普通聊天消息误当成整卡重写。改成在
_companionRunChat 开头(任何 early-return 之前)就读出并清掉。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 新卡首存飞行中短路 chat,避免编辑丢失(Codex P2 #3333457418)
chat 模式下 Save 是放开的(草稿已生成)。用户点了首存、又在它收尾前发一条 chat 编辑:
saveCatgirlFromPanel 已用「编辑前快照」序列化,这次 /chat 应用的编辑会在「首存成功关面板 /
开卡面」分支里随面板一起没掉,_companionTryAutoSave 又 rebind 不上 → 编辑静默丢失。
_companionRunChat 在打 LLM 前加 guard:state.isNew && !state.form._autoCreated &&
dataset.submitting === 'true' 时短路,弹一条 aiCompanionSaveInProgress 提示让用户等保存收尾
(消息还在 chatHistory 里,存好后再发即可)。问答/生成流程下 Save 本就被禁,这里只兜 chat 这条路。
新增 aiCompanionSaveInProgress i18n key,8 个 locale 全量补齐。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): use agent model without watermark
* fix(card-assist): reserve free agent quota
* fix(agent): free agent quota 改按实际 agent model 判定,不再绑 IS_FREE_VERSION
问题:本地每日配额按全局 IS_FREE_VERSION 判定。但 core/assist 已解耦——core=free
+ assist=付费、或自定义付费 agent model 时,IS_FREE_VERSION 仍为 True,导致用户用着
自费模型却被免费试用配额(每日 500)拦截。
改动:
- consume_agent_daily_quota 的 gate 从 is_free_version() 改为「实际 agent model ==
free-agent-model」(get_model_api_config('agent'))。自费/自定义 agent model 自然放行;
自定义覆盖也被尊重。IS_FREE_VERSION 保留给免填 key、藏云端模型、默认音色等其它职责。
- 摘掉 task_executor 4 处 + deduper 1 处 quota 调用:这些判定器走的是 summary/emotion
模型而非 agent model,本就不该计入 agent 配额;删 _check_agent_quota 方法。
- 真正走 agent model 的 computer_use / browser_use / card_assist 保留,按新 gate 计数。
- 单测:test_agent_quota_notify 改 mock get_model_api_config;新增「自费 agent model
即便 IS_FREE_VERSION=True 也放行」回归;清理 stage1_filtering 里失效的 _check_agent_quota stub。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): companion teardown 后阻止迟到 finally 重新禁用 Save (Codex #3333702549)
未落库新卡的 companion 在 /clarify 飞行中被关闭时:_companionTeardown 已无条件恢复
详情表单的 Save 并置 state.closed,但迟到的 clarify finally 仍会 _companionSetBusy(false)
→ _companionUpdateQuickAvailability,按「未落库新卡 + 非 chat 模式」规则把 Save 又禁回去。
此时 companion 已销毁、详情面板还开着,用户再也点不动 Save。
修法:_companionUpdateQuickAvailability 开头加 state.closed 早退,teardown 后不再碰表单控件。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(card-assist): 澄清失败回退后放开未落库新卡的 Save (Codex #3333683160)
未落库新卡的 Save 原先在 mode !== 'chat' 时一律禁用,但 awaiting_description(companion
首启 + /clarify 失败回退到此)并没有任何在途生成——禁 Save 零竞态收益,只把用户困住:
澄清失败(如没配 API)后想放弃 AI、手动建卡却点不动,得先关 companion 才行。
改为只在 busy 或 asking_questions(答最后一题即触发生成)时禁 Save,放开 awaiting_description。
保留 #3329022313/#3329817833/#3333137733 的「答题/生成期间禁 Save」保护;手动 Save 撞后续
生成的竞态本就由 busy + _companionTryAutoSave 的 wait/replay 兜底。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: breakkh811-star <breakkh811@gmail.com>
Co-authored-by: yiyiyiyiGKY <17858325501@163.com>
Co-authored-by: Hongzhi Wen <cartabio.coder1@gmail.com>1 parent 7e8657e commit 1aee875
25 files changed
Lines changed: 4937 additions & 130 deletions
File tree
- app
- brain
- config
- prompts
- main_routers
- static
- css
- js
- locales
- tests
- frontend
- unit
- utils
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1641 | 1641 | | |
1642 | 1642 | | |
1643 | 1643 | | |
| 1644 | + | |
1644 | 1645 | | |
1645 | 1646 | | |
1646 | 1647 | | |
| |||
1772 | 1773 | | |
1773 | 1774 | | |
1774 | 1775 | | |
| 1776 | + | |
1775 | 1777 | | |
1776 | 1778 | | |
1777 | 1779 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | 53 | | |
65 | 54 | | |
66 | 55 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
297 | 297 | | |
298 | 298 | | |
299 | 299 | | |
300 | | - | |
301 | | - | |
302 | | - | |
303 | | - | |
304 | 300 | | |
305 | 301 | | |
306 | 302 | | |
| |||
441 | 437 | | |
442 | 438 | | |
443 | 439 | | |
444 | | - | |
445 | | - | |
446 | | - | |
447 | | - | |
448 | | - | |
449 | | - | |
450 | | - | |
451 | 440 | | |
452 | 441 | | |
453 | 442 | | |
| |||
973 | 962 | | |
974 | 963 | | |
975 | 964 | | |
976 | | - | |
977 | | - | |
978 | | - | |
979 | | - | |
980 | 965 | | |
981 | 966 | | |
982 | 967 | | |
| |||
1177 | 1162 | | |
1178 | 1163 | | |
1179 | 1164 | | |
1180 | | - | |
1181 | | - | |
1182 | | - | |
1183 | | - | |
1184 | 1165 | | |
1185 | 1166 | | |
1186 | 1167 | | |
| |||
1337 | 1318 | | |
1338 | 1319 | | |
1339 | 1320 | | |
1340 | | - | |
1341 | | - | |
1342 | | - | |
1343 | | - | |
1344 | | - | |
1345 | | - | |
1346 | | - | |
1347 | | - | |
1348 | | - | |
1349 | | - | |
1350 | 1321 | | |
1351 | 1322 | | |
1352 | 1323 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| 44 | + | |
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
0 commit comments