36 Commits

Author SHA1 Message Date
9b94159f8e register works 2026-01-23 11:23:07 +01:00
aa4d7f73a8 setup zig node 2026-01-23 10:24:21 +01:00
1efb94b09c Add zig to flake packages 2026-01-23 09:07:05 +01:00
Felix Hungenberg
5570d975f5 feat: unmigrate number into universal float, inherit step if unset
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m1s
2026-01-22 23:57:56 +01:00
Felix Hungenberg
8c1ba2ee65 feat: move add context menu within view if outside
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m59s
2026-01-22 23:26:56 +01:00
Felix Hungenberg
3e019e4e21 feat: don't move graph on right click drag 2026-01-22 23:26:26 +01:00
Felix Hungenberg
a58b19e935 Merge branch 'main' of github.com:jim-fx/nodarium
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m57s
2026-01-22 14:06:44 +01:00
Max Richter
6f5c5bb46e feat: change initial camera position so that nodes are visible
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m3s
2026-01-22 12:07:37 +01:00
7f2214f15c fix(utils): make sure we do not build a .wasm file for utils
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m54s
2026-01-21 17:24:54 +01:00
43ef563ae7 feat: show all nodes in add menu
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m57s
2026-01-21 17:08:47 +01:00
Felix Hungenberg
714d01da94 chore: move pnpm links to workspace (auto link) 2026-01-21 16:39:54 +01:00
Felix Hungenberg
92308fc43a chore: run ui debug server from root
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m57s
2026-01-21 16:36:20 +01:00
Felix Hungenberg
5adf67ed52 Merge branch 'main' of git.max-richter.dev:max/nodarium
Some checks failed
Deploy to GitHub Pages / build_site (push) Has been cancelled
2026-01-21 16:35:34 +01:00
Felix Hungenberg
f54cde734e fix: integer width 2026-01-21 16:35:13 +01:00
70d8095869 Merge pull request 'feat: project manager' (#21) from feat/project-manager into main
Some checks failed
Deploy to GitHub Pages / build_site (push) Has been cancelled
Reviewed-on: #21
2026-01-21 16:35:03 +01:00
Felix Hungenberg
2a90d5de3f chore: dev scripts & linting 2026-01-21 16:31:26 +01:00
d7e9e8b8de chore: remove some old console.logs 2026-01-21 16:01:11 +01:00
bdbaab25a4 feat: initial working version of project manager 2026-01-21 15:02:34 +01:00
Felix Hungenberg
c7bfb0f05b fix(ui): integrate number input to exports, ui page, benchmark
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m46s
2026-01-21 11:55:10 +01:00
Felix Hungenberg
d3a46af4c2 chore: update pnpm corepack up
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m47s
2026-01-21 11:24:53 +01:00
4c76c62a3e feat: add header element 2026-01-21 11:09:51 +01:00
36cf9211d2 fix: run pnpm i :)
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m46s
2026-01-20 19:28:35 +01:00
97a2ffb683 feat: use workspace instead of link for app/package.json
Some checks failed
Deploy to GitHub Pages / build_site (push) Failing after 6s
2026-01-20 19:18:17 +01:00
fffa8c7cdf feat: cleanup Dockerfiles and use prepared image for deployments
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m46s
2026-01-20 19:12:56 +01:00
de799c2d55 Merge pull request 'feat/remove-wasm-bindgen' (#19) from feat/remove-wasm-bindgen into main
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m45s
Reviewed-on: #19
2026-01-20 18:57:42 +01:00
24bef0460c Merge branch 'main' into feat/remove-wasm-bindgen 2026-01-20 18:57:34 +01:00
93b64fc7dd feat: add app/Dockerfile
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m4s
2026-01-20 18:50:54 +01:00
64ac28f60c chore: cleanup node buildscripts 2026-01-20 18:26:48 +01:00
bd0c2eaacd Merge remote-tracking branch 'origin/main' into feat/remove-wasm-bindgen 2026-01-20 18:04:56 +01:00
8693c63d16 feat: resize canvases to fit window height
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m3s
Closes #16

The canvases fit their parents size, so adding a wrapper with 100vh
solved it. https://threlte.xyz/docs/reference/core/canvas#size
2026-01-20 17:55:58 +01:00
1ea7d6629f chore: remove ai comments from dockerfile 2026-01-20 14:44:20 +01:00
617dfb0c9d feat: add Dockerfile for app to deploy preview 2026-01-20 14:08:45 +01:00
8e5412c25c Merge remote-tracking branch 'origin/main' into feat/remove-wasm-bindgen 2026-01-19 16:26:06 +01:00
e84c715f4c chore: Add flake and direnv stuff 2026-01-19 12:51:33 +01:00
Max Richter
ecbcc814ed chore: remove store code 2026-01-19 01:29:28 +01:00
Max Richter
be97387252 feat: trying to remove wasm-bindgen 2026-01-19 01:29:12 +01:00
147 changed files with 1575 additions and 4449 deletions

View File

@@ -1,12 +1,14 @@
{ {
"$schema": "https://dprint.dev/schemas/v0.json", "$schema": "https://dprint.dev/schemas/v0.json",
"indentWidth": 2, "indentWidth": 2,
"lineWidth": 100,
"typescript": { "typescript": {
// https://dprint.dev/plugins/typescript/config/ // https://dprint.dev/plugins/typescript/config/
"quoteStyle": "preferSingle", "quoteStyle": "preferSingle",
"trailingCommas": "never" "trailingCommas": "never",
}, },
"json": { "json": {
// https://dprint.dev/plugins/json/config/
}, },
"markdown": { "markdown": {
}, },
@@ -21,6 +23,9 @@
"malva": { "malva": {
}, },
"markup": { "markup": {
// https://dprint.dev/plugins/markup_fmt/config/
"scriptIndent": true,
"styleIndent": true,
}, },
"yaml": { "yaml": {
}, },
@@ -28,19 +33,24 @@
}, },
"exec": { "exec": {
"cwd": "${configDir}", "cwd": "${configDir}",
"commands": [{ "commands": [{
"command": "rustfmt --edition 2024", "command": "rustfmt",
"exts": ["rs"], "exts": ["rs"],
// add the config files for automatic cache invalidation when the rust version or rustfmt config changes
"cacheKeyFiles": [ "cacheKeyFiles": [
"rustfmt.toml", "rustfmt.toml",
"rust-toolchain.toml" "rust-toolchain.toml",
] ],
}] }],
}, },
"excludes": [ "excludes": [
"**/node_modules", "**/node_modules",
"**/*-lock.yaml" "**/build",
"**/.svelte-kit",
"**/package",
"**/*-lock.yaml",
"**/yaml.lock",
"**/.DS_Store",
], ],
"plugins": [ "plugins": [
"https://plugins.dprint.dev/typescript-0.95.13.wasm", "https://plugins.dprint.dev/typescript-0.95.13.wasm",
@@ -54,6 +64,7 @@
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.25.3.wasm", "https://plugins.dprint.dev/g-plane/markup_fmt-v0.25.3.wasm",
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm", "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm",
"https://plugins.dprint.dev/g-plane/pretty_graphql-v0.2.3.wasm", "https://plugins.dprint.dev/g-plane/pretty_graphql-v0.2.3.wasm",
"https://plugins.dprint.dev/exec-0.6.0.json@a054130d458f124f9b5c91484833828950723a5af3f8ff2bd1523bd47b83b364" "https://plugins.dprint.dev/exec-0.6.0.json@a054130d458f124f9b5c91484833828950723a5af3f8ff2bd1523bd47b83b364",
] "https://plugins.dprint.dev/biome-0.11.10.wasm",
],
} }

367
Cargo.lock generated
View File

@@ -1,176 +1,80 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.2.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "box" name = "box"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
] ]
[[package]] [[package]]
name = "branch" name = "branch"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"glam",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
] ]
[[package]] [[package]]
name = "float" name = "float"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
] ]
[[package]] [[package]]
name = "glam" name = "glam"
version = "0.27.0" version = "0.30.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9"
[[package]] [[package]]
name = "gravity" name = "gravity"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"glam", "glam",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"noise", ]
"serde",
"serde-wasm-bindgen", [[package]]
"wasm-bindgen", name = "instance"
"wasm-bindgen-test", version = "0.1.0"
"web-sys", dependencies = [
"glam",
"nodarium_macros",
"nodarium_utils",
] ]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.11" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]] [[package]]
name = "math" name = "math"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
] ]
[[package]] [[package]]
name = "max-plantarium-triangle" name = "memchr"
version = "0.1.0" version = "2.7.6"
dependencies = [ source = "registry+https://github.com/rust-lang/crates.io-index"
"console_error_panic_hook", checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
"nodarium_macros",
"nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
]
[[package]]
name = "max-plantarium-vec3"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"nodarium_macros",
"nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
]
[[package]]
name = "nodarium_instance"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"glam",
"nodarium_macros",
"nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
]
[[package]] [[package]]
name = "nodarium_macros" name = "nodarium_macros"
@@ -180,7 +84,7 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"serde_json", "serde_json",
"syn 1.0.109", "syn",
] ]
[[package]] [[package]]
@@ -195,29 +99,19 @@ dependencies = [
name = "nodarium_utils" name = "nodarium_utils"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"glam", "glam",
"noise", "noise 0.9.0",
"serde", "serde",
"serde_json", "serde_json",
"wasm-bindgen",
"web-sys",
] ]
[[package]] [[package]]
name = "nodes-noise" name = "noise"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"glam",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"noise", "noise 0.9.0",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
] ]
[[package]] [[package]]
@@ -233,48 +127,35 @@ dependencies = [
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.18" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "output" name = "output"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"glam",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"serde",
"serde_json",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.81" version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.36" version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -307,103 +188,76 @@ dependencies = [
name = "random" name = "random"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
] ]
[[package]] [[package]]
name = "rotate" name = "rotate"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"glam", "glam",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"serde", "serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
] ]
[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.198" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-wasm-bindgen"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.198" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.116" version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "memchr",
"serde", "serde",
"serde_core",
"zmij",
] ]
[[package]] [[package]]
name = "stem" name = "stem"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -411,119 +265,30 @@ dependencies = [
] ]
[[package]] [[package]]
name = "syn" name = "triangle"
version = "2.0.60" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [ dependencies = [
"proc-macro2", "nodarium_macros",
"quote", "nodarium_utils",
"unicode-ident",
] ]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]] [[package]]
name = "wasm-bindgen" name = "vec3"
version = "0.2.92" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [ dependencies = [
"cfg-if", "nodarium_macros",
"wasm-bindgen-macro", "nodarium_utils",
"serde",
] ]
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "zmij"
version = "0.2.92" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.60",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "web-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
]

View File

@@ -6,7 +6,10 @@ members = [
"packages/types", "packages/types",
"packages/utils", "packages/utils",
] ]
exclude = ["nodes/max/plantarium/.template"] exclude = [
"nodes/max/plantarium/.template",
"nodes/max/plantarium/zig"
]
[profile.release] [profile.release]
lto = true lto = true

View File

@@ -2,10 +2,6 @@ FROM node:24-alpine
RUN apk add --no-cache --update curl rclone g++ RUN apk add --no-cache --update curl rclone g++
RUN RUSTUP_URL="https://sh.rustup.rs" \
&& curl --silent --show-error --location --fail --retry 3 --proto '=https' --tlsv1.2 --output /tmp/rustup-linux-install.sh $RUSTUP_URL \
&& sh /tmp/rustup-linux-install.sh -y
ENV RUSTUP_HOME=/usr/local/rustup \ ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \ CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH PATH=/usr/local/cargo/bin:$PATH
@@ -15,7 +11,5 @@ RUN curl --silent --show-error --location --fail --retry 3 \
--output /tmp/rustup-init.sh https://sh.rustup.rs \ --output /tmp/rustup-init.sh https://sh.rustup.rs \
&& sh /tmp/rustup-init.sh -y --no-modify-path --profile minimal \ && sh /tmp/rustup-init.sh -y --no-modify-path --profile minimal \
&& rm /tmp/rustup-init.sh \ && rm /tmp/rustup-init.sh \
&& rustup default stable \
&& rustup target add wasm32-unknown-unknown \ && rustup target add wasm32-unknown-unknown \
&& cargo install wasm-pack \
&& npm i -g pnpm && npm i -g pnpm

43
app/Dockerfile Normal file
View File

@@ -0,0 +1,43 @@
FROM jimfx/nodes:latest AS builder
WORKDIR /app
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json Cargo.lock Cargo.toml ./
COPY packages/ ./packages/
COPY nodes/ ./nodes/
COPY app/package.json ./app/
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target \
pnpm install --frozen-lockfile
COPY . .
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target \
pnpm build:nodes && \
pnpm --filter @nodarium/app... build
FROM nginx:alpine AS runner
RUN rm /etc/nginx/conf.d/default.conf
COPY <<EOF /etc/nginx/conf.d/app.conf
server {
listen 80;
server_name _;
root /app;
index index.html;
location / {
try_files \$uri \$uri/ /index.html;
}
}
EOF
COPY --from=builder /app/app/build /app
EXPOSE 80

View File

@@ -10,9 +10,9 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@nodarium/registry": "link:../packages/registry", "@nodarium/registry": "workspace:*",
"@nodarium/ui": "link:../packages/ui", "@nodarium/ui": "workspace:*",
"@nodarium/utils": "link:../packages/utils", "@nodarium/utils": "workspace:*",
"@sveltejs/kit": "^2.50.0", "@sveltejs/kit": "^2.50.0",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"@threlte/core": "8.3.1", "@threlte/core": "8.3.1",
@@ -22,12 +22,13 @@
"idb": "^8.0.3", "idb": "^8.0.3",
"jsondiffpatch": "^0.7.3", "jsondiffpatch": "^0.7.3",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"three": "^0.182.0" "three": "^0.182.0",
"wabt": "^1.0.39"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/tabler": "^1.2.26", "@iconify-json/tabler": "^1.2.26",
"@iconify/tailwind4": "^1.2.1", "@iconify/tailwind4": "^1.2.1",
"@nodarium/types": "link:../packages/types", "@nodarium/types": "workspace:",
"@sveltejs/adapter-static": "^3.0.10", "@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/vite-plugin-svelte": "^6.2.4", "@sveltejs/vite-plugin-svelte": "^6.2.4",
"@tsconfig/svelte": "^5.0.6", "@tsconfig/svelte": "^5.0.6",

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { HTML } from "@threlte/extras"; import type { NodeId, NodeInstance } from '@nodarium/types';
import { onMount } from "svelte"; import { HTML } from '@threlte/extras';
import type { NodeInstance, NodeId } from "@nodarium/types"; import { onMount } from 'svelte';
import { getGraphManager, getGraphState } from "../graph-state.svelte"; import { getGraphManager, getGraphState } from '../graph-state.svelte';
type Props = { type Props = {
onnode: (n: NodeInstance) => void; onnode: (n: NodeInstance) => void;
@@ -14,6 +14,7 @@
const graphState = getGraphState(); const graphState = getGraphState();
let input: HTMLInputElement; let input: HTMLInputElement;
let wrapper: HTMLDivElement;
let value = $state<string>(); let value = $state<string>();
let activeNodeId = $state<NodeId>(); let activeNodeId = $state<NodeId>();
@@ -22,10 +23,10 @@
: graph.getNodeDefinitions(); : graph.getNodeDefinitions();
function filterNodes() { function filterNodes() {
return allNodes.filter((node) => node.id.includes(value ?? "")); return allNodes.filter((node) => node.id.includes(value ?? ''));
} }
const nodes = $derived(value === "" ? allNodes : filterNodes()); const nodes = $derived(value === '' ? allNodes : filterNodes());
$effect(() => { $effect(() => {
if (nodes) { if (nodes) {
if (activeNodeId === undefined) { if (activeNodeId === undefined) {
@@ -39,38 +40,38 @@
} }
}); });
function handleNodeCreation(nodeType: NodeInstance["type"]) { function handleNodeCreation(nodeType: NodeInstance['type']) {
if (!graphState.addMenuPosition) return; if (!graphState.addMenuPosition) return;
onnode?.({ onnode?.({
id: -1, id: -1,
type: nodeType, type: nodeType,
position: [...graphState.addMenuPosition], position: [...graphState.addMenuPosition],
props: {}, props: {},
state: {}, state: {}
}); });
} }
function handleKeyDown(event: KeyboardEvent) { function handleKeyDown(event: KeyboardEvent) {
event.stopImmediatePropagation(); event.stopImmediatePropagation();
if (event.key === "Escape") { if (event.key === 'Escape') {
graphState.addMenuPosition = null; graphState.addMenuPosition = null;
return; return;
} }
if (event.key === "ArrowDown") { if (event.key === 'ArrowDown') {
const index = nodes.findIndex((node) => node.id === activeNodeId); const index = nodes.findIndex((node) => node.id === activeNodeId);
activeNodeId = nodes[(index + 1) % nodes.length].id; activeNodeId = nodes[(index + 1) % nodes.length].id;
return; return;
} }
if (event.key === "ArrowUp") { if (event.key === 'ArrowUp') {
const index = nodes.findIndex((node) => node.id === activeNodeId); const index = nodes.findIndex((node) => node.id === activeNodeId);
activeNodeId = nodes[(index - 1 + nodes.length) % nodes.length].id; activeNodeId = nodes[(index - 1 + nodes.length) % nodes.length].id;
return; return;
} }
if (event.key === "Enter") { if (event.key === 'Enter') {
if (activeNodeId && graphState.addMenuPosition) { if (activeNodeId && graphState.addMenuPosition) {
handleNodeCreation(activeNodeId); handleNodeCreation(activeNodeId);
} }
@@ -81,6 +82,16 @@
onMount(() => { onMount(() => {
input.disabled = false; input.disabled = false;
setTimeout(() => input.focus(), 50); setTimeout(() => input.focus(), 50);
const rect = wrapper.getBoundingClientRect();
const deltaY = rect.bottom - window.innerHeight;
const deltaX = rect.right - window.innerWidth;
if (deltaY > 0) {
wrapper.style.marginTop = `-${deltaY + 30}px`;
}
if (deltaX > 0) {
wrapper.style.marginLeft = `-${deltaX + 30}px`;
}
}); });
</script> </script>
@@ -89,7 +100,7 @@
position.z={graphState.addMenuPosition?.[1]} position.z={graphState.addMenuPosition?.[1]}
transform={false} transform={false}
> >
<div class="add-menu-wrapper"> <div class="add-menu-wrapper" bind:this={wrapper}>
<div class="header"> <div class="header">
<input <input
id="add-menu" id="add-menu"
@@ -112,7 +123,7 @@
tabindex="0" tabindex="0"
aria-selected={node.id === activeNodeId} aria-selected={node.id === activeNodeId}
onkeydown={(event) => { onkeydown={(event) => {
if (event.key === "Enter") { if (event.key === 'Enter') {
handleNodeCreation(node.id); handleNodeCreation(node.id);
} }
}} }}
@@ -125,7 +136,7 @@
activeNodeId = node.id; activeNodeId = node.id;
}} }}
> >
{node.id.split("/").at(-1)} {node.id.split('/').at(-1)}
</div> </div>
{/each} {/each}
</div> </div>
@@ -167,6 +178,8 @@
min-height: none; min-height: none;
width: 100%; width: 100%;
color: var(--text-color); color: var(--text-color);
max-height: 300px;
overflow-y: auto;
} }
.result { .result {

View File

@@ -1,4 +1,5 @@
import type { Box } from '@nodarium/types'; import type { Box } from '@nodarium/types';
import type { Color } from 'three';
import { Vector3 } from 'three/src/math/Vector3.js'; import { Vector3 } from 'three/src/math/Vector3.js';
import Component from './Debug.svelte'; import Component from './Debug.svelte';
import { lines, points, rects } from './store'; import { lines, points, rects } from './store';
@@ -11,7 +12,6 @@ export function debugPosition(x: number, y: number) {
} }
export function debugRect(rect: Box) { export function debugRect(rect: Box) {
console.log(rect);
rects.update((r) => { rects.update((r) => {
r.push(rect); r.push(rect);
return r; return r;

View File

@@ -1,4 +1,5 @@
import throttle from '$lib/helpers/throttle'; import throttle from '$lib/helpers/throttle';
import { RemoteNodeRegistry } from '@nodarium/registry';
import type { import type {
Edge, Edge,
Graph, Graph,
@@ -18,6 +19,8 @@ import { HistoryManager } from './history-manager';
const logger = createLogger('graph-manager'); const logger = createLogger('graph-manager');
logger.mute(); logger.mute();
const remoteRegistry = new RemoteNodeRegistry('');
const clone = 'structuredClone' in self const clone = 'structuredClone' in self
? self.structuredClone ? self.structuredClone
: (args: any) => JSON.parse(JSON.stringify(args)); : (args: any) => JSON.parse(JSON.stringify(args));
@@ -109,6 +112,7 @@ export class GraphManager extends EventEmitter<{
const serialized = { const serialized = {
id: this.graph.id, id: this.graph.id,
settings: $state.snapshot(this.settings), settings: $state.snapshot(this.settings),
meta: $state.snapshot(this.graph.meta),
nodes, nodes,
edges edges
}; };
@@ -172,7 +176,9 @@ export class GraphManager extends EventEmitter<{
return areSocketsCompatible(edgeOutputSocketType, accepted); return areSocketsCompatible(edgeOutputSocketType, accepted);
}); });
const bestOutputIdx = draggedOutputs.findIndex(outputType => areSocketsCompatible(outputType, targetAcceptedTypes)); const bestOutputIdx = draggedOutputs.findIndex(outputType =>
areSocketsCompatible(outputType, targetAcceptedTypes)
);
if (!bestInputEntry || bestOutputIdx === -1) { if (!bestInputEntry || bestOutputIdx === -1) {
logger.error('Could not find compatible sockets for drop'); logger.error('Could not find compatible sockets for drop');
@@ -273,7 +279,7 @@ export class GraphManager extends EventEmitter<{
}) })
); );
const edges = graph.edges.map((edge) => { this.edges = graph.edges.map((edge) => {
const from = nodes.get(edge[0]); const from = nodes.get(edge[0]);
const to = nodes.get(edge[2]); const to = nodes.get(edge[2]);
if (!from || !to) { if (!from || !to) {
@@ -286,8 +292,6 @@ export class GraphManager extends EventEmitter<{
return [from, edge[1], to, edge[3]] as Edge; return [from, edge[1], to, edge[3]] as Edge;
}); });
this.edges = [...edges];
this.nodes.clear(); this.nodes.clear();
for (const [id, node] of nodes) { for (const [id, node] of nodes) {
this.nodes.set(id, node); this.nodes.set(id, node);
@@ -304,11 +308,26 @@ export class GraphManager extends EventEmitter<{
this.status = 'loading'; this.status = 'loading';
this.id = graph.id; this.id = graph.id;
logger.info('loading graph', $state.snapshot(graph)); logger.info('loading graph', { nodes: graph.nodes, edges: graph.edges, id: graph.id });
const nodeIds = Array.from(new Set([...graph.nodes.map((n) => n.type)])); const nodeIds = Array.from(new Set([...graph.nodes.map((n) => n.type)]));
await this.registry.load(nodeIds); await this.registry.load(nodeIds);
// Fetch all nodes from all collections of the loaded nodes
const allCollections = new Set<`${string}/${string}`>();
for (const id of nodeIds) {
const [user, collection] = id.split('/');
allCollections.add(`${user}/${collection}`);
}
for (const collection of allCollections) {
remoteRegistry
.fetchCollection(collection)
.then((collection: { nodes: { id: NodeId }[] }) => {
const ids = collection.nodes.map((n) => n.id);
return this.registry.load(ids);
});
}
logger.info('loaded node types', this.registry.getAllNodes()); logger.info('loaded node types', this.registry.getAllNodes());
for (const node of this.graph.nodes) { for (const node of this.graph.nodes) {
@@ -644,6 +663,13 @@ export class GraphManager extends EventEmitter<{
if (this.currentUndoGroup) return; if (this.currentUndoGroup) return;
const state = this.serialize(); const state = this.serialize();
this.history.save(state); this.history.save(state);
// This is some stupid race condition where the graph-manager emits a save event
// when the graph is not fully loaded
if (this.nodes.size === 0 && this.edges.length === 0) {
return;
}
this.emit('save', state); this.emit('save', state);
logger.log('saving graphs', state); logger.log('saving graphs', state);
} }

View File

@@ -58,11 +58,13 @@ export class GraphState {
wrapper = $state<HTMLDivElement>(null!); wrapper = $state<HTMLDivElement>(null!);
rect: DOMRect = $derived( rect: DOMRect = $derived(
(this.wrapper && this.width && this.height) ? this.wrapper.getBoundingClientRect() : new DOMRect(0, 0, 0, 0) (this.wrapper && this.width && this.height)
? this.wrapper.getBoundingClientRect()
: new DOMRect(0, 0, 0, 0)
); );
camera = $state<OrthographicCamera>(null!); camera = $state<OrthographicCamera>(null!);
cameraPosition: [number, number, number] = $state([0, 0, 100]); cameraPosition: [number, number, number] = $state([140, 100, 3.5]);
clipboard: null | { clipboard: null | {
nodes: NodeInstance[]; nodes: NodeInstance[];
@@ -187,13 +189,13 @@ export class GraphState {
} }
const height = 5 const height = 5
+ 10 + 10
* Object.keys(node.inputs).filter( * Object.keys(node.inputs).filter(
(p) => (p) =>
p !== 'seed' p !== 'seed'
&& node?.inputs && node?.inputs
&& !('setting' in node?.inputs?.[p]) && !('setting' in node?.inputs?.[p])
&& node.inputs[p].hidden !== true && node.inputs[p].hidden !== true
).length; ).length;
this.nodeHeightCache[nodeTypeId] = height; this.nodeHeightCache[nodeTypeId] = height;
return height; return height;
} }
@@ -333,4 +335,8 @@ export class GraphState {
&& node.position[1] < this.cameraBounds[3] && node.position[1] < this.cameraBounds[3]
); );
} }
openNodePalette() {
this.addMenuPosition = [this.mousePosition[0], this.mousePosition[1]];
}
} }

View File

@@ -1,23 +1,23 @@
<script lang="ts"> <script lang="ts">
import type { Edge, NodeInstance } from "@nodarium/types"; import type { Edge, NodeInstance } from '@nodarium/types';
import { createKeyMap } from "../../helpers/createKeyMap"; import { Canvas } from '@threlte/core';
import AddMenu from "../components/AddMenu.svelte"; import { HTML } from '@threlte/extras';
import Background from "../background/Background.svelte"; import { createKeyMap } from '../../helpers/createKeyMap';
import BoxSelection from "../components/BoxSelection.svelte"; import Background from '../background/Background.svelte';
import EdgeEl from "../edges/Edge.svelte"; import AddMenu from '../components/AddMenu.svelte';
import NodeEl from "../node/Node.svelte"; import BoxSelection from '../components/BoxSelection.svelte';
import Camera from "../components/Camera.svelte"; import Camera from '../components/Camera.svelte';
import { Canvas } from "@threlte/core"; import HelpView from '../components/HelpView.svelte';
import HelpView from "../components/HelpView.svelte"; import Debug from '../debug/Debug.svelte';
import { getGraphManager, getGraphState } from "../graph-state.svelte"; import EdgeEl from '../edges/Edge.svelte';
import { HTML } from "@threlte/extras"; import { getGraphManager, getGraphState } from '../graph-state.svelte';
import { maxZoom, minZoom } from "./constants"; import NodeEl from '../node/Node.svelte';
import Debug from "../debug/Debug.svelte"; import { maxZoom, minZoom } from './constants';
import { FileDropEventManager } from "./drop.events"; import { FileDropEventManager } from './drop.events';
import { MouseEventManager } from "./mouse.events"; import { MouseEventManager } from './mouse.events';
const { const {
keymap, keymap
}: { }: {
keymap: ReturnType<typeof createKeyMap>; keymap: ReturnType<typeof createKeyMap>;
} = $props(); } = $props();
@@ -45,19 +45,18 @@
const newNode = graph.createNode({ const newNode = graph.createNode({
type: node.type, type: node.type,
position: node.position, position: node.position,
props: node.props, props: node.props
}); });
if (!newNode) return; if (!newNode) return;
if (graphState.activeSocket) { if (graphState.activeSocket) {
if (typeof graphState.activeSocket.index === "number") { if (typeof graphState.activeSocket.index === 'number') {
const socketType = const socketType = graphState.activeSocket.node.state?.type?.outputs?.[
graphState.activeSocket.node.state?.type?.outputs?.[ graphState.activeSocket.index
graphState.activeSocket.index ];
];
const input = Object.entries(newNode?.state?.type?.inputs || {}).find( const input = Object.entries(newNode?.state?.type?.inputs || {}).find(
(inp) => inp[1].type === socketType, (inp) => inp[1].type === socketType
); );
if (input) { if (input) {
@@ -65,14 +64,13 @@
graphState.activeSocket.node, graphState.activeSocket.node,
graphState.activeSocket.index, graphState.activeSocket.index,
newNode, newNode,
input[0], input[0]
); );
} }
} else { } else {
const socketType = const socketType = graphState.activeSocket.node.state?.type?.inputs?.[
graphState.activeSocket.node.state?.type?.inputs?.[ graphState.activeSocket.index
graphState.activeSocket.index ];
];
const output = newNode.state?.type?.outputs?.find((out) => { const output = newNode.state?.type?.outputs?.find((out) => {
if (socketType?.type === out) return true; if (socketType?.type === out) return true;
@@ -85,7 +83,7 @@
newNode, newNode,
output.indexOf(output), output.indexOf(output),
graphState.activeSocket.node, graphState.activeSocket.node,
graphState.activeSocket.index, graphState.activeSocket.index
); );
} }
} }
@@ -97,14 +95,15 @@
</script> </script>
<svelte:window <svelte:window
onmousemove={(ev) => mouseEvents.handleMouseMove(ev)} onmousemove={(ev) => mouseEvents.handleWindowMouseMove(ev)}
onmouseup={(ev) => mouseEvents.handleMouseUp(ev)} onmouseup={(ev) => mouseEvents.handleWindowMouseUp(ev)}
/> />
<div <div
onwheel={(ev) => mouseEvents.handleMouseScroll(ev)} onwheel={(ev) => mouseEvents.handleMouseScroll(ev)}
bind:this={graphState.wrapper} bind:this={graphState.wrapper}
class="graph-wrapper" class="graph-wrapper"
style="height: 100%"
class:is-panning={graphState.isPanning} class:is-panning={graphState.isPanning}
class:is-hovering={graphState.hoveredNodeId !== -1} class:is-hovering={graphState.hoveredNodeId !== -1}
aria-label="Graph" aria-label="Graph"
@@ -114,6 +113,7 @@
bind:clientHeight={graphState.height} bind:clientHeight={graphState.height}
onkeydown={(ev) => keymap.handleKeyboardEvent(ev)} onkeydown={(ev) => keymap.handleKeyboardEvent(ev)}
onmousedown={(ev) => mouseEvents.handleMouseDown(ev)} onmousedown={(ev) => mouseEvents.handleMouseDown(ev)}
oncontextmenu={(ev) => mouseEvents.handleContextMenu(ev)}
{...fileDropEvents.getEventListenerProps()} {...fileDropEvents.getEventListenerProps()}
> >
<input <input
@@ -146,20 +146,18 @@
<BoxSelection <BoxSelection
cameraPosition={graphState.cameraPosition} cameraPosition={graphState.cameraPosition}
p1={{ p1={{
x: x: graphState.cameraPosition[0]
graphState.cameraPosition[0] + + (graphState.mouseDown[0] - graphState.width / 2)
(graphState.mouseDown[0] - graphState.width / 2) / / graphState.cameraPosition[2],
graphState.cameraPosition[2], y: graphState.cameraPosition[1]
y: + (graphState.mouseDown[1] - graphState.height / 2)
graphState.cameraPosition[1] + / graphState.cameraPosition[2]
(graphState.mouseDown[1] - graphState.height / 2) /
graphState.cameraPosition[2],
}} }}
p2={{ x: graphState.mousePosition[0], y: graphState.mousePosition[1] }} p2={{ x: graphState.mousePosition[0], y: graphState.mousePosition[1] }}
/> />
{/if} {/if}
{#if graph.status === "idle"} {#if graph.status === 'idle'}
{#if graphState.addMenuPosition} {#if graphState.addMenuPosition}
<AddMenu onnode={handleNodeCreation} /> <AddMenu onnode={handleNodeCreation} />
{/if} {/if}
@@ -206,9 +204,9 @@
{/each} {/each}
</div> </div>
</HTML> </HTML>
{:else if graph.status === "loading"} {:else if graph.status === 'loading'}
<span>Loading</span> <span>Loading</span>
{:else if graph.status === "error"} {:else if graph.status === 'error'}
<span>Error</span> <span>Error</span>
{/if} {/if}
</Canvas> </Canvas>

View File

@@ -11,7 +11,7 @@
import { setupKeymaps } from "../keymaps"; import { setupKeymaps } from "../keymaps";
type Props = { type Props = {
graph: Graph; graph?: Graph;
registry: NodeRegistry; registry: NodeRegistry;
settings?: Record<string, any>; settings?: Record<string, any>;
@@ -70,12 +70,6 @@
} }
}); });
$effect(() => {
if (settingTypes && settings) {
manager.setSettings(settings);
}
});
manager.on("settings", (_settings) => { manager.on("settings", (_settings) => {
settingTypes = { ...settingTypes, ..._settings.types }; settingTypes = { ...settingTypes, ..._settings.types };
settings = _settings.values; settings = _settings.values;
@@ -85,7 +79,11 @@
manager.on("save", (save) => onsave?.(save)); manager.on("save", (save) => onsave?.(save));
manager.load(graph); $effect(() => {
if (graph) {
manager.load(graph);
}
});
</script> </script>
<GraphEl {keymap} /> <GraphEl {keymap} />

View File

@@ -1,7 +1,7 @@
import { animate, lerp } from '$lib/helpers'; import { animate, lerp } from '$lib/helpers';
import { type NodeInstance } from '@nodarium/types'; import { type NodeInstance } from '@nodarium/types';
import type { GraphManager } from '../graph-manager.svelte'; import type { GraphManager } from '../graph-manager.svelte';
import type { GraphState } from '../graph-state.svelte'; import { type GraphState } from '../graph-state.svelte';
import { snapToGrid as snapPointToGrid } from '../helpers'; import { snapToGrid as snapPointToGrid } from '../helpers';
import { maxZoom, minZoom, zoomSpeed } from './constants'; import { maxZoom, minZoom, zoomSpeed } from './constants';
import { EdgeInteractionManager } from './edge.events'; import { EdgeInteractionManager } from './edge.events';
@@ -16,7 +16,7 @@ export class MouseEventManager {
this.edgeInteractionManager = new EdgeInteractionManager(graph, state); this.edgeInteractionManager = new EdgeInteractionManager(graph, state);
} }
handleMouseUp(event: MouseEvent) { handleWindowMouseUp(event: MouseEvent) {
this.edgeInteractionManager.handleMouseUp(); this.edgeInteractionManager.handleMouseUp();
this.state.isPanning = false; this.state.isPanning = false;
if (!this.state.mouseDown) return; if (!this.state.mouseDown) return;
@@ -151,7 +151,19 @@ export class MouseEventManager {
this.state.addMenuPosition = null; this.state.addMenuPosition = null;
} }
handleContextMenu(event: MouseEvent) {
if (!this.state.addMenuPosition) {
event.preventDefault();
this.state.openNodePalette();
}
}
handleMouseDown(event: MouseEvent) { handleMouseDown(event: MouseEvent) {
// Right click
if (event.button === 2) {
return;
}
if (this.state.mouseDown) return; if (this.state.mouseDown) return;
this.state.edgeEndPosition = null; this.state.edgeEndPosition = null;
@@ -229,7 +241,7 @@ export class MouseEventManager {
this.state.edgeEndPosition = null; this.state.edgeEndPosition = null;
} }
handleMouseMove(event: MouseEvent) { handleWindowMouseMove(event: MouseEvent) {
let mx = event.clientX - this.state.rect.x; let mx = event.clientX - this.state.rect.x;
let my = event.clientY - this.state.rect.y; let my = event.clientY - this.state.rect.y;
@@ -245,7 +257,7 @@ export class MouseEventManager {
for (const socket of this.state.possibleSockets) { for (const socket of this.state.possibleSockets) {
const dist = Math.sqrt( const dist = Math.sqrt(
(socket.position[0] - this.state.mousePosition[0]) ** 2 (socket.position[0] - this.state.mousePosition[0]) ** 2
+ (socket.position[1] - this.state.mousePosition[1]) ** 2 + (socket.position[1] - this.state.mousePosition[1]) ** 2
); );
if (dist < smallestDist) { if (dist < smallestDist) {
smallestDist = dist; smallestDist = dist;
@@ -377,9 +389,9 @@ export class MouseEventManager {
// Update camera position and zoom level // Update camera position and zoom level
this.state.cameraPosition[0] = this.state.mousePosition[0] this.state.cameraPosition[0] = this.state.mousePosition[0]
- (this.state.mousePosition[0] - this.state.cameraPosition[0]) - (this.state.mousePosition[0] - this.state.cameraPosition[0])
/ zoomRatio; / zoomRatio;
this.state.cameraPosition[1] = this.state.mousePosition[1] this.state.cameraPosition[1] = this.state.mousePosition[1]
- (this.state.mousePosition[1] - this.state.cameraPosition[1]) - (this.state.mousePosition[1] - this.state.cameraPosition[1])
/ zoomRatio, this.state.cameraPosition[2] = newZoom; / zoomRatio, this.state.cameraPosition[2] = newZoom;
} }
} }

View File

@@ -59,9 +59,7 @@ export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: Gr
key: 'A', key: 'A',
shift: true, shift: true,
description: 'Add new Node', description: 'Add new Node',
callback: () => { callback: () => graphState.openNodePalette()
graphState.addMenuPosition = [graphState.mousePosition[0], graphState.mousePosition[1]];
}
}); });
keymap.addShortcut({ keymap.addShortcut({

View File

@@ -1,9 +1,9 @@
import { createWasmWrapper } from "@nodarium/utils"; import { createWasmWrapper } from '@nodarium/utils';
import fs from "fs/promises"; import fs from 'fs/promises';
import path from "path"; import path from 'path';
export async function getWasm(id: `${string}/${string}/${string}`) { export async function getWasm(id: `${string}/${string}/${string}`) {
const filePath = path.resolve(`../nodes/${id}/pkg/index_bg.wasm`); const filePath = path.resolve(`./static/nodes/${id}`);
try { try {
await fs.access(filePath); await fs.access(filePath);
@@ -36,12 +36,12 @@ export async function getNode(id: `${string}/${string}/${string}`) {
} }
export async function getCollectionNodes(userId: `${string}/${string}`) { export async function getCollectionNodes(userId: `${string}/${string}`) {
const nodes = await fs.readdir(path.resolve(`../nodes/${userId}`)); const nodes = await fs.readdir(path.resolve(`./static/nodes/${userId}`));
return nodes return nodes
.filter((n) => n !== "pkg" && n !== ".template") .filter((n) => n !== 'pkg' && n !== '.template')
.map((n) => { .map((n) => {
return { return {
id: `${userId}/${n}`, id: `${userId}/${n}`
}; };
}); });
} }
@@ -50,20 +50,20 @@ export async function getCollection(userId: `${string}/${string}`) {
const nodes = await getCollectionNodes(userId); const nodes = await getCollectionNodes(userId);
return { return {
id: userId, id: userId,
nodes, nodes
}; };
} }
export async function getUserCollections(userId: string) { export async function getUserCollections(userId: string) {
const collections = await fs.readdir(path.resolve(`../nodes/${userId}`)); const collections = await fs.readdir(path.resolve(`./static/nodes/${userId}`));
return Promise.all( return Promise.all(
collections.map(async (n) => { collections.map(async (n) => {
const nodes = await getCollectionNodes(`${userId}/${n}`); const nodes = await getCollectionNodes(`${userId}/${n}`);
return { return {
id: `${userId}/${n}`, id: `${userId}/${n}`,
nodes, nodes
}; };
}), })
); );
} }
@@ -71,20 +71,20 @@ export async function getUser(userId: string) {
const collections = await getUserCollections(userId); const collections = await getUserCollections(userId);
return { return {
id: userId, id: userId,
collections, collections
}; };
} }
export async function getUsers() { export async function getUsers() {
const nodes = await fs.readdir(path.resolve("../nodes")); const nodes = await fs.readdir(path.resolve('./static/nodes'));
const users = await Promise.all( const users = await Promise.all(
nodes.map(async (n) => { nodes.map(async (n) => {
const collections = await getUserCollections(n); const collections = await getUserCollections(n);
return { return {
id: n, id: n,
collections, collections
}; };
}), })
); );
return users; return users;
} }

View File

@@ -0,0 +1,108 @@
<script lang="ts">
import type { Graph } from "$lib/types";
import { defaultPlant, plant, lottaFaces } from "$lib/graph-templates";
import type { ProjectManager } from "./project-manager.svelte";
const { projectManager } = $props<{ projectManager: ProjectManager }>();
let showNewProject = $state(false);
let newProjectName = $state("");
let selectedTemplate = $state("defaultPlant");
const templates = [
{
name: "Default Plant",
value: "defaultPlant",
graph: defaultPlant as unknown as Graph,
},
{ name: "Plant", value: "plant", graph: plant as unknown as Graph },
{
name: "Lotta Faces",
value: "lottaFaces",
graph: lottaFaces as unknown as Graph,
},
];
function handleCreate() {
const template =
templates.find((t) => t.value === selectedTemplate) || templates[0];
projectManager.handleCreateProject(template.graph, newProjectName);
newProjectName = "";
showNewProject = false;
}
</script>
<header
class="flex justify-between px-4 h-[70px] border-b-1 border-[var(--outline)] items-center"
>
<h3>Project</h3>
<button
class="px-3 py-1 bg-[var(--layer-0)] rounded"
onclick={() => (showNewProject = !showNewProject)}
>
New
</button>
</header>
{#if showNewProject}
<div class="flex flex-col px-4 py-3 border-b-1 border-[var(--outline)] gap-2">
<input
type="text"
bind:value={newProjectName}
placeholder="Project name"
class="w-full px-2 py-2 bg-gray-800 border border-gray-700 rounded"
onkeydown={(e) => e.key === "Enter" && handleCreate()}
/>
<select
bind:value={selectedTemplate}
class="w-full px-2 py-2 bg-gray-800 border border-gray-700 rounded"
>
{#each templates as template}
<option value={template.value}>{template.name}</option>
{/each}
</select>
<button
class="cursor-pointer self-end px-3 py-1 bg-blue-600 rounded"
onclick={() => handleCreate()}
>
Create
</button>
</div>
{/if}
<div class="p-4 text-white min-h-screen">
{#if projectManager.loading}
<p>Loading...</p>
{/if}
<ul class="space-y-2">
{#each projectManager.projects as project (project.id)}
<li>
<div
class="w-full text-left px-3 py-2 rounded cursor-pointer {projectManager
.activeProjectId.value === project.id
? 'bg-blue-600'
: 'bg-gray-800 hover:bg-gray-700'}"
onclick={() => projectManager.handleSelectProject(project.id!)}
role="button"
tabindex="0"
onkeydown={(e) =>
e.key === "Enter" &&
projectManager.handleSelectProject(project.id!)}
>
<div class="flex justify-between items-center">
<span>{project.meta?.title || "Untitled"}</span>
<button
class="text-red-400 hover:text-red-300"
onclick={() => {
projectManager.handleDeleteProject(project.id!);
}}
>
×
</button>
</div>
</div>
</li>
{/each}
</ul>
</div>

View File

@@ -0,0 +1,52 @@
import type { Graph } from '@nodarium/types';
import { type IDBPDatabase, openDB } from 'idb';
export interface GraphDatabase {
projects: Graph;
}
const DB_NAME = 'nodarium-graphs';
const DB_VERSION = 1;
const STORE_NAME = 'graphs';
let dbPromise: Promise<IDBPDatabase<GraphDatabase>> | null = null;
export function getDB() {
if (!dbPromise) {
dbPromise = openDB<GraphDatabase>(DB_NAME, DB_VERSION, {
upgrade(db) {
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
}
}
});
}
return dbPromise;
}
export async function getGraph(id: number): Promise<Graph | undefined> {
const db = await getDB();
return db.get(STORE_NAME, id);
}
export async function saveGraph(graph: Graph): Promise<Graph> {
const db = await getDB();
graph.meta = { ...graph.meta, lastModified: new Date().toISOString() };
await db.put(STORE_NAME, graph);
return graph;
}
export async function deleteGraph(id: number): Promise<void> {
const db = await getDB();
await db.delete(STORE_NAME, id);
}
export async function getGraphs(): Promise<Graph[]> {
const db = await getDB();
return db.getAll(STORE_NAME);
}
export async function clear(): Promise<void> {
const db = await getDB();
return db.clear(STORE_NAME);
}

View File

@@ -0,0 +1,85 @@
import * as templates from '$lib/graph-templates';
import { localState } from '$lib/helpers/localState.svelte';
import type { Graph } from '@nodarium/types';
import * as db from './project-database.svelte';
export class ProjectManager {
public graph = $state<Graph>();
private projects = $state<Graph[]>([]);
private activeProjectId = localState<number | undefined>(
'node.activeProjectId',
undefined
);
public readonly loading = $derived(this.graph?.id !== this.activeProjectId.value);
constructor() {
this.init();
}
async saveGraph(g: Graph) {
db.saveGraph(g);
}
private async init() {
await db.getDB();
this.projects = await db.getGraphs();
if (this.activeProjectId.value !== undefined) {
let loadedGraph = await db.getGraph(this.activeProjectId.value);
if (loadedGraph) {
this.graph = loadedGraph;
}
}
if (!this.graph) {
if (this.projects?.length && this.projects[0]?.id !== undefined) {
this.graph = this.projects[0];
this.activeProjectId.value = this.graph.id;
}
}
if (!this.graph) {
this.handleCreateProject();
}
}
public handleCreateProject(
g: Graph = templates.defaultPlant as unknown as Graph,
title: string = 'New Project'
) {
let id = g?.id || 0;
while (this.projects.find((p) => p.id === id)) {
id++;
}
g.id = id;
if (!g.meta) g.meta = {};
if (!g.meta.title) g.meta.title = title;
db.saveGraph(g);
this.projects = [...this.projects, g];
this.handleSelectProject(id);
}
public async handleDeleteProject(projectId: number) {
await db.deleteGraph(projectId);
if (this.projects.length === 1) {
this.graph = undefined;
this.projects = [];
} else {
this.projects = this.projects.filter((p) => p.id !== projectId);
const id = this.projects[0].id;
if (id !== undefined) {
this.handleSelectProject(id);
}
}
}
public async handleSelectProject(id: number) {
if (this.activeProjectId.value !== id) {
const project = await db.getGraph(id);
this.graph = project;
this.activeProjectId.value = id;
}
}
}

View File

@@ -92,13 +92,6 @@
geo.attributes.position.array[i + 2], geo.attributes.position.array[i + 2],
] as Vector3Tuple; ] as Vector3Tuple;
} }
// $effect(() => {
// console.log({
// geometries: $state.snapshot(geometries),
// indices: appSettings.value.debug.showIndices,
// });
// });
</script> </script>
<Camera {center} {centerCamera} /> <Camera {center} {centerCamera} />

View File

@@ -95,12 +95,14 @@
<SmallPerformanceViewer {fps} store={perf} /> <SmallPerformanceViewer {fps} store={perf} />
{/if} {/if}
<Canvas> <div style="height: 100%">
<Scene <Canvas>
bind:this={sceneComponent} <Scene
{lines} bind:this={sceneComponent}
{centerCamera} {lines}
bind:scene {centerCamera}
bind:fps bind:scene
/> bind:fps
</Canvas> />
</Canvas>
</div>

View File

@@ -244,13 +244,14 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
} }
this.perf?.addPoint("cache-hit", 0); this.perf?.addPoint("cache-hit", 0);
log.group(`executing ${node_type.id || node.id}`); log.group(`executing ${node_type.id}-${node.id}`);
log.log(`Inputs:`, inputs); log.log(`Inputs:`, inputs);
a = performance.now(); a = performance.now();
results[node.id] = node_type.execute(encoded_inputs); results[node.id] = node_type.execute(encoded_inputs);
log.log("Executed", node.type, node.id)
b = performance.now(); b = performance.now();
if (this.cache) { if (this.cache && node.id !== outputNode.id) {
this.cache.set(inputHash, results[node.id]); this.cache.set(inputHash, results[node.id]);
} }

View File

@@ -43,7 +43,4 @@
align-items: center; align-items: center;
padding-left: 1em; padding-left: 1em;
} }
h3 {
margin: 0px;
}
</style> </style>

View File

@@ -1,147 +1,148 @@
<script lang="ts" module> <script lang="ts" module>
let result: let result:
| { stdev: number; avg: number; duration: number; samples: number[] } | { stdev: number; avg: number; duration: number; samples: number[] }
| undefined = $state(); | undefined = $state();
</script> </script>
<script lang="ts"> <script lang="ts">
import { Integer } from "@nodarium/ui"; import { humanizeDuration } from '$lib/helpers';
import { writable } from "svelte/store"; import { localState } from '$lib/helpers/localState.svelte';
import { humanizeDuration } from "$lib/helpers"; import Monitor from '$lib/performance/Monitor.svelte';
import Monitor from "$lib/performance/Monitor.svelte"; import { Number } from '@nodarium/ui';
import { localState } from "$lib/helpers/localState.svelte"; import { writable } from 'svelte/store';
function calculateStandardDeviation(array: number[]) { function calculateStandardDeviation(array: number[]) {
const n = array.length; const n = array.length;
const mean = array.reduce((a, b) => a + b) / n; const mean = array.reduce((a, b) => a + b) / n;
return Math.sqrt( return Math.sqrt(
array.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n, array.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n
); );
} }
type Props = { type Props = {
run: () => Promise<any>; run: () => Promise<any>;
}; };
const { run }: Props = $props(); const { run }: Props = $props();
let isRunning = $state(false); let isRunning = $state(false);
let amount = localState<number>("nodes.benchmark.samples", 500); let amount = localState<number>('nodes.benchmark.samples', 500);
let samples = $state(0); let samples = $state(0);
let warmUp = writable(0); let warmUp = writable(0);
let warmUpAmount = 10; let warmUpAmount = 10;
let status = ""; let status = '';
const copyContent = async (text?: string | number) => { const copyContent = async (text?: string | number) => {
if (!text) return; if (!text) return;
if (typeof text !== "string") { if (typeof text !== 'string') {
text = (Math.floor(text * 100) / 100).toString(); text = (Math.floor(text * 100) / 100).toString();
} }
try { try {
await navigator.clipboard.writeText(text); await navigator.clipboard.writeText(text);
} catch (err) { } catch (err) {
console.error("Failed to copy: ", err); console.error('Failed to copy: ', err);
} }
}; };
async function benchmark() { async function benchmark() {
if (isRunning) return; if (isRunning) return;
isRunning = true; isRunning = true;
result = undefined; result = undefined;
samples = 0; samples = 0;
$warmUp = 0; $warmUp = 0;
await new Promise((r) => setTimeout(r, 50)); await new Promise((r) => setTimeout(r, 50));
// warm up // warm up
for (let i = 0; i < warmUpAmount; i++) { for (let i = 0; i < warmUpAmount; i++) {
await run(); await run();
$warmUp = i + 1; $warmUp = i + 1;
} }
let a = performance.now(); let a = performance.now();
let results = []; let results = [];
// perform run // perform run
for (let i = 0; i < amount.value; i++) { for (let i = 0; i < amount.value; i++) {
const a = performance.now(); const a = performance.now();
await run(); await run();
samples = i; samples = i;
const b = performance.now(); const b = performance.now();
await new Promise((r) => setTimeout(r, 20)); await new Promise((r) => setTimeout(r, 20));
results.push(b - a); results.push(b - a);
} }
result = { result = {
stdev: calculateStandardDeviation(results), stdev: calculateStandardDeviation(results),
samples: results, samples: results,
duration: performance.now() - a, duration: performance.now() - a,
avg: results.reduce((a, b) => a + b) / results.length, avg: results.reduce((a, b) => a + b) / results.length
}; };
} }
</script> </script>
{status} {status}
<div class="wrapper" class:running={isRunning}> <div class="wrapper" class:running={isRunning}>
{#if result} {#if result}
<h3>Finished ({humanizeDuration(result.duration)})</h3> <h3>Finished ({humanizeDuration(result.duration)})</h3>
<div class="monitor-wrapper"> <div class="monitor-wrapper">
<Monitor points={result.samples} /> <Monitor points={result.samples} />
</div> </div>
<label for="bench-avg">Average </label> <label for="bench-avg">Average </label>
<button <button
id="bench-avg" id="bench-avg"
onkeydown={(ev) => ev.key === "Enter" && copyContent(result?.avg)} onkeydown={(ev) => ev.key === 'Enter' && copyContent(result?.avg)}
onclick={() => copyContent(result?.avg)} onclick={() => copyContent(result?.avg)}
>{Math.floor(result.avg * 100) / 100}</button >
> {Math.floor(result.avg * 100) / 100}
<i </button>
role="button" <i
tabindex="0" role="button"
onkeydown={(ev) => ev.key === "Enter" && copyContent(result?.avg)} tabindex="0"
onclick={() => copyContent(result?.avg)}>(click to copy)</i onkeydown={(ev) => ev.key === 'Enter' && copyContent(result?.avg)}
> onclick={() => copyContent(result?.avg)}
<label for="bench-stdev">Standard Deviation σ</label> >(click to copy)</i>
<button id="bench-stdev" onclick={() => copyContent(result?.stdev)} <label for="bench-stdev">Standard Deviation σ</label>
>{Math.floor(result.stdev * 100) / 100}</button <button id="bench-stdev" onclick={() => copyContent(result?.stdev)}>
> {Math.floor(result.stdev * 100) / 100}
<i </button>
role="button" <i
tabindex="0" role="button"
onkeydown={(ev) => ev.key === "Enter" && copyContent(result?.avg)} tabindex="0"
onclick={() => copyContent(result?.stdev + "")}>(click to copy)</i onkeydown={(ev) => ev.key === 'Enter' && copyContent(result?.avg)}
> onclick={() => copyContent(result?.stdev + '')}
<div> >(click to copy)</i>
<button onclick={() => (isRunning = false)}>reset</button> <div>
</div> <button onclick={() => (isRunning = false)}>reset</button>
{:else if isRunning} </div>
<p>WarmUp ({$warmUp}/{warmUpAmount})</p> {:else if isRunning}
<progress value={$warmUp} max={warmUpAmount} <p>WarmUp ({$warmUp}/{warmUpAmount})</p>
>{Math.floor(($warmUp / warmUpAmount) * 100)}%</progress <progress value={$warmUp} max={warmUpAmount}>
> {Math.floor(($warmUp / warmUpAmount) * 100)}%
<p>Progress ({samples}/{amount.value})</p> </progress>
<progress value={samples} max={amount.value} <p>Progress ({samples}/{amount.value})</p>
>{Math.floor((samples / amount.value) * 100)}%</progress <progress value={samples} max={amount.value}>
> {Math.floor((samples / amount.value) * 100)}%
{:else} </progress>
<label for="bench-samples">Samples</label> {:else}
<Integer id="bench-sample" bind:value={amount.value} max={1000} /> <label for="bench-samples">Samples</label>
<button onclick={benchmark} disabled={isRunning}> start </button> <Number id="bench-sample" bind:value={amount.value} max={1000} />
{/if} <button onclick={benchmark} disabled={isRunning}>start</button>
{/if}
</div> </div>
<style> <style>
.wrapper { .wrapper {
padding: 1em; padding: 1em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1em; gap: 1em;
} }
.monitor-wrapper { .monitor-wrapper {
border: solid thin var(--outline); border: solid thin var(--outline);
border-bottom: none; border-bottom: none;
} }
i { i {
opacity: 0.5; opacity: 0.5;
font-size: 0.8em; font-size: 0.8em;
} }
</style> </style>

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { Graph } from "$lib/types"; import type { Graph } from "$lib/types";
const { graph }: { graph: Graph } = $props(); const { graph }: { graph?: Graph } = $props();
function convert(g: Graph): string { function convert(g: Graph): string {
return JSON.stringify( return JSON.stringify(
@@ -16,5 +16,5 @@
</script> </script>
<pre> <pre>
{convert(graph)} {graph ? convert(graph) : 'No graph loaded'}
</pre> </pre>

View File

@@ -28,6 +28,8 @@
import BenchmarkPanel from "$lib/sidebar/panels/BenchmarkPanel.svelte"; import BenchmarkPanel from "$lib/sidebar/panels/BenchmarkPanel.svelte";
import { debounceAsyncFunction } from "$lib/helpers"; import { debounceAsyncFunction } from "$lib/helpers";
import GraphSource from "$lib/sidebar/panels/GraphSource.svelte"; import GraphSource from "$lib/sidebar/panels/GraphSource.svelte";
import { ProjectManager } from "$lib/project-manager/project-manager.svelte";
import ProjectManagerEl from "$lib/project-manager/ProjectManager.svelte";
let performanceStore = createPerformanceStore(); let performanceStore = createPerformanceStore();
@@ -37,6 +39,7 @@
const runtimeCache = new MemoryRuntimeCache(); const runtimeCache = new MemoryRuntimeCache();
const memoryRuntime = new MemoryRuntimeExecutor(nodeRegistry, runtimeCache); const memoryRuntime = new MemoryRuntimeExecutor(nodeRegistry, runtimeCache);
memoryRuntime.perf = performanceStore; memoryRuntime.perf = performanceStore;
const pm = new ProjectManager();
const runtime = $derived( const runtime = $derived(
appSettings.value.debug.useWorker ? workerRuntime : memoryRuntime, appSettings.value.debug.useWorker ? workerRuntime : memoryRuntime,
@@ -64,15 +67,6 @@
let activeNode = $state<NodeInstance | undefined>(undefined); let activeNode = $state<NodeInstance | undefined>(undefined);
let scene = $state<Group>(null!); let scene = $state<Group>(null!);
let graph = $state(
localStorage.getItem("graph")
? JSON.parse(localStorage.getItem("graph")!)
: templates.defaultPlant,
);
function handleSave(graph: Graph) {
localStorage.setItem("graph", JSON.stringify(graph));
}
let graphInterface = $state<ReturnType<typeof GraphInterface>>(null!); let graphInterface = $state<ReturnType<typeof GraphInterface>>(null!);
let viewerComponent = $state<ReturnType<typeof Viewer>>(); let viewerComponent = $state<ReturnType<typeof Viewer>>();
const manager = $derived(graphInterface?.manager); const manager = $derived(graphInterface?.manager);
@@ -91,21 +85,16 @@
callback: () => randomGenerate(), callback: () => randomGenerate(),
}, },
]); ]);
let graphSettings = $state<Record<string, any>>({}); let graphSettings = $state<Record<string, any>>({});
let graphSettingTypes = $state({
randomSeed: { type: "boolean", value: false },
});
$effect(() => { $effect(() => {
if (graphSettings) { if (graphSettings && graphSettingTypes) {
manager?.setSettings($state.snapshot(graphSettings)); manager?.setSettings($state.snapshot(graphSettings));
} }
}); });
type BooleanSchema = {
[key: string]: {
type: "boolean";
value: false;
};
};
let graphSettingTypes = $state<BooleanSchema>({
randomSeed: { type: "boolean", value: false },
});
async function update( async function update(
g: Graph, g: Graph,
@@ -183,19 +172,21 @@
/> />
</Grid.Cell> </Grid.Cell>
<Grid.Cell> <Grid.Cell>
<GraphInterface {#if pm.graph}
{graph} <GraphInterface
bind:this={graphInterface} graph={pm.graph}
registry={nodeRegistry} bind:this={graphInterface}
showGrid={appSettings.value.nodeInterface.showNodeGrid} registry={nodeRegistry}
snapToGrid={appSettings.value.nodeInterface.snapToGrid} showGrid={appSettings.value.nodeInterface.showNodeGrid}
bind:activeNode snapToGrid={appSettings.value.nodeInterface.snapToGrid}
bind:showHelp={appSettings.value.nodeInterface.showHelp} bind:activeNode
bind:settings={graphSettings} bind:showHelp={appSettings.value.nodeInterface.showHelp}
bind:settingTypes={graphSettingTypes} bind:settings={graphSettings}
onresult={(result) => handleUpdate(result)} bind:settingTypes={graphSettingTypes}
onsave={(graph) => handleSave(graph)} onsave={(g) => pm.saveGraph(g)}
/> onresult={(result) => handleUpdate(result)}
/>
{/if}
<Sidebar> <Sidebar>
<Panel id="general" title="General" icon="i-[tabler--settings]"> <Panel id="general" title="General" icon="i-[tabler--settings]">
<NestedSettings <NestedSettings
@@ -236,13 +227,16 @@
<PerformanceViewer data={$performanceStore} /> <PerformanceViewer data={$performanceStore} />
{/if} {/if}
</Panel> </Panel>
<Panel id="projects" icon="i-[tabler--folder-open]">
<ProjectManagerEl projectManager={pm} />
</Panel>
<Panel <Panel
id="graph-source" id="graph-source"
title="Graph Source" title="Graph Source"
hidden={!appSettings.value.debug.showGraphJson} hidden={!appSettings.value.debug.showGraphJson}
icon="i-[tabler--code]" icon="i-[tabler--code]"
> >
<GraphSource graph={graph && manager.serialize()} /> <GraphSource graph={pm.graph ?? manager?.serialize()} />
</Panel> </Panel>
<Panel <Panel
id="benchmark" id="benchmark"

View File

@@ -0,0 +1,8 @@
<script lang="ts">
import type { Snippet } from "svelte";
const { children } = $props<{ children?: Snippet }>();
</script>
<main class="w-screen overflow-x-hidden">
{@render children()}
</main>

View File

@@ -1,29 +1,119 @@
<script lang="ts"> <script lang="ts">
import Grid from "$lib/grid"; import NodeHTML from "$lib/graph-interface/node/NodeHTML.svelte";
import { localState } from "$lib/helpers/localState.svelte";
import Panel from "$lib/sidebar/Panel.svelte"; import Panel from "$lib/sidebar/Panel.svelte";
import Sidebar from "$lib/sidebar/Sidebar.svelte"; import Sidebar from "$lib/sidebar/Sidebar.svelte";
import { IndexDBCache, RemoteNodeRegistry } from "@nodarium/registry";
import { type NodeId, type NodeInstance } from "@nodarium/types";
import Code from "./Code.svelte";
import Grid from "$lib/grid";
import {
concatEncodedArrays,
createWasmWrapper,
encodeNestedArray,
} from "@nodarium/utils";
const registryCache = new IndexDBCache("node-registry");
const nodeRegistry = new RemoteNodeRegistry("", registryCache);
let activeNode = localState<NodeId | undefined>(
"node.dev.activeNode",
undefined,
);
let nodeWasm = $state<ArrayBuffer>();
let nodeInstance = $state<NodeInstance>();
let nodeWasmWrapper = $state<ReturnType<typeof createWasmWrapper>>();
async function fetchNodeData(nodeId?: NodeId) {
nodeWasm = undefined;
nodeInstance = undefined;
if (!nodeId) return;
const data = await nodeRegistry.fetchNodeDefinition(nodeId);
nodeWasm = await nodeRegistry.fetchArrayBuffer("nodes/" + nodeId + ".wasm");
nodeInstance = {
id: 0,
type: nodeId,
position: [0, 0] as [number, number],
props: {},
state: {
type: data,
},
};
nodeWasmWrapper = createWasmWrapper(nodeWasm);
}
$effect(() => {
fetchNodeData(activeNode.value);
});
$effect(() => {
if (nodeInstance?.props && nodeWasmWrapper) {
const keys = Object.keys(nodeInstance.state.type?.inputs || {});
let ins = Object.values(nodeInstance.props) as number[];
if (keys[0] === "plant") {
ins = [[0, 0, 0, 0, 0, 0, 0, 0], ...ins];
}
const inputs = concatEncodedArrays(encodeNestedArray(ins));
nodeWasmWrapper?.execute(inputs);
}
});
</script> </script>
<div class="node-wrapper absolute bottom-8 left-8">
{#if nodeInstance}
<NodeHTML inView position="relative" z={5} bind:node={nodeInstance} />
{/if}
</div>
<Grid.Row> <Grid.Row>
<Grid.Cell></Grid.Cell>
<Grid.Cell> <Grid.Cell>
<Sidebar> <pre>
<Panel <code>
id="node-store" {JSON.stringify(nodeInstance?.props)}
classes="text-green-400" </code>
title="Node Store" </pre>
icon="i-[tabler--database]" </Grid.Cell>
>
<div class="p-4"> <Grid.Cell>
<input type="text" class="bg-red rounded-sm p-2" /> <div class="h-screen w-[80vw] overflow-y-auto">
</div> {#if nodeWasm}
</Panel> <Code wasm={nodeWasm} />
</Sidebar> {/if}
</div>
</Grid.Cell> </Grid.Cell>
</Grid.Row> </Grid.Row>
<Sidebar>
<Panel
id="node-store"
classes="text-green-400"
title="Node Store"
icon="i-[tabler--database]"
>
<div class="p-4 flex flex-col gap-2">
{#await nodeRegistry.fetchCollection("max/plantarium")}
<p>Loading Nodes...</p>
{:then result}
{#each result.nodes as n}
<button
class="cursor-pointer p-2 bg-layer-1 {activeNode.value === n.id
? 'outline outline-offset-1'
: ''}"
onclick={() => (activeNode.value = n.id)}>{n.id}</button
>
{/each}
{/await}
</div>
</Panel>
</Sidebar>
<style> <style>
:global body { :global body {
height: 100vh; height: 100vh;
width: 100vw;
overflow: hidden;
} }
</style> </style>

View File

@@ -0,0 +1,26 @@
<script lang="ts">
import wabtInit from "wabt";
const { wasm } = $props<{ wasm: ArrayBuffer }>();
async function toWat(arrayBuffer: ArrayBuffer) {
const wabt = await wabtInit();
const module = wabt.readWasm(new Uint8Array(arrayBuffer), {
readDebugNames: true,
});
module.generateNames();
module.applyNames();
return module.toText({ foldExprs: false, inlineExport: false });
}
</script>
{#await toWat(wasm)}
<p>Converting to WAT</p>
{:then c}
<pre>
<code class="text-gray-50">{c}</code>
</pre>
{/await}

1
app/static/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
nodes/

View File

@@ -4,7 +4,7 @@
}; };
outputs = {nixpkgs, ...}: let outputs = {nixpkgs, ...}: let
systems = ["aarch64-darwin" "x86_64-linux"]; systems = ["aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux"];
eachSystem = function: eachSystem = function:
nixpkgs.lib.genAttrs systems (system: nixpkgs.lib.genAttrs systems (system:
function { function {
@@ -19,14 +19,15 @@
pkgs.nodejs_24 pkgs.nodejs_24
pkgs.pnpm_10 pkgs.pnpm_10
# wasm/rust stuff # wasm stuff
pkgs.rustc pkgs.rustc
pkgs.cargo pkgs.cargo
pkgs.rust-analyzer pkgs.rust-analyzer
pkgs.rustfmt pkgs.rustfmt
pkgs.wasm-bindgen-cli pkgs.binaryen
pkgs.wasm-pack
pkgs.lld pkgs.lld
pkgs.zig
pkgs.zls
# frontend # frontend
pkgs.vscode-langservers-extracted pkgs.vscode-langservers-extracted
@@ -35,6 +36,10 @@
pkgs.tailwindcss-language-server pkgs.tailwindcss-language-server
pkgs.svelte-language-server pkgs.svelte-language-server
]; ];
shellHook = ''
unset ZIG_GLOBAL_CACHE_DIR
'';
}; };
}); });
}; };

View File

@@ -11,18 +11,8 @@ crate-type = ["cdylib", "rlib"]
default = ["console_error_panic_hook"] default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
utils = { version = "0.1.0", path = "../../../../packages/utils" } utils = { version = "0.1.0", path = "../../../../packages/utils" }
macros = { version = "0.1.0", path = "../../../../packages/macros" } macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] } web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,22 +7,6 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,18 +1,15 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
concat_args, encode_float, evaluate_float, geometry::calculate_normals, log, set_panic_hook, encode_float, evaluate_float, geometry::calculate_normals,log,
split_args, wrap_arg, split_args, wrap_arg,
}; };
use wasm_bindgen::prelude::*;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[rustfmt::skip] #[nodarium_execute]
#[wasm_bindgen]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
let args = split_args(input); let args = split_args(input);
log!("WASM(cube): input: {:?} -> {:?}", input, args); log!("WASM(cube): input: {:?} -> {:?}", input, args);
@@ -22,7 +19,6 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
let p = encode_float(size); let p = encode_float(size);
let n = encode_float(-size); let n = encode_float(-size);
// [[1,3, x, y, z, x, y,z,x,y,z]]; // [[1,3, x, y, z, x, y,z,x,y,z]];
let mut cube_geometry = [ let mut cube_geometry = [

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,23 +7,6 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
glam = "0.27.0"
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,20 +1,19 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
concat_arg_vecs, evaluate_float, evaluate_int, concat_arg_vecs, evaluate_float, evaluate_int,
geometry::{ geometry::{
create_path, interpolate_along_path, rotate_vector_by_angle, wrap_path, wrap_path_mut, create_path, interpolate_along_path, rotate_vector_by_angle, wrap_path, wrap_path_mut,
}, },
log, set_panic_hook, split_args, log, split_args,
}; };
use std::f32::consts::PI; use std::f32::consts::PI;
use wasm_bindgen::prelude::*;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
let args = split_args(input); let args = split_args(input);
let paths = split_args(args[0]); let paths = split_args(args[0]);

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,21 +7,6 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,9 +1,9 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use wasm_bindgen::prelude::*; use nodarium_macros::nodarium_execute;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(args: &[i32]) -> Vec<i32> { pub fn execute(args: &[i32]) -> Vec<i32> {
args.into() args.into()
} }

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,24 +7,7 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] } glam = "0.30.10"
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
noise = "0.9.0"
glam = "0.27.0"
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,22 +1,20 @@
use glam::Vec3; use glam::Vec3;
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
concat_args, evaluate_float, evaluate_int, concat_args, evaluate_float, evaluate_int,
geometry::{wrap_path, wrap_path_mut}, geometry::{wrap_path, wrap_path_mut},
log, reset_call_count, set_panic_hook, split_args, log, reset_call_count, split_args,
}; };
use wasm_bindgen::prelude::*;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
fn lerp_vec3(a: Vec3, b: Vec3, t: f32) -> Vec3 { fn lerp_vec3(a: Vec3, b: Vec3, t: f32) -> Vec3 {
a + (b - a) * t a + (b - a) * t
} }
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
reset_call_count(); reset_call_count();
let args = split_args(input); let args = split_args(input);

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "nodarium_instance" name = "instance"
version = "0.1.0" version = "0.1.0"
authors = ["Max Richter <jim-x@web.de>"] authors = ["Max Richter <jim-x@web.de>"]
edition = "2018" edition = "2018"
@@ -7,23 +7,7 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] } glam = "0.30.10"
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
glam = "0.27.0"
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,20 +1,18 @@
use glam::{Mat4, Quat, Vec3}; use glam::{Mat4, Quat, Vec3};
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_execute;
use nodarium_macros::nodarium_definition_file;
use nodarium_utils::{ use nodarium_utils::{
concat_args, encode_float, evaluate_float, evaluate_int, concat_args, evaluate_float, evaluate_int,
geometry::{ geometry::{
calculate_normals, create_instance_data, wrap_geometry_data, wrap_instance_data, wrap_path, create_instance_data, wrap_geometry_data, wrap_instance_data, wrap_path,
}, },
log, set_panic_hook, split_args, wrap_arg, log, split_args,
}; };
use wasm_bindgen::prelude::*;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
let args = split_args(input); let args = split_args(input);
let mut inputs = split_args(args[0]); let mut inputs = split_args(args[0]);
log!("WASM(instance): inputs: {:?}", inputs); log!("WASM(instance): inputs: {:?}", inputs);

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,17 +7,6 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,12 +1,13 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_utils::{concat_args, set_panic_hook, split_args}; use nodarium_macros::nodarium_execute;
use wasm_bindgen::prelude::*; use nodarium_utils::{
concat_args, split_args
};
include_definition_file!("src/input.json"); #[nodarium_execute]
#[wasm_bindgen]
pub fn execute(args: &[i32]) -> Vec<i32> { pub fn execute(args: &[i32]) -> Vec<i32> {
set_panic_hook();
let args = split_args(args); let args = split_args(args);
concat_args(vec![&[0], args[0], args[1], args[2]]) concat_args(vec![&[0], args[0], args[1], args[2]])
} }
nodarium_definition_file!("src/input.json");

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "nodes-noise" name = "noise"
version = "0.1.0" version = "0.1.0"
authors = ["Max Richter <jim-x@web.de>"] authors = ["Max Richter <jim-x@web.de>"]
edition = "2018" edition = "2018"
@@ -7,24 +7,8 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
noise = "0.9.0" noise = "0.9.0"
glam = "0.27.0"
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,21 +1,19 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
concat_args, evaluate_float, evaluate_int, evaluate_vec3, geometry::wrap_path_mut, log, concat_args, evaluate_float, evaluate_int, evaluate_vec3, geometry::wrap_path_mut,
reset_call_count, set_panic_hook, split_args, reset_call_count, split_args,
}; };
use noise::{HybridMulti, MultiFractal, NoiseFn, OpenSimplex}; use noise::{HybridMulti, MultiFractal, NoiseFn, OpenSimplex};
use wasm_bindgen::prelude::*;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
fn lerp(a: f32, b: f32, t: f32) -> f32 { fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + t * (b - a) a + t * (b - a)
} }
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
reset_call_count(); reset_call_count();
let args = split_args(input); let args = split_args(input);

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,24 +7,6 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
console_error_panic_hook = ["dep:console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
glam = "0.27.0"
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,17 +1,15 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
concat_args, evaluate_int, concat_args, evaluate_int,
geometry::{extrude_path, wrap_path}, geometry::{extrude_path, wrap_path},
log, set_panic_hook, split_args, log, split_args,
}; };
use wasm_bindgen::prelude::*;
include_definition_file!("src/inputs.json"); nodarium_definition_file!("src/inputs.json");
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
log!("WASM(output): input: {:?}", input); log!("WASM(output): input: {:?}", input);
let args = split_args(input); let args = split_args(input);

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,21 +7,7 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,12 +1,11 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_utils::{concat_args, set_panic_hook, split_args}; use nodarium_macros::nodarium_execute;
use wasm_bindgen::prelude::*; use nodarium_utils::{concat_args, split_args};
include_definition_file!("src/definition.json"); nodarium_definition_file!("src/definition.json");
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(args: &[i32]) -> Vec<i32> { pub fn execute(args: &[i32]) -> Vec<i32> {
set_panic_hook();
let args = split_args(args); let args = split_args(args);
concat_args(vec![&[1], args[0], args[1], args[2]]) concat_args(vec![&[1], args[0], args[1], args[2]])
} }

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,23 +7,8 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4" glam = "0.30.10"
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
glam = "0.27.0"
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,16 +1,15 @@
use glam::{Mat4, Vec3}; use glam::{Mat4, Vec3};
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
concat_args, evaluate_float, evaluate_int, geometry::wrap_path_mut, log, set_panic_hook, concat_args, evaluate_float, evaluate_int, geometry::wrap_path_mut, log,
split_args, split_args,
}; };
use wasm_bindgen::prelude::*;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
log!("DEBUG args: {:?}", input); log!("DEBUG args: {:?}", input);

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -7,23 +7,6 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"
[package.metadata.wasm-pack.profile.release.wasm-bindgen]
debug-js-glue = true
demangle-name-section = true
dwarf-debug-info = false
omit-default-module-path = true

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,17 +1,15 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
evaluate_float, evaluate_int, evaluate_vec3, evaluate_float, evaluate_int, evaluate_vec3,
geometry::{create_multiple_paths, wrap_multiple_paths}, geometry::{create_multiple_paths, wrap_multiple_paths},
log, reset_call_count, set_panic_hook, split_args, log, reset_call_count, split_args,
}; };
use wasm_bindgen::prelude::*;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
reset_call_count(); reset_call_count();
let args = split_args(input); let args = split_args(input);

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "max-plantarium-triangle" name = "triangle"
version = "0.1.0" version = "0.1.0"
authors = ["Max Richter <jim-x@web.de>"] authors = ["Max Richter <jim-x@web.de>"]
edition = "2018" edition = "2018"
@@ -7,22 +7,6 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,25 +1,21 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
decode_float, encode_float, evaluate_int, set_panic_hook, split_args, wrap_arg, decode_float, encode_float, evaluate_int, split_args, wrap_arg, log
}; };
use wasm_bindgen::prelude::*;
use web_sys::console;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[rustfmt::skip] #[nodarium_execute]
#[wasm_bindgen]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
set_panic_hook();
let args = split_args(input); let args = split_args(input);
let size = evaluate_int(args[0]); let size = evaluate_int(args[0]);
let decoded = decode_float(size); let decoded = decode_float(size);
let negative_size = encode_float(-decoded); let negative_size = encode_float(-decoded);
console::log_1(&format!("WASM(triangle): input: {:?} -> {}", args[0],decoded).into()); log!("WASM(triangle): input: {:?} -> {}", args[0],decoded);
// [[1,3, x, y, z, x, y,z,x,y,z]]; // [[1,3, x, y, z, x, y,z,x,y,z]];
wrap_arg(&[ wrap_arg(&[

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "max-plantarium-vec3" name = "vec3"
version = "0.1.0" version = "0.1.0"
authors = ["Max Richter <jim-x@web.de>"] authors = ["Max Richter <jim-x@web.de>"]
edition = "2018" edition = "2018"
@@ -7,22 +7,8 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies] [dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@@ -1,6 +0,0 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features",
"dev": "cargo watch -s 'wasm-pack build --dev --out-name index --no-default-features'"
}
}

View File

@@ -1,10 +1,10 @@
use nodarium_macros::include_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute;
use nodarium_utils::{concat_args, log, split_args}; use nodarium_utils::{concat_args, log, split_args};
use wasm_bindgen::prelude::*;
include_definition_file!("src/input.json"); nodarium_definition_file!("src/input.json");
#[wasm_bindgen] #[nodarium_execute]
pub fn execute(input: &[i32]) -> Vec<i32> { pub fn execute(input: &[i32]) -> Vec<i32> {
let args = split_args(input); let args = split_args(input);
log!("vec3 input: {:?}", input); log!("vec3 input: {:?}", input);

View File

@@ -1,13 +0,0 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

2
nodes/max/plantarium/zig/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.zig-cache/
zig-out/

View File

@@ -0,0 +1,19 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.resolveTargetQuery(.{ .os_tag = .freestanding, .abi = .none, .cpu_arch = .wasm32 });
const release = b.option(bool, "release", "To build a wasm release") orelse false;
const exe = b.addExecutable(.{
.name = "zig",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = if (release) .ReleaseSmall else .Debug,
}),
});
exe.rdynamic = true;
exe.entry = .disabled;
b.installArtifact(exe);
}

View File

@@ -0,0 +1,81 @@
.{
// This is the default name used by packages depending on this one. For
// example, when a user runs `zig fetch --save <url>`, this field is used
// as the key in the `dependencies` table. Although the user can choose a
// different name, most users will stick with this provided value.
//
// It is redundant to include "zig" in this name because it is already
// within the Zig package namespace.
.name = .math,
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",
// Together with name, this represents a globally unique package
// identifier. This field is generated by the Zig toolchain when the
// package is first created, and then *never changes*. This allows
// unambiguous detection of one package being an updated version of
// another.
//
// When forking a Zig project, this id should be regenerated (delete the
// field and run `zig build`) if the upstream project is still maintained.
// Otherwise, the fork is *hostile*, attempting to take control over the
// original project's identity. Thus it is recommended to leave the comment
// on the following line intact, so that it shows up in code reviews that
// modify the field.
.fingerprint = 0xa927044d8d610b01, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a
// supported use case.
.minimum_zig_version = "0.15.2",
// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
//.example = .{
// // When updating this field to a new URL, be sure to delete the corresponding
// // `hash`, otherwise you are communicating that you expect to find the old hash at
// // the new URL. If the contents of a URL change this will result in a hash mismatch
// // which will prevent zig from using it.
// .url = "https://example.com/foo.tar.gz",
//
// // This is computed from the file contents of the directory of files that is
// // obtained after fetching `url` and applying the inclusion rules given by
// // `paths`.
// //
// // This field is the source of truth; packages do not come from a `url`; they
// // come from a `hash`. `url` is just one of many possible mirrors for how to
// // obtain a package matching this `hash`.
// //
// // Uses the [multihash](https://multiformats.io/multihash/) format.
// .hash = "...",
//
// // When this is provided, the package is found in a directory relative to the
// // build root. In this case the package's hash is irrelevant and therefore not
// // computed. This field and `url` are mutually exclusive.
// .path = "foo",
//
// // When this is set to `true`, a package is declared to be lazily
// // fetched. This makes the dependency only get fetched if it is
// // actually used.
// .lazy = false,
//},
},
// Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package. Only files listed here will remain on disk
// when using the zig package manager. As a rule of thumb, one should list
// files required for compilation plus any license(s).
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths = .{
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}

View File

@@ -0,0 +1,27 @@
{
"id": "max/nodarium/zig",
"outputs": [
"float"
],
"inputs": {
"op_type": {
"label": "type",
"type": "select",
"options": [
"add",
"subtract",
"multiply",
"divide"
],
"internal": true
},
"a": {
"type": "float",
"value": 2
},
"b": {
"type": "float",
"value": 2
}
}
}

View File

@@ -0,0 +1,29 @@
const std = @import("std");
const def = @embedFile("input.json");
export fn execute(ptr: *anyopaque, len: c_int) c_int {
_ = ptr; // autofix
_ = len; // autofix
return 0;
}
export fn __alloc(len: c_int) ?*anyopaque {
if (len < 0) return null;
const mem = std.heap.wasm_allocator.alloc(u8, @intCast(len)) catch return null;
return mem.ptr;
}
export fn __free(ptr: *anyopaque, len: c_int) void {
if (len < 1) return;
const mem: [*]u8 = @ptrCast(@alignCast(ptr));
std.heap.wasm_allocator.free(mem[0..@intCast(len)]);
}
export fn getDefinitionPtr() *const anyopaque {
return def.ptr;
}
export fn getDefinitionLen() usize {
return def.len;
}

View File

@@ -1,13 +1,18 @@
{ {
"scripts": { "scripts": {
"postinstall": "pnpm run -r --filter 'ui' build",
"build": "pnpm build:nodes && pnpm build:app", "build": "pnpm build:nodes && pnpm build:app",
"build:story": "pnpm -r --filter 'ui' story:build", "build:story": "pnpm -r --filter 'ui' story:build",
"build:app": "BASE_PATH=/ui pnpm -r --filter 'ui' build && pnpm -r --filter 'app' build", "build:app": "BASE_PATH=/ui pnpm -r --filter 'ui' build && pnpm -r --filter 'app' build",
"build:nodes": "pnpm -r --filter './nodes/**' build", "build:nodes": "cargo build --workspace --target wasm32-unknown-unknown --release && rm -rf ./app/static/nodes/max/plantarium/ && mkdir -p ./app/static/nodes/max/plantarium/ && cp -R ./target/wasm32-unknown-unknown/release/*.wasm ./app/static/nodes/max/plantarium/",
"build:deploy": "pnpm build", "build:deploy": "pnpm build",
"dev:all": "pnpm -r dev", "dev:nodes": "chokidar './nodes/**' --initial -i '/pkg/' -c 'pnpm build:nodes'",
"dev:nodes": "pnpm -r --parallel --filter './nodes/**' dev", "dev:app_ui": "pnpm -r --parallel --filter 'app' --filter './packages/ui' dev",
"dev": "pnpm -r --filter 'app' --filter './packages/ui' dev" "dev_ui": "pnpm -r --filter 'ui' dev:ui",
"dev": "pnpm run \"/^dev:.*/\""
}, },
"packageManager": "pnpm@10.24.0" "packageManager": "pnpm@10.28.1+sha512.7d7dbbca9e99447b7c3bf7a73286afaaf6be99251eb9498baefa7d406892f67b879adb3a1d7e687fc4ccc1a388c7175fbaae567a26ab44d1067b54fcb0d6a316",
"devDependencies": {
"chokidar-cli": "catalog:"
}
} }

View File

@@ -11,7 +11,7 @@ repository = "https://github.com/jim-fx/nodes"
proc-macro = true proc-macro = true
[dependencies] [dependencies]
syn = { version = "1.0", features = ["full"] } syn = { version = "2.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] } serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
quote = "1.0" quote = "1.0"

View File

@@ -5,32 +5,7 @@ use quote::quote;
use std::env; use std::env;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use syn::{parse_macro_input, LitStr}; use syn::parse_macro_input;
#[proc_macro]
pub fn node_definition(input: TokenStream) -> TokenStream {
let input_string = parse_macro_input!(input as LitStr).value();
// Validate JSON format
let json: NodeDefinition = match serde_json::from_str(&input_string) {
Ok(json) => json,
Err(e) => panic!("Invalid JSON input: {}", e),
};
// Convert the validated JSON back to a pretty-printed string
let formatted_json = serde_json::to_string_pretty(&json).expect("Failed to serialize JSON");
// Generate the output function
let expanded = quote! {
#[wasm_bindgen]
pub fn get_definition() -> String {
String::from(#formatted_json)
}
};
// Convert the generated code back to a TokenStream
TokenStream::from(expanded)
}
fn add_line_numbers(input: String) -> String { fn add_line_numbers(input: String) -> String {
return input return input
@@ -41,39 +16,120 @@ fn add_line_numbers(input: String) -> String {
.join("\n"); .join("\n");
} }
#[proc_macro] #[proc_macro_attribute]
pub fn include_definition_file(input: TokenStream) -> TokenStream { pub fn nodarium_execute(_attr: TokenStream, item: TokenStream) -> TokenStream {
let file_path = syn::parse_macro_input!(input as syn::LitStr).value(); let input_fn = parse_macro_input!(item as syn::ItemFn);
let _fn_name = &input_fn.sig.ident;
let _fn_vis = &input_fn.vis;
let fn_body = &input_fn.block;
// Retrieve the directory containing the Cargo.toml file let first_arg_ident = if let Some(syn::FnArg::Typed(pat_type)) = input_fn.sig.inputs.first() {
let project_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
let full_path = Path::new(&project_dir).join(&file_path); &pat_ident.ident
} else {
panic!("Expected a simple identifier for the first argument");
}
} else {
panic!("The execute function must have at least one argument (the input slice)");
};
// Read the JSON file content // We create a wrapper that handles the C ABI and pointer math
let json_content = fs::read_to_string(full_path).unwrap_or_else(|err| {
panic!(
"Failed to read JSON file at '{}/{}': {}",
project_dir, file_path, err
)
});
// Optionally, validate that the content is valid JSON
let _: NodeDefinition = serde_json::from_str(&json_content).unwrap_or_else(|err| {
panic!(
"JSON file contains invalid JSON: \n{} \n{}",
err,
add_line_numbers(json_content.clone())
)
});
// Generate the function that returns the JSON string
let expanded = quote! { let expanded = quote! {
#[wasm_bindgen] extern "C" {
pub fn get_definition() -> String { fn host_log_panic(ptr: *const u8, len: usize);
String::from(#json_content) fn host_log(ptr: *const u8, len: usize);
}
fn setup_panic_hook() {
static SET_HOOK: std::sync::Once = std::sync::Once::new();
SET_HOOK.call_once(|| {
std::panic::set_hook(Box::new(|info| {
let msg = info.to_string();
unsafe { host_log_panic(msg.as_ptr(), msg.len()); }
}));
});
}
#[no_mangle]
pub extern "C" fn __alloc(len: usize) -> *mut i32 {
let mut buf = Vec::with_capacity(len);
let ptr = buf.as_mut_ptr();
std::mem::forget(buf);
ptr
}
#[no_mangle]
pub extern "C" fn __free(ptr: *mut i32, len: usize) {
unsafe {
let _ = Vec::from_raw_parts(ptr, 0, len);
}
}
static mut OUTPUT_BUFFER: Vec<i32> = Vec::new();
#[no_mangle]
pub extern "C" fn execute(ptr: *const i32, len: usize) -> *mut i32 {
setup_panic_hook();
// 1. Convert raw pointer to slice
let input = unsafe { core::slice::from_raw_parts(ptr, len) };
// 2. Call the logic (which we define below)
let result_data: Vec<i32> = internal_logic(input);
// 3. Use the static buffer for the result
let result_len = result_data.len();
unsafe {
OUTPUT_BUFFER.clear();
OUTPUT_BUFFER.reserve(result_len + 1);
OUTPUT_BUFFER.push(result_len as i32);
OUTPUT_BUFFER.extend(result_data);
OUTPUT_BUFFER.as_mut_ptr()
}
}
fn internal_logic(#first_arg_ident: &[i32]) -> Vec<i32> {
#fn_body
}
};
TokenStream::from(expanded)
}
#[proc_macro]
pub fn nodarium_definition_file(input: TokenStream) -> TokenStream {
let path_lit = syn::parse_macro_input!(input as syn::LitStr);
let file_path = path_lit.value();
let project_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let full_path = Path::new(&project_dir).join(&file_path);
let json_content = fs::read_to_string(&full_path).unwrap_or_else(|err| {
panic!("Failed to read JSON file at '{}/{}': {}", project_dir, file_path, err)
});
let _: NodeDefinition = serde_json::from_str(&json_content).unwrap_or_else(|err| {
panic!("JSON file contains invalid JSON: \n{} \n{}", err, add_line_numbers(json_content.clone()))
});
// We use the span from the input path literal
let bytes = syn::LitByteStr::new(json_content.as_bytes(), path_lit.span());
let len = json_content.len();
let expanded = quote! {
#[link_section = "nodarium_definition"]
static DEFINITION_DATA: [u8; #len] = *#bytes;
#[no_mangle]
pub extern "C" fn get_definition_ptr() -> *const u8 {
DEFINITION_DATA.as_ptr()
}
#[no_mangle]
pub extern "C" fn get_definition_len() -> usize {
DEFINITION_DATA.len()
} }
}; };
// Convert the generated code back to a TokenStream
TokenStream::from(expanded) TokenStream::from(expanded)
} }

View File

@@ -10,8 +10,8 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@nodarium/types": "link:../types", "@nodarium/types": "workspace:",
"@nodarium/utils": "link:../utils", "@nodarium/utils": "workspace:",
"idb": "^8.0.3" "idb": "^8.0.3"
} }
} }

View File

@@ -1,33 +1,31 @@
import { import {
NodeDefinitionSchema,
type AsyncCache, type AsyncCache,
type NodeDefinition, type NodeDefinition,
type NodeRegistry, NodeDefinitionSchema,
} from "@nodarium/types"; type NodeRegistry
import { createLogger, createWasmWrapper } from "@nodarium/utils"; } from '@nodarium/types';
import { createLogger, createWasmWrapper } from '@nodarium/utils';
const log = createLogger("node-registry"); const log = createLogger('node-registry');
log.mute(); log.mute();
export class RemoteNodeRegistry implements NodeRegistry { export class RemoteNodeRegistry implements NodeRegistry {
status: "loading" | "ready" | "error" = "loading"; status: 'loading' | 'ready' | 'error' = 'loading';
private nodes: Map<string, NodeDefinition> = new Map(); private nodes: Map<string, NodeDefinition> = new Map();
constructor( constructor(
private url: string, private url: string,
public cache?: AsyncCache<ArrayBuffer | string>, public cache?: AsyncCache<ArrayBuffer | string>
) { } ) { }
async fetchJson(url: string, skipCache = false) { async fetchJson(url: string, skipCache = false) {
const finalUrl = `${this.url}/${url}`; const finalUrl = `${this.url}/${url}`;
if (!skipCache && this.cache) { if (!skipCache && this.cache) {
const cachedValue = await this.cache?.get<string>(finalUrl); const cachedValue = await this.cache?.get<string>(finalUrl);
if (cachedValue) { if (cachedValue) {
// fetch again in the background, maybe implement that only refetch after a certain time // fetch again in the background, maybe implement that only refetch after a certain time
this.fetchJson(url, true) this.fetchJson(url, true);
return JSON.parse(cachedValue); return JSON.parse(cachedValue);
} }
} }
@@ -47,14 +45,13 @@ export class RemoteNodeRegistry implements NodeRegistry {
} }
async fetchArrayBuffer(url: string, skipCache = false) { async fetchArrayBuffer(url: string, skipCache = false) {
const finalUrl = `${this.url}/${url}`; const finalUrl = `${this.url}/${url}`;
if (!skipCache && this.cache) { if (!skipCache && this.cache) {
const cachedNode = await this.cache?.get<ArrayBuffer>(finalUrl); const cachedNode = await this.cache?.get<ArrayBuffer>(finalUrl);
if (cachedNode) { if (cachedNode) {
// fetch again in the background, maybe implement that only refetch after a certain time // fetch again in the background, maybe implement that only refetch after a certain time
this.fetchArrayBuffer(url, true) this.fetchArrayBuffer(url, true);
return cachedNode; return cachedNode;
} }
} }
@@ -80,7 +77,7 @@ export class RemoteNodeRegistry implements NodeRegistry {
async fetchCollection(userCollectionId: `${string}/${string}`) { async fetchCollection(userCollectionId: `${string}/${string}`) {
const col = await this.fetchJson(`nodes/${userCollectionId}.json`); const col = await this.fetchJson(`nodes/${userCollectionId}.json`);
return col return col;
} }
async fetchNodeDefinition(nodeId: `${string}/${string}/${string}`) { async fetchNodeDefinition(nodeId: `${string}/${string}/${string}`) {
@@ -88,7 +85,6 @@ export class RemoteNodeRegistry implements NodeRegistry {
} }
private async fetchNodeWasm(nodeId: `${string}/${string}/${string}`) { private async fetchNodeWasm(nodeId: `${string}/${string}/${string}`) {
const node = await this.fetchArrayBuffer(`nodes/${nodeId}.wasm`); const node = await this.fetchArrayBuffer(`nodes/${nodeId}.wasm`);
if (!node) { if (!node) {
throw new Error(`Failed to load node wasm ${nodeId}`); throw new Error(`Failed to load node wasm ${nodeId}`);
@@ -100,7 +96,7 @@ export class RemoteNodeRegistry implements NodeRegistry {
async load(nodeIds: `${string}/${string}/${string}`[]) { async load(nodeIds: `${string}/${string}/${string}`[]) {
const a = performance.now(); const a = performance.now();
const nodes = await Promise.all( const nodes = (await Promise.all(
[...new Set(nodeIds).values()].map(async (id) => { [...new Set(nodeIds).values()].map(async (id) => {
if (this.nodes.has(id)) { if (this.nodes.has(id)) {
return this.nodes.get(id)!; return this.nodes.get(id)!;
@@ -108,17 +104,23 @@ export class RemoteNodeRegistry implements NodeRegistry {
const wasmBuffer = await this.fetchNodeWasm(id); const wasmBuffer = await this.fetchNodeWasm(id);
return this.register(wasmBuffer); try {
}), return await this.register(wasmBuffer);
); } catch (e) {
console.log('Failed to register: ', id);
console.error(e);
return;
}
})
)).filter(Boolean) as NodeDefinition[];
const duration = performance.now() - a; const duration = performance.now() - a;
log.group("loaded nodes in", duration, "ms"); log.group('loaded nodes in', duration, 'ms');
log.info(nodeIds); log.info(nodeIds);
log.info(nodes); log.info(nodes);
log.groupEnd(); log.groupEnd();
this.status = "ready"; this.status = 'ready';
return nodes; return nodes;
} }
@@ -129,17 +131,16 @@ export class RemoteNodeRegistry implements NodeRegistry {
const definition = NodeDefinitionSchema.safeParse(wrapper.get_definition()); const definition = NodeDefinitionSchema.safeParse(wrapper.get_definition());
if (definition.error) { if (definition.error) {
console.error(definition.error);
throw definition.error; throw definition.error;
} }
if (this.cache) { if (this.cache) {
await this.cache.set(definition.data.id, wasmBuffer); this.cache.set(definition.data.id, wasmBuffer);
} }
let node = { let node = {
...definition.data, ...definition.data,
execute: wrapper.execute, execute: wrapper.execute
}; };
this.nodes.set(definition.data.id, node); this.nodes.set(definition.data.id, node);

View File

@@ -4,7 +4,6 @@ version = "0.1.0"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
description = "Types for Nodarium" description = "Types for Nodarium"
website = "https://nodes.max-richter.dev"
repository = "https://github.com/jim-fx/nodes" repository = "https://github.com/jim-fx/nodes"
[dependencies] [dependencies]

View File

@@ -3,6 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
"dev": "chokidar './src/**' --initial -c 'pnpm build'", "dev": "chokidar './src/**' --initial -c 'pnpm build'",
"dev:ui": "vite",
"build": "vite build && npm run package", "build": "vite build && npm run package",
"preview": "vite preview", "preview": "vite preview",
"package": "svelte-kit sync && svelte-package && publint", "package": "svelte-kit sync && svelte-package && publint",
@@ -10,6 +11,7 @@
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test": "vitest", "test": "vitest",
"lint-dprint": "dprint check -c '../../.dprint.jsonc' .",
"lint": "eslint ." "lint": "eslint ."
}, },
"exports": { "exports": {
@@ -28,7 +30,7 @@
"svelte": "^4.0.0" "svelte": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@nodarium/types": "link:../types", "@nodarium/types": "workspace:",
"@sveltejs/adapter-static": "^3.0.10", "@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.50.0", "@sveltejs/kit": "^2.50.0",
"@sveltejs/package": "^2.5.7", "@sveltejs/package": "^2.5.7",

View File

@@ -1,10 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { NodeInput } from '@nodarium/types'; import type { NodeInput } from '@nodarium/types';
import Checkbox from './inputs/Checkbox.svelte'; import { Checkbox, Number, Select, Vec3 } from './index.js';
import Number from './inputs/Number.svelte';
import Select from './inputs/Select.svelte';
import Vec3 from './inputs/Vec3.svelte';
interface Props { interface Props {
input: NodeInput; input: NodeInput;
@@ -16,7 +13,7 @@
</script> </script>
{#if input.type === 'float'} {#if input.type === 'float'}
<Number bind:value min={input?.min} max={input?.max} step={0.01} /> <Number bind:value min={input?.min} max={input?.max} step={input?.step} />
{:else if input.type === 'integer'} {:else if input.type === 'integer'}
<Number bind:value min={input?.min} max={input?.max} /> <Number bind:value min={input?.min} max={input?.max} />
{:else if input.type === 'boolean'} {:else if input.type === 'boolean'}

View File

@@ -40,6 +40,30 @@
} }
@theme {
--color-neutral-100: #E7E7E7;
--color-neutral-200: #CECECE;
--color-neutral-300: #7C7C7C;
--color-neutral-400: #2D2D2D;
--color-neutral-500: #171717;
--color-neutral-800: #111111;
--color-neutral-900: #060606;
--color-layer-0: var(--neutral-900);
--color-layer-1: var(--neutral-500);
--color-layer-2: var(--neutral-400);
--color-layer-3: var(--neutral-200);
--color-active: #ffffff;
--color-selected: #c65a19;
--color-outline: var(--neutral-400);
--color-connection: #333333;
--color-edge: var(--connection, var(--outline));
--color-text-color: var(--neutral-200);
}
html { html {
--neutral-100: #E7E7E7; --neutral-100: #E7E7E7;
--neutral-200: #CECECE; --neutral-200: #CECECE;

View File

@@ -1,12 +1,12 @@
export { default as Input } from "./Input.svelte" export { default as Input } from './Input.svelte';
export { default as Float } from "./inputs/Float.svelte" export { default as Checkbox } from './inputs/Checkbox.svelte';
export { default as Integer } from "./inputs/Integer.svelte" export { default as Float, default as Number } from './inputs/Float.svelte';
export { default as Select } from "./inputs/Select.svelte" export { default as Integer } from './inputs/Integer.svelte';
export { default as Checkbox } from "./inputs/Checkbox.svelte" export { default as Select } from './inputs/Select.svelte';
export { default as Vec3 } from "./inputs/Vec3.svelte"; export { default as Vec3 } from './inputs/Vec3.svelte';
export { default as Details } from "./Details.svelte" export { default as Details } from './Details.svelte';
export { default as ShortCut } from "./ShortCut.svelte"; export { default as ShortCut } from './ShortCut.svelte';
import Input from './Input.svelte'; import Input from './Input.svelte';
export default Input; export default Input;

Some files were not shown because too many files have changed in this diff Show More