fix(mystery): improve mobile terminal and chips
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-05-10 05:53:32 -05:00
parent 29fd371b89
commit 33933b00d7
17 changed files with 122 additions and 18 deletions
+3 -2
View File
@@ -36,14 +36,15 @@ describe('computeChips — sample world', () => {
s = dispatch(s, { kind: 'go', direction: 'n' }, world).state
s = dispatch(s, { kind: 'go', direction: 'e' }, world).state
const chips = computeChips(s, world)
expect(chips.find((c) => c.kind === 'encounter' && c.command.includes('rat'))).toBeTruthy()
expect(chips.find((c) => c.kind === 'encounter' && c.label === 'ATTACK RAT' && c.command === 'attack rat')).toBeTruthy()
})
it('always includes LOOK, INV, and HELP', () => {
it('always includes LOOK, INV, USE, and HELP', () => {
const s = initialStateFor(world)
const chips = computeChips(s, world)
expect(chips.find((c) => c.command === 'look')).toBeTruthy()
expect(chips.find((c) => c.command === 'inventory')).toBeTruthy()
expect(chips.find((c) => c.label === 'USE' && c.command === 'use ')).toBeTruthy()
expect(chips.find((c) => c.command === 'help')).toBeTruthy()
})
})
+5 -4
View File
@@ -53,12 +53,12 @@ export function computeChips(state: GameState, world: World): Chip[] {
const phase = def?.phases[state.encounterState[room.encounter]!]
if (def && phase) {
for (const t of phase.transitions) {
const targetLabel = t.target && t.target !== '*' ? ` ${t.target.toUpperCase()}` : ''
const command = t.target && t.target !== '*' ? `${t.verb} ${t.target}` : t.verb
const targetLabel = t.target && t.target !== '*' ? ` ${t.target.replace(/-/g, ' ').toUpperCase()}` : ''
const command = t.target && t.target !== '*' ? `${t.verb} ${t.target.replace(/-/g, ' ')}` : t.verb
out.push({
kind: 'encounter',
label: `${t.verb.toUpperCase()}${targetLabel}`,
command,
label: t.chipLabel ?? `${t.verb.toUpperCase()}${targetLabel}`,
command: t.chipCommand ?? command,
disabled: false,
})
}
@@ -68,6 +68,7 @@ export function computeChips(state: GameState, world: World): Chip[] {
// Persistent meta chips.
out.push({ kind: 'meta', label: 'LOOK', command: 'look', disabled: false })
out.push({ kind: 'meta', label: 'INV', command: 'inventory', disabled: false })
out.push({ kind: 'meta', label: 'USE', command: 'use ', disabled: false })
out.push({ kind: 'meta', label: 'HELP', command: 'help', disabled: false })
return out
+33
View File
@@ -1,3 +1,11 @@
html,
body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
:root[data-mystery-theme='amber'] {
--m-bg: #1a0d00;
--m-fg: #ffb000;
@@ -21,6 +29,8 @@
.mystery-root {
position: fixed;
inset: 0;
height: 100dvh;
min-height: 0;
background: var(--m-bezel);
color: var(--m-fg);
font-family: 'Courier New', 'Cascadia Mono', 'Consolas', monospace;
@@ -36,6 +46,7 @@
.mystery-bezel {
flex: 1;
min-height: 0;
background: var(--m-bg);
border-radius: 6px;
padding: 22px 26px 14px;
@@ -109,9 +120,12 @@
.mystery-transcript {
flex: 1;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: anywhere;
position: relative;
z-index: 2;
padding-right: 6px;
@@ -123,6 +137,16 @@
margin-top: 0.6em;
}
.mystery-transcript .ascii-art {
max-width: 100%;
overflow-x: auto;
white-space: pre;
word-break: normal;
overflow-wrap: normal;
font-size: clamp(9px, 2vw, 14px);
line-height: 1.1;
}
.mystery-transcript .help {
color: var(--m-fg);
font-weight: normal;
@@ -204,11 +228,20 @@
@media (max-width: 640px) {
.mystery-root {
padding: 8px;
font-size: 13px;
}
.mystery-bezel {
padding: 18px 14px 12px;
}
.mystery-transcript .ascii-art {
font-size: clamp(7px, 2.35vw, 10px);
}
.mystery-chip {
padding: 4px 7px;
}
}
[data-mystery-input].ended {
+8
View File
@@ -49,6 +49,11 @@ if (!transcriptEl || !inputEl) {
function refreshChips(): void {
renderChips(computeChips(state, world), (command) => {
inputEl!.value = command
if (command.endsWith(' ')) {
inputEl!.focus()
inputEl!.setSelectionRange(command.length, command.length)
return
}
inputEl!.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))
})
}
@@ -96,6 +101,9 @@ if (!transcriptEl || !inputEl) {
for (const line of lines) {
const el = document.createElement('div')
el.className = line.kind
if (line.kind === 'system' && line.text.includes('|_| |_|')) {
el.classList.add('ascii-art')
}
el.textContent = line.text
transcriptEl.appendChild(el)
}