From 29260f59c25dc70c932c9d109e25548336133276 Mon Sep 17 00:00:00 2001 From: wuyupeng Date: Mon, 1 Jun 2026 09:04:16 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(game):=20=E5=9B=BE=E9=89=B4=20Codex=20?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E2=80=94=E2=80=94=E5=89=AA=E5=BD=B1=E8=A7=A3?= =?UTF-8?q?=E9=94=81=20+=20=E5=85=A8=E5=B1=8F=E8=AF=A6=E6=83=85=20+=20?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E6=80=AA=E5=85=BD=E5=8F=AF=E7=82=B9=E5=87=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在右上角背包上方新增可折叠「图鉴」面板(renderCodex/openCodex): - 初始七只怪兽均为剪影 + 「?」,击败后正式解锁显形 - 点开看全屏详情:简介 / 它提醒你什么 / 扩展链接 - 标题屏「怪兽一览」精灵改为可点击,跳对应图鉴(预览模式) 扩展文案集中在 CODEX_EXTRA(按怪兽 id 配 reminds + links),便于后续增补。 复用既有 spriteCanvasHTML / paintDomSprites / G.defeated,零侵入主流程。 Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/game.html | 99 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/docs/game.html b/docs/game.html index 3ef594b..e7d2af8 100644 --- a/docs/game.html +++ b/docs/game.html @@ -843,13 +843,14 @@ else if (G.screen === 'victory'){ r.innerHTML = victoryHTML(); bindEnd(); } paintDomSprites(); renderBackpack(); + renderCodex(); } // ================================================================ // 标题屏 // ================================================================ function titleHTML(){ - const emojis = MONSTERS.filter(m => m.tier!=='mob').map(m => spriteCanvasHTML(m.id, 56)).join(''); + const emojis = MONSTERS.filter(m => m.tier!=='mob').map(m => '').join(''); return `

GitHire · 知识冒险

HireRPG

@@ -871,6 +872,7 @@

HireRPG

function bindTitle(){ const b = document.getElementById('btn-start'); if (b) b.onclick = startGame; + document.querySelectorAll('.mon-pick').forEach(function(p){ p.onclick = function(){ openCodex(p.dataset.codex, true); }; }); } // ================================================================ @@ -1648,6 +1650,101 @@

HireRPG

} // 右上角常驻背包面板:map / battle 屏全程可见,显示持有的复活道具与装备状态 +/* ════════════ 图鉴 Codex 系统 ════════════ */ +(function injectCodexCSS(){ + var s=document.createElement('style'); + s.textContent="\n /* ── 图鉴 Codex ───────────────────────────── */\n #hud-tr{ position:fixed; top:72px; right:12px; z-index:50; width:230px; max-width:72vw; display:flex; flex-direction:column; gap:8px; font-family:var(--fp); }\n #hud-tr #backpack{ position:static; top:auto; right:auto; width:100%; max-width:100%; }\n #codex{ width:100%; font-family:var(--fp); }\n .cx-head{ width:100%; display:flex; align-items:center; gap:.4rem; cursor:pointer; background:var(--panel-d); color:#fff; border:3px solid var(--rink); box-shadow:3px 3px 0 rgba(31,36,48,.3); padding:.5rem .6rem; font-size:.7rem; user-select:none; }\n .cx-count{ background:var(--rgold); color:var(--rink); border:2px solid #000; padding:0 .35rem; }\n .cx-arrow{ margin-left:auto; }\n .cx-body{ background:var(--panel); border:3px solid var(--rink); border-top:none; box-shadow:3px 3px 0 rgba(31,36,48,.3); padding:.55rem; display:grid; grid-template-columns:repeat(4,1fr); gap:.4rem; }\n .cx-slot{ position:relative; background:#fff; border:2px solid #d8d2bf; padding:.3rem .1rem .22rem; display:flex; flex-direction:column; align-items:center; gap:.12rem; cursor:pointer; transition:transform .07s, border-color .07s; }\n .cx-slot:hover{ transform:translateY(-2px); border-color:var(--accent); }\n .cx-slot canvas{ image-rendering:pixelated; }\n .cx-slot.locked canvas{ filter:brightness(0) opacity(.45); }\n .cx-slot .cx-q{ position:absolute; top:8px; left:50%; transform:translateX(-50%); font-size:1rem; font-weight:800; color:#9b93c0; pointer-events:none; }\n .cx-nm{ font-size:.5rem; color:var(--rink); text-align:center; line-height:1.05; width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n .cx-slot.locked .cx-nm{ color:#9aa; }\n /* 全屏图鉴详情 */\n .cx-ov{ position:fixed; inset:0; z-index:120; display:flex; align-items:center; justify-content:center; background:rgba(8,10,16,.78); padding:1rem; font-family:var(--fp); }\n .cx-card{ position:relative; width:min(520px,100%); max-height:88vh; overflow:auto; background:var(--panel); border:4px solid var(--rink); border-top:10px solid var(--mc,var(--accent)); box-shadow:7px 7px 0 rgba(31,36,48,.35); padding:1.4rem 1.3rem 1.5rem; color:var(--rink); }\n .cx-x{ position:absolute; top:.5rem; right:.7rem; background:none; border:none; color:var(--rink); font-size:1.5rem; cursor:pointer; line-height:1; }\n .cx-hero{ display:flex; align-items:center; gap:1rem; margin:.2rem 0 1rem; }\n .cx-hero canvas{ image-rendering:pixelated; flex:none; }\n .cx-hero.locked canvas{ filter:brightness(0) opacity(.5); }\n .cx-htxt h2{ margin:0; font-size:1.1rem; line-height:1.3; }\n .cx-zone{ display:inline-block; margin-top:.4rem; font-size:.56rem; background:#fff; border:2px solid var(--mc,var(--accent)); padding:.1rem .5rem; color:var(--rink); }\n .cx-sec{ margin:.9rem 0 0; }\n .cx-sec h3{ margin:0 0 .3rem; font-size:.62rem; letter-spacing:.04em; color:var(--mc,var(--accent)); }\n .cx-sec p{ margin:0; font-size:.72rem; line-height:1.85; color:#3a3f30; }\n .cx-links{ display:flex; flex-wrap:wrap; gap:.45rem; margin-top:.5rem; }\n .cx-links a{ font-size:.62rem; color:#fff; background:var(--mc,var(--accent)); border:2px solid var(--rink); box-shadow:2px 2px 0 rgba(31,36,48,.25); padding:.32rem .6rem; text-decoration:none; }\n .cx-locked-note{ text-align:center; color:#6a6f55; font-size:.72rem; line-height:2; padding:1rem 0 .3rem; }\n .monster-row .mon-pick{ background:none; border:none; padding:0; cursor:pointer; transition:transform .1s; line-height:0; }\n .monster-row .mon-pick:hover{ transform:translateY(-3px) scale(1.08); }\n @media (max-width:560px){ #hud-tr{ top:64px; right:6px; width:172px; } .cx-head{ font-size:.62rem; } }\n"; + (document.head||document.documentElement).appendChild(s); +})(); +// 图鉴扩展文案:每只怪兽"提醒你什么问题" + 可选额外链接。文案与链接均可随时编辑、增补。 +const CODEX_EXTRA = { + ghost: { reminds:'写 Issue 时先把【目标 / 约束 / 非目标 / 验证标准】写全——每一处空白,都是 AI 自由发挥的入口。', links:[] }, + fire: { reminds:'别"先上线看看"。先在具备真实依赖、生产量级数据的沙盒里验证,再谈合入主干。', links:[] }, + scan: { reminds:'AI review 说"逻辑正确"不等于系统安全。凡涉及数据规模 / QPS 的代码,必须带系统侧上下文复核。', links:[] }, + dragon: { reminds:'"AI 评过了"不是签字。合入主干的代码,必须有一位架构师认领系统侧后果。', links:[] }, + zombie: { reminds:'延期先别急着加人。先问:是 framing 不够,还是吞吐不够?(布鲁克斯定律)', links:[] }, + clock: { reminds:'用可观测的决策点完成度替代主观时间承诺——deadline 滑掉,多半败在 framing 与 review,而非 coding。', links:[] }, + robot: { reminds:'多 agent 并发也要守住概念完整性:讲不清动机与取舍的 PR,不 merge。', links:[{ label:'GitHire 方法:人思考 · AI 执行 · 人验收', url:'./index.html' }] }, +}; +function codexMonsters(){ return MONSTERS.filter(function(m){ return m.tier!=='mob'; }); } +function codexUnlocked(id){ return !!(typeof G!=='undefined' && G.defeated && G.defeated[id]); } +function hudWrap(){ + var w = document.getElementById('hud-tr'); + if (!w){ w = document.createElement('div'); w.id='hud-tr'; document.body.appendChild(w); } + return w; +} +function renderCodex(){ + if (typeof G==='undefined') return; + var show = (G.screen === 'map' || G.screen === 'battle'); + var w = hudWrap(); + var cx = document.getElementById('codex'); + if (!cx){ cx = document.createElement('div'); cx.id='codex'; } + var bp = document.getElementById('backpack'); + if (bp && bp.parentNode !== w) w.appendChild(bp); + if (cx.parentNode !== w || w.firstChild !== cx) w.insertBefore(cx, w.firstChild); + cx.style.display = show ? 'block' : 'none'; + if (!show) return; + var ms = codexMonsters(); + var unlocked = ms.filter(function(m){ return codexUnlocked(m.id); }).length; + var slots = ms.map(function(m){ + var ok = codexUnlocked(m.id); + return '
' + + spriteCanvasHTML(m.id, 38) + + (ok?'':'?') + + ''+(ok?m.name:'???')+'
'; + }).join(''); + cx.innerHTML = + '
📖 图鉴 '+unlocked+'/'+ms.length+'' + + ''+(G.cxOpen?'▾':'▸')+'
' + + (G.cxOpen?('
'+slots+'
'):''); + var head = document.getElementById('cx-head'); + if (head) head.onclick = function(){ G.cxOpen = !G.cxOpen; renderCodex(); }; + cx.querySelectorAll('.cx-slot').forEach(function(s){ s.onclick = function(){ openCodex(s.dataset.codex); }; }); + paintDomSprites(); +} +function openCodex(id, preview){ + var m = codexMonsters().find(function(x){ return x.id===id; }); if(!m) return; + var ok = preview || codexUnlocked(id); + var ex = (typeof CODEX_EXTRA!=='undefined' && CODEX_EXTRA[id]) || {}; + closeCodex(); + var ov = document.createElement('div'); ov.className='cx-ov'; ov.id='cx-ov'; + var links = []; + if (m.learnHref) links.push({ label:(m.learnText||'了解更多'), url:m.learnHref }); + if (ex.links) links = links.concat(ex.links); + var linksHTML = (ok && links.length) + ? '

扩展阅读

' + : ''; + if (ok){ + ov.innerHTML = + '
' + + '' + + '
'+spriteCanvasHTML(m.id, 84) + + '

'+(m.emoji||'')+' '+m.name+'

' + + '📍 '+(m.zone||'')+'
' + + '

简介

'+(m.flavor||'')+'

' + + (ex.reminds?('

它提醒你什么

'+ex.reminds+'

'):'') + + linksHTML + + '
'; + } else { + ov.innerHTML = + '
' + + '' + + '
'+spriteCanvasHTML(m.id, 84) + + '

???

未解锁
' + + '

这只怪兽还藏在地图深处。
在冒险中击败它,即可解锁完整图鉴。

' + + '
'; + } + document.body.appendChild(ov); + ov.onclick = function(e){ if(e.target===ov) closeCodex(); }; + var x = document.getElementById('cx-x'); if(x) x.onclick = closeCodex; + paintDomSprites(); +} +function closeCodex(){ var o=document.getElementById('cx-ov'); if(o) o.remove(); } +document.addEventListener('keydown', function(e){ if(e.key==='Escape') closeCodex(); }); +/* ════════════ /图鉴 Codex ════════════ */ + function renderBackpack(){ let bp = document.getElementById('backpack'); const show = (G.screen === 'map' || G.screen === 'battle'); From 330e570449a383a24e676d8777e0e10ceeb68456 Mon Sep 17 00:00:00 2001 From: wuyupeng Date: Mon, 1 Jun 2026 09:04:31 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(codex):=20=E6=96=B0=E5=A2=9E=20game-co?= =?UTF-8?q?dex.html=20=E6=96=87=E6=A1=88=E8=81=9A=E5=90=88=20/=20=E4=B8=AD?= =?UTF-8?q?=E8=8B=B1=E5=8F=8C=E8=AF=AD=E7=AE=A1=E7=90=86=E5=8F=B0=EF=BC=88?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=20noindex=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 游戏全部「文言」的聚合预览 + 双语编辑后台: - 数据 fetch(game.html) 后正则解析:怪兽(MONSTERS+CODEX_EXTRA) + 全量中文串 (道具/NPC/地图/题库/界面,约 259 条),按行号自动归类 - 怪兽卡显示真实像素精灵(隐藏 iframe 调 getSprite()),连不上回退 emoji - 中文 + 英文均可在线编辑,存浏览器 localStorage;怪兽名/reminds 内置英文默认译 - 只「导出 JSON」无导入、无任何在线写回——文案更新只能本地 commit/PR - 工具条:对照/中文/EN 切换、分类筛选、显式「查询」按钮 + 回车、清除 - 怪兽图鉴纳入 filter 体系(不再永久置顶) 注意:须经 http(s)/本地服务器访问,双击 file:// 会被浏览器禁止读取本地文件。 Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/game-codex.html | 451 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 docs/game-codex.html diff --git a/docs/game-codex.html b/docs/game-codex.html new file mode 100644 index 0000000..aa26408 --- /dev/null +++ b/docs/game-codex.html @@ -0,0 +1,451 @@ + + + + + + +HireRPG · 文案 / 图鉴管理台(后台) + + + +
+

GitHire · HireRPG 后台

+

文案 / 图鉴管理台

+

游戏里所有「文言」的聚合页:怪兽、题库、道具、NPC、地图、界面文案集中预览,中英双语均可在线编辑

+

编辑只存在你本地浏览器。用 导出译文 拿到 JSON 后,在本地 commit / PR 回灌 game.html 上线——本页不提供任何在线写回,线上无人能直接改文案。

+ ← 返回游戏 + GitHire 首页 + + +
+ +
+ +
+
+ + + + + +
+
+ +
+ + + + + +
+ +
+
+
+ + + + + + From bbc6a8785fcbb0c5f57063488b0726b1afcb3786 Mon Sep 17 00:00:00 2001 From: wuyupeng Date: Mon, 1 Jun 2026 09:17:51 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix(review):=20harvest=20=E8=A1=A5=E9=87=87?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=AD=97=E7=AC=A6=E4=B8=B2=E4=B8=8E=20HTML?= =?UTF-8?q?=20=E6=96=87=E6=9C=AC=E8=8A=82=E7=82=B9=EF=BC=8C=E6=B6=88?= =?UTF-8?q?=E9=99=A4=E5=8F=8C=E8=AF=AD=E7=AE=A1=E7=90=86=E5=8F=B0=E6=BC=8F?= =?UTF-8?q?=E9=87=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原 harvest() 只逐行匹配单/双引号字符串,标题屏说明/按钮(进入世界 →)、 地图提示、以及多行模板字符串等线上可见文案都进不了 catalog,导出的 JSON 并非完整文案快照,违背"全量文言"定义。 改为对整源做偏移扫描,统一覆盖:单/双引号 + 反引号模板(含跨行)+ HTML 文本节点;并以字符串字面量区间对 HTML 文本去重,避免 JS 拼接 HTML 被重复 采集。行号/列号由偏移换算,categoryForLine 与跳过区间逻辑不变。 验证(node 跑实际逻辑 against docs/game.html):采集量 86 → 165, 代表文案进入世界/titleSlogan/titleQuote/地图提示均被抓到,4 条多行模板 全部覆盖;node --check 语法通过。建议项(回归断言、锚点切片)本次未做, 属优化非 blocker。 Reviewed-Comment: https://github.com/realRoc/git-hired/pull/30#issuecomment-4588840979 Co-Authored-By: Claude --- docs/game-codex.html | 55 ++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/docs/game-codex.html b/docs/game-codex.html index aa26408..0eb1e94 100644 --- a/docs/game-codex.html +++ b/docs/game-codex.html @@ -256,24 +256,49 @@

文案 / 图鉴管理台

while ((m = re.exec(block))) out[m[1]] = unq(m[2]); return out; } -/* 抓全部含中文的字符串字面量;跳过怪兽区(336-368)与 CODEX_EXTRA(1655-1672) */ +/* 抓全部含中文的「文言」:单/双引号 + 反引号模板字符串(可跨行)+ HTML 文本节点。 + 对整源做偏移扫描(而非逐行),故能覆盖多行模板字符串与标题屏/地图提示等 DOM 文本, + 不会漏掉线上可见文案。跳过怪兽区(336-368)与 CODEX_EXTRA(1655-1672)——这两块 + 另由 parseMonsters / parseExtra 单独解析,避免在全量表里重复列出。 */ function harvest(src){ - const lines = src.split('\n'); const cjk = /[一-鿿 -〿＀-￯]/; const out = []; const seen = {}; - const strRe = /'((?:[^'\\]|\\.)*)'|"((?:[^"\\]|\\.)*)"/g; - for (let i=0;i=336 && lineNo<=368) continue; - if (lineNo>=1655 && lineNo<=1672) continue; - const ln = lines[i]; let m; strRe.lastIndex = 0; - while ((m = strRe.exec(ln))){ - const raw = (m[1]!==undefined ? m[1] : m[2]); - if (!cjk.test(raw)) continue; - const dedup = lineNo+'#'+m.index; - if (seen[dedup]) continue; seen[dedup]=1; - out.push({ key:'L'+lineNo+'#'+m.index, line:lineNo, col:m.index, cat:categoryForLine(lineNo), zh:unq(raw) }); - } + // 行起点偏移表:把整源字符偏移换算成行号 / 列号 + const lineStart = [0]; + for (let i=0;i>1; if (lineStart[mid]<=idx) lo=mid; else hi=mid-1; } + return lo; // 0-based 行下标 + } + function inSkip(lineNo){ return (lineNo>=336&&lineNo<=368)||(lineNo>=1655&&lineNo<=1672); } + function push(idx, raw){ + if (!cjk.test(raw)) return; + const li = lineOf(idx), lineNo = li+1; + if (inSkip(lineNo)) return; + const col = idx - lineStart[li]; + const key = 'L'+lineNo+'#'+col; + if (seen[key]) return; seen[key]=1; + out.push({ key:key, line:lineNo, col:col, cat:categoryForLine(lineNo), zh:unq(raw) }); + } + // 1) 字符串字面量:单/双引号 + 反引号模板(反引号字符类含换行,故跨行模板亦可匹配) + const spans = []; // 记录每个字面量的 [start,end),供 HTML 文本去重 + const strRe = /'((?:[^'\\]|\\.)*)'|"((?:[^"\\]|\\.)*)"|`((?:[^`\\]|\\.)*)`/g; + let m; + while ((m = strRe.exec(src))){ + spans.push([m.index, strRe.lastIndex]); + push(m.index, (m[1]!==undefined ? m[1] : (m[2]!==undefined ? m[2] : m[3]))); + } + // 2) HTML 文本节点:>文本<。跳过落在字符串字面量内的(那是 JS 拼接的 HTML,已整段采过) + function inStr(idx){ + for (let i=0;i=spans[i][0] && idx([^<>]*)