Start help system.

This commit is contained in:
Condorra 2024-11-17 18:39:34 +11:00
parent 43a4a4f4d2
commit 5895e2508a
8 changed files with 174 additions and 17 deletions

28
Cargo.lock generated
View File

@ -781,9 +781,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
version = "0.3.70"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
dependencies = [
"wasm-bindgen",
]
@ -1443,9 +1443,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
dependencies = [
"cfg-if",
"once_cell",
@ -1454,9 +1454,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
dependencies = [
"bumpalo",
"log",
@ -1481,9 +1481,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1491,9 +1491,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
@ -1504,15 +1504,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
[[package]]
name = "web-sys"
version = "0.3.69"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
dependencies = [
"js-sys",
"wasm-bindgen",

View File

@ -13,7 +13,7 @@ piccolo = { git = "https://github.com/kyren/piccolo.git", rev = "fcbaabc92429217
unicode-segmentation = "1.11.0"
unicode-width = "0.1.13"
wasm-bindgen = "0.2.92"
web-sys = { version = "0.3.69", features = ["ResizeObserver", "DomRect", "CssStyleDeclaration", "HtmlAnchorElement"] }
web-sys = { version = "0.3.72", features = ["ResizeObserver", "DomRect", "CssStyleDeclaration", "HtmlAnchorElement", "XmlHttpRequest"] }
yew = { version = "0.21.0", features = ["csr"] }
minicrossterm = { git = "https://git.blastmud.org/blasthavers/minicrossterm.git", rev = "0c8c6d4f0cf445adf7bb957811081a1b710bd933" }
thiserror = "1.0.63"

26
help.json Normal file
View File

@ -0,0 +1,26 @@
{
"_index": "Try #help command_name (leave off the # from command names) for command help.\r\n\r\n\"#help getting started\" has tips to get started.",
"act": "Use #act to set a trigger that will automatically run commands based on a match to the MUD's output. Example: #act {^The (.*) attacks you.} {\"Hey $1, you're going to wish you hadn't done that!\"}.\r\nUse #act by itself to list triggers. See #unact for info on how to remove triggers.",
"alias": "Use #alias to set a command alias.\r\nFor example, #alias {^hi} {#echo Hello} will make typing 'hi' run the echo command.\r\nUse #alias by itself to list aliases. See also: #unalias",
"connect_mud": "Use #connect_mud to connect to a MUD. Provide a name for the connection and the WebSocket server URL, e.g. #connect_mud ex wss://example.com/ws.",
"copy_paste": "Use Ctrl Insert to copy and Shift Insert to paste in most browsers.",
"create_script": "Use the '+' icon in the script browser to create a new script. You will need to name it first.",
"delay": "Use #delay to schedule a command to run after a specified time (in seconds).\r\nFor example, #delay 5 {#echo Ding!} will run the echo command after 5 seconds.\r\nOptionally give it a name with a third argument.\r\nUse #delay by itself to list delays.\r\nSee also: #undelay",
"deletelogs": "Use #deletelogs to permanently delete logs.\r\nExample: #deletelogs mylog 2024-10-01 2024-10-10\r\nThe first argument is the log name. Optionally, follow with a second and third argument to give the date range - or just one date to only specify the end of the date range.\r\nSee also: #downloadlogs",
"downloadlogs": "Use #downloadlogs to download logs within a date range. Example: #downloadlogs mylog 2024-10-01 2024-10-10.",
"editor": "Use #editor to open the script editor where you can manage Lua scripts.",
"getting started": "Use #help connect_mud to learn how to connect if you aren't already.\r\nSend basic commands to the current MUD just by typing them in. Use a number like #5 n to repeat a command. Use semicolons to send commands in sequence, e.g. n;#4 e\r\nCopy and paste using Ctrl+Insert and Shift+Insert",
"hsplit": "Use #hsplit to split the screen horizontally. For example, #hsplit {} 2 will create a horizontal split with frame 2.",
"include": "To run a script, use #include scriptname. Example: #include myscript.lua.",
"listlogs": "Use #listlogs to list stored logs and the number of entries for each logname.",
"log": "Use #log to start logging a MUD's output. Example: #log mymud mylog will start logging for the 'mymud' connection.",
"lua_scripting": "Scripts use Lua code. Use commands like 'commands.command(\"command to send\")' to execute MUD commands. For example, to set an alias, use 'commands.alias(\"^hi\", \"#echo Hello\")'.",
"panel_merge": "Use #panel_merge followed by the frame path to merge panels. For example, #panel_merge {} will merge the current panel with the top-level split.",
"send_multiple_commands": "To send several commands in quick succession, separate them with a semicolon. For example, use 'n;e' to go north and then east.",
"tick": "Use #tick to set a recurring command to run at a specified time interval (in seconds).\r\nFor example, #tick 60 {#echo Cockadoodledoo!} will repeat every 60 seconds.\r\nOptionally give it a name with a third argument.\r\nUse #tick by itself to list active ticks.\r\nSee also: #untick",
"unact": "Use #unact followed by the trigger pattern to delete a trigger. Example: #unact {^The (.*) attacks you.}.",
"unalias": "Use #unalias followed by the alias pattern to delete it. For example, #unalias {^hi}.",
"undelay": "Use #undelay followed by the delay name to cancel it. Example: #undelay ding to cancel a delay named 'ding'.",
"untick": "Use #untick followed by the tick name to cancel it. Example: #untick rooster to cancel a tick named 'rooster'.",
"vsplit": "Use #vsplit to split the screen vertically. For example, #vsplit {} 2 will create a vertical split with frame 2."
}

View File

@ -27,6 +27,7 @@
<script data-trunk src="node_modules/@xterm/xterm/lib/xterm.js"></script>
<script data-trunk src="node_modules/@xterm/addon-fit/lib/addon-fit.js"></script>
<link data-trunk rel="css" href="styles.css"/>
<link data-trunk rel="copy-file" href="help.json"/>
<link data-trunk rel="copy-file" href="assets/fonts/JetBrainsMono-Regular.woff2"/>
<link data-trunk rel="copy-file" href="node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff" data-target-path="fonts"/>
<link data-trunk rel="copy-file" href="node_modules/@codemirror/view/dist/index.js" data-target-path="@codemirror/view"></link>

View File

@ -377,8 +377,8 @@ async fn immediate_download_logs_refutable(
let buf_bytes = buf.as_bytes();
let buf_array = Uint8Array::new_with_length(buf_bytes.len() as u32);
buf_array.copy_from(buf_bytes);
let mut blobprops: BlobPropertyBag = Default::default();
blobprops.type_("text/plain");
let blobprops: BlobPropertyBag = Default::default();
blobprops.set_type("text/plain");
let blob = Blob::new_with_u8_array_sequence_and_options(
&[&buf_array].into_iter().collect::<Array>(),
&blobprops,

View File

@ -1,5 +1,6 @@
use self::{frameroutes::*, frames::*, muds::*, storage::*};
use anyhow::Error;
use help::{cmd_help, load_help};
use muds::telopt::configure_telopt_table;
use piccolo::{
async_callback::{AsyncSequence, Locals},
@ -29,7 +30,9 @@ pub struct LuaState {
mod frameroutes;
pub mod frames;
pub mod help;
pub mod muds;
pub mod named_closure;
pub mod storage;
impl LuaState {
@ -217,6 +220,7 @@ pub fn install_lua_globals(
register_command!(echo_frame);
register_command!(echo_frame_raw);
register_command!(editor);
register_stateless_command!(cmd_help, "help");
register_command!(hsplit);
register_stateless_command!(cmd_include, "include");
register_command!(cmd_list_logs, "listlogs");
@ -387,6 +391,7 @@ fn lua_global_initialisation(global_memo: &GlobalMemoCell) -> Result<(), String>
Function::Callback(ensure_frame_instance(ctx, &FrameId(1))),
(),
);
let _ = load_help(global_memo, ctx, "help.json");
});
lua_engine_ref
.interp

90
src/lua_engine/help.rs Normal file
View File

@ -0,0 +1,90 @@
use std::collections::BTreeMap;
use crate::GlobalMemoCell;
use anyhow::Result;
use piccolo::{Callback, CallbackReturn, Context, FromValue, Function, Table, Value, Variadic};
use std::rc::Rc;
use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
use web_sys::{console, XmlHttpRequest};
use super::named_closure::{release_named_global_closure, store_named_global_closure};
pub fn load_help(global_memo: &GlobalMemoCell, ctx: Context, url: &str) -> Result<()> {
let xhr: Rc<XmlHttpRequest> = XmlHttpRequest::new()
.map_err(|_e| anyhow::Error::msg("Error creating XmlHttpRequest"))?
.into();
let global_memo = global_memo.clone();
let xhr_cb = xhr.clone();
let load_callback: Closure<dyn Fn()> = Closure::new(move || {
let _ = global_memo.lua_engine.borrow_mut().interp.try_enter(|ctx| {
release_named_global_closure(ctx, "_help_load_callback");
if let Ok(Some(txt)) = xhr_cb.response_text() {
match serde_json::from_str::<BTreeMap<String, String>>(&txt) {
Err(_) => console::log_1(&JsValue::from_str(
"Loading help failed because result wasn't a String -> String map",
)),
Ok(d) => {
let help_tbl: Value = ctx.get_global("help")?;
let help_tbl = if help_tbl.is_nil() {
let tbl = Table::new(&ctx);
ctx.set_global("help", tbl);
tbl
} else {
Table::from_value(ctx, help_tbl)?
};
for (k, v) in d.iter() {
help_tbl.set(ctx, k.clone(), v.clone())?;
}
}
}
}
Ok(())
});
});
xhr.set_onload(Some(load_callback.as_ref().unchecked_ref()));
store_named_global_closure(ctx, "_help_load_callback", load_callback);
xhr.open("GET", url)
.map_err(|_e| anyhow::Error::msg("Error opening XmlHttpRequest"))?;
xhr.send()
.map_err(|_e| anyhow::Error::msg("Error sending XmlHttpRequest"))?;
Ok(())
}
pub(super) fn cmd_help<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let help: Value = ctx.get_global("help")?;
let echo: Function = ctx.get_global::<Table>("commands")?.get(ctx, "echo")?;
let args: Variadic<Vec<String>> = stack.consume(ctx)?;
let mut path: String = args.join(" ");
if let Some(p) = path.strip_prefix('#') {
path = p.to_owned();
}
path = path.trim().to_lowercase();
if path.is_empty() {
path = "_index".to_owned();
}
if help.is_nil() {
stack.into_back(ctx, "Help has not (yet) loaded successfully");
return Ok(CallbackReturn::Call {
function: echo,
then: None,
});
}
let help: Table = Table::from_value(ctx, help)?;
let help_value: Value = help.get(ctx, path)?;
let help_value = if help_value.is_nil() {
"No help available on that.".to_owned()
} else {
String::from_value(ctx, help_value)?
};
stack.into_back(ctx, help_value);
Ok(CallbackReturn::Call {
function: echo,
then: None,
})
})
}

View File

@ -0,0 +1,35 @@
use gc_arena::Collect;
use gc_arena::Gc;
use gc_arena::Rootable;
use piccolo::{Context, UserData, Value};
use std::rc::Rc;
use wasm_bindgen::prelude::Closure;
#[derive(Collect)]
#[collect(require_static)]
struct ClosureCollect<F: ?Sized> {
#[allow(unused)]
inner: Rc<Closure<F>>,
}
pub fn store_named_global_closure<'gc, F: 'static + ?Sized>(
ctx: Context<'gc>,
name: &'static str,
closure: Closure<F>,
) {
ctx.set_global(
name,
UserData::new::<Rootable![Gc<'_, ClosureCollect<F>>]>(
&ctx,
Gc::new(
&ctx,
ClosureCollect {
inner: Rc::new(closure),
},
),
),
);
}
pub fn release_named_global_closure(ctx: Context, name: &'static str) {
ctx.set_global(name, Value::Nil);
}