feat: add outline to themes
This commit is contained in:
parent
d8ada83db3
commit
c62cfbf75e
@ -1,164 +1,163 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { GraphManager } from './graph-manager.js';
|
import type { GraphManager } from "./graph-manager.js";
|
||||||
import { HTML } from '@threlte/extras';
|
import { HTML } from "@threlte/extras";
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
export let position: [x: number, y: number] | null;
|
export let position: [x: number, y: number] | null;
|
||||||
|
|
||||||
export let graph: GraphManager;
|
export let graph: GraphManager;
|
||||||
|
|
||||||
let input: HTMLInputElement;
|
let input: HTMLInputElement;
|
||||||
let value: string = '';
|
let value: string = "";
|
||||||
let activeNodeId: string = '';
|
let activeNodeId: string = "";
|
||||||
|
|
||||||
const allNodes = graph.getNodeTypes();
|
const allNodes = graph.getNodeTypes();
|
||||||
|
|
||||||
function filterNodes() {
|
function filterNodes() {
|
||||||
return allNodes.filter((node) => node.id.includes(value));
|
return allNodes.filter((node) => node.id.includes(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
$: nodes = value === '' ? allNodes : filterNodes();
|
$: nodes = value === "" ? allNodes : filterNodes();
|
||||||
$: if (nodes) {
|
$: if (nodes) {
|
||||||
if (activeNodeId === '') {
|
if (activeNodeId === "") {
|
||||||
activeNodeId = nodes[0].id;
|
activeNodeId = nodes[0].id;
|
||||||
} else if (nodes.length) {
|
} else if (nodes.length) {
|
||||||
const node = nodes.find((node) => node.id === activeNodeId);
|
const node = nodes.find((node) => node.id === activeNodeId);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
activeNodeId = nodes[0].id;
|
activeNodeId = nodes[0].id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyDown(event: KeyboardEvent) {
|
function handleKeyDown(event: KeyboardEvent) {
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
const value = (event.target as HTMLInputElement).value;
|
|
||||||
|
|
||||||
if (event.key === 'Escape') {
|
if (event.key === "Escape") {
|
||||||
position = null;
|
position = 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 && position) {
|
if (activeNodeId && position) {
|
||||||
graph.createNode({ type: activeNodeId, position });
|
graph.createNode({ type: activeNodeId, position });
|
||||||
position = null;
|
position = null;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
input.disabled = false;
|
input.disabled = false;
|
||||||
setTimeout(() => input.focus(), 50);
|
setTimeout(() => input.focus(), 50);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<HTML position.x={position?.[0]} position.z={position?.[1]} transform={false}>
|
<HTML position.x={position?.[0]} position.z={position?.[1]} transform={false}>
|
||||||
<div class="add-menu-wrapper">
|
<div class="add-menu-wrapper">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<input
|
<input
|
||||||
id="add-menu"
|
id="add-menu"
|
||||||
type="text"
|
type="text"
|
||||||
aria-label="Search for a node type"
|
aria-label="Search for a node type"
|
||||||
role="searchbox"
|
role="searchbox"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
disabled={false}
|
disabled={false}
|
||||||
on:keydown={handleKeyDown}
|
on:keydown={handleKeyDown}
|
||||||
bind:value
|
bind:value
|
||||||
bind:this={input}
|
bind:this={input}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{#each nodes as node}
|
{#each nodes as node}
|
||||||
<div
|
<div
|
||||||
class="result"
|
class="result"
|
||||||
role="treeitem"
|
role="treeitem"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-selected={node.id === activeNodeId}
|
aria-selected={node.id === activeNodeId}
|
||||||
on:keydown={(event) => {
|
on:keydown={(event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === "Enter") {
|
||||||
if (position) {
|
if (position) {
|
||||||
graph.createNode({ type: node.id, position });
|
graph.createNode({ type: node.id, position, props: {} });
|
||||||
position = null;
|
position = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
on:mousedown={() => {
|
on:mousedown={() => {
|
||||||
if (position) {
|
if (position) {
|
||||||
graph.createNode({ type: node.id, position });
|
graph.createNode({ type: node.id, position, props: {} });
|
||||||
position = null;
|
position = null;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
on:focus={() => {
|
on:focus={() => {
|
||||||
activeNodeId = node.id;
|
activeNodeId = node.id;
|
||||||
}}
|
}}
|
||||||
class:selected={node.id === activeNodeId}
|
class:selected={node.id === activeNodeId}
|
||||||
on:mouseover={() => {
|
on:mouseover={() => {
|
||||||
activeNodeId = node.id;
|
activeNodeId = node.id;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{node.id}
|
{node.id}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</HTML>
|
</HTML>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
input {
|
input {
|
||||||
background: var(--background-color-lighter);
|
background: var(--layer-0);
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
padding: 0.8em;
|
padding: 0.8em;
|
||||||
width: calc(100% - 2px);
|
width: calc(100% - 2px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus {
|
input:focus {
|
||||||
outline: solid 2px rgba(255, 255, 255, 0.2);
|
outline: solid 2px rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-menu-wrapper {
|
.add-menu-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: var(--background-color);
|
background: var(--layer-1);
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: solid 2px var(--background-color-lighter);
|
border: solid 2px var(--layer-2);
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
min-height: none;
|
min-height: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.result {
|
.result {
|
||||||
padding: 1em 0.9em;
|
padding: 1em 0.9em;
|
||||||
border-bottom: solid 1px var(--background-color-lighter);
|
border-bottom: solid 1px var(--layer-2);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result[aria-selected='true'] {
|
.result[aria-selected="true"] {
|
||||||
background: var(--background-color-lighter);
|
background: var(--layer-2);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,28 +1,31 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { HTML } from '@threlte/extras';
|
import { HTML } from "@threlte/extras";
|
||||||
|
|
||||||
export let p1 = { x: 0, y: 0 };
|
export let p1 = { x: 0, y: 0 };
|
||||||
export let p2 = { x: 0, y: 0 };
|
export let p2 = { x: 0, y: 0 };
|
||||||
|
|
||||||
export let cameraPosition = [0, 1, 0];
|
export let cameraPosition = [0, 1, 0];
|
||||||
|
|
||||||
$: width = Math.abs(p1.x - p2.x) * cameraPosition[2];
|
$: width = Math.abs(p1.x - p2.x) * cameraPosition[2];
|
||||||
$: height = Math.abs(p1.y - p2.y) * cameraPosition[2];
|
$: height = Math.abs(p1.y - p2.y) * cameraPosition[2];
|
||||||
|
|
||||||
$: x = Math.max(p1.x, p2.x) - width / cameraPosition[2];
|
$: x = Math.max(p1.x, p2.x) - width / cameraPosition[2];
|
||||||
$: y = Math.max(p1.y, p2.y) - height / cameraPosition[2];
|
$: y = Math.max(p1.y, p2.y) - height / cameraPosition[2];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<HTML position.x={x} position.z={y} transform={false}>
|
<HTML position.x={x} position.z={y} transform={false}>
|
||||||
<div class="box-selection" style={`width: ${width}px; height: ${height}px;`}></div>
|
<div
|
||||||
|
class="box-selection"
|
||||||
|
style={`width: ${width}px; height: ${height}px;`}
|
||||||
|
></div>
|
||||||
</HTML>
|
</HTML>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.box-selection {
|
.box-selection {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border: solid 0.2px rgba(200, 200, 200, 0.8);
|
border: solid 0.2px var(--outline);
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -746,7 +746,7 @@
|
|||||||
<div
|
<div
|
||||||
on:wheel={handleMouseScroll}
|
on:wheel={handleMouseScroll}
|
||||||
bind:this={wrapper}
|
bind:this={wrapper}
|
||||||
class="wrapper"
|
class="graph-wrapper"
|
||||||
aria-label="Graph"
|
aria-label="Graph"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@ -794,8 +794,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.wrapper {
|
.graph-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
const manager = new GraphManager(registry);
|
const manager = new GraphManager(registry);
|
||||||
|
|
||||||
|
export const status = manager.status;
|
||||||
|
|
||||||
const updateSettings = debounce((s) => {
|
const updateSettings = debounce((s) => {
|
||||||
manager.setSettings(s);
|
manager.setSettings(s);
|
||||||
}, 200);
|
}, 200);
|
||||||
|
@ -16,6 +16,8 @@ export const colors = writable({
|
|||||||
layer2: new Color().setStyle("#2D2D2D"),
|
layer2: new Color().setStyle("#2D2D2D"),
|
||||||
layer3: new Color().setStyle("#A6A6A6"),
|
layer3: new Color().setStyle("#A6A6A6"),
|
||||||
outline: new Color().setStyle("#000000"),
|
outline: new Color().setStyle("#000000"),
|
||||||
|
active: new Color().setStyle("#c65a19"),
|
||||||
|
selected: new Color().setStyle("#ffffff"),
|
||||||
});
|
});
|
||||||
|
|
||||||
if ("getComputedStyle" in globalThis) {
|
if ("getComputedStyle" in globalThis) {
|
||||||
@ -30,6 +32,8 @@ if ("getComputedStyle" in globalThis) {
|
|||||||
const layer2 = style.getPropertyValue("--layer-2");
|
const layer2 = style.getPropertyValue("--layer-2");
|
||||||
const layer3 = style.getPropertyValue("--layer-3");
|
const layer3 = style.getPropertyValue("--layer-3");
|
||||||
const outline = style.getPropertyValue("--outline");
|
const outline = style.getPropertyValue("--outline");
|
||||||
|
const active = style.getPropertyValue("--active");
|
||||||
|
const selected = style.getPropertyValue("--selected");
|
||||||
|
|
||||||
colors.update(col => {
|
colors.update(col => {
|
||||||
col.layer0.setStyle(layer0);
|
col.layer0.setStyle(layer0);
|
||||||
@ -42,6 +46,10 @@ if ("getComputedStyle" in globalThis) {
|
|||||||
col.layer3.convertLinearToSRGB();
|
col.layer3.convertLinearToSRGB();
|
||||||
col.outline.setStyle(outline);
|
col.outline.setStyle(outline);
|
||||||
col.outline.convertLinearToSRGB();
|
col.outline.convertLinearToSRGB();
|
||||||
|
col.active.setStyle(active);
|
||||||
|
col.active.convertLinearToSRGB();
|
||||||
|
col.selected.setStyle(selected);
|
||||||
|
col.selected.convertLinearToSRGB();
|
||||||
return col;
|
return col;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,12 +6,8 @@ uniform float uHeight;
|
|||||||
|
|
||||||
uniform vec3 uColorDark;
|
uniform vec3 uColorDark;
|
||||||
uniform vec3 uColorBright;
|
uniform vec3 uColorBright;
|
||||||
uniform vec3 uSelectedColor;
|
|
||||||
uniform vec3 uActiveColor;
|
|
||||||
|
|
||||||
uniform bool uSelected;
|
|
||||||
uniform bool uActive;
|
|
||||||
|
|
||||||
|
uniform vec3 uStrokeColor;
|
||||||
uniform float uStrokeWidth;
|
uniform float uStrokeWidth;
|
||||||
|
|
||||||
float msign(in float x) { return (x < 0.0) ? -1.0 : 1.0; }
|
float msign(in float x) { return (x < 0.0) ? -1.0 : 1.0; }
|
||||||
@ -47,16 +43,9 @@ void main(){
|
|||||||
// outside
|
// outside
|
||||||
gl_FragColor = vec4(0.0,0.0,0.0, 0.0);
|
gl_FragColor = vec4(0.0,0.0,0.0, 0.0);
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
if (distance.w > -uStrokeWidth || mod(y+5.0, 10.0) < uStrokeWidth/2.0) {
|
if (distance.w > -uStrokeWidth || mod(y+5.0, 10.0) < uStrokeWidth/2.0) {
|
||||||
// draw the outer stroke
|
// draw the outer stroke
|
||||||
if (uSelected) {
|
gl_FragColor = vec4(uStrokeColor, 1.0);
|
||||||
gl_FragColor = vec4(uSelectedColor, 1.0);
|
|
||||||
} else if (uActive) {
|
|
||||||
gl_FragColor = vec4(uActiveColor, 1.0);
|
|
||||||
} else {
|
|
||||||
gl_FragColor = vec4(uColorBright, 1.0);
|
|
||||||
}
|
|
||||||
}else if (y<5.0){
|
}else if (y<5.0){
|
||||||
// draw the header
|
// draw the header
|
||||||
gl_FragColor = vec4(uColorBright, 1.0);
|
gl_FragColor = vec4(uColorBright, 1.0);
|
||||||
|
@ -67,18 +67,18 @@
|
|||||||
uniforms={{
|
uniforms={{
|
||||||
uColorBright: { value: new Color("#171717") },
|
uColorBright: { value: new Color("#171717") },
|
||||||
uColorDark: { value: new Color("#151515") },
|
uColorDark: { value: new Color("#151515") },
|
||||||
uSelectedColor: { value: new Color("#9d5f28") },
|
uStrokeColor: { value: new Color("#9d5f28") },
|
||||||
uActiveColor: { value: new Color("white") },
|
|
||||||
uSelected: { value: false },
|
|
||||||
uActive: { value: false },
|
|
||||||
uStrokeWidth: { value: 1.0 },
|
uStrokeWidth: { value: 1.0 },
|
||||||
uWidth: { value: 20 },
|
uWidth: { value: 20 },
|
||||||
uHeight: { value: height },
|
uHeight: { value: height },
|
||||||
}}
|
}}
|
||||||
uniforms.uSelected.value={isSelected}
|
|
||||||
uniforms.uActive.value={isActive}
|
|
||||||
uniforms.uColorBright.value={$colors.layer2}
|
uniforms.uColorBright.value={$colors.layer2}
|
||||||
uniforms.uColorDark.value={$colors.layer1}
|
uniforms.uColorDark.value={$colors.layer1}
|
||||||
|
uniforms.uStrokeColor.value={isSelected
|
||||||
|
? $colors.selected
|
||||||
|
: isActive
|
||||||
|
? $colors.active
|
||||||
|
: $colors.outline}
|
||||||
uniforms.uStrokeWidth.value={(7 - z) / 3}
|
uniforms.uStrokeWidth.value={(7 - z) / 3}
|
||||||
/>
|
/>
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
@ -122,12 +122,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.node.active {
|
.node.active {
|
||||||
--stroke: white;
|
--stroke: var(--active);
|
||||||
--stroke-width: 1px;
|
--stroke-width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.selected {
|
.node.selected {
|
||||||
--stroke: #9d5f28;
|
--stroke: var(--selected);
|
||||||
--stroke-width: 1px;
|
--stroke-width: 2px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
duuude
|
@ -11,7 +11,6 @@
|
|||||||
export let store: Writable<Record<string, any>>;
|
export let store: Writable<Record<string, any>>;
|
||||||
export let depth = 0;
|
export let depth = 0;
|
||||||
|
|
||||||
console.log(settings);
|
|
||||||
const keys = Object.keys(settings);
|
const keys = Object.keys(settings);
|
||||||
function isNodeInput(v: NodeInput | Nested): v is NodeInput {
|
function isNodeInput(v: NodeInput | Nested): v is NodeInput {
|
||||||
return "type" in v;
|
return "type" in v;
|
||||||
@ -23,12 +22,18 @@
|
|||||||
<div class="wrapper" class:first-level={depth === 0}>
|
<div class="wrapper" class:first-level={depth === 0}>
|
||||||
{#if isNodeInput(value)}
|
{#if isNodeInput(value)}
|
||||||
<div class="input input-{settings[key].type}">
|
<div class="input input-{settings[key].type}">
|
||||||
<label for={key}>{settings[key].label || key}</label>
|
{#if settings[key].type === "button"}
|
||||||
<Input
|
<button on:click={() => settings[key]?.callback?.()}
|
||||||
id={key}
|
>{key || settings[key].label}</button
|
||||||
input={value}
|
>
|
||||||
bind:value={$store[value?.setting || key]}
|
{:else}
|
||||||
/>
|
<label for={key}>{settings[key].label || key}</label>
|
||||||
|
<Input
|
||||||
|
id={key}
|
||||||
|
input={value}
|
||||||
|
bind:value={$store[value?.setting || key]}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<details>
|
<details>
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { NodeInput } from "@nodes/types";
|
|
||||||
import type { Writable } from "svelte/store";
|
|
||||||
import NestedSettings from "./NestedSettings.svelte";
|
|
||||||
|
|
||||||
export let setting: {
|
|
||||||
icon: string;
|
|
||||||
id: string;
|
|
||||||
definition: Record<string, NodeInput>;
|
|
||||||
settings: Writable<Record<string, unknown>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const store = setting.settings;
|
|
||||||
|
|
||||||
interface Nested {
|
|
||||||
[key: string]: NodeInput | Nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
$: 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>
|
|
||||||
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<h1 class="m-0 p-4">{setting.id}</h1>
|
|
||||||
<NestedSettings settings={nestedSettings} {store} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
h1 {
|
|
||||||
border-bottom: solid thin var(--outline);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,11 +1,11 @@
|
|||||||
<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 SettingsComponent from "./Panel.svelte";
|
|
||||||
import localStore from "$lib/helpers/localStore";
|
import localStore from "$lib/helpers/localStore";
|
||||||
import type { SvelteComponent } from "svelte";
|
import type { SvelteComponent } from "svelte";
|
||||||
|
import NestedSettings from "./NestedSettings.svelte";
|
||||||
|
|
||||||
export let settings: Record<
|
export let panels: Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
icon: string;
|
icon: string;
|
||||||
@ -16,13 +16,15 @@
|
|||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const activePanel = localStore<keyof typeof settings | false>(
|
const activePanel = localStore<keyof typeof panels | false>(
|
||||||
"nodes.settings.activePanel",
|
"nodes.settings.activePanel",
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
$: keys = Object.keys(settings) as unknown as (keyof typeof settings)[];
|
$: keys = panels
|
||||||
|
? (Object.keys(panels) as unknown as (keyof typeof panels)[])
|
||||||
|
: [];
|
||||||
|
|
||||||
function setActivePanel(panel: keyof typeof settings | false) {
|
function setActivePanel(panel: keyof typeof panels | false) {
|
||||||
if (panel === $activePanel) {
|
if (panel === $activePanel) {
|
||||||
$activePanel = false;
|
$activePanel = false;
|
||||||
} else if (panel) {
|
} else if (panel) {
|
||||||
@ -31,6 +33,28 @@
|
|||||||
$activePanel = false;
|
$activePanel = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Nested {
|
||||||
|
[key: string]: NodeInput | Nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructNested(panel: (typeof panels)[keyof typeof panels]) {
|
||||||
|
const nested: Nested = {};
|
||||||
|
|
||||||
|
for (const key in panel.definition) {
|
||||||
|
const parts = key.split(".");
|
||||||
|
let current = nested;
|
||||||
|
for (let i = 0; i < parts.length; i++) {
|
||||||
|
if (i === parts.length - 1) {
|
||||||
|
current[parts[i]] = panel.definition[key];
|
||||||
|
} else {
|
||||||
|
current[parts[i]] = current[parts[i]] || {};
|
||||||
|
current = current[parts[i]] as Nested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper" class:visible={$activePanel}>
|
<div class="wrapper" class:visible={$activePanel}>
|
||||||
@ -42,26 +66,32 @@
|
|||||||
>
|
>
|
||||||
<span class="absolute i-tabler-chevron-left w-6 h-6 block"></span>
|
<span class="absolute i-tabler-chevron-left w-6 h-6 block"></span>
|
||||||
</button>
|
</button>
|
||||||
{#each keys as panel (settings[panel].id)}
|
{#each keys as panel (panels[panel].id)}
|
||||||
<button
|
<button
|
||||||
class="tab"
|
class="tab"
|
||||||
class:active={panel === $activePanel}
|
class:active={panel === $activePanel}
|
||||||
on:click={() => setActivePanel(panel)}
|
on:click={() => setActivePanel(panel)}
|
||||||
>
|
>
|
||||||
<i class={`block w-6 h-6 ${settings[panel].icon}`} />
|
<i class={`block w-6 h-6 ${panels[panel].icon}`} />
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{#if $activePanel && settings[$activePanel]}
|
{#if $activePanel && panels[$activePanel]}
|
||||||
|
<h1 class="m-0 p-4">{panels[$activePanel].id}</h1>
|
||||||
{#key $activePanel}
|
{#key $activePanel}
|
||||||
{#if settings[$activePanel].component}
|
{#if panels[$activePanel]?.component}
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={settings[$activePanel].component}
|
this={panels[$activePanel].component}
|
||||||
{...settings[$activePanel]}
|
{...panels[$activePanel]}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<SettingsComponent setting={settings[$activePanel]} />
|
<div class="flex flex-col">
|
||||||
|
<NestedSettings
|
||||||
|
settings={constructNested(panels[$activePanel])}
|
||||||
|
store={panels[$activePanel].settings}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
@ -84,6 +114,10 @@
|
|||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
border-bottom: solid thin var(--outline);
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
background: var(--layer-1);
|
background: var(--layer-1);
|
||||||
}
|
}
|
||||||
@ -97,6 +131,7 @@
|
|||||||
.tabs > button {
|
.tabs > button {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
border-radius: 0px;
|
||||||
background: none;
|
background: none;
|
||||||
color: var(--outline);
|
color: var(--outline);
|
||||||
border: none;
|
border: none;
|
||||||
|
@ -33,6 +33,19 @@ export const AppSettingTypes = {
|
|||||||
label: "Show Grid",
|
label: "Show Grid",
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
|
stressTest: {
|
||||||
|
amount: {
|
||||||
|
type: "integer",
|
||||||
|
min: 2,
|
||||||
|
max: 15
|
||||||
|
},
|
||||||
|
loadGrid: {
|
||||||
|
type: "button"
|
||||||
|
},
|
||||||
|
loadTree: {
|
||||||
|
type: "button"
|
||||||
|
},
|
||||||
|
},
|
||||||
debug: {
|
debug: {
|
||||||
wireframe: {
|
wireframe: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
import Viewer from "$lib/viewer/Viewer.svelte";
|
import Viewer from "$lib/viewer/Viewer.svelte";
|
||||||
import Settings from "$lib/settings/Settings.svelte";
|
import Settings from "$lib/settings/Settings.svelte";
|
||||||
import { AppSettings, AppSettingTypes } from "$lib/settings/app-settings";
|
import { AppSettings, AppSettingTypes } from "$lib/settings/app-settings";
|
||||||
import { get, writable } from "svelte/store";
|
import { get, writable, type Writable } from "svelte/store";
|
||||||
|
import Keymap from "$lib/settings/Keymap.svelte";
|
||||||
|
|
||||||
const nodeRegistry = new RemoteNodeRegistry("http://localhost:3001");
|
const nodeRegistry = new RemoteNodeRegistry("http://localhost:3001");
|
||||||
const runtimeExecutor = new MemoryRuntimeExecutor(nodeRegistry);
|
const runtimeExecutor = new MemoryRuntimeExecutor(nodeRegistry);
|
||||||
@ -19,6 +20,8 @@
|
|||||||
? JSON.parse(localStorage.getItem("graph")!)
|
? JSON.parse(localStorage.getItem("graph")!)
|
||||||
: templates.grid(3, 3);
|
: templates.grid(3, 3);
|
||||||
|
|
||||||
|
let managerStatus: Writable<"loading" | "error" | "idle">;
|
||||||
|
|
||||||
function handleResult(event: CustomEvent<Graph>) {
|
function handleResult(event: CustomEvent<Graph>) {
|
||||||
res = runtimeExecutor.execute(event.detail, get(settings?.graph?.settings));
|
res = runtimeExecutor.execute(event.detail, get(settings?.graph?.settings));
|
||||||
}
|
}
|
||||||
@ -34,6 +37,12 @@
|
|||||||
settings: AppSettings,
|
settings: AppSettings,
|
||||||
definition: AppSettingTypes,
|
definition: AppSettingTypes,
|
||||||
},
|
},
|
||||||
|
graph: {},
|
||||||
|
shortcuts: {
|
||||||
|
id: "shortcuts",
|
||||||
|
icon: "i-tabler-keyboard",
|
||||||
|
component: Keymap,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleSettings(
|
function handleSettings(
|
||||||
@ -42,27 +51,28 @@
|
|||||||
types: Record<string, unknown>;
|
types: Record<string, unknown>;
|
||||||
}>,
|
}>,
|
||||||
) {
|
) {
|
||||||
settings = {
|
settings.general.definition.stressTest.loadGrid.callback = function () {
|
||||||
...settings,
|
const store = get(settings.general.settings);
|
||||||
graph: {
|
graph = templates.grid(store.amount, store.amount);
|
||||||
icon: "i-tabler-chart-bar",
|
|
||||||
id: "graph",
|
|
||||||
settings: writable(ev.detail.values),
|
|
||||||
definition: ev.detail.types,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
settings.general.definition.stressTest.loadTree.callback = function () {
|
||||||
|
const store = get(settings.general.settings);
|
||||||
|
graph = templates.tree(store.amount);
|
||||||
|
};
|
||||||
|
|
||||||
|
settings.graph = {
|
||||||
|
icon: "i-tabler-chart-bar",
|
||||||
|
id: "graph",
|
||||||
|
settings: writable(ev.detail.values),
|
||||||
|
definition: ev.detail.types,
|
||||||
|
};
|
||||||
|
settings = settings;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper manager-{$managerStatus}">
|
||||||
<header>
|
<header></header>
|
||||||
header
|
|
||||||
<button
|
|
||||||
on:click={() => {
|
|
||||||
graph = templates.grid(15, 15);
|
|
||||||
}}>grid stress-test</button
|
|
||||||
>
|
|
||||||
</header>
|
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
<Grid.Cell>
|
<Grid.Cell>
|
||||||
<Viewer result={res} />
|
<Viewer result={res} />
|
||||||
@ -72,12 +82,13 @@
|
|||||||
<GraphInterface
|
<GraphInterface
|
||||||
registry={nodeRegistry}
|
registry={nodeRegistry}
|
||||||
{graph}
|
{graph}
|
||||||
|
bind:status={managerStatus}
|
||||||
settings={settings?.graph?.settings}
|
settings={settings?.graph?.settings}
|
||||||
on:settings={handleSettings}
|
on:settings={handleSettings}
|
||||||
on:result={handleResult}
|
on:result={handleResult}
|
||||||
on:save={handleSave}
|
on:save={handleSave}
|
||||||
/>
|
/>
|
||||||
<Settings {settings}></Settings>
|
<Settings panels={settings}></Settings>
|
||||||
{/key}
|
{/key}
|
||||||
</Grid.Cell>
|
</Grid.Cell>
|
||||||
</Grid.Row>
|
</Grid.Row>
|
||||||
@ -97,6 +108,17 @@
|
|||||||
grid-template-rows: 50px 1fr;
|
grid-template-rows: 50px 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wrapper :global(canvas) {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manager-loading :global(.graph-wrapper),
|
||||||
|
.manager-loading :global(canvas) {
|
||||||
|
opacity: 0.2;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
:global(html) {
|
:global(html) {
|
||||||
background: rgb(13, 19, 32);
|
background: rgb(13, 19, 32);
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
|
0
packages/ui/src/lib/ShortCut.svelte
Normal file
0
packages/ui/src/lib/ShortCut.svelte
Normal file
@ -57,6 +57,9 @@ body {
|
|||||||
--layer-2: var(--neutral-400);
|
--layer-2: var(--neutral-400);
|
||||||
--layer-3: var(--neutral-200);
|
--layer-3: var(--neutral-200);
|
||||||
|
|
||||||
|
--active: #ffffff;
|
||||||
|
--selected: #c65a19;
|
||||||
|
|
||||||
--outline: var(--neutral-400);
|
--outline: var(--neutral-400);
|
||||||
|
|
||||||
--text-color: var(--neutral-200);
|
--text-color: var(--neutral-200);
|
||||||
@ -76,6 +79,8 @@ body.theme-light {
|
|||||||
--layer-1: var(--neutral-100);
|
--layer-1: var(--neutral-100);
|
||||||
--layer-2: var(--neutral-100);
|
--layer-2: var(--neutral-100);
|
||||||
--layer-3: var(--neutral-500);
|
--layer-3: var(--neutral-500);
|
||||||
|
--active: #000000;
|
||||||
|
--selected: #c65a19;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.theme-solarized {
|
body.theme-solarized {
|
||||||
@ -83,8 +88,10 @@ body.theme-solarized {
|
|||||||
--outline: #93a1a1;
|
--outline: #93a1a1;
|
||||||
--layer-0: #fdf6e3;
|
--layer-0: #fdf6e3;
|
||||||
--layer-1: #eee8d5;
|
--layer-1: #eee8d5;
|
||||||
--layer-2: #93a1a1;
|
--layer-2: #c4c0b4;
|
||||||
--layer-3: #586e75;
|
--layer-3: #586e75;
|
||||||
|
--active: #000000;
|
||||||
|
--selected: #268bd2;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.theme-catppuccin {
|
body.theme-catppuccin {
|
||||||
@ -107,17 +114,13 @@ body.theme-high-contrast {
|
|||||||
|
|
||||||
body.theme-nord {
|
body.theme-nord {
|
||||||
--text-color: #D8DEE9;
|
--text-color: #D8DEE9;
|
||||||
/* Nord snow */
|
|
||||||
--outline: #4C566A;
|
--outline: #4C566A;
|
||||||
/* Nord frost */
|
|
||||||
--layer-0: #2E3440;
|
--layer-0: #2E3440;
|
||||||
/* Nord polar night */
|
|
||||||
--layer-1: #3B4252;
|
--layer-1: #3B4252;
|
||||||
/* Nord polar night */
|
|
||||||
--layer-2: #434C5E;
|
--layer-2: #434C5E;
|
||||||
/* Nord polar night */
|
|
||||||
--layer-3: #5E81AC;
|
--layer-3: #5E81AC;
|
||||||
/* Nord frost */
|
--active: #8999bd;
|
||||||
|
--selected: #b76c3f
|
||||||
}
|
}
|
||||||
|
|
||||||
body.theme-dracula {
|
body.theme-dracula {
|
||||||
@ -133,6 +136,9 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* canvas { */
|
button {
|
||||||
/* display: none !important; */
|
background-color: var(--layer-2);
|
||||||
/* } */
|
border: 1px solid var(--outline);
|
||||||
|
padding: 8px 9px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user