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

@@ -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) {
<button class="quick-cmd" onclick="runChatCommand('/samsay')" title="SAM speech">/samsay</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 {
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

View File

@@ -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 ');

View File

@@ -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;