rust runs in obsidian plugin, yay

This commit is contained in:
Sebastian Moser
2026-02-18 19:34:10 +01:00
parent 56f658da1e
commit 48e691f163
27 changed files with 3047 additions and 697 deletions

2
.gitignore vendored
View File

@@ -1,8 +1,10 @@
target
# for nix
result
dist
node_modules
# I put testing stuff for this projekt that I don't want to push to github into a folder called gitignore
gitignore

659
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,11 @@ version = "0.1.0"
edition = "2021"
[dependencies]
clap = "4.5.59"
deno_core = "0.385.0"
mize = { path = "../mize/" }
clap = { version = "4.5.59", optional = true }
deno_core = { version = "0.385.0", optional = true }
mize = { path = "../mize/", default-features = false }
[features]
default = ["target-os"]
target-os = [ "clap", "mize/target-os" ]
target-wasm = [ "mize/target-wasm" ]

View File

@@ -1,4 +1,6 @@
#[cfg(feature = "target-os")]
mod cli;
#[cfg(feature = "target-os")]
pub use cli::*;
//mod js;

View File

@@ -17,12 +17,12 @@ crate-type = ["cdylib", "rlib"]
[[bin]]
name = "mize"
path = "src/platform/os/main.rs"
required-features = [ "os-target" ]
required-features = [ "target-os" ]
[features]
default = [ "os-target" ]
os-target = [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req" ]
wasm-target = [ "wasm-bindgen", "console_error_panic_hook", "wasm-bindgen-cli", "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"]
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" ]
target-wasm = [ "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"]
async = ["tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util"]
@@ -40,14 +40,14 @@ dyn-clone = "1.0.17"
sha2 = "0.10.8"
flume = "0.11.1"
mize_macros = { path = "../macro_mize" }
async-trait = "0.1.89"
# buildin modules
# builtin modules
#mize_module_blob = { path = "./modules/modules/Blob" }
# wasm dependencies
wasm-bindgen = { version = "0.2.84", optional = true }
console_error_panic_hook = { version = "0.1.7", optional = true }
wasm-bindgen-cli = { version = "0.2.92", optional = true }
web-sys = { version = "0.3.70", optional = true }
serde-wasm-bindgen = { version = "0.6.5", optional = true }
wasm-bindgen-futures = { version = "0.4.49", optional = true }
@@ -75,7 +75,6 @@ http_req = { version = "0.12.0", default-features = false, features = [ "rust-tl
axum = { version = "0.6.1", features = ["ws", "headers"], optional = true }
axum-extra = { version = "0.4.2", features = ["spa"], optional = true }
axum-macros = { version = "0.3.0", optional = true }
async-trait = "0.1.89"
#tokio-core = "0.1.18"
#tokio-io = "0.1.13"

View File

@@ -18,5 +18,5 @@ nix build github:c2vi/mize#npmPackage
### without nix
```bash
wasm-pack build -- --features wasm-target
wasm-pack build -- --features target-wasm
```

View File

@@ -55,7 +55,7 @@ let
src = self;
doCheck = false; # tests does not work in wasm
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
cargoExtraArgs = "--features wasm-target --no-default-features";
cargoExtraArgs = "--features target-wasm --no-default-features";
RUSTFLAGS="-C linker=wasm-ld";
buildInputs = with pkgs; [ cargo-binutils lld ];
});
@@ -109,7 +109,7 @@ in {
one = craneLib.buildPackage {
src = "${self}";
#cargoExtraArgs = "--bin mize --features os-target";
#cargoExtraArgs = "--bin mize --features target-os";
cargoExtraArgs = "--bin mize";
doCheck = false; # tests does not work in wasm
CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu";
@@ -123,13 +123,13 @@ in {
default = osCrane.buildPackage {
src = "${self}";
cargoExtraArgs = "--bin mize --features os-target";
cargoExtraArgs = "--bin mize --features target-os";
};
wasm = wasmCrane.buildPackage {
src = "${self}";
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
cargoExtraArgs = "--bin mize --features wasm-target";
cargoExtraArgs = "--bin mize --features target-wasm ";
doCheck = false;
};
inherit wasmArtifacts;
@@ -154,7 +154,7 @@ in {
mkdir -p $out/pkg
export HOME=$(mktemp -d fake-homeXXXX)
wasm-pack build --out-dir $out/pkg --scope=c2vi -- --no-default-features --features wasm-target
wasm-pack build --out-dir $out/pkg --scope=c2vi -- --no-default-features --features target-wasm
'';
buildInputs = with pkgs; [ wasm-bindgen-cli binaryen wasm-pack wasmToolchain ];

View File

@@ -269,7 +269,9 @@ rec {
mkMizeRustShell = attrs: mkMizeModuleShell (attrs // {
#_shell_type = "rust";
nativeBuildInputs = attrs.nativeBuildInputs or [] ++ [
pkgs.wasm-pack
pkgs.pkg-config
pkgs.wasm-bindgen-cli
(fenix.packages."x86_64-linux".combine [
fenix.packages."x86_64-linux".latest.toolchain
fenix.packages."x86_64-linux".targets.wasm32-unknown-unknown.latest.toolchain

View File

@@ -17,7 +17,7 @@ module = { mkMizeRustModule, mkMizeRustShell, hostSystem, pkgsCross, pkgs, lib,
# linux stuff
// (lib.attrsets.optionalAttrs (hostSystem.kernel.name == "linux") {
strictDeps = true;
cargoExtraArgs = "--bin mize --features os-target";
cargoExtraArgs = "--bin mize --features target-os";
mizeInstallPhase = ''
mkdir -p $out
cp $build_dir/target/${hostSystem.nameRust}/$debugOrRelease/mize $out/
@@ -67,7 +67,7 @@ module = { mkMizeRustModule, mkMizeRustShell, hostSystem, pkgsCross, pkgs, lib,
strictDeps = true;
doCheck = false;
cargoExtraArgs = "--bin mize --features os-target";
cargoExtraArgs = "--bin mize --features target-os";
CARGO_BUILD_TARGET = "${hostSystem.cpu.name}-pc-windows-gnu";
@@ -94,14 +94,14 @@ module = { mkMizeRustModule, mkMizeRustShell, hostSystem, pkgsCross, pkgs, lib,
src = ./.;
doCheck = false; # tests does not work in wasm
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
cargoExtraArgs = "--features wasm-target --no-default-features";
cargoExtraArgs = "--features target-wasm --no-default-features";
RUSTFLAGS="-C linker=wasm-ld";
buildInputs = with pkgs; [ cargo-binutils lld ];
});
mizeBuildPhase = ''
cd $build_dir
RUST_LOG=off wasm-pack build --target no-modules --dev --out-dir $out -- --features wasm-target --no-default-features
RUST_LOG=off wasm-pack build --target no-modules --dev --out-dir $out -- --features target-wasm --no-default-features
cat $build_dir/src/platform/wasm/init.js >> $out/mize.js
'';
mizeInstallPhase = "";
@@ -113,7 +113,7 @@ module = { mkMizeRustModule, mkMizeRustShell, hostSystem, pkgsCross, pkgs, lib,
buildPhaseCargoCommand = ''
mkdir -p $out/pkg
HOME=$(mktemp -d fake-homeXXXX) wasm-pack build --target no-modules --out-dir $out/pkg --scope=c2vi -- --features wasm-target --no-default-features
HOME=$(mktemp -d fake-homeXXXX) wasm-pack build --target no-modules --out-dir $out/pkg --scope=c2vi -- --features target-wasm --no-default-features
'';
postInstall = ''

View File

@@ -246,7 +246,7 @@ impl Mize {
)),
};
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
{
let instance_clone = instance.clone();
let op_rx_clone = op_rx.clone();
@@ -259,7 +259,7 @@ impl Mize {
}
// set up async update "threads" when using wasm
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-wasm ")]
{
let instance_clone = instance.clone();
let op_rx_clone = op_rx.clone();
@@ -275,14 +275,14 @@ impl Mize {
//let msg_closure = move || msg_thread(op_rx, instance_clone);
//instance.spawn("msg_thread", closure)?;
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-wasm ")]
console_log!("before loading build time config");
// load the config from build time
let config = ItemData::from_toml(BUILD_TIME_CONFIG)?;
instance.set_blocking("0", config);
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-wasm ")]
console_log!("after loading build time config");
return Ok(instance);
@@ -343,7 +343,7 @@ impl Mize {
match self.get("0/config/load_modules")?.value_string() {
Ok(modules_to_load) => {
for module in modules_to_load.split(" ") {
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-wasm ")]
console_log!("loading module in Instance::init() ... {}", module);
self.load_module(module)?;
}
@@ -695,10 +695,10 @@ impl Mize {
*next_thread_id += 1;
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
thread::spawn(move || to_spawn());
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-wasm ")]
{
//console_log!("in instance::spawn with wasm target")
}

View File

@@ -22,10 +22,10 @@ pub trait Module {
// console_log macro
// that can be copied into other files for debugging purposes
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-wasm ")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-wasm ")]
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]

View File

@@ -60,7 +60,7 @@ pub mod platform {
#[cfg(feature = "wasm-target")]
pub mod wasm;
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
pub mod os;
pub mod any {
@@ -68,35 +68,37 @@ pub mod platform {
#[cfg(feature = "wasm-target")]
pub use super::wasm::wasm_instance_init as instance_init;
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
pub use super::os::os_instance_init as instance_init;
#[cfg(not(any(feature = "os-target", feature = "wasm-target")))]
#[cfg(not(any(feature = "target-os", feature = "target-wasm ")))]
pub use super::super::instance_init;
//////////// load_module
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
pub use super::os::load_module;
#[cfg(feature = "wasm-target")]
pub use super::wasm::load_module;
#[cfg(not(any(feature = "os-target", feature = "wasm-target")))]
#[cfg(not(any(feature = "target-os", feature = "target-wasm ")))]
pub use super::super::load_module;
//////////// fetch_module
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
pub use super::os::fetch_module;
#[cfg(feature = "wasm-target")]
pub use super::super::fetch_module;
#[cfg(not(any(feature = "os-target", feature = "wasm-target")))]
#[cfg(not(any(feature = "target-os", feature = "target-wasm ")))]
pub use super::super::fetch_module;
}
}
pub fn instance_init(instance: &mut core::instance::Mize) {}
pub fn instance_init(instance: &mut core::instance::Mize) -> MizeResult<()> {
Ok(())
}
pub fn load_module(
instance: &mut core::instance::Mize,

View File

@@ -10,12 +10,12 @@ wasm-opt = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = [ "os-target" ]
default = [ "target-os" ]
qt = [ "slint", "cpp", "qt_core", "qt_gui", "qt_widgets", "comandr/qt" ]
slint = [ "i-slint-backend-qt", "dep:slint", "slint-interpreter", "spin_on", "qt" ]
os-target = [ "tao", "wry", "tracing-subscriber", "clap", "webkit2gtk", "mize/os-target", "glib" ]
#os-target = [ "tao", "wry", "tracing-subscriber", "clap" ]
wasm-target = [ "wasm-bindgen", "console_error_panic_hook", "wasm-bindgen-cli", "web-sys", "comandr/wasm-target", "mize/wasm-target" ]
target-os= [ "tao", "wry", "tracing-subscriber", "clap", "webkit2gtk", "mize/target-os", "glib" ]
#target-os= [ "tao", "wry", "tracing-subscriber", "clap" ]
target-wasm = [ "wasm-bindgen", "console_error_panic_hook", "wasm-bindgen-cli", "web-sys", "comandr/target-wasm ", "mize/wasm-target" ]
console_error_panic_hook = ["dep:console_error_panic_hook"]
[target.'cfg(unix)'.dependencies]

View File

@@ -18,7 +18,7 @@ use wasm_bindgen::JsValue;
#[cfg(feature = "wasm-target")]
use web_sys::js_sys::Function;
//#[cfg(feature = "os-target")]
//#[cfg(feature = "target-os")]
//use crate::implementors::qt_widget::QtWidgetSlot;
#[cfg(feature = "qt")]
use qt_core::{qs, QString, QTimer, SlotNoArgs};
@@ -142,7 +142,7 @@ impl Module for Mme {
println!("MmeModule init");
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
{
//self.mize.spawn("mme-main", || self.create_x_window());
let mut cloned_self = self.clone();
@@ -212,7 +212,7 @@ impl Mme {
Ok(())
}
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
pub fn create_x_window(&mut self) -> MizeResult<()> {
use std::fs;
@@ -451,19 +451,19 @@ impl Mme {
}
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
use tao::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
use wry::{
http::{self, header::CONTENT_TYPE, Request, Response},
WebViewBuilder,
};
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
fn get_wry_response(request: Request<Vec<u8>>, mut mme: Mme) -> Result<http::Response<Vec<u8>>, Box<dyn std::error::Error>> {
use comandr::core::module;
use mize::item::ItemData;

View File

@@ -7,13 +7,13 @@ use ciborium::Value as CborValue;
use flume::{ Receiver, Sender };
use ciborium_io::Read;
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
use wry::http::Request;
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
use wry::WebView;
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
use tao::event_loop::EventLoopProxy;
#[cfg(feature = "wasm-target")]
@@ -54,7 +54,7 @@ use mize::MizeError;
#[cfg(feature = "os-target")]
#[cfg(feature = "target-os")]
pub fn mme_setup_weview_con_host(mme: &mut Mme, rx: Receiver<MizeMessage>, event_loop_proxy: EventLoopProxy<MizeMessage>) -> MizeResult<()> {
let mize_clone = mme.mize.clone();

View File

@@ -4,14 +4,24 @@ version = "0.1.0"
edition = "2024"
[dependencies]
mize = { path = "../mize" }
marts = { path = "../marts/" }
clap = "4.5.59"
mize = { path = "../mize", default-features = false }
marts = { path = "../marts/", default-features = false }
clap = { version = "4.5.59", optional = true }
js-sys = { version = "0.3.85", optional = true }
wasm-bindgen = { version = "0.2.108", optional = true }
[[bin]]
name = "ppc"
path = "src/platform/os/main.rs"
[lib]
crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[features]
default = ["target-os"]
target-os = []
target-os = [ "clap", "mize/target-os" ]
target-obsidian = [ "js-sys", "wasm-bindgen", "mize/target-wasm", "marts/target-wasm" ]
[package.metadata.wasm-pack.profile.release]
wasm-opt = false

4
crates/ppc/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
mod platform {
#[cfg(feature = "target-obsidian")]
mod obsidian;
}

View File

@@ -0,0 +1,54 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
*/
`;
const prod = (process.argv[2] === "production");
const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["plugin.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
format: "cjs",
target: "es2020",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "dist/main.js",
loader: {
".wasm": "binary",
".workerjs": "text"
},
define: {
"import.meta.url": '"obsidian-plugin"'
},
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}

View File

@@ -0,0 +1,49 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = (process.argv[2] === "production");
const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["./plugin.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "dist/main.js",
minify: false,
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}

View File

@@ -0,0 +1,10 @@
{
"id": "ppc",
"name": "PPC Obsidian Plugin",
"version": "0.1.0",
"minAppVersion": "1.5.0",
"main": "main.js",
"author": "ppc",
"isDesktopOnly": true,
"description": "Turns Obsidian into a full on PPC client"
}

View File

@@ -0,0 +1,23 @@
use mize::Mize;
use wasm_bindgen::prelude::*;
// console_log macro
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => ({ log(&format_args!($($t)*).to_string())})
}
#[wasm_bindgen]
pub fn obsidian_mize_entrypoint() {
console_log!("hiiiiiiiiiiiii from rust");
let mize = Mize::new();
console_log!("Obsidian Mize initialized");
}

View File

@@ -0,0 +1,46 @@
{
"name": "ppc-obsidian-plugin",
"version": "0.1.0",
"description": "Turnes obsidian into an PPC client",
"main": "main.js",
"scripts": {
"build:rust": "wasm-pack -vvvvv build -d src/platform/obsidian/rust_dist --target web --no-default-features --features target-obsidian",
"build": "npm run build:rust && mkdir -p dist && touch dist/.hotreload && cp manifest.json dist && node esbuild.config.js production",
"dev": ""
},
"keywords": [
"obsidian",
"plugin"
],
"author": "c2vi",
"license": "MIT",
"dependencies": {
"@flowershow/remark-wiki-link": "^2.0.4",
"@instantdb/admin": "^0.21.3",
"@instantdb/react": "^0.20.28",
"@react-oauth/google": "^0.12.2",
"esbuild": "^0.27.3",
"mdast": "^3.0.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@wasm-tool/rollup-plugin-rust": "^3.1.4",
"binaryen": "^125.0.0",
"builtin-modules": "3.3.0",
"obsidian": "latest",
"tailwindcss": "^4",
"tslib": "^2.6.2",
"typescript": "^5.5.4"
}
}

View File

@@ -0,0 +1,598 @@
import { Plugin, TFile, Vault, MetadataCache } from "obsidian";
import { id, i, init, InstaQLEntity } from "@instantdb/react";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { unified } from "unified";
import { visit } from "unist-util-visit";
import { Root, Content, Heading, List, ListItem, Text } from "mdast";
import remarkFrontmatter from "remark-frontmatter";
import remarkGfm from "remark-gfm";
import wikiLinkPlugin from "@flowershow/remark-wiki-link";
import wasmBinary from "./rust_dist/ppc_bg.wasm";
import * as wasmNamespace from "./rust_dist/ppc";
const OTASK_PROP_NAMES = ["short", "outcome", "priority"];
const ACTION_PROP_NAMES = [];
const REMARK = unified()
.use(remarkParse)
.use(remarkFrontmatter)
.use(remarkGfm)
.use(remarkStringify, { bullet: "-" });
export default class PPCObsidianPlugin extends Plugin {
async onload() {
// init mize from wasm
const wasmModule = await import("./rust_dist/ppc.js");
await wasmModule.default({ module_or_path: wasmBinary });
console.log("hoooo wasmModule:", wasmModule);
wasmModule.initSync();
//console.log("");
//const buffer = Uint8Array.from(atob(rustPlugin), (c) => c.charCodeAt(0));
wasmModule.obsidian_mize_entrypoint();
// Listen for file modifications in the vault
//this.registerEvent(
//this.app.vault.on("modify", (file: TFile) => {
//this.handleFileUpdate(file);
//})
//);
//this.registerView(
//"system-c2-app",
//(leaf) => new AppReactView(leaf, this)
//);
this.addCommand({
id: "app",
name: "Open SystemC2 App page",
callback: async () => {
const leaf = this.app.workspace.getLeaf(true);
await leaf.setViewState({
type: "system-c2-app",
active: true,
});
this.app.workspace.revealLeaf(leaf);
},
});
this.addCommand({
id: "full-parse",
name: "Parse all otask files into the db",
callback: async () => {
await full_parse();
},
});
//full_parse(this.db)
}
async handleFileUpdate(file: TFile) {
if (!(file instanceof TFile) || file.extension !== "md") {
return; // only care about markdown files
}
const metadata = this.app.metadataCache.getFileCache(file);
// Example: check if the file has a specific tag
if (metadata?.frontmatter?.tags?.includes("t/otask")) {
this.parseOtask(file);
}
}
async parseOtask(file: TFile) {
if (!this.isLoggedIn()) {
return;
}
const isRrootOtask = file.basename === "my-projects";
if (isRrootOtask) {
return;
}
}
async isLoggedIn() {
const user = this.db.getAuth();
user;
}
onunload() {
this.app.workspace.detachLeavesOfType("system-c2-app");
}
}
let OTASK_PARSE_QUEUE = [];
let OTASK_LIST = [];
let DBG = false;
function dbg() {
if (DBG) {
console.log(arguments);
}
}
function queue_otasks_from_list_item(listItem, otask_path, priority = 0) {
OTASK_PARSE_QUEUE.push({
listItem,
otask_path,
priority,
});
}
function queue_otasks_from_file(file, otask_path, priority = 0) {
OTASK_PARSE_QUEUE.push({
file,
otask_path,
priority,
});
}
// parsing helper functions
async function full_parse() {
console.log("full parse.......................... yay");
let otasks = [];
OTASK_PARSE_QUEUE = [];
OTASK_LIST = [];
const root_file = app.vault.getAbstractFileByPath("task/my-projects.md");
const content = await app.vault.read(root_file);
const list = await getListUnderHeading(content, "root");
for (const entry of list) {
queue_otasks_from_list_item(entry, []);
}
// go through the queue of otaskParseTasks
// using a queue instead of recursive calls, to make it more debuggable...
do {
const task = OTASK_PARSE_QUEUE.shift();
dbg("task", task);
if (task.file) {
const otask = await otasks_from_file(
task.file,
task.otask_path,
task.priority,
);
dbg("otask", otask);
if (otask) {
OTASK_LIST.push(otask);
}
} else {
const otask = await otasks_from_list_item(
task.listItem,
task.otask_path,
task.priority,
);
dbg("otask", otask);
if (otask) {
OTASK_LIST.push(otask);
}
}
} while (OTASK_PARSE_QUEUE.length > 0);
console.log("otasks::", OTASK_LIST);
render_sub0_file(OTASK_LIST);
}
async function render_sub0_file(otasks) {
const sub0_file = app.vault.getAbstractFileByPath("private/sub0-tasks.md");
if (!(sub0_file instanceof TFile)) {
console.error(
"SystemC2: sub0_file not found:",
"private/sub0-tasks.md",
sub0_file,
);
return;
}
//render sub0
const sub0_otasks = otasks.filter(
(otask) => otask.priority == 0 && !otask.is_file,
);
console.log("sub0 otasks...", sub0_otasks);
let rendered_otasks_string = "";
for (const otask of sub0_otasks) {
const path_part = otask.path.length > 0 ? otask.path.join(": ") + ": " : "";
let content_part = otask.content ? otask.content + "\n" : "";
content_part = content_part
.split("\n")
.map((line) => "\t" + line)
.join("\n"); // add a \t in front of every otask.content line
rendered_otasks_string += `- ${path_part}${otask.longName}\n${content_part}\n`;
}
//console.log("newContent...", rendered_otasks_string)
await replaceHeading(sub0_file, "sub0", rendered_otasks_string);
//render sub-1
}
async function replaceHeading(
file: TFile,
heading: string,
newContent: string,
) {
const data = await this.app.vault.read(file);
// Parse the Markdown into an AST
const tree = REMARK.parse(data) as Root;
// Split new content into AST nodes
const newContentAst = REMARK.parse(newContent) as Root;
const children = tree.children;
const newChildren: Content[] = [];
let insideTarget = false;
let targetLevel = 0;
for (let i = 0; i < children.length; i++) {
const node = children[i];
if (node.type === "heading") {
const headingNode = node as Heading;
const text = headingNode.children
.filter((c) => c.type === "text")
.map((c: any) => c.value)
.join("");
// If we encounter our target heading
if (text.trim() === heading) {
insideTarget = true;
targetLevel = headingNode.depth;
newChildren.push(node); // Keep the heading itself
// Insert new content right after
newChildren.push(...newContentAst.children);
continue;
}
// If we’re currently inside target section and see a heading of same or higher level
if (insideTarget && headingNode.depth <= targetLevel) {
insideTarget = false;
}
}
// If we’re not inside the target section, keep the node
if (!insideTarget) {
newChildren.push(node);
}
}
const newTree: Root = { ...tree, children: newChildren };
// Convert AST back to Markdown
const newMarkdown = await REMARK.stringify(newTree);
await this.app.vault.modify(file, newMarkdown);
}
async function otasks_from_list_item(listItem, otask_path, priority = 0) {
// the main otask this listItem is about
let otask = {
description: [],
actions: [],
path: [],
priority,
};
dbg("SystemC2: otasks_from_list_item", listItem, otask_path);
if (!listItem.children[0].type === "paragraph") {
dbg(
"listItem[0] of a otask item is not a paragraph... the otask:",
listItem,
);
return null;
}
let longName = nodeToString(listItem.children[0]);
dbg("longName:", longName);
// check for [[anotherotask]], call otasks_from_file, return those
const match = longName.match(/\[\[([^\]]+)\]\]/);
if (match) {
const file = app.metadataCache.getFirstLinkpathDest(match[1], "");
if (file) {
const metadata = app.metadataCache.getFileCache(file);
if (metadata?.frontmatter?.tags?.includes("t/otask")) {
queue_otasks_from_file(file, otask_path);
return null;
} else {
dbg(
"SystemC2: listItem with Link to '",
match[1],
"', which is not an otask",
longName,
);
}
}
}
//check for [x] and remove from name
if (longName.startsWith("[ ") || longName.startsWith("[x")) {
longName = longName.slice(4);
}
//check for "ot:" and remove from name
if (longName.startsWith("ot:")) {
longName = longName.slice(4);
}
otask.longName = longName;
otask.short = longName;
otask.path = otask_path;
let new_otask_path = [...otask_path, otask.short];
// go through sub list items
// - if it's a prop for the otask... handle that
// - if it's an "ac:" handle that
// - if it's "ot: " handle as another otask -> recursive call
// - if it's "[ ]" handle as another otask -> recursive call
// - if it's a link to a otask file... handle as another otask -> call otasks_from_file()
// - else add it as description item list to otask
if (!listItem.children || listItem.children.length < 2) {
return otask;
}
// add content as text to otask
if (otask.longName == "hosting updates") {
console.log("listItem... ", listItem.children[1]);
let hii = REMARK.stringify(listItem.children[1]);
console.log("text...", hii);
}
let content_text = REMARK.stringify(listItem.children[1]);
otask.content = content_text;
for (const subListItem of listItem.children[1].children) {
let text = nodeToString(subListItem.children[0]);
// check prop
if (check_otask_prop_from_list_item(subListItem, otask)) {
new_otask_path = [...otask_path, otask.short]; // have to redo it here, in case short changed from aprop
continue; // don't do any other processing on this sub-listItem
}
// remove "[ ]"
if (text.startsWith("[ ") || text.startsWith("[x")) {
text = text.slice(4);
}
// check ac
if (text.startsWith("ac:")) {
otask.actions = [
...otask.actions,
action_from_list_item(subListItem, new_otask_path, otask.priority),
];
continue; // don't do any other processing on this sub-listItem
}
// check ot
if (text.startsWith("ot:")) {
queue_otasks_from_list_item(subListItem, new_otask_path, otask.priority);
continue; // don't do any other processing on this sub-listItem
}
// check [ ]
if (text.startsWith("[ ] ") || text.startsWith("[x] ")) {
queue_otasks_from_list_item(subListItem, new_otask_path, otask.priority);
continue; // don't do any other processing on this sub-listItem
}
// check link to otask file
const match = text.match(/\[\[([^\]]+)\]\]/);
if (match) {
const file = app.metadataCache.getFirstLinkpathDest(match[1], "");
if (file) {
const metadata = app.metadataCache.getFileCache(file);
if (metadata?.frontmatter?.tags?.includes("t/otask")) {
queue_otasks_from_file(file, new_otask_path, otask.priority);
continue;
} else {
dbg(
"SystemC2: listItem with Link to '",
match[1],
"', which is not an otask",
longName,
);
}
continue; // don't do any other processing on this sub-listItem
}
}
// add as description
const desc = REMARK.stringify(subListItem);
otask.description.push(desc);
}
return otask;
}
async function otasks_from_file(file: TFile, otask_path, priority = 0) {
dbg("SystemC2: otasks_from_file", file, otask_path);
// the main otask this file is about
let otask = {
description: [],
actions: [],
path: [],
priority,
is_file: true,
};
otask.longName = file.basename;
otask.short = otask.longName;
otask.path = otask_path;
let new_otask_path = [...otask_path, otask.short];
const content = await app.vault.read(file);
// parse listItems at the top... for props
const topListItems = getListItemsBeforeFirstHeading(content);
for (const topListItem of topListItems) {
const text = nodeToString(topListItem);
// check for prop
if (check_otask_prop_from_list_item(topListItem, otask)) {
new_otask_path = [...otask_path, otask.short];
continue; // don't do any other processing on this sub-listItem
}
}
// parse all the sub sections
const SUB_COUNT = 10;
for (let sub_number = 0; sub_number < SUB_COUNT; sub_number++) {
let listItems = await getListUnderHeading(content, "sub" + sub_number);
// "sub" without a number should have priority 5
if (sub_number == 5) {
listItems = listItems.concat(await getListUnderHeading(content, "sub"));
}
//dbg(`sub section ${sub_number} has items:`, listItems)
for (const listItem of listItems) {
// check if listItem is an action...
let text = nodeToString(listItem.children[0]);
if (
text.startsWith("ac:") ||
text.startsWith("[ ] ac:") ||
text.startsWith("[x] ac:")
) {
const action = action_from_list_item(listItem, otask_path);
otask.actions.push(action);
}
queue_otasks_from_list_item(
listItem,
new_otask_path,
otask.priority + sub_number,
);
}
}
return otask;
}
function action_from_list_item(listItem, otask_path) {
dbg("SystemC2: action_from_list_item", listItem, otask_path);
let action = {};
let text = nodeToString(listItem.children[0]).slice(4);
if (text.startsWith("[")) {
text = text.slice(4);
}
action.name = text;
return action;
}
function check_otask_prop_from_list_item(listItem, otask) {
let text = nodeToString(listItem.children[0]);
for (const prop_name of OTASK_PROP_NAMES) {
if (text.startsWith(prop_name + ":")) {
const value = text.slice(prop_name.length + 2);
if (prop_name == "priority") {
// parse priority as int
otask[prop_name] = parseInt(value);
} else if (prop_name == "outcome") {
// add sub items to it
let fullText = REMARK.stringify(listItem).slice(prop_name.length + 4);
otask[prop_name] = fullText;
} else {
otask[prop_name] = value;
}
return true;
}
}
return false;
}
// Extract plain text from a node
function nodeToString(node: any): string {
let result = "";
visit(node, "text", (textNode: Text) => {
result += textNode.value;
});
return result.trim();
}
async function getListUnderHeading(
markdown: string,
headingText: string,
): string[] {
const tree = REMARK.parse(markdown);
//dbg("tree", tree)
let capture = false;
let items: string[] = [];
for (let i = 0; i < tree.children.length; i++) {
const node: Content = tree.children[i];
// Detect the target heading
if (
node.type === "heading" &&
nodeToString(node).toLowerCase() === headingText.toLowerCase()
) {
capture = true;
continue;
}
// Stop if another heading of the same or higher level comes
if (capture && node.type === "heading") {
break;
}
// Collect list items if capturing
if (capture && node.type === "list") {
for (const li of (node as List).children) {
if (li.type === "listItem") {
items.push(li);
}
}
}
}
return items;
}
function getListItemsBeforeFirstHeading(markdown: string) {
const tree = REMARK.parse(markdown);
let items = [];
for (const node of tree.children) {
if (node.type === "heading") {
// stop at first heading
break;
}
if (node.type === "list") {
for (const li of (node as List).children) {
if (li.type === "listItem") {
items.push(li);
}
}
}
}
return items;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import { base64 } from "rollup-plugin-base64";
import typescript from "@rollup/plugin-typescript";
import rust from "@wasm-tool/rollup-plugin-rust";
const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
*/
`;
export default {
input: "plugin.ts",
output: {
file: "dist/main.js",
sourcemap: "inline",
format: "cjs",
exports: "default",
banner,
},
external: ["obsidian"],
plugins: [
//nodeResolve({ browser: true }),
//commonjs(),
//base64({ include: "**/*.wasm" }),
typescript(),
rust({
inlineWasm: true,
verbose: true,
extraArgs: {
cargo: ["--no-default-features", "--features", "target-obsidian"],
},
}),
],
};

View File

@@ -0,0 +1 @@
cargo rustc --lib --target wasm32-unknown-unknown --crate-type cdylib --release --config 'profile.release.panic="abort"' --config profile.release.lto=true --config profile.release.codegen-units=1 --config profile.release.strip=true -Z panic-immediate-abort -Z build-std=panic_abort,core,std,alloc,proc_macro -Z build-std-features=optimize_for_size --config build.rustflags=["-Z","location-detail=none","-Z","fmt-debug=none"] --no-default-features --features target-obsidian

View File

@@ -23,7 +23,7 @@ serde_json = "1.0.117"
tar = "0.4.44"
umask = "2.1.0"
#mize = { git = "https://github.com/c2vi/mize", default-features = false }
mize = { path = "../mize", default-features = false, features = ["os-target"] }
mize = { path = "../mize", default-features = false, features = ["target-os"] }
# snix testing
#snix-cli = { git = "https://git.snix.dev/snix/snix" }