diff --git a/website/matrix-client.js b/website/matrix-client.js index 7446794..a7b0211 100644 --- a/website/matrix-client.js +++ b/website/matrix-client.js @@ -858,7 +858,7 @@ export function updateQuickCommands(mode) { const container = document.querySelector('.quick-commands'); const statusLeft = document.querySelector('.status-left'); if (!container) return; - + if (mode === 'chat') { if (statusLeft) statusLeft.textContent = 'Chat Mode'; container.innerHTML = ` @@ -867,6 +867,9 @@ export function updateQuickCommands(mode) { `; + } else if (mode === 'game') { + if (statusLeft) statusLeft.textContent = 'Number Match'; + container.innerHTML = ''; } else { if (statusLeft) statusLeft.textContent = 'Ready'; container.innerHTML = ` @@ -878,6 +881,17 @@ export function updateQuickCommands(mode) { } } +/** + * Run a game command from a button click + * @param {string} command - The game command to submit + * @returns {void} + */ +export function runGameCommand(command) { + if (!inlineInput || !submitInlineInput) return; + inlineInput.value = command; + submitInlineInput(); +} + /** * Run a chat command from button click * @param {string} command - The chat command to run diff --git a/website/scripts/commands/numbermatch.js b/website/scripts/commands/numbermatch.js index 81f2fbb..8217a40 100644 --- a/website/scripts/commands/numbermatch.js +++ b/website/scripts/commands/numbermatch.js @@ -552,9 +552,9 @@ export const gameMode = { */ function getBoardLineCount(game) { const rows = Math.ceil(game.getTiles().length / game.width); - // header + top border + rows + bottom border + empty + status + message + // header + top border + rows + blank lines between rows + bottom border + empty + status + message + controls // (prompt uses write not writeln, so doesn't add a line) - return 1 + 1 + rows + 1 + 1 + 1 + 1; + return 1 + 1 + rows + Math.max(0, rows - 1) + 1 + 1 + 1 + 1 + 1; } /** @@ -603,6 +603,9 @@ function renderBoard(term, game, selectedIndex, message = '') { line += '│'; term.writeln(line); + if (row < rows - 1) { + term.writeln(` │${' '.repeat(width)}│`); + } } // Bottom border @@ -617,6 +620,9 @@ function renderBoard(term, game, selectedIndex, message = '') { const msgText = message || ''; term.writeln(` ${msgText}`.padEnd(50)); + // Clickable controls line + term.writeln(' \x1b[90m[ \x1b[36madd\x1b[90m ] [ \x1b[36mhint\x1b[90m ] [ \x1b[36mnew\x1b[90m ] [ \x1b[36mquit\x1b[90m ]\x1b[0m'); + // Prompt term.write('\x1b[33mgame>\x1b[0m '); diff --git a/website/terminal.js b/website/terminal.js index 595b402..8dda5b8 100644 --- a/website/terminal.js +++ b/website/terminal.js @@ -34,6 +34,7 @@ import { exitChatMode, updateQuickCommands, runChatCommand, + runGameCommand, renderChatPrompt, sendChatMessage } from './matrix-client.js'; @@ -300,6 +301,7 @@ term.registerLinkProvider({ // Find all command names that match our known commands const commandNames = ['help', 'about', 'clear', 'echo', 'date', 'whoami', 'history', 'color', 'banner', 'bluesky', 'chat', 'github', 'contact', 'privacy', 'numbermatch']; + const gameCommandNames = ['add', 'hint', 'new', 'quit']; // When in game mode, detect clickable tile numbers on board lines if (gameMode.active && lineText.includes('│')) { @@ -365,6 +367,33 @@ term.registerLinkProvider({ startIndex = index + 1; } }); + + // When in game mode, register add/hint/new/quit as clickable words on the controls line + if (gameMode.active) { + gameCommandNames.forEach(cmd => { + let startIndex = 0; + while (true) { + const index = lineText.indexOf(cmd, startIndex); + if (index === -1) break; + const charBefore = index > 0 ? lineText[index - 1] : ' '; + const charAfter = index + cmd.length < lineText.length ? lineText[index + cmd.length] : ' '; + if (/[\s\[]/.test(charBefore) && /[\s\]]/.test(charAfter)) { + const capturedCmd = cmd; + links.push({ + range: { + start: { x: index + 1, y: bufferLineNumber }, + end: { x: index + cmd.length + 1, y: bufferLineNumber } + }, + text: cmd, + activate: () => { + if (window.runGameCommand) window.runGameCommand(capturedCmd); + } + }); + } + startIndex = index + 1; + } + }); + } callback(links.length > 0 ? links : undefined); } @@ -521,7 +550,10 @@ const commands = { }, numbermatch: { description: numbermatchCmd.description, - execute: (args) => numbermatchCmd.execute(term, writeClickable, VERSION, args, commandHistory) + execute: (args) => { + numbermatchCmd.execute(term, writeClickable, VERSION, args, commandHistory); + updateQuickCommands('game'); + } }, samsay: { description: samsayCmd.description, @@ -1228,6 +1260,7 @@ async function submitInlineInput() { term.write(rawValue + '\r\n'); const continueGame = processGameInput(term, cmd); if (!continueGame) { + updateQuickCommands('terminal'); writePrompt(); } setTimeout(() => positionInlineInput(), 10); @@ -1317,8 +1350,9 @@ async function init() { welcomeBannerMinimal }); - // Expose chat command for onclick handlers + // Expose chat and game command handlers for onclick buttons window.runChatCommand = runChatCommand; + window.runGameCommand = runGameCommand; // Display welcome banner with clickable commands const cols = term.cols;