feat: add game command functionality and update quick commands for Number Match game

This commit is contained in:
2026-03-15 12:39:28 +11:00
parent df10268a83
commit d762f8e67e
3 changed files with 59 additions and 5 deletions

View File

@@ -867,6 +867,9 @@ export function updateQuickCommands(mode) {
<button class="quick-cmd" onclick="runChatCommand('/samsay')" title="SAM speech">/samsay</button> <button class="quick-cmd" onclick="runChatCommand('/samsay')" title="SAM speech">/samsay</button>
<button class="quick-cmd" onclick="runChatCommand('/quit')" title="Exit chat">/quit</button> <button class="quick-cmd" onclick="runChatCommand('/quit')" title="Exit chat">/quit</button>
`; `;
} else if (mode === 'game') {
if (statusLeft) statusLeft.textContent = 'Number Match';
container.innerHTML = '';
} else { } else {
if (statusLeft) statusLeft.textContent = 'Ready'; if (statusLeft) statusLeft.textContent = 'Ready';
container.innerHTML = ` 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 * Run a chat command from button click
* @param {string} command - The chat command to run * @param {string} command - The chat command to run

View File

@@ -552,9 +552,9 @@ export const gameMode = {
*/ */
function getBoardLineCount(game) { function getBoardLineCount(game) {
const rows = Math.ceil(game.getTiles().length / game.width); 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) // (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 += '│'; line += '│';
term.writeln(line); term.writeln(line);
if (row < rows - 1) {
term.writeln(`${' '.repeat(width)}`);
}
} }
// Bottom border // Bottom border
@@ -617,6 +620,9 @@ function renderBoard(term, game, selectedIndex, message = '') {
const msgText = message || ''; const msgText = message || '';
term.writeln(` ${msgText}`.padEnd(50)); 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 // Prompt
term.write('\x1b[33mgame>\x1b[0m '); term.write('\x1b[33mgame>\x1b[0m ');

View File

@@ -34,6 +34,7 @@ import {
exitChatMode, exitChatMode,
updateQuickCommands, updateQuickCommands,
runChatCommand, runChatCommand,
runGameCommand,
renderChatPrompt, renderChatPrompt,
sendChatMessage sendChatMessage
} from './matrix-client.js'; } from './matrix-client.js';
@@ -300,6 +301,7 @@ term.registerLinkProvider({
// Find all command names that match our known commands // 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 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 // When in game mode, detect clickable tile numbers on board lines
if (gameMode.active && lineText.includes('│')) { if (gameMode.active && lineText.includes('│')) {
@@ -366,6 +368,33 @@ term.registerLinkProvider({
} }
}); });
// 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); callback(links.length > 0 ? links : undefined);
} }
}); });
@@ -521,7 +550,10 @@ const commands = {
}, },
numbermatch: { numbermatch: {
description: numbermatchCmd.description, description: numbermatchCmd.description,
execute: (args) => numbermatchCmd.execute(term, writeClickable, VERSION, args, commandHistory) execute: (args) => {
numbermatchCmd.execute(term, writeClickable, VERSION, args, commandHistory);
updateQuickCommands('game');
}
}, },
samsay: { samsay: {
description: samsayCmd.description, description: samsayCmd.description,
@@ -1228,6 +1260,7 @@ async function submitInlineInput() {
term.write(rawValue + '\r\n'); term.write(rawValue + '\r\n');
const continueGame = processGameInput(term, cmd); const continueGame = processGameInput(term, cmd);
if (!continueGame) { if (!continueGame) {
updateQuickCommands('terminal');
writePrompt(); writePrompt();
} }
setTimeout(() => positionInlineInput(), 10); setTimeout(() => positionInlineInput(), 10);
@@ -1317,8 +1350,9 @@ async function init() {
welcomeBannerMinimal welcomeBannerMinimal
}); });
// Expose chat command for onclick handlers // Expose chat and game command handlers for onclick buttons
window.runChatCommand = runChatCommand; window.runChatCommand = runChatCommand;
window.runGameCommand = runGameCommand;
// Display welcome banner with clickable commands // Display welcome banner with clickable commands
const cols = term.cols; const cols = term.cols;