editor WIP
This commit is contained in:
parent
e5e11a6fac
commit
ba43823d59
6 changed files with 115 additions and 36 deletions
|
|
@ -15,7 +15,9 @@
|
||||||
"@types/node": "npm:@types/node@^24.6.0",
|
"@types/node": "npm:@types/node@^24.6.0",
|
||||||
"svelte": "npm:svelte@^5.39.6",
|
"svelte": "npm:svelte@^5.39.6",
|
||||||
"svelte-check": "npm:svelte-check@^4.3.2",
|
"svelte-check": "npm:svelte-check@^4.3.2",
|
||||||
|
"typeid-js": "npm:typeid-js@^1.2.0",
|
||||||
"typescript": "npm:typescript@~5.9.3",
|
"typescript": "npm:typescript@~5.9.3",
|
||||||
|
"uuid": "npm:uuid@^13.0.0",
|
||||||
"vite": "npm:rolldown-vite@7.1.14",
|
"vite": "npm:rolldown-vite@7.1.14",
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
|
|
|
||||||
24
platform/loom/deno.lock
generated
24
platform/loom/deno.lock
generated
|
|
@ -9,10 +9,16 @@
|
||||||
"npm:@xyflow/svelte@^1.3.1": "1.3.1_svelte@5.39.11__acorn@8.15.0",
|
"npm:@xyflow/svelte@^1.3.1": "1.3.1_svelte@5.39.11__acorn@8.15.0",
|
||||||
"npm:express@*": "5.1.0",
|
"npm:express@*": "5.1.0",
|
||||||
"npm:rolldown-vite@7.1.14": "7.1.14_@types+node@24.7.0_picomatch@4.0.3",
|
"npm:rolldown-vite@7.1.14": "7.1.14_@types+node@24.7.0_picomatch@4.0.3",
|
||||||
|
"npm:svelte-check@*": "4.3.3_svelte@5.39.11__acorn@8.15.0_typescript@5.9.3",
|
||||||
"npm:svelte-check@^4.3.2": "4.3.3_svelte@5.39.11__acorn@8.15.0_typescript@5.9.3",
|
"npm:svelte-check@^4.3.2": "4.3.3_svelte@5.39.11__acorn@8.15.0_typescript@5.9.3",
|
||||||
"npm:svelte-splitpanes@8": "8.0.9_svelte@5.39.11__acorn@8.15.0",
|
"npm:svelte-splitpanes@8": "8.0.9_svelte@5.39.11__acorn@8.15.0",
|
||||||
"npm:svelte@^5.39.6": "5.39.11_acorn@8.15.0",
|
"npm:svelte@^5.39.6": "5.39.11_acorn@8.15.0",
|
||||||
|
"npm:typeid-js@*": "1.2.0",
|
||||||
|
"npm:typeid-js@^1.2.0": "1.2.0",
|
||||||
|
"npm:typescript@5.9.3": "5.9.3",
|
||||||
"npm:typescript@~5.9.3": "5.9.3",
|
"npm:typescript@~5.9.3": "5.9.3",
|
||||||
|
"npm:uuid@*": "10.0.0",
|
||||||
|
"npm:uuid@13": "13.0.0",
|
||||||
"npm:vite@*": "7.1.9_@types+node@24.7.0_picomatch@4.0.3"
|
"npm:vite@*": "7.1.9_@types+node@24.7.0_picomatch@4.0.3"
|
||||||
},
|
},
|
||||||
"npm": {
|
"npm": {
|
||||||
|
|
@ -1286,6 +1292,12 @@
|
||||||
"mime-types"
|
"mime-types"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"typeid-js@1.2.0": {
|
||||||
|
"integrity": "sha512-t76ZucAnvGC60ea/HjVsB0TSoB0cw9yjnfurUgtInXQWUI/VcrlZGpO23KN3iSe8yOGUgb1zr7W7uEzJ3hSljA==",
|
||||||
|
"dependencies": [
|
||||||
|
"uuid@10.0.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
"typescript@5.9.3": {
|
"typescript@5.9.3": {
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"bin": true
|
"bin": true
|
||||||
|
|
@ -1296,6 +1308,14 @@
|
||||||
"unpipe@1.0.0": {
|
"unpipe@1.0.0": {
|
||||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
|
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
|
||||||
},
|
},
|
||||||
|
"uuid@10.0.0": {
|
||||||
|
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||||
|
"bin": true
|
||||||
|
},
|
||||||
|
"uuid@13.0.0": {
|
||||||
|
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||||
|
"bin": true
|
||||||
|
},
|
||||||
"vary@1.1.2": {
|
"vary@1.1.2": {
|
||||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
|
||||||
},
|
},
|
||||||
|
|
@ -1345,7 +1365,9 @@
|
||||||
"npm:svelte-check@^4.3.2",
|
"npm:svelte-check@^4.3.2",
|
||||||
"npm:svelte-splitpanes@8",
|
"npm:svelte-splitpanes@8",
|
||||||
"npm:svelte@^5.39.6",
|
"npm:svelte@^5.39.6",
|
||||||
"npm:typescript@~5.9.3"
|
"npm:typeid-js@^1.2.0",
|
||||||
|
"npm:typescript@~5.9.3",
|
||||||
|
"npm:uuid@13"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,36 +6,6 @@
|
||||||
|
|
||||||
// import { Splitpanes, Pane } from 'svelte-splitpanes'
|
// import { Splitpanes, Pane } from 'svelte-splitpanes'
|
||||||
|
|
||||||
import { SvelteFlow, Controls, Background, MiniMap, Panel, ConnectionLineType } from '@xyflow/svelte';
|
|
||||||
import '@xyflow/svelte/dist/style.css';
|
|
||||||
|
|
||||||
let nodes = $state.raw<Node[]>([
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
position: { x: 0, y: 0 },
|
|
||||||
data: { label: 'Hello' },
|
|
||||||
type: 'input'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
position: { x: 100, y: 100 },
|
|
||||||
data: { label: 'Hello you' },
|
|
||||||
type: 'output'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
position: { x: 200, y: 0 },
|
|
||||||
data: { label: 'what about me'}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
let edges = $state.raw<Edge[]>([
|
|
||||||
{
|
|
||||||
id: 'e1-2',
|
|
||||||
source: '1',
|
|
||||||
target: '2',
|
|
||||||
type: 'smoothstep'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
|
|
||||||
0
platform/loom/editor/src/lib/CustomHandle.svelte
Normal file
0
platform/loom/editor/src/lib/CustomHandle.svelte
Normal file
55
platform/loom/editor/src/lib/CustomNode.svelte
Normal file
55
platform/loom/editor/src/lib/CustomNode.svelte
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { NodeResizer,
|
||||||
|
Position,
|
||||||
|
useSvelteFlow,
|
||||||
|
Handle,
|
||||||
|
type NodeProps } from '@xyflow/svelte';
|
||||||
|
|
||||||
|
let { id, data, selected }: NodeProps = $props();
|
||||||
|
let { updateNodeData } = useSvelteFlow();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={["sigil", !selected ? "border" : '']}>
|
||||||
|
<div>
|
||||||
|
<label for="text">{id}</label>
|
||||||
|
<input
|
||||||
|
id="text"
|
||||||
|
name="text"
|
||||||
|
value={data.text}
|
||||||
|
oninput={(evt) => {
|
||||||
|
const target = evt.target as HTMLInputElement;
|
||||||
|
updateNodeData(id, { text: target?.value });
|
||||||
|
}}
|
||||||
|
class="nodrag"
|
||||||
|
/>
|
||||||
|
{#if selected}
|
||||||
|
<NodeResizer />
|
||||||
|
{/if}
|
||||||
|
<Handle type="target" position={Position.Top} />
|
||||||
|
<Handle type="source" position={Position.Bottom} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sigil > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sigil {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
background-color: ivory;
|
||||||
|
border: 1px solid rgba(0,0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sigil.border {
|
||||||
|
border: 1px solid gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sigil > div > label {
|
||||||
|
font-size: 8px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -5,33 +5,63 @@
|
||||||
MiniMap,
|
MiniMap,
|
||||||
ConnectionLineType
|
ConnectionLineType
|
||||||
} from '@xyflow/svelte';
|
} from '@xyflow/svelte';
|
||||||
|
import type { Node,
|
||||||
|
Edge,
|
||||||
|
Connection
|
||||||
|
} from '@xyflow/svelte';
|
||||||
import '@xyflow/svelte/dist/style.css';
|
import '@xyflow/svelte/dist/style.css';
|
||||||
|
import { TypeID } from 'typeid-js';
|
||||||
|
import { v5, NIL } from 'uuid';
|
||||||
|
|
||||||
|
import CustomNode from './CustomNode.svelte';
|
||||||
|
|
||||||
|
function tid_v5(prefix: string, data: string): TypeID<typeof prefix> {
|
||||||
|
return TypeID.fromUUID(prefix, v5(data, v5(prefix, NIL)));
|
||||||
|
}
|
||||||
|
|
||||||
let nodes = $state.raw<Node[]>([
|
let nodes = $state.raw<Node[]>([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: tid_v5('sigil', '1').toString(),
|
||||||
position: { x: 0, y: 0 },
|
position: { x: 0, y: 0 },
|
||||||
data: { label: 'Hello' },
|
data: { label: 'Hello' },
|
||||||
type: 'input'
|
type: 'sigil'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: tid_v5('sigil', '2').toString(),
|
||||||
position: { x: 100, y: 100 },
|
position: { x: 100, y: 100 },
|
||||||
data: { label: 'Hello you' },
|
data: { label: 'Hello you' },
|
||||||
type: 'output'
|
type: 'output'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: tid_v5('sigil','3').toString(),
|
||||||
position: { x: 200, y: 0 },
|
position: { x: 200, y: 0 },
|
||||||
data: { label: 'what about me'}
|
data: { label: 'what about me'}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
let edges = $state.raw<Edge[]>([]);
|
let edges = $state.raw<Edge[]>([]);
|
||||||
|
|
||||||
|
function onconnect(connection: Connection) {
|
||||||
|
let strandId = tid_v5('strand', connection.source + connection.target);
|
||||||
|
|
||||||
|
let filteredEdges = edges.filter(e => {
|
||||||
|
let sourceHandlesDiffer = e.source !== connection.source
|
||||||
|
|| (e.sourceHandle ?? null) !== (connection.sourceHandle ?? null);
|
||||||
|
let targetHandlesDiffer = e.target !== connection.target
|
||||||
|
|| (e.targetHandle ?? null) !== (connection.targetHandle ?? null);
|
||||||
|
|
||||||
|
return sourceHandlesDiffer && targetHandlesDiffer;
|
||||||
|
});
|
||||||
|
|
||||||
|
edges = [...filteredEdges, { id: strandId.toString(), ...connection } ];
|
||||||
|
}
|
||||||
|
|
||||||
|
let nodeTypes = { sigil: CustomNode };
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SvelteFlow bind:nodes bind:edges fitView snapGrid={[10,10]}
|
<SvelteFlow bind:nodes bind:edges fitView snapGrid={[10,10]}
|
||||||
defaultEdgeOptions={{ type: 'smoothstep', zIndex: 0 }}
|
defaultEdgeOptions={{ type: 'smoothstep', zIndex: 0 }}
|
||||||
connectionLineType={ConnectionLineType.SmoothStep}>
|
connectionLineType={ConnectionLineType.SmoothStep}
|
||||||
|
onconnect={onconnect} nodeTypes={nodeTypes}>
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background />
|
<Background />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue