Start help system.
This commit is contained in:
parent
43a4a4f4d2
commit
5895e2508a
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -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",
|
||||
|
@ -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
26
help.json
Normal 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."
|
||||
}
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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
90
src/lua_engine/help.rs
Normal 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,
|
||||
})
|
||||
})
|
||||
}
|
35
src/lua_engine/named_closure.rs
Normal file
35
src/lua_engine/named_closure.rs
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user