feedback7 G-SMALL: settings can copy local diagnostics for a bug report (PR #321 by @Cloudnyco)
Applied 设置 → 问题反馈. The report stays in the browser: the in-memory error log, device info, and the battle on screen only while 「附上本场战斗」 is on. Nothing is uploaded. Redaction no longer treats a long name as a bare substring. Prose replaces each secret only as a whole token, so a name does not cut a longer word or a chess id. The attached JSON replaces a string only when it equals the secret, so chess_char_1013_chen2 and a note like "charge the char" stay, while an exact "char" value becomes the placeholder. The token boundary is a consumed prefix plus a lookahead, because a lookbehind is a SyntaxError in Safari before 16.4. PLAYING section 12 stays; the other section-12 proposal is not in this batch. Tests: node --test test/diag.test.js — 10 pass. node --test test/client-static.test.js — 315 pass. node --test test/match/runner-pending.test.js test/match/watch-bonds.test.js test/ui/playtest6-unite.test.js test/ui/spectate-effects.test.js test/ui/feedback5-hotkeys.test.js test/i18n-server.test.js test/ui/bosshit-ticker.test.js test/ui/playtest3.test.js test/docs-consistency.test.js test/docs-paths.test.js test/version.test.js test/package.test.js — 138 pass. npm run -s lint — 0 errors. npm run -s typecheck and npm run -s check:imports passed. node tools/i18n.mjs check --all --strict — 1041/1041 in en, ja, ko and zh-TW, 0 errors.
This commit is contained in:
1 parent
073507eca3
commit
3f8844129b
12 files changed
+599
-7
No files matched your search
@@ -3,17 +3,20 @@
|
||||
// the settings modal — which also holds the language switch (ui/lang.js; kept apart in `sp.pref.lang`; under it a note
|
||||
// while the current language's pack is a machine translation, `_meta.machineTranslated`) and the 快捷键
|
||||
// section that rebinds the in-match shortcuts (the key map: ui/gameLogic/shortcuts.js; the community request
|
||||
// 「快捷键可不可以自己设置」, the owner's decision of 2026-10-07).
|
||||
// 「快捷键可不可以自己设置」, the owner's decision of 2026-10-07) and 问题反馈, which copies the diagnostics of this page
|
||||
// for a bug report (diag.js: the error log, this browser, optionally the battle on screen; nothing is uploaded).
|
||||
|
||||
import { useLayoutEffect, useState } from '../../vendor/hooks.module.js';
|
||||
import { useLayoutEffect, useRef, useState } from '../../vendor/hooks.module.js';
|
||||
import { html, Modal, Button, Icon, MicroLabel } from './components.js';
|
||||
import { createStore, useStore, loadPref, savePref } from '../store.js';
|
||||
import { createStore, useStore, loadPref, savePref, store } from '../store.js';
|
||||
import { sanitizeSettings, HOTKEY_ACTIONS, DEFAULT_HOTKEYS, hotkeyLabel, rebindHotkey, isDefaultHotkeys, captureHotkey } from './gameLogic.js';
|
||||
import { audio } from '../audio.js';
|
||||
import { openGuide } from './guide.js';
|
||||
import { detectFeatures } from './device.js';
|
||||
import { LangToggle, machineTranslationNote } from './lang.js';
|
||||
import { t, tc, N_ } from '../../../shared/i18n.js';
|
||||
import { errorCount, currentBattle, diagnosticsText } from '../diag.js';
|
||||
import { copyText } from './clipboard.js';
|
||||
|
||||
const cx = (...p) => p.flat().filter(Boolean).join(' ');
|
||||
|
||||
@@ -144,6 +147,55 @@ function HotkeySection({ keys, touchUi }) {
|
||||
</section>`;
|
||||
}
|
||||
|
||||
/** Where a report goes: the upstream issue tracker (shown as text the player can select; a link may open nothing). */
|
||||
export const ISSUES_URL = 'https://github.com/sganggs/Stronghold-Protocol/issues';
|
||||
|
||||
/**
|
||||
* 问题反馈: copy the diagnostics of this page (diag.js) for a GitHub issue — the errors recorded since the page opened,
|
||||
* this browser and device, where the player is, and, switched on by default while a battle is on screen, that battle's
|
||||
* spec (the dev tools replay it). Player names, the room code and the token are replaced; nothing is uploaded. Laid out
|
||||
* like 快捷键 above it (a head with its button, a hint, the result line); the battle switch is the settings' Toggle, and
|
||||
* when the clipboard refuses (some in-app browsers), the report is shown selected in a box styled like 干员调配's 导出.
|
||||
*/
|
||||
/** The result line of 复制诊断信息, by outcome (msgids: translated when shown, so a language switch reaches it). */
|
||||
const DIAG_NOTES = { copied: N_('已复制诊断信息,可以粘贴到 GitHub issue 中'), refused: N_('无法写入剪贴板,请手动复制下面的内容') };
|
||||
|
||||
function DiagSection() {
|
||||
const [attach, setAttach] = useState(true);
|
||||
const [note, setNote] = useState(null); // 'copied' | 'refused' | null: the result line
|
||||
const [manual, setManual] = useState(null); // the report, when the clipboard refused it
|
||||
const boxRef = useRef(null);
|
||||
// the box opens selected for a manual copy, scrolled to its first line (select() leaves it at the end)
|
||||
useLayoutEffect(() => {
|
||||
const el = boxRef.current;
|
||||
if (manual && el) { el.focus(); el.select(); el.scrollTop = 0; }
|
||||
}, [manual]);
|
||||
const n = errorCount();
|
||||
const battle = currentBattle();
|
||||
const copy = async () => {
|
||||
const text = diagnosticsText({ state: store.get(), settings: settingsStore.get(), attachBattle: attach && !!battle });
|
||||
const ok = await copyText(text);
|
||||
setManual(ok ? null : text);
|
||||
setNote(ok ? 'copied' : 'refused');
|
||||
};
|
||||
return html`<section class="set-diag" aria-labelledby="set-diag-title">
|
||||
<div class="set-keys__head">
|
||||
<span class="set-row__label" id="set-diag-title">${t('问题反馈')}<${MicroLabel}>DIAGNOSTICS<//></span>
|
||||
<${Button} variant="ghost" size="sm" icon="copy" class="set-diag__copy" data-testid="diag-copy" onClick=${copy}>${t('复制诊断信息')}<//>
|
||||
</div>
|
||||
<p class="set-hint">${t('遇到问题时,复制诊断信息并粘贴到 GitHub issue 中,开发者就能看到出错时的情况。诊断信息只在本机生成,不会自动上传;玩家名和房间号会被替换。')}</p>
|
||||
<div class="set-diag__meta">
|
||||
<span class="set-diag__stat">${t('已记录的错误')}<b class=${cx('set-diag__count num', n > 0 && 'is-warn')} data-testid="diag-count">${n}</b></span>
|
||||
<span class="set-diag__url">${ISSUES_URL.replace(/^https:\/\//, '')}</span>
|
||||
</div>
|
||||
${battle ? html`<${Toggle} label=${t('附上本场战斗')} micro="BATTLE DATA" value=${attach} onChange=${setAttach} />
|
||||
<p class="set-hint set-diag__battle-hint">${t('开发者可以用附上的战斗数据重现这场战斗。')}</p>` : null}
|
||||
${note ? html`<p class=${cx('set-keys__note', note === 'refused' && 'is-warn')} role="status" aria-live="polite">${t(DIAG_NOTES[note])}</p>` : null}
|
||||
${manual ? html`<textarea ref=${boxRef} class="set-diag__text" data-testid="diag-text" spellcheck=${false} readOnly
|
||||
aria-label=${t('诊断信息')} value=${manual}></textarea>` : null}
|
||||
</section>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings modal.
|
||||
* @param {{ open: boolean, onClose: Function }} props
|
||||
@@ -176,6 +228,7 @@ export function SettingsModal({ open, onClose }) {
|
||||
</div>
|
||||
</div>
|
||||
<${HotkeySection} keys=${s.keys} touchUi=${touchUi} />
|
||||
<${DiagSection} />
|
||||
<p class="set-hint">${touchUi ? t('触屏操作:点击单位选中(撤退 / 出售)· 长按单位或卡牌查看详情 · 拖动部署后滑动选择朝向') : t('右键查看详情')}</p>
|
||||
</div>
|
||||
<//>`;
|
||||
|
||||
Reference in new issue
Block a user