feat: add theming support

This commit is contained in:
max_richter 2024-04-19 01:27:11 +02:00
parent a15a54c61e
commit d8ada83db3
27 changed files with 569 additions and 285 deletions

60
Cargo.lock generated
View File

@ -36,6 +36,12 @@ version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -48,7 +54,7 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [ dependencies = [
"cfg-if", "cfg-if 1.0.0",
"wasm-bindgen", "wasm-bindgen",
] ]
@ -86,6 +92,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.21" version = "0.4.21"
@ -159,6 +171,12 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "memory_units"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.19.0" version = "1.19.0"
@ -173,12 +191,12 @@ dependencies = [
"glam", "glam",
"macros", "macros",
"serde", "serde",
"serde-wasm-bindgen",
"serde_json", "serde_json",
"utils", "utils",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-test", "wasm-bindgen-test",
"web-sys", "web-sys",
"wee_alloc",
] ]
[[package]] [[package]]
@ -348,7 +366,7 @@ version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if 1.0.0",
"wasm-bindgen-macro", "wasm-bindgen-macro",
] ]
@ -373,7 +391,7 @@ version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if 1.0.0",
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
@ -442,3 +460,37 @@ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "wee_alloc"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
dependencies = [
"cfg-if 0.1.10",
"libc",
"memory_units",
"winapi",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -6,6 +6,18 @@
<link rel="icon" href="%sveltekit.assets%/svelte.svg" /> <link rel="icon" href="%sveltekit.assets%/svelte.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head% %sveltekit.head%
<script>
var store = localStorage.getItem("node-settings");
if (store) {
try {
var value = JSON.parse(store);
var themes = ["dark", "light", "catppuccin"];
if (themes[value.theme]) {
document.body.classList.add("theme-" + themes[value.theme]);
}
} catch (e) { }
}
</script>
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">

View File

@ -8,6 +8,7 @@ uniform vec2 dimensions;
uniform vec3 camPos; uniform vec3 camPos;
uniform vec2 zoomLimits; uniform vec2 zoomLimits;
uniform vec3 backgroundColor; uniform vec3 backgroundColor;
uniform vec3 lineColor;
float grid(float x, float y, float divisions, float thickness) { float grid(float x, float y, float divisions, float thickness) {
x = fract(x * divisions); x = fract(x * divisions);
@ -92,7 +93,7 @@ void main(void) {
float c = mix(large, small, min(nz*2.0+0.05, 1.0)); float c = mix(large, small, min(nz*2.0+0.05, 1.0));
c = mix(c, xsmall, max(min((nz-0.3)/0.7, 1.0), 0.0)); c = mix(c, xsmall, max(min((nz-0.3)/0.7, 1.0), 0.0));
vec3 color = mix(backgroundColor, vec3(1.0), c*0.5); vec3 color = mix(backgroundColor, lineColor, c);
gl_FragColor = vec4(color, 1.0); gl_FragColor = vec4(color, 1.0);
} }

View File

@ -1,52 +1,61 @@
<script lang="ts"> <script lang="ts">
import { T } from '@threlte/core'; import { T } from "@threlte/core";
import BackgroundVert from './Background.vert'; import BackgroundVert from "./Background.vert";
import BackgroundFrag from './Background.frag'; import BackgroundFrag from "./Background.frag";
import { colors } from '../graph/stores.js'; import { colors } from "../graph/stores.js";
import { Color } from "three";
export let minZoom = 4; export let minZoom = 4;
export let maxZoom = 150; export let maxZoom = 150;
export let cameraPosition: [number, number, number] = [0, 1, 0]; export let cameraPosition: [number, number, number] = [0, 1, 0];
export let width = globalThis?.innerWidth || 100; export let width = globalThis?.innerWidth || 100;
export let height = globalThis?.innerHeight || 100; export let height = globalThis?.innerHeight || 100;
let bw = 2; let bw = 2;
let bh = 2; let bh = 2;
$: if (width && height) { $: if (width && height) {
bw = width / cameraPosition[2]; bw = width / cameraPosition[2];
bh = height / cameraPosition[2]; bh = height / cameraPosition[2];
} }
</script> </script>
<T.Group position.x={cameraPosition[0]} position.z={cameraPosition[1]} position.y={-1.0}> <T.Group
<T.Mesh rotation.x={-Math.PI / 2} position.y={0.2} scale.x={bw} scale.y={bh}> position.x={cameraPosition[0]}
<T.PlaneGeometry args={[1, 1]} /> position.z={cameraPosition[1]}
<T.ShaderMaterial position.y={-1.0}
transparent >
vertexShader={BackgroundVert} <T.Mesh rotation.x={-Math.PI / 2} position.y={0.2} scale.x={bw} scale.y={bh}>
fragmentShader={BackgroundFrag} <T.PlaneGeometry args={[1, 1]} />
uniforms={{ <T.ShaderMaterial
camPos: { transparent
value: [0, 1, 0] vertexShader={BackgroundVert}
}, fragmentShader={BackgroundFrag}
backgroundColor: { uniforms={{
value: [0, 0, 0] camPos: {
}, value: [0, 1, 0],
zoomLimits: { },
value: [2, 50] backgroundColor: {
}, value: new Color(0x171717),
dimensions: { },
value: [100, 100] lineColor: {
} value: new Color(0x111111),
}} },
uniforms.camPos.value={cameraPosition} zoomLimits: {
uniforms.backgroundColor.value={$colors.backgroundColorDarker} value: [2, 50],
uniforms.zoomLimits.value={[minZoom, maxZoom]} },
uniforms.dimensions.value={[width, height]} dimensions: {
/> value: [100, 100],
</T.Mesh> },
}}
uniforms.camPos.value={cameraPosition}
uniforms.backgroundColor.value={$colors.layer0}
uniforms.lineColor.value={$colors.layer2}
uniforms.zoomLimits.value={[minZoom, maxZoom]}
uniforms.dimensions.value={[width, height]}
/>
</T.Mesh>
</T.Group> </T.Group>

View File

@ -1,12 +1,9 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
const color = new Color(0x202020); import { colors } from "../graph/stores";
color.convertLinearToSRGB(); import { get } from "svelte/store";
const color2 = color.clone();
color2.convertSRGBToLinear();
const circleMaterial = new MeshBasicMaterial({ const circleMaterial = new MeshBasicMaterial({
color, color: get(colors).outline,
toneMapped: false, toneMapped: false,
}); });
@ -24,7 +21,6 @@
import { T } from "@threlte/core"; import { T } from "@threlte/core";
import { MeshLineMaterial } from "@threlte/extras"; import { MeshLineMaterial } from "@threlte/extras";
import { BufferGeometry, MeshBasicMaterial, Vector3 } from "three"; import { BufferGeometry, MeshBasicMaterial, Vector3 } from "three";
import { Color } from "three/src/math/Color.js";
import { CubicBezierCurve } from "three/src/extras/curves/CubicBezierCurve.js"; import { CubicBezierCurve } from "three/src/extras/curves/CubicBezierCurve.js";
import { Vector2 } from "three/src/math/Vector2.js"; import { Vector2 } from "three/src/math/Vector2.js";
import { createEdgeGeometry } from "./createEdgeGeometry.js"; import { createEdgeGeometry } from "./createEdgeGeometry.js";
@ -59,7 +55,7 @@
const length = Math.floor( const length = Math.floor(
Math.sqrt(Math.pow(new_x, 2) + Math.pow(new_y, 2)) / 4, Math.sqrt(Math.pow(new_x, 2) + Math.pow(new_y, 2)) / 4,
); );
samples = Math.min(Math.max(10, length), 60); samples = Math.min(Math.max(10, length), 60) * 2;
curve.v0.set(0, 0); curve.v0.set(0, 0);
curve.v1.set(mid.x, 0); curve.v1.set(mid.x, 0);
@ -102,11 +98,6 @@
{#if geometry} {#if geometry}
<T.Mesh position.x={from.x} position.z={from.y} position.y={0.1} {geometry}> <T.Mesh position.x={from.x} position.z={from.y} position.y={0.1} {geometry}>
<MeshLineMaterial <MeshLineMaterial width={3} attenuate={false} color={$colors.outline} />
width={4}
attenuate={false}
color={color2}
toneMapped={false}
/>
</T.Mesh> </T.Mesh>
{/if} {/if}

View File

@ -435,7 +435,6 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
save() { save() {
if (this.currentUndoGroup) return; if (this.currentUndoGroup) return;
const state = this.serialize(); const state = this.serialize();
console.log(state);
this.history.save(state); this.history.save(state);
this.emit("save", state); this.emit("save", state);
logger.log("saving graphs", state); logger.log("saving graphs", state);

View File

@ -1,6 +1,11 @@
<script lang="ts"> <script lang="ts">
import { animate, lerp, snapToGrid } from "../helpers/index.js"; import { animate, lerp, snapToGrid } from "../helpers/index.js";
import { LinearSRGBColorSpace } from "three"; import {
LinearSRGBColorSpace,
LinearToneMapping,
NoToneMapping,
SRGBColorSpace,
} from "three";
import { Canvas } from "@threlte/core"; import { Canvas } from "@threlte/core";
import type { OrthographicCamera } from "three"; import type { OrthographicCamera } from "three";
import Background from "../background/Background.svelte"; import Background from "../background/Background.svelte";
@ -750,12 +755,7 @@
on:keydown={handleKeyDown} on:keydown={handleKeyDown}
on:mousedown={handleMouseDown} on:mousedown={handleMouseDown}
> >
<Canvas <Canvas shadows={false} renderMode="on-demand" colorManagementEnabled={false}>
shadows={false}
renderMode="on-demand"
colorManagementEnabled={false}
colorSpace={LinearSRGBColorSpace}
>
<Camera bind:camera position={cameraPosition} /> <Camera bind:camera position={cameraPosition} />
<Background {cameraPosition} {maxZoom} {minZoom} {width} {height} /> <Background {cameraPosition} {maxZoom} {minZoom} {width} {height} />

View File

@ -11,9 +11,11 @@ export const possibleSockets: Writable<Socket[]> = writable([]);
export const possibleSocketIds: Writable<Set<string> | null> = writable(null); export const possibleSocketIds: Writable<Set<string> | null> = writable(null);
export const colors = writable({ export const colors = writable({
backgroundColorDarker: new Color().setStyle("#101010"), layer0: new Color().setStyle("#060606"),
backgroundColor: new Color().setStyle("#151515"), layer1: new Color().setStyle("#171717"),
backgroundColorLighter: new Color().setStyle("#202020") layer2: new Color().setStyle("#2D2D2D"),
layer3: new Color().setStyle("#A6A6A6"),
outline: new Color().setStyle("#000000"),
}); });
if ("getComputedStyle" in globalThis) { if ("getComputedStyle" in globalThis) {
@ -23,19 +25,32 @@ if ("getComputedStyle" in globalThis) {
function updateColors() { function updateColors() {
const style = getComputedStyle(body); const style = getComputedStyle(body);
const backgroundColorDarker = style.getPropertyValue("--background-color-darker"); const layer0 = style.getPropertyValue("--layer-0");
const backgroundColor = style.getPropertyValue("--background-color"); const layer1 = style.getPropertyValue("--layer-1");
const backgroundColorLighter = style.getPropertyValue("--background-color-lighter"); const layer2 = style.getPropertyValue("--layer-2");
const layer3 = style.getPropertyValue("--layer-3");
const outline = style.getPropertyValue("--outline");
colors.update(col => { colors.update(col => {
col.backgroundColorDarker.setStyle(backgroundColorDarker); col.layer0.setStyle(layer0);
col.backgroundColor.setStyle(backgroundColor); col.layer0.convertLinearToSRGB();
col.backgroundColorLighter.setStyle(backgroundColorLighter); col.layer1.setStyle(layer1);
col.layer1.convertLinearToSRGB();
col.layer2.setStyle(layer2);
col.layer2.convertLinearToSRGB();
col.layer3.setStyle(layer3);
col.layer3.convertLinearToSRGB();
col.outline.setStyle(outline);
col.outline.convertLinearToSRGB();
return col; return col;
}); });
} }
updateColors();
window.onload = updateColors;
body.addEventListener("transitionstart", () => { body.addEventListener("transitionstart", () => {
updateColors(); updateColors();
}) })

View File

@ -4,6 +4,7 @@
import NodeHeader from "./NodeHeader.svelte"; import NodeHeader from "./NodeHeader.svelte";
import NodeParameter from "./NodeParameter.svelte"; import NodeParameter from "./NodeParameter.svelte";
import { activeNodeId, selectedNodes } from "../graph/stores.js"; import { activeNodeId, selectedNodes } from "../graph/stores.js";
import { colors } from "../graph/stores";
import { T } from "@threlte/core"; import { T } from "@threlte/core";
import { Color, type Mesh } from "three"; import { Color, type Mesh } from "three";
import NodeFrag from "./Node.frag"; import NodeFrag from "./Node.frag";
@ -25,7 +26,6 @@
const zOffset = (node.tmp?.random || 0) * 0.5; const zOffset = (node.tmp?.random || 0) * 0.5;
const zLimit = 2 - zOffset; const zLimit = 2 - zOffset;
$: visible = inView && z > zLimit;
const parameters = Object.entries(type?.inputs || {}) const parameters = Object.entries(type?.inputs || {})
.filter((p) => p[1].type !== "seed") .filter((p) => p[1].type !== "seed")
@ -49,13 +49,6 @@
node.tmp.mesh = meshRef; node.tmp.mesh = meshRef;
updateNodePosition(node); updateNodePosition(node);
}); });
const colorDark = new Color();
colorDark.setStyle("#151515");
//colorDark.();
const colorBright = new Color("#202020");
//colorBright.convertLinearToSRGB();
</script> </script>
<T.Mesh <T.Mesh
@ -72,8 +65,8 @@
fragmentShader={NodeFrag} fragmentShader={NodeFrag}
transparent transparent
uniforms={{ uniforms={{
uColorBright: { value: colorBright }, uColorBright: { value: new Color("#171717") },
uColorDark: { value: colorDark }, uColorDark: { value: new Color("#151515") },
uSelectedColor: { value: new Color("#9d5f28") }, uSelectedColor: { value: new Color("#9d5f28") },
uActiveColor: { value: new Color("white") }, uActiveColor: { value: new Color("white") },
uSelected: { value: false }, uSelected: { value: false },
@ -84,6 +77,8 @@
}} }}
uniforms.uSelected.value={isSelected} uniforms.uSelected.value={isSelected}
uniforms.uActive.value={isActive} uniforms.uActive.value={isActive}
uniforms.uColorBright.value={$colors.layer2}
uniforms.uColorDark.value={$colors.layer1}
uniforms.uStrokeWidth.value={(7 - z) / 3} uniforms.uStrokeWidth.value={(7 - z) / 3}
/> />
</T.Mesh> </T.Mesh>
@ -122,7 +117,7 @@
z-index: 1; z-index: 1;
opacity: calc((var(--cz) - 2.5) / 3.5); opacity: calc((var(--cz) - 2.5) / 3.5);
font-weight: 300; font-weight: 300;
--stroke: var(--background-color-lighter); --stroke: var(--outline);
--stroke-width: 2px; --stroke-width: 2px;
} }

View File

@ -1,129 +1,137 @@
<script lang="ts"> <script lang="ts">
import { createNodePath } from '../helpers/index.js'; import { createNodePath } from "../helpers/index.js";
import type { Node, Socket } from '@nodes/types'; import type { Node, Socket } from "@nodes/types";
import { getContext } from 'svelte'; import { getContext } from "svelte";
export let node: Node; export let node: Node;
const setDownSocket = getContext<(socket: Socket) => void>('setDownSocket'); const setDownSocket = getContext<(socket: Socket) => void>("setDownSocket");
const getSocketPosition = const getSocketPosition =
getContext<(node: Node, index: number) => [number, number]>('getSocketPosition'); getContext<(node: Node, index: number) => [number, number]>(
"getSocketPosition",
);
function handleMouseDown(event: MouseEvent) { function handleMouseDown(event: MouseEvent) {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
setDownSocket({ setDownSocket({
node, node,
index: 0, index: 0,
position: getSocketPosition(node, 0) position: getSocketPosition(node, 0),
}); });
} }
const cornerTop = 10; const cornerTop = 10;
const rightBump = !!node?.tmp?.type?.outputs?.length; const rightBump = !!node?.tmp?.type?.outputs?.length;
const aspectRatio = 0.25; const aspectRatio = 0.25;
const path = createNodePath({ const path = createNodePath({
depth: 4, depth: 4,
height: 24, height: 24,
y: 50, y: 50,
cornerTop, cornerTop,
rightBump, rightBump,
aspectRatio aspectRatio,
}); });
const pathDisabled = createNodePath({ const pathDisabled = createNodePath({
depth: 0, depth: 0,
height: 15, height: 15,
y: 50, y: 50,
cornerTop, cornerTop,
rightBump, rightBump,
aspectRatio aspectRatio,
}); });
const pathHover = createNodePath({ const pathHover = createNodePath({
depth: 5, depth: 5,
height: 30, height: 30,
y: 50, y: 50,
cornerTop, cornerTop,
rightBump, rightBump,
aspectRatio aspectRatio,
}); });
</script> </script>
<div class="wrapper" data-node-id={node.id}> <div class="wrapper" data-node-id={node.id}>
<div class="content"> <div class="content">
{node.type.split('/').pop()} {node.type.split("/").pop()}
</div> </div>
<div class="click-target" role="button" tabindex="0" on:mousedown={handleMouseDown} /> <div
<svg class="click-target"
xmlns="http://www.w3.org/2000/svg" role="button"
viewBox="0 0 100 100" tabindex="0"
width="100" on:mousedown={handleMouseDown}
height="100" />
preserveAspectRatio="none" <svg
style={` xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
width="100"
height="100"
preserveAspectRatio="none"
style={`
--path: path("${path}"); --path: path("${path}");
--hover-path: path("${pathHover}"); --hover-path: path("${pathHover}");
`} `}
> >
<path vector-effect="non-scaling-stroke" stroke="white" stroke-width="0.1"></path> <path vector-effect="non-scaling-stroke" stroke="white" stroke-width="0.1"
</svg> ></path>
</svg>
</div> </div>
<style> <style>
.wrapper { .wrapper {
position: relative; position: relative;
width: 100%; width: 100%;
height: 50px; height: 50px;
} }
.click-target { .click-target {
position: absolute; position: absolute;
right: 0px; right: 0px;
top: 50%; top: 50%;
transform: translateX(50%) translateY(-50%); transform: translateX(50%) translateY(-50%);
height: 30px; height: 30px;
width: 30px; width: 30px;
z-index: 100; z-index: 100;
border-radius: 50%; border-radius: 50%;
/* background: red; */ /* background: red; */
/* opacity: 0.2; */ /* opacity: 0.2; */
} }
.click-target:hover + svg path { .click-target:hover + svg path {
d: var(--hover-path); d: var(--hover-path);
} }
svg { svg {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
z-index: -1; z-index: -1;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: visible; overflow: visible;
} }
svg path { svg path {
stroke-width: 0.2px; stroke-width: 0.2px;
transition: transition:
d 0.3s ease, d 0.3s ease,
fill 0.3s ease; fill 0.3s ease;
fill: var(--background-color-lighter); fill: var(--layer-2);
stroke: var(--stroke); stroke: var(--stroke);
stroke-width: var(--stroke-width); stroke-width: var(--stroke-width);
d: var(--path); d: var(--path);
} }
.content { .content {
font-size: 1em; font-size: 1em;
display: flex; display: flex;
align-items: center; align-items: center;
padding-left: 20px; padding-left: 20px;
height: 100%; height: 100%;
} }
svg:hover path { svg:hover path {
d: var(--hover-path) !important; d: var(--hover-path) !important;
} }
</style> </style>

View File

@ -175,7 +175,7 @@
transition: transition:
d 0.3s ease, d 0.3s ease,
fill 0.3s ease; fill 0.3s ease;
fill: var(--background-color); fill: var(--layer-1);
stroke: var(--stroke); stroke: var(--stroke);
stroke-width: var(--stroke-width); stroke-width: var(--stroke-width);
d: var(--path); d: var(--path);

View File

@ -57,9 +57,20 @@
overflow: hidden; overflow: hidden;
} }
.seperator { .seperator {
background: white; position: relative;
cursor: ew-resize; cursor: ew-resize;
height: 100%; height: 100%;
width: 5px; width: 1px;
background: var(--outline);
}
.seperator::before {
content: "";
cursor: ew-resize;
position: absolute;
pointer-events: all;
height: 100%;
width: 14px;
z-index: 2;
left: -7px;
} }
</style> </style>

View File

@ -17,7 +17,7 @@
setContext("sizes", sizes); setContext("sizes", sizes);
$: cols = $sizes.map((size, i) => `${i > 0 ? "5px " : ""}` + size).join(" "); $: cols = $sizes.map((size, i) => `${i > 0 ? "1px " : ""}` + size).join(" ");
</script> </script>
<div class="wrapper" style={`grid-template-columns: ${cols};`}> <div class="wrapper" style={`grid-template-columns: ${cols};`}>

View File

View File

@ -0,0 +1,81 @@
<script lang="ts">
import type { NodeInput } from "@nodes/types";
import Input, { Details } from "@nodes/ui";
import type { Writable } from "svelte/store";
interface Nested {
[key: string]: Nested | NodeInput;
}
export let settings: Nested;
export let store: Writable<Record<string, any>>;
export let depth = 0;
console.log(settings);
const keys = Object.keys(settings);
function isNodeInput(v: NodeInput | Nested): v is NodeInput {
return "type" in v;
}
</script>
{#each keys as key}
{@const value = settings[key]}
<div class="wrapper" class:first-level={depth === 0}>
{#if isNodeInput(value)}
<div class="input input-{settings[key].type}">
<label for={key}>{settings[key].label || key}</label>
<Input
id={key}
input={value}
bind:value={$store[value?.setting || key]}
/>
</div>
{:else}
<details>
<summary>{key}</summary>
<div class="content">
<svelte:self settings={settings[key]} {store} depth={depth + 1} />
</div>
</details>
{/if}
</div>
{/each}
<style>
summary {
cursor: pointer;
user-select: none;
}
details {
padding: 1rem;
border-bottom: solid thin var(--outline);
}
.input {
margin-top: 15px;
margin-bottom: 15px;
display: flex;
flex-direction: column;
gap: 10px;
padding-left: 14px;
}
.input-boolean {
display: flex;
flex-direction: row;
}
.input-boolean > label {
order: 2;
}
.first-level > .input {
padding-right: 1rem;
}
.first-level {
border-bottom: solid thin var(--outline);
}
.first-level > details {
border: none;
}
</style>

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { NodeInput } from "@nodes/types"; import type { NodeInput } from "@nodes/types";
import type { Writable } from "svelte/store"; import type { Writable } from "svelte/store";
import { Input } from "@nodes/ui"; import NestedSettings from "./NestedSettings.svelte";
export let setting: { export let setting: {
icon: string; icon: string;
@ -12,27 +12,38 @@
const store = setting.settings; const store = setting.settings;
const keys = setting?.definition interface Nested {
? (Object.keys( [key: string]: NodeInput | Nested;
setting.definition, }
) as unknown as (keyof typeof setting.definition)[])
: []; $: nestedSettings = constructNested();
function constructNested() {
const nested: Nested = {};
for (const key in setting.definition) {
const parts = key.split(".");
let current = nested;
for (let i = 0; i < parts.length; i++) {
if (i === parts.length - 1) {
current[parts[i]] = setting.definition[key];
} else {
current[parts[i]] = current[parts[i]] || {};
current = current[parts[i]] as Nested;
}
}
}
return nested;
}
</script> </script>
<div class="flex flex-col gap-4"> <div class="flex flex-col">
<h1 class="m-0">{setting.id}</h1> <h1 class="m-0 p-4">{setting.id}</h1>
{#each keys as key} <NestedSettings settings={nestedSettings} {store} />
<div>
{#if setting.definition && key in setting.definition}
<Input
id="test"
input={setting.definition[key]}
bind:value={$store[key]}
></Input>
{/if}
<label for="test">
{key}
</label>
</div>
{/each}
</div> </div>
<style>
h1 {
border-bottom: solid thin var(--outline);
}
</style>

View File

@ -3,12 +3,14 @@
import type { Writable } from "svelte/store"; import type { Writable } from "svelte/store";
import SettingsComponent from "./Panel.svelte"; import SettingsComponent from "./Panel.svelte";
import localStore from "$lib/helpers/localStore"; import localStore from "$lib/helpers/localStore";
import type { SvelteComponent } from "svelte";
export let settings: Record< export let settings: Record<
string, string,
{ {
icon: string; icon: string;
id: string; id: string;
component?: typeof SvelteComponent<{}, {}, {}>;
definition: Record<string, NodeInput>; definition: Record<string, NodeInput>;
settings: Writable<Record<string, unknown>>; settings: Writable<Record<string, unknown>>;
} }
@ -50,10 +52,17 @@
</button> </button>
{/each} {/each}
</div> </div>
<div class="content p-2"> <div class="content">
{#if $activePanel && settings[$activePanel]} {#if $activePanel && settings[$activePanel]}
{#key $activePanel} {#key $activePanel}
<SettingsComponent setting={settings[$activePanel]} /> {#if settings[$activePanel].component}
<svelte:component
this={settings[$activePanel].component}
{...settings[$activePanel]}
/>
{:else}
<SettingsComponent setting={settings[$activePanel]} />
{/if}
{/key} {/key}
{/if} {/if}
</div> </div>
@ -66,7 +75,6 @@
display: grid; display: grid;
grid-template-columns: 30px 1fr; grid-template-columns: 30px 1fr;
height: 100%; height: 100%;
background: transparent;
right: 0px; right: 0px;
transform: translateX(calc(100% - 30px)); transform: translateX(calc(100% - 30px));
transition: transition:
@ -76,27 +84,31 @@
min-width: 300px; min-width: 300px;
} }
.content {
background: var(--layer-1);
}
.tabs { .tabs {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-right: solid thin white; border-right: solid thin var(--outline);
} }
.tabs > button { .tabs > button {
height: 30px; height: 30px;
padding: 5px;
background: none; background: none;
background: blue; color: var(--outline);
color: white;
border: none; border: none;
display: flex; display: flex;
align-items: center; align-items: center;
border-bottom: solid thin white; border-bottom: solid thin var(--outline);
border-left: solid thin white; border-left: solid thin var(--outline);
} }
.tabs > button.active { .tabs > button.active {
color: black; color: var(--layer-3);
background: white; background: var(--layer-1);
} }
.visible .tabs > button { .visible .tabs > button {
@ -105,7 +117,7 @@
.visible .tabs { .visible .tabs {
margin-left: -1px; margin-left: -1px;
border-left: solid thin white; border-left: solid thin var(--outline);
} }
.visible > .tabs button:first-child { .visible > .tabs button:first-child {
@ -114,7 +126,7 @@
.visible { .visible {
transform: translateX(0); transform: translateX(0);
border-left: solid thin white; border-left: solid thin var(--outline);
background: black; background: var(--layer-0);
} }
</style> </style>

View File

@ -1,4 +1,5 @@
import localStore from "$lib/helpers/localStore"; import localStore from "$lib/helpers/localStore";
import { label } from "three/examples/jsm/nodes/Nodes.js";
export const AppSettings = localStore("node-settings", { export const AppSettings = localStore("node-settings", {
theme: 0, theme: 0,
@ -7,31 +8,42 @@ export const AppSettings = localStore("node-settings", {
showIndices: false, showIndices: false,
}); });
const themes = ["dark", "light", "catppuccin", "solarized", "high-contrast", "nord", "dracula"];
AppSettings.subscribe((value) => { AppSettings.subscribe((value) => {
if (value.theme === 0) { const classes = document.body.classList;
document.body.classList.remove("theme-catppuccin"); const newClassName = `theme-${themes[value.theme]}`;
} else { for (const className of classes) {
document.body.classList.add("theme-catppuccin"); if (className.startsWith("theme-") && className !== newClassName) {
classes.remove(className);
}
} }
document.body.classList.add(newClassName);
}); });
export const AppSettingTypes = { export const AppSettingTypes = {
theme: { theme: {
type: "select", type: "select",
options: ["dark", "cat"], options: themes,
value: "dark", label: "Theme",
value: themes[0],
}, },
showGrid: { showGrid: {
type: "boolean", type: "boolean",
label: "Show Grid",
value: true, value: true,
}, },
wireframe: { debug: {
type: "boolean", wireframe: {
value: false, type: "boolean",
}, label: "Wireframe",
showIndices: { value: false,
type: "boolean", },
value: false, showIndices: {
}, type: "boolean",
label: "Show Indices",
value: false,
},
}
} }

View File

@ -14,17 +14,13 @@
const runtimeExecutor = new MemoryRuntimeExecutor(nodeRegistry); const runtimeExecutor = new MemoryRuntimeExecutor(nodeRegistry);
let res: Int32Array; let res: Int32Array;
let time = 0;
let graph = localStorage.getItem("graph") let graph = localStorage.getItem("graph")
? JSON.parse(localStorage.getItem("graph")!) ? JSON.parse(localStorage.getItem("graph")!)
: templates.grid(3, 3); : templates.grid(3, 3);
function handleResult(event: CustomEvent<Graph>) { function handleResult(event: CustomEvent<Graph>) {
let a = performance.now();
res = runtimeExecutor.execute(event.detail, get(settings?.graph?.settings)); res = runtimeExecutor.execute(event.detail, get(settings?.graph?.settings));
time = performance.now() - a;
console.log({ res, time });
} }
function handleSave(event: CustomEvent<Graph>) { function handleSave(event: CustomEvent<Graph>) {
@ -89,7 +85,8 @@
<style> <style>
header { header {
border-bottom: solid thin white; border-bottom: solid thin var(--outline);
background-color: var(--layer-1);
} }
.wrapper { .wrapper {

View File

@ -8,7 +8,7 @@ edition = "2018"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features] [features]
default = ["console_error_panic_hook"] default = ["console_error_panic_hook", "wee_alloc"]
console_error_panic_hook = ["dep:console_error_panic_hook"] console_error_panic_hook = ["dep:console_error_panic_hook"]
[dependencies] [dependencies]
@ -22,7 +22,6 @@ utils = { version = "0.1.0", path = "../../../../packages/utils" }
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"] }
macros = { version = "0.1.0", path = "../../../../packages/macros" } macros = { version = "0.1.0", path = "../../../../packages/macros" }
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"] }
glam = "0.27.0" glam = "0.27.0"

View File

@ -14,10 +14,10 @@
}, },
"exports": { "exports": {
".": { ".": {
"types": "./dist/index.d.ts", "types": "./src/lib/index.ts",
"svelte": "./dist/index.js" "svelte": "./src/lib/index.ts"
}, },
"./app.css": "./dist/app.css" "./app.css": "./src/lib/app.css"
}, },
"files": [ "files": [
"dist", "dist",

View File

@ -1,21 +1,34 @@
<script lang="ts"> <script lang="ts">
export let title = "Details"; export let title = "Details";
export let transparent = false;
</script> </script>
<details> <details class:transparent>
<summary>{title}</summary> <summary>{title}</summary>
<div class="content">
<slot /> <slot />
</div>
</details> </details>
<style> <style>
details { details {
padding: 1em; padding: 1em;
color: white; color: var(--text-color);
padding-left: 20px;
background-color: #202020; background-color: #202020;
outline: solid 0.1px white; outline: solid 0.1px white;
border-radius: 2px; border-radius: 2px;
font-weight: 300; font-weight: 300;
font-size: 0.9em; font-size: 0.9em;
} }
details.transparent {
background-color: transparent;
padding: 0;
outline: none;
}
.content {
padding-left: 12px;
}
</style> </style>

View File

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import Checkbox from "$lib/elements/Checkbox.svelte"; import Checkbox from "./elements/Checkbox.svelte";
import Float from "$lib/elements/Float.svelte"; import Float from "./elements/Float.svelte";
import Integer from "$lib/elements/Integer.svelte"; import Integer from "./elements/Integer.svelte";
import Select from "$lib/elements/Select.svelte"; import Select from "./elements/Select.svelte";
import type { NodeInput } from "@nodes/types"; import type { NodeInput } from "@nodes/types";

View File

@ -41,24 +41,92 @@
body { body {
overflow: hidden; overflow: hidden;
--neutral-100: #E7E7E7;
--neutral-200: #A6A6A6;
--neutral-300: #7C7C7C;
--neutral-400: #2D2D2D;
--neutral-500: #171717;
--neutral-800: #111111;
--neutral-900: #060606;
/* Secondary color */ /* Secondary color */
--secondary-color: #6c757d; --secondary-color: #6c757d;
/* Background color */
--background-color-lighter: #202020; --layer-0: var(--neutral-900);
--background-color: #151515; --layer-1: var(--neutral-500);
--background-color-darker: #101010; --layer-2: var(--neutral-400);
--text-color: #aeaeae; --layer-3: var(--neutral-200);
--outline: var(--neutral-400);
--text-color: var(--neutral-200);
color: var(--text-color); color: var(--text-color);
background-color: var(--layer-0);
}
background-color: var(--background-color-darker); body * {
color: var(--text-color);
}
body.theme-light {
--text-color: var(--neutral-800);
--outline: var(--neutral-300);
--layer-0: var(--neutral-100);
--layer-1: var(--neutral-100);
--layer-2: var(--neutral-100);
--layer-3: var(--neutral-500);
}
body.theme-solarized {
--text-color: #425055;
--outline: #93a1a1;
--layer-0: #fdf6e3;
--layer-1: #eee8d5;
--layer-2: #93a1a1;
--layer-3: #586e75;
} }
body.theme-catppuccin { body.theme-catppuccin {
--text-color: #CDD6F4; --text-color: #CDD6F4;
--background-color-lighter: #313244; --outline: #3e3e4f;
--background-color: #1E1E2E; --layer-3: #a8aac8;
--background-color-darker: #11111b; --layer-2: #313244;
--layer-1: #1E1E2E;
--layer-0: #11111b;
}
body.theme-high-contrast {
--text-color: #FFFFFF;
--outline: white;
--layer-0: #000000;
--layer-1: black;
--layer-2: #222222;
--layer-3: #FFFFFF;
}
body.theme-nord {
--text-color: #D8DEE9;
/* Nord snow */
--outline: #4C566A;
/* Nord frost */
--layer-0: #2E3440;
/* Nord polar night */
--layer-1: #3B4252;
/* Nord polar night */
--layer-2: #434C5E;
/* Nord polar night */
--layer-3: #5E81AC;
/* Nord frost */
}
body.theme-dracula {
--text-color: #F8F8F2;
--outline: #6272A4;
--layer-0: #282A36;
--layer-1: #44475A;
--layer-2: #32374D;
--layer-3: #BD93F9;
} }
body { body {

View File

@ -120,12 +120,11 @@
<style> <style>
.component-wrapper { .component-wrapper {
position: relative; position: relative;
background-color: var(--background-color-lighter, #4b4b4b); background-color: var(--layer-2, #4b4b4b);
border-radius: 2px; border-radius: 2px;
user-select: none; user-select: none;
transition: box-shadow 0.3s ease; transition: box-shadow 0.3s ease;
box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.2); outline: solid 1px var(--outline);
outline: none !important;
overflow: hidden; overflow: hidden;
border-radius: var(--border-radius, 2px); border-radius: var(--border-radius, 2px);
} }

View File

@ -109,12 +109,11 @@
.component-wrapper { .component-wrapper {
position: relative; position: relative;
display: flex; display: flex;
background-color: var(--background-color-lighter, #4b4b4b); background-color: var(--layer-2, #4b4b4b);
border-radius: 2px; border-radius: 2px;
user-select: none; user-select: none;
transition: box-shadow 0.3s ease; transition: box-shadow 0.3s ease;
box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.2); outline: solid 1px var(--outline);
outline: none !important;
overflow: hidden; overflow: hidden;
border-radius: var(--border-radius, 2px); border-radius: var(--border-radius, 2px);
} }
@ -141,7 +140,7 @@
top: 0px; top: 0px;
left: 0px; left: 0px;
height: 100%; height: 100%;
background-color: var(--text-color); background-color: var(--layer-3);
opacity: 0.3; opacity: 0.3;
pointer-events: none; pointer-events: none;
} }
@ -166,4 +165,3 @@
border-style: none; border-style: none;
} }
</style> </style>

View File

@ -12,9 +12,10 @@
<style> <style>
select { select {
background: var(--background-color-lighter); background: var(--layer-2);
color: var(--text-color); color: var(--text-color);
font-family: var(--font-family); font-family: var(--font-family);
outline: solid 1px var(--outline);
padding: 0.8em 1em; padding: 0.8em 1em;
border-radius: 5px; border-radius: 5px;
border: none; border: none;