Move help to JSON and add server docstrings

This commit is contained in:
Jage9
2026-02-21 16:51:07 -05:00
parent 68bd2cf2ce
commit 35f837e96d
11 changed files with 207 additions and 40 deletions

View File

@@ -31,45 +31,7 @@
class="hidden"
aria-label="Chat Grid, press arrows to move."
></canvas>
<div id="instructions" class="hidden">
<h2>Help</h2>
<h3>Movement</h3>
<p><b>Arrow Keys:</b> Move</p>
<p><b>C:</b> Speak coordinates</p>
<p><b>Escape:</b> Disconnect/cancel</p>
<h3>Users, Nickname, and Chat</h3>
<p><b>L:</b> Locate nearest user</p>
<p><b>Shift+L:</b> List users</p>
<p><b>Shift+U:</b> Speak connected users</p>
<p><b>N:</b> Change nickname</p>
<p><b>Slash:</b> Start chat</p>
<p><b>Comma / Period:</b> Previous/next message</p>
<p><b>Less Than / Greater Than:</b> First/last message</p>
<h3>Items</h3>
<p><b>I:</b> Locate nearest item</p>
<p><b>Shift+I:</b> List items</p>
<p><b>A:</b> Add item</p>
<p><b>O:</b> Edit item properties</p>
<p><b>Shift+O:</b> Read all item properties</p>
<p><b>D:</b> Pick up/drop item</p>
<p><b>Shift+D:</b> Delete item</p>
<p><b>U:</b> Use item</p>
<h3>Audio</h3>
<p><b>P:</b> Ping server</p>
<p><b>M:</b> Mute/unmute yourself</p>
<p><b>Shift+M:</b> Toggle stereo/mono output</p>
<p><b>! (Shift+1):</b> Toggle loopback monitor</p>
<p><b>1:</b> Toggle voice layer</p>
<p><b>2:</b> Toggle item sounds layer</p>
<p><b>3:</b> Toggle media layer</p>
<p><b>4:</b> Toggle world layer (other users)</p>
<p><b>E:</b> Select voice effect</p>
<p><b>Dash or Equals:</b> Lower/raise active effect value</p>
</div>
<div id="instructions" class="hidden"></div>
<footer id="appFooter">
<small id="appVersion">Another AI experiment with Jage. Version</small>

52
client/public/help.json Normal file
View File

@@ -0,0 +1,52 @@
{
"sections": [
{
"title": "Movement",
"items": [
{ "keys": "Arrow Keys", "description": "Move" },
{ "keys": "C", "description": "Speak coordinates" },
{ "keys": "Escape", "description": "Disconnect/cancel" }
]
},
{
"title": "Users, Nickname, and Chat",
"items": [
{ "keys": "L", "description": "Locate nearest user" },
{ "keys": "Shift+L", "description": "List users" },
{ "keys": "Shift+U", "description": "Speak connected users" },
{ "keys": "N", "description": "Change nickname" },
{ "keys": "Slash", "description": "Start chat" },
{ "keys": "Comma / Period", "description": "Previous/next message" },
{ "keys": "Less Than / Greater Than", "description": "First/last message" }
]
},
{
"title": "Items",
"items": [
{ "keys": "I", "description": "Locate nearest item" },
{ "keys": "Shift+I", "description": "List items" },
{ "keys": "A", "description": "Add item" },
{ "keys": "O", "description": "Edit item properties" },
{ "keys": "Shift+O", "description": "Read all item properties" },
{ "keys": "D", "description": "Pick up/drop item" },
{ "keys": "Shift+D", "description": "Delete item" },
{ "keys": "U", "description": "Use item" }
]
},
{
"title": "Audio",
"items": [
{ "keys": "P", "description": "Ping server" },
{ "keys": "M", "description": "Mute/unmute yourself" },
{ "keys": "Shift+M", "description": "Toggle stereo/mono output" },
{ "keys": "! (Shift+1)", "description": "Toggle loopback monitor" },
{ "keys": "1", "description": "Toggle voice layer" },
{ "keys": "2", "description": "Toggle item sounds layer" },
{ "keys": "3", "description": "Toggle media layer" },
{ "keys": "4", "description": "Toggle world layer (other users)" },
{ "keys": "E", "description": "Select voice effect" },
{ "keys": "Dash or Equals", "description": "Lower/raise active effect value" }
]
}
]
}

View File

@@ -1,5 +1,5 @@
// Maintainer-controlled web client version.
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
window.CHGRID_WEB_VERSION = "2026.02.21 R104";
window.CHGRID_WEB_VERSION = "2026.02.21 R105";
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
window.CHGRID_TIME_ZONE = "America/Detroit";

View File

@@ -105,6 +105,20 @@ type ChangelogData = {
sections: ChangelogSection[];
};
type HelpItem = {
keys: string;
description: string;
};
type HelpSection = {
title: string;
items: HelpItem[];
};
type HelpData = {
sections: HelpSection[];
};
type AudioLayerState = {
voice: boolean;
item: boolean;
@@ -245,6 +259,7 @@ audio.setOutputMode(outputMode);
loadEffectLevels();
loadAudioLayerState();
void loadHelp();
void loadChangelog();
function requiredById<T extends HTMLElement>(id: string): T {
@@ -297,6 +312,42 @@ function setUpdatesExpanded(expanded: boolean): void {
dom.updatesPanel.classList.toggle('hidden', !expanded);
}
function renderHelp(help: HelpData): void {
dom.instructions.innerHTML = '';
const heading = document.createElement('h2');
heading.textContent = 'Help';
dom.instructions.appendChild(heading);
for (const section of help.sections) {
const sectionHeading = document.createElement('h3');
sectionHeading.textContent = section.title;
dom.instructions.appendChild(sectionHeading);
for (const item of section.items) {
const line = document.createElement('p');
const keys = document.createElement('b');
keys.textContent = `${item.keys}:`;
line.appendChild(keys);
line.append(` ${item.description}`);
dom.instructions.appendChild(line);
}
}
}
async function loadHelp(): Promise<void> {
try {
const response = await fetch(withBase('help.json'), { cache: 'no-store' });
if (!response.ok) {
return;
}
const help = (await response.json()) as HelpData;
if (!Array.isArray(help.sections) || help.sections.length === 0) {
return;
}
renderHelp(help);
} catch {
// Keep existing/static help if loading fails.
}
}
function renderChangelog(changelog: ChangelogData): void {
dom.updatesPanel.innerHTML = '';
for (const section of changelog.sections) {