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;