recent mize changes
This commit is contained in:
10397
Cargo.lock
generated
Normal file
10397
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,27 +4,34 @@ version = "0.0.1"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
description = 'A strongly typed "filesystem" for the age of connectivity.'
|
description = 'A strongly typed "filesystem" for the age of connectivity.'
|
||||||
repository = "https://github.com/c2vi/mize"
|
repository = "https://git.ppc.social/ppc/mize"
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.dev]
|
|
||||||
wasm-opt = false
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "mize"
|
name = "mize"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "mize"
|
|
||||||
path = "src/platform/os/main.rs"
|
|
||||||
required-features = [ "target-os" ]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "target-os" ]
|
default = [ "target-os" ]
|
||||||
target-os = [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req", "tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util" ]
|
target-os = [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req", "tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util" ]
|
||||||
target-js = [ "wasm-bindgen", "console_error_panic_hook", "web-sys", "web-sys/Worker", "web-sys/Window", "web-sys/Request", "web-sys/RequestInit", "web-sys/RequestMode", "web-sys/Response", "web-sys/WorkerOptions", "web-sys/WorkerType", "serde-wasm-bindgen", "wasm-bindgen-futures", "wee_alloc", "ciborium/default" ]
|
target-js = [ "wasm-bindgen", "console_error_panic_hook", "web-sys", "web-sys/Worker", "web-sys/Window", "web-sys/Request", "web-sys/RequestInit", "web-sys/RequestMode", "web-sys/Response", "web-sys/WorkerOptions", "web-sys/WorkerType", "serde-wasm-bindgen", "wasm-bindgen-futures", "wee_alloc", "ciborium/default" ]
|
||||||
async = [ "tokio", ]
|
async = [ "tokio", ]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"marts",
|
||||||
|
"macros",
|
||||||
|
"apps/os",
|
||||||
|
"apps/obsidian-plugin",
|
||||||
|
]
|
||||||
|
default-members = [
|
||||||
|
"apps/obsidian-plugin",
|
||||||
|
]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
|
[package.metadata.wasm-pack.profile.dev]
|
||||||
|
wasm-opt = false # for debug build times
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# core dependencies
|
# core dependencies
|
||||||
@@ -39,7 +46,7 @@ toml = { version = "0.8.13", features = ["parse"], optional = false }
|
|||||||
dyn-clone = "1.0.17"
|
dyn-clone = "1.0.17"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
flume = { version = "0.11.1", default-features = false, features = [ "async" ] }
|
flume = { version = "0.11.1", default-features = false, features = [ "async" ] }
|
||||||
mize_macros = { path = "../macros" }
|
mize_macros = { path = "./macros" }
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
|
|
||||||
# wasm dependencies
|
# wasm dependencies
|
||||||
8
apps/obsidian-plugin/Cargo.toml
Normal file
8
apps/obsidian-plugin/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "mize-obsidian-plugin"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mize = { path = "../.." }
|
||||||
|
marts = { path = "../../marts" }
|
||||||
3
apps/obsidian-plugin/src/main.rs
Normal file
3
apps/obsidian-plugin/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
8
apps/os/Cargo.toml
Normal file
8
apps/os/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "mize-bin"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mize = { path = "../../" }
|
||||||
|
marts = { path = "../../marts" }
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
mize_modules = {
|
mize_modules = {
|
||||||
url = "github:c2vi/mize-modules?submodules=1";
|
url = "github:c2vi/mize-modules";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -258,6 +258,7 @@ in {
|
|||||||
pkgs.rsync
|
pkgs.rsync
|
||||||
pkgs.openssh
|
pkgs.openssh
|
||||||
pkgs.git
|
pkgs.git
|
||||||
|
pkgs.cmake
|
||||||
|
|
||||||
# to build ../grid
|
# to build ../grid
|
||||||
pkgs.rustPlatform.bindgenHook
|
pkgs.rustPlatform.bindgenHook
|
||||||
@@ -300,6 +301,7 @@ in {
|
|||||||
wasm-bindgen-cli
|
wasm-bindgen-cli
|
||||||
lld_20
|
lld_20
|
||||||
xdotool
|
xdotool
|
||||||
|
bzip2
|
||||||
];
|
];
|
||||||
|
|
||||||
LD_LIBRARY_PATH = with pkgs; pkgs.lib.makeLibraryPath [
|
LD_LIBRARY_PATH = with pkgs; pkgs.lib.makeLibraryPath [
|
||||||
@@ -324,6 +326,9 @@ in {
|
|||||||
libxcb
|
libxcb
|
||||||
pipewire
|
pipewire
|
||||||
wayland
|
wayland
|
||||||
|
|
||||||
|
# for rust-analyzer to work for zed
|
||||||
|
bzip2
|
||||||
];
|
];
|
||||||
|
|
||||||
LIBRARY_PATH = LD_LIBRARY_PATH;
|
LIBRARY_PATH = LD_LIBRARY_PATH;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
clap = { version = "4.5.59", optional = true, features = ["string"] }
|
clap = { version = "4.5.59", optional = true, features = ["string"] }
|
||||||
flume = "0.12.0"
|
flume = "0.12.0"
|
||||||
futures = "0.3.32"
|
futures = "0.3.32"
|
||||||
mize = { path = "../mize/", default-features = false, features = [] }
|
mize = { path = "../", default-features = false, features = [] }
|
||||||
tokio = { version = "1.49.0", default-features = false, features = ["time"] }
|
tokio = { version = "1.49.0", default-features = false, features = ["time"] }
|
||||||
# https://github.com/rscarson/rustyscript/pull/415
|
# https://github.com/rscarson/rustyscript/pull/415
|
||||||
#rustyscript = { git = 'https://github.com/rscarson/rustyscript', rev = 'e39529eac2c5d37cfcbb7d7d63da7264d7a5afdb', optional = true }
|
#rustyscript = { git = 'https://github.com/rscarson/rustyscript', rev = 'e39529eac2c5d37cfcbb7d7d63da7264d7a5afdb', optional = true }
|
||||||
@@ -19,7 +19,7 @@ tokio = { version = "1.49.0", default-features = false, features = ["time"] }
|
|||||||
reqwest = { version = "0.13.2", features = ["blocking", "json"] }
|
reqwest = { version = "0.13.2", features = ["blocking", "json"] }
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
rusqlite = { version = "0.31", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.37", features = ["bundled"], optional = true }
|
||||||
dioxus = { version = "0.7.1", optional = true }
|
dioxus = { version = "0.7.1", optional = true }
|
||||||
spacetimedb-sdk = { version = "2.6.0", default-features = false }
|
spacetimedb-sdk = { version = "2.6.0", default-features = false }
|
||||||
generated = { path = "./generated" }
|
generated = { path = "./generated" }
|
||||||
@@ -39,7 +39,10 @@ log = "0.4"
|
|||||||
chrono = { version = "0.4.45", features = [], default-features = false }
|
chrono = { version = "0.4.45", features = [], default-features = false }
|
||||||
chrono-tz = { version = "0.10.4", default-features = false }
|
chrono-tz = { version = "0.10.4", default-features = false }
|
||||||
url = "2.5.8"
|
url = "2.5.8"
|
||||||
|
serenity = { version = "0.12.5", optional = true }
|
||||||
|
matrix-sdk = "0.18.0"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["target-os", "desktop", "server", "client"]
|
default = ["target-os", "desktop", "server", "client"]
|
||||||
#default = ["target-js", "target-obsidian"] # to get rust analyzer to analyzer js only parts
|
#default = ["target-js", "target-obsidian"] # to get rust analyzer to analyzer js only parts
|
||||||
@@ -51,3 +54,4 @@ desktop = ["dioxus/desktop"]
|
|||||||
client = []
|
client = []
|
||||||
server = []
|
server = []
|
||||||
clap = ["dep:clap"]
|
clap = ["dep:clap"]
|
||||||
|
serenity = ["dep:serenity"]
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ impl ClientAuth {
|
|||||||
|
|
||||||
pub fn refresh(&mut self) -> MizeResult<()> {
|
pub fn refresh(&mut self) -> MizeResult<()> {
|
||||||
#[cfg(feature = "target-js")] {
|
#[cfg(feature = "target-js")] {
|
||||||
return Err(mize_err!("refresh is not supported on wasm"));
|
console_log!("refresh is not implemented yet on wasm");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "target-os")] {
|
#[cfg(feature = "target-os")] {
|
||||||
@@ -321,6 +321,7 @@ pub fn part_client_auth(mize: &mut Mize) -> MizeResult<()> {
|
|||||||
// register command for obsidian
|
// register command for obsidian
|
||||||
#[cfg(feature = "target-obsidian")] {
|
#[cfg(feature = "target-obsidian")] {
|
||||||
use crate::MartsCommand;
|
use crate::MartsCommand;
|
||||||
|
console_log!("register obsidian command...");
|
||||||
|
|
||||||
let obsidian = mize.get_part_native::<crate::ObsidianPart>("obsidian")?;
|
let obsidian = mize.get_part_native::<crate::ObsidianPart>("obsidian")?;
|
||||||
obsidian.add_command(MartsCommand {
|
obsidian.add_command(MartsCommand {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ impl C2vi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn c2vi(mize: &mut Mize) -> MizeResult<()> {
|
pub fn part_c2vi(mize: &mut Mize) -> MizeResult<()> {
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
if mize
|
if mize
|
||||||
.get_config("c2vi.dash.enable")
|
.get_config("c2vi.dash.enable")
|
||||||
@@ -62,6 +62,22 @@ pub fn c2vi(mize: &mut Mize) -> MizeResult<()> {
|
|||||||
{
|
{
|
||||||
launch_c2vi_dashboard(mize);
|
launch_c2vi_dashboard(mize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-obsidian")] {
|
||||||
|
use crate::MartsCommand;
|
||||||
|
|
||||||
|
let obsidian = mize.get_part_native::<crate::ObsidianPart>("obsidian").unwrap();
|
||||||
|
obsidian.add_command(MartsCommand {
|
||||||
|
id: "sc2-publish-daily-journal".to_string(),
|
||||||
|
name: "SC2 Publish Daily Journal".to_string(),
|
||||||
|
callback: Box::new(|mut mize| {
|
||||||
|
let matrix = mize.get_part_native::<crate::matrix::MatrixPart>("matrix")?;
|
||||||
|
matrix.send_message(room_id);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
167
marts/src/discord/mod.rs
Normal file
167
marts/src/discord/mod.rs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
pub use mize::*;
|
||||||
|
|
||||||
|
// ========== WASM-specific imports (Obsidian plugin) ==========
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
use js_sys;
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
use web_sys;
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
use crate::obsidian::{ObsidianPart, MartsCommand};
|
||||||
|
|
||||||
|
// ========== Native-specific imports (OS daemon) ==========
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
|
use serenity::{
|
||||||
|
async_trait as serenity_async_trait,
|
||||||
|
client::{Client, Context, EventHandler},
|
||||||
|
model::{
|
||||||
|
channel::Message,
|
||||||
|
gateway::Ready,
|
||||||
|
id::ChannelId,
|
||||||
|
},
|
||||||
|
prelude::GatewayIntents,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
|
use mize::async_trait;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[mize_part]
|
||||||
|
pub struct DiscordPart {
|
||||||
|
pub mize: Mize,
|
||||||
|
// Native: serenity client
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
|
pub client: Option<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiscordPart {
|
||||||
|
// ========== WASM (Obsidian plugin) methods ==========
|
||||||
|
|
||||||
|
/// Read a file from the Obsidian vault using the Obsidian API
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
pub fn read_obsidian_file(&self, path: &str) -> MizeResult<String> {
|
||||||
|
// TODO: Implement using js_sys to call Obsidian's vault.read() or similar
|
||||||
|
// Example approach:
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
use js_sys::Reflect;
|
||||||
|
let obsidian_part = self.mize.get_part_native::<ObsidianPart>("obsidian")?;
|
||||||
|
let plugin = &obsidian_part.plugin();
|
||||||
|
let app = Reflect::get(plugin, &"app".into())?;
|
||||||
|
let vault = Reflect::get(&app, &"vault".into())?;
|
||||||
|
let content = Reflect::call(&vault, &"read".into(), &path.into())?;
|
||||||
|
content.as_string().ok_or_else(|| mize_err!("Failed to read file"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send content to Discord channel using Discord REST API
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
pub fn send_to_discord_rest(&self, content: String, channel_id: u64) -> MizeResult<()> {
|
||||||
|
// TODO: Implement using web_sys to call Discord REST API
|
||||||
|
// 1. Get token: mize.get_config("discord.token")
|
||||||
|
// 2. POST to https://discord.com/api/v10/channels/{channel_id}/messages
|
||||||
|
// 3. Headers: Authorization: Bot <token>, Content-Type: application/json
|
||||||
|
// 4. Body: {"content": content}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== Native (OS daemon) methods ==========
|
||||||
|
|
||||||
|
/// Start the serenity Discord bot
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
|
pub async fn start_serenity_bot(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
let token = mize.get_config("discord.token")?.value_string()?;
|
||||||
|
|
||||||
|
// Define event handler
|
||||||
|
struct Handler;
|
||||||
|
|
||||||
|
#[serenity_async_trait]
|
||||||
|
impl EventHandler for Handler {
|
||||||
|
async fn ready(&self, _: Context, ready: Ready) {
|
||||||
|
println!("Discord bot logged in as {}", ready.user.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add message handler for receiving commands from plugin
|
||||||
|
// This is where you'll implement communication with the Obsidian plugin
|
||||||
|
// Example:
|
||||||
|
// async fn message(&self, ctx: Context, msg: Message) {
|
||||||
|
// if msg.content.starts_with("!send") {
|
||||||
|
// // Parse command and send file to channel
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut client = Client::builder(&token, GatewayIntents::non_privileged())
|
||||||
|
.event_handler(Handler)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Spawn the client in a tokio task
|
||||||
|
tokio::spawn({
|
||||||
|
let client_clone = client.clone();
|
||||||
|
async move {
|
||||||
|
if let Err(why) = client_clone.start().await {
|
||||||
|
eprintln!("Discord client error: {:?}", why);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.client = Some(client);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePart for DiscordPart {
|
||||||
|
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
// WASM: Add command to Obsidian
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
{
|
||||||
|
let obsidian_part = mize.get_part_native::<ObsidianPart>("obsidian")?;
|
||||||
|
|
||||||
|
let callback = {
|
||||||
|
let mize_clone = mize.clone();
|
||||||
|
move |mize_inner: Mize| -> MizeResult<()> {
|
||||||
|
// Get configuration values
|
||||||
|
let file_path = mize_inner.get_config("sc2.shared_daily_journal_file")?
|
||||||
|
.value_string()?;
|
||||||
|
let channel_id_str = mize_inner.get_config("sc2.shared_daily_journal_channel")?
|
||||||
|
.value_string()?;
|
||||||
|
let channel_id = channel_id_str.parse::<u64>()?;
|
||||||
|
|
||||||
|
// Get DiscordPart and send the file
|
||||||
|
let mut mize_mut = mize_inner;
|
||||||
|
let discord_part = mize_mut.get_part_native::<DiscordPart>("discord")?;
|
||||||
|
let content = discord_part.read_obsidian_file(&file_path)?;
|
||||||
|
|
||||||
|
// TODO: COMMUNICATION WITH DAEMON GOES HERE
|
||||||
|
// For now, we send directly using REST API.
|
||||||
|
// Replace this with your plugin-to-daemon messaging logic.
|
||||||
|
discord_part.send_to_discord_rest(content, channel_id)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
obsidian_part.add_command(MartsCommand {
|
||||||
|
id: "send_to_discord".to_string(),
|
||||||
|
name: "Send to Discord".to_string(),
|
||||||
|
callback: Box::new(callback),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Native: Override async_init to start serenity bot
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
|
async fn async_init(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
self.start_serenity_bot(mize).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePartCreate for DiscordPart {}
|
||||||
|
|
||||||
|
pub fn part_discord(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
mize.register_part(Box::new(DiscordPart {
|
||||||
|
mize: mize.clone(),
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
|
client: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -15,7 +15,6 @@ pub mod c2vi {
|
|||||||
pub mod part;
|
pub mod part;
|
||||||
pub mod dashboard;
|
pub mod dashboard;
|
||||||
}
|
}
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub use c2vi::part::*;
|
pub use c2vi::part::*;
|
||||||
|
|
||||||
#[cfg(feature = "target-obsidian")]
|
#[cfg(feature = "target-obsidian")]
|
||||||
@@ -36,6 +35,13 @@ pub mod ppc;
|
|||||||
|
|
||||||
pub mod timetrack;
|
pub mod timetrack;
|
||||||
|
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
pub mod matrix;
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
pub use matrix::part_matrix;
|
||||||
|
|
||||||
|
pub mod sc2;
|
||||||
|
|
||||||
//#[cfg(feature = "target-os")]
|
//#[cfg(feature = "target-os")]
|
||||||
//pub mod js;
|
//pub mod js;
|
||||||
//#[cfg(feature = "target-os")]
|
//#[cfg(feature = "target-os")]
|
||||||
|
|||||||
247
marts/src/matrix/mod.rs
Normal file
247
marts/src/matrix/mod.rs
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
use mize::{Mize, MizeResult, MizePart, MizePartCreate, mize_part};
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen_futures::spawn_local;
|
||||||
|
use js_sys::Function;
|
||||||
|
use web_sys::HtmlElement;
|
||||||
|
|
||||||
|
use matrix_sdk::{
|
||||||
|
Client, ClientConfig, SyncState,
|
||||||
|
ruma::{
|
||||||
|
user_id::UserId,
|
||||||
|
events::room::message::RoomMessageEventContent,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "target-js")]
|
||||||
|
mod ui;
|
||||||
|
use ui::mount_chat_app;
|
||||||
|
|
||||||
|
const ROOM_ID: &str = "!XwqdXGPlmaLatdGUiN:ppc.social";
|
||||||
|
const ACCOUNT_NAME: &str = "default";
|
||||||
|
const INITIAL_LOAD_LIMIT: usize = 50;
|
||||||
|
|
||||||
|
/// Matrix part that manages Matrix client and UI
|
||||||
|
#[mize_part("matrix")]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MatrixPart {
|
||||||
|
mize: Mize,
|
||||||
|
pub client: Option<Client>,
|
||||||
|
pub timeline: Vec<matrix_sdk::SyncRoomEvent>,
|
||||||
|
pub message_draft: String,
|
||||||
|
pub edit_event_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePart for MatrixPart {}
|
||||||
|
impl MizePartCreate for MatrixPart {}
|
||||||
|
|
||||||
|
impl MatrixPart {
|
||||||
|
/// Create a new MatrixPart
|
||||||
|
pub fn new(mize: Mize) -> Self {
|
||||||
|
Self {
|
||||||
|
mize,
|
||||||
|
client: None,
|
||||||
|
timeline: Vec::new(),
|
||||||
|
message_draft: String::new(),
|
||||||
|
edit_event_id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the Matrix client from config
|
||||||
|
pub async fn init_client(&mut self) -> Result<(), String> {
|
||||||
|
let homeserver = self.mize
|
||||||
|
.get_config(&format!("matrix.accounts.{}.homeserver", ACCOUNT_NAME))
|
||||||
|
.map_err(|e| format!("Failed to get homeserver config: {:?}", e))?;
|
||||||
|
|
||||||
|
let access_token = self.mize
|
||||||
|
.get_config(&format!("matrix.accounts.{}.access_token", ACCOUNT_NAME))
|
||||||
|
.map_err(|e| format!("Failed to get access_token config: {:?}", e))?;
|
||||||
|
|
||||||
|
let user_id_str = self.mize
|
||||||
|
.get_config(&format!("matrix.accounts.{}.user_id", ACCOUNT_NAME))
|
||||||
|
.map_err(|e| format!("Failed to get user_id config: {:?}", e))?;
|
||||||
|
|
||||||
|
let device_id = self.mize
|
||||||
|
.get_config(&format!("matrix.accounts.{}.device_id", ACCOUNT_NAME))
|
||||||
|
.map_err(|e| format!("Failed to get device_id config: {:?}", e))?;
|
||||||
|
|
||||||
|
let user_id = UserId::parse(&user_id_str)
|
||||||
|
.map_err(|e| format!("Invalid user_id: {:?}", e))?;
|
||||||
|
|
||||||
|
let config = ClientConfig::new()
|
||||||
|
.homeserver_url(homeserver)
|
||||||
|
.device_id(device_id);
|
||||||
|
|
||||||
|
let client = Client::new_with_config(user_id, Some(access_token), config)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to create client: {:?}", e))?;
|
||||||
|
|
||||||
|
self.client = Some(client.clone());
|
||||||
|
|
||||||
|
// Start sync in background
|
||||||
|
self.start_sync(client).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start sync and set up event listeners
|
||||||
|
async fn start_sync(&mut self, client: Client) -> Result<(), String> {
|
||||||
|
let room_id = ROOM_ID.to_string();
|
||||||
|
let mize = self.mize.clone();
|
||||||
|
|
||||||
|
// Start sync
|
||||||
|
let sync_token = client.sync_token().await;
|
||||||
|
let mut sync_state = SyncState::new(sync_token);
|
||||||
|
|
||||||
|
let client_clone = client.clone();
|
||||||
|
|
||||||
|
spawn_local(async move {
|
||||||
|
loop {
|
||||||
|
match client_clone.sync(&mut sync_state).await {
|
||||||
|
Ok(response) => {
|
||||||
|
// Process sync response - update MatrixPart via mize
|
||||||
|
if let Some(rooms) = response.rooms {
|
||||||
|
if let Some(join) = rooms.join {
|
||||||
|
if let Some(room_events) = join.get(&room_id) {
|
||||||
|
// Get MatrixPart and update timeline
|
||||||
|
if let Ok(mut matrix_part) = mize.get_part_native::<MatrixPart>("matrix") {
|
||||||
|
for event in &room_events.timeline.events {
|
||||||
|
if event.room_id() == room_id {
|
||||||
|
matrix_part.timeline.push(event.clone());
|
||||||
|
// Keep only last INITIAL_LOAD_LIMIT events
|
||||||
|
if matrix_part.timeline.len() > INITIAL_LOAD_LIMIT {
|
||||||
|
matrix_part.timeline.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should stop syncing
|
||||||
|
if sync_state.should_stop() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
console_log!("Sync error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a message to the room
|
||||||
|
pub async fn send_message(&mut self, room_id: &str, content: Option<>) -> MizeResult<()> {
|
||||||
|
let client = self.client.as_ref()
|
||||||
|
.ok_or_else(|| mize::error::Error::new("Client not initialized"))?;
|
||||||
|
|
||||||
|
if self.message_draft.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = RoomMessageEventContent::text_plain(&self.message_draft);
|
||||||
|
|
||||||
|
if let Some(_event_id) = &self.edit_event_id {
|
||||||
|
// Editing existing message - placeholder for now
|
||||||
|
console_log!("Editing message (not yet implemented)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send message
|
||||||
|
client.send_room_message(room_id, content).await?;
|
||||||
|
|
||||||
|
// Clear draft after sending
|
||||||
|
self.message_draft.clear();
|
||||||
|
self.edit_event_id = None;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Entry point for the Matrix part
|
||||||
|
pub fn part_matrix(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
#[cfg(feature = "target-obsidian")] {
|
||||||
|
use crate::ObsidianPart;
|
||||||
|
|
||||||
|
// Get the Obsidian part
|
||||||
|
let obsidian = mize.get_part_native::<ObsidianPart>("obsidian")?;
|
||||||
|
|
||||||
|
// Create and register MatrixPart
|
||||||
|
let matrix_part = MatrixPart::new(mize.clone());
|
||||||
|
mize.register_part(Box::new(matrix_part));
|
||||||
|
|
||||||
|
// Get the registered MatrixPart to initialize it
|
||||||
|
let mize_clone = mize.clone();
|
||||||
|
spawn_local(async move {
|
||||||
|
if let Ok(mut matrix_part) = mize_clone.get_part_native::<MatrixPart>("matrix") {
|
||||||
|
if let Err(e) = matrix_part.init_client().await {
|
||||||
|
console_log!("Failed to initialize Matrix client: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a JS function that will be called when the view is opened
|
||||||
|
let mize_for_view = mize.clone();
|
||||||
|
let view_creator = Closure::<dyn Fn(JsValue)>::wrap(Box::new(move |leaf: JsValue| {
|
||||||
|
if let Some(element) = extract_dom_element(leaf) {
|
||||||
|
// Get MatrixPart state for the UI
|
||||||
|
if let Ok(matrix_part) = mize_for_view.get_part_native::<MatrixPart>("matrix") {
|
||||||
|
let timeline = matrix_part.timeline.clone();
|
||||||
|
let message_draft = matrix_part.message_draft.clone();
|
||||||
|
let edit_event_id = matrix_part.edit_event_id.clone();
|
||||||
|
|
||||||
|
mount_chat_app(element, timeline, message_draft, edit_event_id, mize_for_view.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console_log!("Failed to extract DOM element from leaf");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Convert closure to JS Function
|
||||||
|
let view_creator_fn: &Function = view_creator.as_ref().unchecked_ref();
|
||||||
|
|
||||||
|
// Register the view with Obsidian
|
||||||
|
ObsidianPart::register_view(&obsidian.plugin(), "matrix-chat-view", view_creator_fn)?;
|
||||||
|
|
||||||
|
// Prevent the closure from being dropped
|
||||||
|
view_creator.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract DOM element from Obsidian leaf JSValue
|
||||||
|
fn extract_dom_element(leaf: JsValue) -> Option<HtmlElement> {
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
use js_sys::Reflect;
|
||||||
|
|
||||||
|
// Try to get the containerEl from the leaf
|
||||||
|
let container_value = Reflect::get(&leaf, &"containerEl".into()).ok()?;
|
||||||
|
let container = container_value.dyn_into::<HtmlElement>().ok()?;
|
||||||
|
|
||||||
|
// Create a div to mount into
|
||||||
|
let doc = web_sys::window()?.document()?;
|
||||||
|
let mount_point = doc.create_element("div").ok()?;
|
||||||
|
mount_point.set_attribute("style", "height: 100%; width: 100%;").ok()?;
|
||||||
|
|
||||||
|
container.append_child(&mount_point).ok()?;
|
||||||
|
mount_point.dyn_into::<HtmlElement>().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Console log macros
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! console_log {
|
||||||
|
($($t:tt)*) => {
|
||||||
|
web_sys::console::log_1(&format!($($t)*).into());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! console_error {
|
||||||
|
($($t:tt)*) => {
|
||||||
|
web_sys::console::error_1(&format!($($t)*).into());
|
||||||
|
};
|
||||||
|
}
|
||||||
269
marts/src/matrix/ui.rs
Normal file
269
marts/src/matrix/ui.rs
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_web::events::FormEvent;
|
||||||
|
use mize::Mize;
|
||||||
|
use matrix_sdk::SyncRoomEvent;
|
||||||
|
|
||||||
|
const ROOM_ID: &str = "!XwqdXGPlmaLatdGUiN:ppc.social";
|
||||||
|
|
||||||
|
/// Props for the chat component
|
||||||
|
#[derive(Props, Clone)]
|
||||||
|
pub struct ChatProps {
|
||||||
|
pub mize: Mize,
|
||||||
|
pub initial_timeline: Vec<SyncRoomEvent>,
|
||||||
|
pub initial_draft: String,
|
||||||
|
pub initial_edit_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message component - renders a single message
|
||||||
|
#[component]
|
||||||
|
pub fn Message(cx: Scope, event: SyncRoomEvent) -> Element {
|
||||||
|
let sender = event.sender().to_string();
|
||||||
|
let timestamp = event.origin_server_ts();
|
||||||
|
|
||||||
|
// Extract display name from MXID (basic version)
|
||||||
|
let display_name = if let Some(at_pos) = sender.find('@') {
|
||||||
|
if let Some(colon_pos) = sender.rfind(':') {
|
||||||
|
&sender[at_pos + 1..colon_pos]
|
||||||
|
} else {
|
||||||
|
&sender[at_pos + 1..]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
&sender
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format timestamp
|
||||||
|
let time_str = {
|
||||||
|
let dt = js_sys::Date::new(&(timestamp as f64));
|
||||||
|
dt.to_locale_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract message content
|
||||||
|
let content = event
|
||||||
|
.as_original()
|
||||||
|
.and_then(|e| e.content().deserialize().ok())
|
||||||
|
.and_then(|c: matrix_sdk::ruma::events::room::message::RoomMessageEventContent| {
|
||||||
|
c.body().map(|s| s.to_string())
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// Check if this is an edit
|
||||||
|
let is_edit = event
|
||||||
|
.as_original()
|
||||||
|
.map(|e| {
|
||||||
|
e.content()
|
||||||
|
.get("m.relates_to")
|
||||||
|
.and_then(|v| v.as_object())
|
||||||
|
.and_then(|o| o.get("rel_type"))
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
== Some("m.replace")
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-event",
|
||||||
|
style: "margin: 14px 16px; padding-bottom: 10px; border-bottom: 1px solid var(--background-modifier-border);",
|
||||||
|
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-msghead",
|
||||||
|
style: "font-size: 1em; font-weight: 500; color: var(--interactive-accent); display: flex; align-items: center; gap: 10px;",
|
||||||
|
|
||||||
|
span { display_name }
|
||||||
|
span {
|
||||||
|
style: "font-size: 0.85em; color: var(--text-muted); margin-left: auto;",
|
||||||
|
{time_str.clone()}
|
||||||
|
if is_edit { " · edited" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-msgbody",
|
||||||
|
style: "margin: 6px 0 0 0; font-size: 1em;",
|
||||||
|
{content}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Editor component - message input area
|
||||||
|
#[component]
|
||||||
|
pub fn Editor(cx: Scope, mize: Mize, draft: UseSignal<String>, edit_id: UseSignal<Option<String>>) -> Element {
|
||||||
|
let on_input = move |evt: FormEvent| {
|
||||||
|
draft.set(evt.value.clone());
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_send = move |_| {
|
||||||
|
let mize_clone = mize.clone();
|
||||||
|
let draft_val = draft.read().clone();
|
||||||
|
let edit_id_val = edit_id.read().clone();
|
||||||
|
|
||||||
|
cx.spawn(async move {
|
||||||
|
if let Ok(mut matrix_part) = mize_clone.get_part_native::<crate::matrix::MatrixPart>("matrix") {
|
||||||
|
matrix_part.message_draft = draft_val;
|
||||||
|
matrix_part.edit_event_id = edit_id_val;
|
||||||
|
if let Err(e) = matrix_part.send_message().await {
|
||||||
|
console_log!("Failed to send message: {:?}", e);
|
||||||
|
}
|
||||||
|
// Clear local draft
|
||||||
|
draft.set(String::new());
|
||||||
|
edit_id.set(None);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_keydown = move |evt: dioxus_web::events::KeyboardEvent| {
|
||||||
|
if (evt.ctrl_key() || evt.meta_key()) && evt.key() == "Enter" {
|
||||||
|
on_send(evt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_cancel = move |_| {
|
||||||
|
draft.set(String::new());
|
||||||
|
edit_id.set(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-editor",
|
||||||
|
style: "border-top: 1px solid var(--background-modifier-border); background: var(--background-primary); padding: 8px 12px;",
|
||||||
|
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-editor-inner",
|
||||||
|
style: "border: 1px solid var(--background-modifier-border); border-radius: 6px; overflow: hidden; background: var(--background-secondary);",
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
r#type: "text",
|
||||||
|
rows: "3",
|
||||||
|
style: "width: 99%; min-height: 40px; resize: vertical; background: transparent; border: none; outline: none; padding: 8px; font-family: inherit; font-size: inherit; color: var(--text-normal);",
|
||||||
|
value: "{draft}",
|
||||||
|
oninput: on_input,
|
||||||
|
onkeydown: on_keydown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
r#type: "button",
|
||||||
|
style: "margin-top: 3px; float: right; padding: 6px 14px; font-size: 0.97em; border-radius: 5px; border: none; background: var(--interactive-accent); color: white; cursor: pointer;",
|
||||||
|
onclick: on_send,
|
||||||
|
if edit_id.read().is_some() { "Save" } else { "Send" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if edit_id.read().is_some() {
|
||||||
|
button {
|
||||||
|
r#type: "button",
|
||||||
|
style: "margin-right: 12px; padding: 6px 12px; font-size: 0.93em; border-radius: 5px; border: none; color: var(--text-normal); background: var(--background-secondary); cursor: pointer;",
|
||||||
|
onclick: on_cancel,
|
||||||
|
"Cancel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timeline component - list of messages
|
||||||
|
#[component]
|
||||||
|
pub fn Timeline(cx: Scope, mize: Mize, timeline: UseSignal<Vec<SyncRoomEvent>>) -> Element {
|
||||||
|
// Periodically refresh timeline from MatrixPart
|
||||||
|
cx.use_effect((), move |_| {
|
||||||
|
let mize_clone = mize.clone();
|
||||||
|
let timeline_clone = timeline.clone();
|
||||||
|
|
||||||
|
// Set up interval to poll for updates
|
||||||
|
let interval = js_sys::set_interval(
|
||||||
|
&Closure::<dyn FnMut()>::wrap(Box::new(move || {
|
||||||
|
if let Ok(matrix_part) = mize_clone.get_part_native::<crate::matrix::MatrixPart>("matrix") {
|
||||||
|
*timeline_clone.write() = matrix_part.timeline.clone();
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
1000, // 1 second
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cleanup on unmount
|
||||||
|
move || {
|
||||||
|
js_sys::clear_interval(interval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-scroll",
|
||||||
|
style: "flex-grow: 1; overflow-y: auto; padding: 8px 0; background: var(--background-secondary);",
|
||||||
|
|
||||||
|
// Room name header
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-roomname",
|
||||||
|
style: "font-weight: 600; text-align: center; font-size: 1.1em; margin: 2px 0 12px 0; border-bottom: 1px solid var(--background-modifier-border); color: var(--text-accent); background: var(--background-primary);",
|
||||||
|
"Matrix Chat - {ROOM_ID}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages
|
||||||
|
for event in timeline.read().iter() {
|
||||||
|
rsx! { Message { event: event.clone() } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Root chat component
|
||||||
|
#[component]
|
||||||
|
pub fn Chat(cx: Scope, props: ChatProps) -> Element {
|
||||||
|
let timeline = use_signal(&cx, || props.initial_timeline);
|
||||||
|
let draft = use_signal(&cx, || props.initial_draft);
|
||||||
|
let edit_id = use_signal(&cx, || props.initial_edit_id);
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-root",
|
||||||
|
style: "height: 100%; display: flex; flex-direction: column; background: var(--background-primary); color: var(--text-normal);",
|
||||||
|
|
||||||
|
// Title
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-title",
|
||||||
|
style: "padding: 12px; font-weight: bold; font-size: 1.2em; text-align: center; border-bottom: 1px solid var(--background-modifier-border);",
|
||||||
|
"Matrix Chat"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info
|
||||||
|
div {
|
||||||
|
class: "matrix-chat-info",
|
||||||
|
style: "font-size: 0.85em; color: var(--text-muted); text-align: center; margin-bottom: 5px;",
|
||||||
|
"Room: {ROOM_ID}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeline
|
||||||
|
Timeline { mize: props.mize.clone(), timeline }
|
||||||
|
|
||||||
|
// Editor
|
||||||
|
Editor { mize: props.mize.clone(), draft, edit_id }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mount the Dioxus app into a DOM element
|
||||||
|
pub fn mount_chat_app(
|
||||||
|
element: web_sys::HtmlElement,
|
||||||
|
initial_timeline: Vec<SyncRoomEvent>,
|
||||||
|
initial_draft: String,
|
||||||
|
initial_edit_id: Option<String>,
|
||||||
|
mize: Mize,
|
||||||
|
) {
|
||||||
|
dioxus_web::launch(
|
||||||
|
dioxus::prelude::App::new(move || rsx! {
|
||||||
|
Chat {
|
||||||
|
mize: mize.clone(),
|
||||||
|
initial_timeline: initial_timeline.clone(),
|
||||||
|
initial_draft: initial_draft.clone(),
|
||||||
|
initial_edit_id: initial_edit_id.clone(),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
element,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Console log macro
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! console_log {
|
||||||
|
($($t:tt)*) => {
|
||||||
|
web_sys::console::log_1(&format!($($t)*).into());
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -65,7 +65,7 @@ impl ObsidianPart {
|
|||||||
Reflect::set(&obsidian_cmd, &"id".into(), &"login".into()).mres()?;
|
Reflect::set(&obsidian_cmd, &"id".into(), &"login".into()).mres()?;
|
||||||
Reflect::set(&obsidian_cmd, &"name".into(), &"Login".into()).mres()?;
|
Reflect::set(&obsidian_cmd, &"name".into(), &"Login".into()).mres()?;
|
||||||
|
|
||||||
// callback: () => run_login()
|
// callback
|
||||||
let mize_clone = self.mize.clone();
|
let mize_clone = self.mize.clone();
|
||||||
let cb = Closure::<dyn FnMut()>::wrap(Box::new(move || {
|
let cb = Closure::<dyn FnMut()>::wrap(Box::new(move || {
|
||||||
if let Err(e) = (cmd.callback)(mize_clone.clone()) {
|
if let Err(e) = (cmd.callback)(mize_clone.clone()) {
|
||||||
@@ -127,6 +127,7 @@ fn obsidian_mize_main(mize: &mut Mize, plugin: JsValue, app: JsValue) -> MizeRes
|
|||||||
crate::part_obsidian(mize, plugin, app)?;
|
crate::part_obsidian(mize, plugin, app)?;
|
||||||
crate::part_client_auth(mize)?;
|
crate::part_client_auth(mize)?;
|
||||||
crate::spacetime(mize)?;
|
crate::spacetime(mize)?;
|
||||||
|
crate::part_matrix(mize)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
7
marts/src/sc2/mod.rs
Normal file
7
marts/src/sc2/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
use mize::*;
|
||||||
|
|
||||||
|
pub fn part_sc2(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::config::ConfigOpt;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -788,3 +789,30 @@ impl PartialEq for ItemData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Eq for ItemData {}
|
impl Eq for ItemData {}
|
||||||
|
|
||||||
|
pub fn set_config_from_data(mize: &mut Mize, mut data: ItemData) -> MizeResult<()> {
|
||||||
|
let mut config_opts = mize.config_opts.lock().unwrap();
|
||||||
|
for path in data.get_paths_recursive()? {
|
||||||
|
let mut conf_name = path.replace("/", ".");
|
||||||
|
if conf_name.starts_with(".") {
|
||||||
|
conf_name.remove(0);
|
||||||
|
}
|
||||||
|
let val = data.get_path(path.split("/").filter(|s| s != &"").collect::<Vec<&str>>())?;
|
||||||
|
match config_opts.get_mut(&conf_name) {
|
||||||
|
Some(opt) => {
|
||||||
|
opt.val = Some(val);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
config_opts.insert(
|
||||||
|
conf_name.clone(),
|
||||||
|
ConfigOpt {
|
||||||
|
name: conf_name,
|
||||||
|
val: Some(val),
|
||||||
|
thunk: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -47,10 +47,64 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
|||||||
|
|
||||||
pub fn wasm_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
pub fn wasm_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
||||||
console_log!("Hello world from wasm_instance_init!!!!!!!!!!");
|
console_log!("Hello world from wasm_instance_init!!!!!!!!!!");
|
||||||
|
|
||||||
|
#[cfg(feature = "target-obsidian")] {
|
||||||
|
////// populate values from $MIZE_CONFIG_FILES
|
||||||
|
if let Ok(config_file_paths) = obsidian_get_env_var("MIZE_CONFIG_FILES") {
|
||||||
|
for config_file_path in config_file_paths.split(":") {
|
||||||
|
println!("reading config file: {config_file_path}");
|
||||||
|
let content = std::fs::read_to_string(config_file_path)?;
|
||||||
|
let mut data = ItemData::from_toml(content.as_str())?;
|
||||||
|
crate::core::item::set_config_from_data(mize, data)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////// load config from MIZE_CONFIG env var
|
||||||
|
match std::env::var("MIZE_CONFIG") {
|
||||||
|
Ok(config_string) => {
|
||||||
|
let data = data_from_string(config_string)?;
|
||||||
|
crate::core::item::set_config_from_data(mize, data)?;
|
||||||
|
}
|
||||||
|
Err(var_err) => match var_err {
|
||||||
|
VarError::NotPresent => {
|
||||||
|
debug!("env var MIZE_CONFIG NOT present")
|
||||||
|
}
|
||||||
|
VarError::NotUnicode(_) => {
|
||||||
|
warn!("env var MIZE_CONFIG is not Unicode, so not reading it")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
pub fn obsidian_get_env_var(name: &str) -> Option<String> {
|
||||||
|
use js_sys::{Reflect, Object};
|
||||||
|
// 1. Get the global `process` object
|
||||||
|
let global = js_sys::global();
|
||||||
|
let process = Reflect::get(&global, &JsValue::from_str("process")).ok()?;
|
||||||
|
|
||||||
|
if process.is_undefined() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Get the `env` object from `process`
|
||||||
|
let env = Reflect::get(&process, &JsValue::from_str("env")).ok()?;
|
||||||
|
if env.is_undefined() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Get the `MIZE_STORE` value from `env`
|
||||||
|
let mize_store = Reflect::get(&env, &JsValue::from_str("MIZE_STORE")).ok()?;
|
||||||
|
|
||||||
|
// 4. Convert the JsValue to a Rust String if it exists
|
||||||
|
mize_store.as_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn load_module(mize: &mut Mize, name: &str, path: Option<String>) -> MizeResult<()> {
|
pub fn load_module(mize: &mut Mize, name: &str, path: Option<String>) -> MizeResult<()> {
|
||||||
console_log!("loading module NOT IMPLEMENTED: {}", name);
|
console_log!("loading module NOT IMPLEMENTED: {}", name);
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ pub fn os_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
|||||||
println!("reading config file: {config_file_path}");
|
println!("reading config file: {config_file_path}");
|
||||||
let content = std::fs::read_to_string(config_file_path)?;
|
let content = std::fs::read_to_string(config_file_path)?;
|
||||||
let mut data = ItemData::from_toml(content.as_str())?;
|
let mut data = ItemData::from_toml(content.as_str())?;
|
||||||
set_config_from_data(mize, data)?;
|
crate::core::item::set_config_from_data(mize, data)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ pub fn os_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
|||||||
match std::env::var("MIZE_CONFIG") {
|
match std::env::var("MIZE_CONFIG") {
|
||||||
Ok(config_string) => {
|
Ok(config_string) => {
|
||||||
let data = data_from_string(config_string)?;
|
let data = data_from_string(config_string)?;
|
||||||
set_config_from_data(mize, data)?;
|
crate::core::item::set_config_from_data(mize, data)?;
|
||||||
}
|
}
|
||||||
Err(var_err) => match var_err {
|
Err(var_err) => match var_err {
|
||||||
VarError::NotPresent => {
|
VarError::NotPresent => {
|
||||||
@@ -195,32 +195,6 @@ pub fn os_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn set_config_from_data(mize: &mut Mize, mut data: ItemData) -> MizeResult<()> {
|
|
||||||
let mut config_opts = mize.config_opts.lock().unwrap();
|
|
||||||
for path in data.get_paths_recursive()? {
|
|
||||||
let mut conf_name = path.replace("/", ".");
|
|
||||||
if conf_name.starts_with(".") {
|
|
||||||
conf_name.remove(0);
|
|
||||||
}
|
|
||||||
let val = data.get_path(path.split("/").filter(|s| s != &"").collect::<Vec<&str>>())?;
|
|
||||||
match config_opts.get_mut(&conf_name) {
|
|
||||||
Some(opt) => {
|
|
||||||
opt.val = Some(val);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
config_opts.insert(
|
|
||||||
conf_name.clone(),
|
|
||||||
ConfigOpt {
|
|
||||||
name: conf_name,
|
|
||||||
val: Some(val),
|
|
||||||
thunk: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn seconds_since_modification(path: &Path) -> MizeResult<u64> {
|
pub fn seconds_since_modification(path: &Path) -> MizeResult<u64> {
|
||||||
let metadata = fs::metadata(path)?;
|
let metadata = fs::metadata(path)?;
|
||||||
Reference in New Issue
Block a user