The title / login screen has a settings button: it opens the in-match settings dialog (local preferences only) (#183)

One gear button in the title connection row opens the existing SettingsModal; it reads and writes the same local preferences and touches no server state. Contributed by @zasx77771.
This commit is contained in:
MayAsteroidea authored and GitHub committed 2026-10-06 14:35:23 +08:00
1 parent f6794a2f2c
commit 19a89085a4
3 files changed
+15 -1

No files matched your search

+1 -1
View File
@@ -103,7 +103,7 @@ html:not(.sp-in-match) .toast-host { margin-top: var(--sa-t); }
.fsbtn { display: grid; place-items: center; padding: 0; background: rgba(8, 11, 10, .85); border: 1px solid var(--line-2); color: var(--text-lo); transition: color var(--t-fast), border-color var(--t-fast); }
.fsbtn .icon { width: .26rem; height: .26rem; }
.fsbtn:hover, .fsbtn[aria-pressed="true"] { color: var(--mint-400); border-color: var(--mint-700); }
.title-fs { width: .4rem; height: .4rem; margin-left: .06rem; }
.title-fs, .title-settings { width: .4rem; height: .4rem; margin-left: .06rem; }
/* standalone / fullscreen: the browser bars are gone, nothing to reserve */
.sp-standalone .rotate-hint__micro::after { content: ' · PWA'; }
+7
View File
@@ -17,6 +17,8 @@ import { net, identity } from '../net.js';
import { store, useStore, shallowEqual } from '../store.js';
import { data, useData } from '../data.js';
import { FullscreenButton, detectFeatures } from '../ui/device.js';
import { GIcon } from '../ui/gameComponents.js';
import { SettingsModal } from '../ui/settings.js';
// Same character classes as server/net.js sanitizeName (control, zero-width, bidi, BOM), so a name
// the client accepts is never rejected by the server's hello validation.
@@ -183,6 +185,7 @@ export function TitleScreen() {
const conn = useStore((s) => s.connection, shallowEqual);
const pendingJoin = useStore((s) => s.ui.pendingJoin);
const [name, setName] = useState(() => store.get().me.name || identity.loadName() || '');
const [settingsOpen, setSettingsOpen] = useState(false);
const assetsSettled = useData('assets');
const assets = data.get('assets');
const backdrop = findUiAsset(assets, BACKDROP_KEYS);
@@ -258,11 +261,15 @@ export function TitleScreen() {
<span>${STATUS_TEXT[conn.status] || conn.status}</span>
${conn.status === 'online' ? html`<${PingPill} ms=${conn.ping} />` : null}
<${GuideButton} class="title-guide" />
<button type="button" class="title-settings fsbtn tapx" aria-label="设置" title="设置"
onClick=${() => setSettingsOpen(true)}><${GIcon} name="gear" /></button>
<${FullscreenButton} class="title-fs" />
</div>
</div>
</main>
<${SettingsModal} open=${settingsOpen} onClose=${() => setSettingsOpen(false)} />
<footer class="title-foot">
<span>非官方同人复刻 · 游戏素材版权归 上海鹰角网络 / Yostar 所有</span>
<${MicroLabel}>v${APP_VERSION} · WEB SIMULATION<//>
+7
View File
@@ -968,6 +968,13 @@ describe('screen helpers', () => {
assert.equal(findUiAsset({ ui: {} }, ['x']), null);
});
test('title exposes the shared settings modal', () => {
const source = readFileSync(path.join(PUBLIC, 'js/screens/title.js'), 'utf8');
assert.match(source, /import \{ SettingsModal \} from '\.\.\/ui\/settings\.js'/);
assert.match(source, /class="title-settings fsbtn tapx"/, 'title screen includes the settings control');
assert.match(source, /<\$\{SettingsModal\} open=\$\{settingsOpen\}/, 'settings control opens the shared modal');
});
test('lobby: normalizeCode / parseRoomParam / difficultyInfo', async () => {
const { normalizeCode, parseRoomParam, difficultyInfo, MODE_TEXT } = await mod('screens/lobby.js');
assert.equal(normalizeCode('ab-c d9'), 'ABCD');