Spaces:
Running
Running
Box resizing - not perfect yet.
Browse files- server/ops.py +2 -2
- web/package-lock.json +6 -0
- web/package.json +1 -0
- web/src/LynxKiteFlow.svelte +21 -3
- web/src/LynxKiteNode.svelte +32 -8
server/ops.py
CHANGED
@@ -39,7 +39,7 @@ class BaseConfig(pydantic.BaseModel):
|
|
39 |
class Parameter(BaseConfig):
|
40 |
'''Defines a parameter for an operation.'''
|
41 |
name: str
|
42 |
-
default:
|
43 |
type: Type = None
|
44 |
|
45 |
@staticmethod
|
@@ -77,7 +77,7 @@ def basic_outputs(*names):
|
|
77 |
|
78 |
|
79 |
class Op(BaseConfig):
|
80 |
-
func:
|
81 |
name: str
|
82 |
params: dict[str, Parameter]
|
83 |
inputs: dict[str, Input]
|
|
|
39 |
class Parameter(BaseConfig):
|
40 |
'''Defines a parameter for an operation.'''
|
41 |
name: str
|
42 |
+
default: typing.Any
|
43 |
type: Type = None
|
44 |
|
45 |
@staticmethod
|
|
|
77 |
|
78 |
|
79 |
class Op(BaseConfig):
|
80 |
+
func: typing.Callable = pydantic.Field(exclude=True)
|
81 |
name: str
|
82 |
params: dict[str, Parameter]
|
83 |
inputs: dict[str, Input]
|
web/package-lock.json
CHANGED
@@ -13,6 +13,7 @@
|
|
13 |
"@sveltestack/svelte-query": "^1.6.0",
|
14 |
"@xyflow/svelte": "^0.1.3",
|
15 |
"bootstrap": "^5.3.3",
|
|
|
16 |
"echarts": "^5.5.0",
|
17 |
"fuse.js": "^7.0.0",
|
18 |
"svelte-echarts": "^1.0.0-rc1",
|
@@ -1423,6 +1424,11 @@
|
|
1423 |
}
|
1424 |
}
|
1425 |
},
|
|
|
|
|
|
|
|
|
|
|
1426 |
"node_modules/deepmerge": {
|
1427 |
"version": "4.3.1",
|
1428 |
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
|
|
13 |
"@sveltestack/svelte-query": "^1.6.0",
|
14 |
"@xyflow/svelte": "^0.1.3",
|
15 |
"bootstrap": "^5.3.3",
|
16 |
+
"deep-object-diff": "^1.1.9",
|
17 |
"echarts": "^5.5.0",
|
18 |
"fuse.js": "^7.0.0",
|
19 |
"svelte-echarts": "^1.0.0-rc1",
|
|
|
1424 |
}
|
1425 |
}
|
1426 |
},
|
1427 |
+
"node_modules/deep-object-diff": {
|
1428 |
+
"version": "1.1.9",
|
1429 |
+
"resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz",
|
1430 |
+
"integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA=="
|
1431 |
+
},
|
1432 |
"node_modules/deepmerge": {
|
1433 |
"version": "4.3.1",
|
1434 |
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
web/package.json
CHANGED
@@ -26,6 +26,7 @@
|
|
26 |
"@sveltestack/svelte-query": "^1.6.0",
|
27 |
"@xyflow/svelte": "^0.1.3",
|
28 |
"bootstrap": "^5.3.3",
|
|
|
29 |
"echarts": "^5.5.0",
|
30 |
"fuse.js": "^7.0.0",
|
31 |
"svelte-echarts": "^1.0.0-rc1",
|
|
|
26 |
"@sveltestack/svelte-query": "^1.6.0",
|
27 |
"@xyflow/svelte": "^0.1.3",
|
28 |
"bootstrap": "^5.3.3",
|
29 |
+
"deep-object-diff": "^1.1.9",
|
30 |
"echarts": "^5.5.0",
|
31 |
"fuse.js": "^7.0.0",
|
32 |
"svelte-echarts": "^1.0.0-rc1",
|
web/src/LynxKiteFlow.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import {
|
3 |
import { writable, derived } from 'svelte/store';
|
4 |
import {
|
5 |
SvelteFlow,
|
@@ -131,16 +131,34 @@
|
|
131 |
if (doNotSave) return;
|
132 |
const dragging = g.nodes.find((n) => n.dragging);
|
133 |
if (dragging) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
g = JSON.parse(JSON.stringify(g));
|
135 |
for (const node of g.nodes) {
|
136 |
-
delete node.
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
delete node.selected;
|
138 |
}
|
139 |
const ws = orderedJSON(g);
|
140 |
const bd = orderedJSON($backendWorkspace.data);
|
141 |
if (ws === bd) return;
|
|
|
142 |
$mutation.mutate({ path, ws: g });
|
143 |
-
}
|
144 |
function onconnect(connection: Connection) {
|
145 |
edges.update((edges) => {
|
146 |
// Only one source can connect to a given target.
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { diff } from 'deep-object-diff';
|
3 |
import { writable, derived } from 'svelte/store';
|
4 |
import {
|
5 |
SvelteFlow,
|
|
|
131 |
if (doNotSave) return;
|
132 |
const dragging = g.nodes.find((n) => n.dragging);
|
133 |
if (dragging) return;
|
134 |
+
const resizing = g.nodes.find((n) => n.data?.beingResized);
|
135 |
+
if (resizing) return;
|
136 |
+
scheduleSave(g);
|
137 |
+
});
|
138 |
+
let saveTimeout;
|
139 |
+
function scheduleSave(g) {
|
140 |
+
// A slight delay, so we don't send a million requests when a node is resized, for example.
|
141 |
+
clearTimeout(saveTimeout);
|
142 |
+
saveTimeout = setTimeout(() => save(g), 500);
|
143 |
+
}
|
144 |
+
function save(g) {
|
145 |
g = JSON.parse(JSON.stringify(g));
|
146 |
for (const node of g.nodes) {
|
147 |
+
delete node.measured;
|
148 |
+
delete node.selected;
|
149 |
+
delete node.dragging;
|
150 |
+
delete node.beingResized;
|
151 |
+
}
|
152 |
+
for (const node of g.edges) {
|
153 |
+
delete node.markerEnd;
|
154 |
delete node.selected;
|
155 |
}
|
156 |
const ws = orderedJSON(g);
|
157 |
const bd = orderedJSON($backendWorkspace.data);
|
158 |
if (ws === bd) return;
|
159 |
+
console.log('changed', JSON.stringify(diff(g, $backendWorkspace.data), null, 2));
|
160 |
$mutation.mutate({ path, ws: g });
|
161 |
+
}
|
162 |
function onconnect(connection: Connection) {
|
163 |
edges.update((edges) => {
|
164 |
// Only one source can connect to a given target.
|
web/src/LynxKiteNode.svelte
CHANGED
@@ -1,6 +1,9 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import { Handle, type NodeProps } from '@xyflow/svelte';
|
|
|
3 |
|
|
|
|
|
4 |
type $$Props = NodeProps;
|
5 |
|
6 |
export let nodeStyle = '';
|
@@ -21,10 +24,12 @@
|
|
21 |
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = undefined; positionAbsoluteY;
|
22 |
export let onToggle = () => {};
|
23 |
|
24 |
-
|
25 |
function titleClicked() {
|
26 |
-
|
|
|
27 |
onToggle({ expanded });
|
|
|
28 |
}
|
29 |
function asPx(n: number | undefined) {
|
30 |
return n ? n + 'px' : undefined;
|
@@ -34,7 +39,8 @@
|
|
34 |
const handleOffsetDirection = { top: 'left', bottom: 'left', left: 'top', right: 'top' };
|
35 |
</script>
|
36 |
|
37 |
-
<div class="node-container"
|
|
|
38 |
<div class="lynxkite-node" style={nodeStyle}>
|
39 |
<div class="title" on:click={titleClicked}>
|
40 |
{data.title}
|
@@ -62,6 +68,17 @@
|
|
62 |
</Handle>
|
63 |
{/each}
|
64 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
</div>
|
66 |
|
67 |
<style>
|
@@ -76,15 +93,15 @@
|
|
76 |
}
|
77 |
.node-container {
|
78 |
padding: 8px;
|
79 |
-
|
80 |
-
max-width: 400px;
|
81 |
-
max-height: 400px;
|
82 |
}
|
83 |
.lynxkite-node {
|
84 |
box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.3);
|
|
|
85 |
background: white;
|
|
|
|
|
86 |
overflow-y: auto;
|
87 |
-
border-radius: 4px;
|
88 |
height: 100%;
|
89 |
}
|
90 |
.title {
|
@@ -120,4 +137,11 @@
|
|
120 |
.node-container:hover .handle-name {
|
121 |
visibility: visible;
|
122 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
</style>
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { Handle, useSvelteFlow, useUpdateNodeInternals, type NodeProps, NodeResizeControl } from '@xyflow/svelte';
|
3 |
+
import ChevronDownRight from 'virtual:icons/tabler/chevron-down-right';
|
4 |
|
5 |
+
const { updateNodeData } = useSvelteFlow();
|
6 |
+
const updateNodeInternals = useUpdateNodeInternals();
|
7 |
type $$Props = NodeProps;
|
8 |
|
9 |
export let nodeStyle = '';
|
|
|
24 |
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = undefined; positionAbsoluteY;
|
25 |
export let onToggle = () => {};
|
26 |
|
27 |
+
$: expanded = !data.collapsed;
|
28 |
function titleClicked() {
|
29 |
+
updateNodeData(id, { collapsed: expanded });
|
30 |
+
data = data;
|
31 |
onToggle({ expanded });
|
32 |
+
updateNodeInternals();
|
33 |
}
|
34 |
function asPx(n: number | undefined) {
|
35 |
return n ? n + 'px' : undefined;
|
|
|
39 |
const handleOffsetDirection = { top: 'left', bottom: 'left', left: 'top', right: 'top' };
|
40 |
</script>
|
41 |
|
42 |
+
<div class="node-container" class:expanded={expanded}
|
43 |
+
style:width={asPx(width)} style:height={asPx(expanded ? height : undefined)} style={containerStyle}>
|
44 |
<div class="lynxkite-node" style={nodeStyle}>
|
45 |
<div class="title" on:click={titleClicked}>
|
46 |
{data.title}
|
|
|
68 |
</Handle>
|
69 |
{/each}
|
70 |
</div>
|
71 |
+
{#if expanded}
|
72 |
+
<NodeResizeControl
|
73 |
+
minWidth={100}
|
74 |
+
minHeight={50}
|
75 |
+
style="background: transparent; border: none;"
|
76 |
+
onResizeStart={() => updateNodeData(id, { beingResized: true })}
|
77 |
+
onResizeEnd={() => updateNodeData(id, { beingResized: false })}
|
78 |
+
>
|
79 |
+
<ChevronDownRight class="node-resizer" />
|
80 |
+
</NodeResizeControl>
|
81 |
+
{/if}
|
82 |
</div>
|
83 |
|
84 |
<style>
|
|
|
93 |
}
|
94 |
.node-container {
|
95 |
padding: 8px;
|
96 |
+
position: relative;
|
|
|
|
|
97 |
}
|
98 |
.lynxkite-node {
|
99 |
box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.3);
|
100 |
+
border-radius: 4px;
|
101 |
background: white;
|
102 |
+
}
|
103 |
+
.expanded .lynxkite-node {
|
104 |
overflow-y: auto;
|
|
|
105 |
height: 100%;
|
106 |
}
|
107 |
.title {
|
|
|
137 |
.node-container:hover .handle-name {
|
138 |
visibility: visible;
|
139 |
}
|
140 |
+
:global(.node-resizer) {
|
141 |
+
position: absolute;
|
142 |
+
bottom: 8px;
|
143 |
+
right: 8px;
|
144 |
+
cursor: nwse-resize;
|
145 |
+
color: var(--bs-border-color);
|
146 |
+
}
|
147 |
</style>
|