Spaces:
Running
Running
Manage an "in_progress" property on each node.
Browse files
lynxkite-app/src/lynxkite_app/crdt.py
CHANGED
|
@@ -86,6 +86,7 @@ def clean_input(ws_pyd):
|
|
| 86 |
for node in ws_pyd.nodes:
|
| 87 |
node.data.display = None
|
| 88 |
node.data.error = None
|
|
|
|
| 89 |
node.position.x = 0
|
| 90 |
node.position.y = 0
|
| 91 |
if node.model_extra:
|
|
@@ -175,7 +176,6 @@ delayed_executions = {}
|
|
| 175 |
async def workspace_changed(name: str, changes: pycrdt.MapEvent, ws_crdt: pycrdt.Map):
|
| 176 |
"""Callback to react to changes in the workspace.
|
| 177 |
|
| 178 |
-
|
| 179 |
Args:
|
| 180 |
name: Name of the workspace.
|
| 181 |
changes: Changes performed to the workspace.
|
|
@@ -224,18 +224,17 @@ async def execute(
|
|
| 224 |
assert path.is_relative_to(config.DATA_PATH), "Provided workspace path is invalid"
|
| 225 |
# Save user changes before executing, in case the execution fails.
|
| 226 |
workspace.save(ws_pyd, path)
|
| 227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
await workspace.execute(ws_pyd)
|
| 229 |
workspace.save(ws_pyd, path)
|
| 230 |
|
| 231 |
|
| 232 |
-
def add_crdt_bindings(ws_pyd: workspace.Workspace, ws_crdt: pycrdt.Map):
|
| 233 |
-
for nc, np in zip(ws_crdt["nodes"], ws_pyd.nodes):
|
| 234 |
-
if "data" not in nc:
|
| 235 |
-
nc["data"] = pycrdt.Map()
|
| 236 |
-
np._crdt = nc
|
| 237 |
-
|
| 238 |
-
|
| 239 |
@contextlib.asynccontextmanager
|
| 240 |
async def lifespan(app):
|
| 241 |
global websocket_server
|
|
|
|
| 86 |
for node in ws_pyd.nodes:
|
| 87 |
node.data.display = None
|
| 88 |
node.data.error = None
|
| 89 |
+
node.data.in_progress = False
|
| 90 |
node.position.x = 0
|
| 91 |
node.position.y = 0
|
| 92 |
if node.model_extra:
|
|
|
|
| 176 |
async def workspace_changed(name: str, changes: pycrdt.MapEvent, ws_crdt: pycrdt.Map):
|
| 177 |
"""Callback to react to changes in the workspace.
|
| 178 |
|
|
|
|
| 179 |
Args:
|
| 180 |
name: Name of the workspace.
|
| 181 |
changes: Changes performed to the workspace.
|
|
|
|
| 224 |
assert path.is_relative_to(config.DATA_PATH), "Provided workspace path is invalid"
|
| 225 |
# Save user changes before executing, in case the execution fails.
|
| 226 |
workspace.save(ws_pyd, path)
|
| 227 |
+
with ws_crdt.doc.transaction():
|
| 228 |
+
for nc, np in zip(ws_crdt["nodes"], ws_pyd.nodes):
|
| 229 |
+
if "data" not in nc:
|
| 230 |
+
nc["data"] = pycrdt.Map()
|
| 231 |
+
nc["data"]["in_progress"] = True
|
| 232 |
+
# Nodes get a reference to their CRDT maps, so they can update them as the results come in.
|
| 233 |
+
np._crdt = nc
|
| 234 |
await workspace.execute(ws_pyd)
|
| 235 |
workspace.save(ws_pyd, path)
|
| 236 |
|
| 237 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
@contextlib.asynccontextmanager
|
| 239 |
async def lifespan(app):
|
| 240 |
global websocket_server
|
lynxkite-app/web/src/apiTypes.ts
CHANGED
|
@@ -41,6 +41,7 @@ export interface WorkspaceNodeData {
|
|
| 41 |
};
|
| 42 |
display?: unknown;
|
| 43 |
error?: string | null;
|
|
|
|
| 44 |
[k: string]: unknown;
|
| 45 |
}
|
| 46 |
export interface Position {
|
|
|
|
| 41 |
};
|
| 42 |
display?: unknown;
|
| 43 |
error?: string | null;
|
| 44 |
+
in_progress?: boolean;
|
| 45 |
[k: string]: unknown;
|
| 46 |
}
|
| 47 |
export interface Position {
|
lynxkite-app/web/src/index.css
CHANGED
|
@@ -90,11 +90,14 @@ body {
|
|
| 90 |
}
|
| 91 |
|
| 92 |
.lynxkite-node .title {
|
| 93 |
-
/* background: oklch(75% 0.2 55); */
|
| 94 |
font-weight: bold;
|
| 95 |
padding: 8px;
|
| 96 |
}
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
.handle-name {
|
| 99 |
font-size: 10px;
|
| 100 |
color: black;
|
|
|
|
| 90 |
}
|
| 91 |
|
| 92 |
.lynxkite-node .title {
|
|
|
|
| 93 |
font-weight: bold;
|
| 94 |
padding: 8px;
|
| 95 |
}
|
| 96 |
|
| 97 |
+
.lynxkite-node .title.in-progress {
|
| 98 |
+
background: yellow;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
.handle-name {
|
| 102 |
font-size: 10px;
|
| 103 |
color: black;
|
lynxkite-app/web/src/workspace/nodes/LynxKiteNode.tsx
CHANGED
|
@@ -71,7 +71,10 @@ export default function LynxKiteNode(props: LynxKiteNodeProps) {
|
|
| 71 |
}}
|
| 72 |
>
|
| 73 |
<div className="lynxkite-node" style={props.nodeStyle}>
|
| 74 |
-
<div
|
|
|
|
|
|
|
|
|
|
| 75 |
{data.title}
|
| 76 |
{data.error && <span className="title-icon">⚠️</span>}
|
| 77 |
{expanded || <span className="title-icon">⋯</span>}
|
|
|
|
| 71 |
}}
|
| 72 |
>
|
| 73 |
<div className="lynxkite-node" style={props.nodeStyle}>
|
| 74 |
+
<div
|
| 75 |
+
className={`title bg-primary ${data.in_progress ? "in-progress" : ""}`}
|
| 76 |
+
onClick={titleClicked}
|
| 77 |
+
>
|
| 78 |
{data.title}
|
| 79 |
{data.error && <span className="title-icon">⚠️</span>}
|
| 80 |
{expanded || <span className="title-icon">⋯</span>}
|
lynxkite-core/src/lynxkite/core/workspace.py
CHANGED
|
@@ -26,6 +26,7 @@ class WorkspaceNodeData(BaseConfig):
|
|
| 26 |
params: dict
|
| 27 |
display: Optional[object] = None
|
| 28 |
error: Optional[str] = None
|
|
|
|
| 29 |
# Also contains a "meta" field when going out.
|
| 30 |
# This is ignored when coming back from the frontend.
|
| 31 |
|
|
@@ -44,6 +45,7 @@ class WorkspaceNode(BaseConfig):
|
|
| 44 |
with self._crdt.doc.transaction():
|
| 45 |
self._crdt["data"]["display"] = result.display
|
| 46 |
self._crdt["data"]["error"] = result.error
|
|
|
|
| 47 |
|
| 48 |
def publish_error(self, error: Exception | str):
|
| 49 |
self.publish_result(ops.Result(error=str(error)))
|
|
|
|
| 26 |
params: dict
|
| 27 |
display: Optional[object] = None
|
| 28 |
error: Optional[str] = None
|
| 29 |
+
in_progress: bool = False
|
| 30 |
# Also contains a "meta" field when going out.
|
| 31 |
# This is ignored when coming back from the frontend.
|
| 32 |
|
|
|
|
| 45 |
with self._crdt.doc.transaction():
|
| 46 |
self._crdt["data"]["display"] = result.display
|
| 47 |
self._crdt["data"]["error"] = result.error
|
| 48 |
+
self._crdt["data"]["in_progress"] = False
|
| 49 |
|
| 50 |
def publish_error(self, error: Exception | str):
|
| 51 |
self.publish_result(ops.Result(error=str(error)))
|