journeystuff

This commit is contained in:
space-nuko
2023-06-02 15:51:53 -05:00
parent 2197994317
commit c8a3276336
8 changed files with 206 additions and 87 deletions

View File

@@ -4,49 +4,23 @@
jump between past and present sets of parameters.
-->
<script lang="ts">
import Graph from './graph/Graph.svelte'
import GraphNode from './graph/GraphNode.svelte'
import GraphEdge from './graph/GraphEdge.svelte'
import type ComfyApp from './ComfyApp';
import type { ComfyBoxWorkflow } from '$lib/stores/workflowState';
import workflowState from '$lib/stores/workflowState';
import type { WritableJourneyStateStore } from '$lib/stores/journeyStates';
import JourneyRenderer from './JourneyRenderer.svelte';
export let app: ComfyApp;
const nodes = [
{ id: 'N1', label: 'Start' },
{ id: 'N2', label: '4' },
{ id: 'N4', label: '8' },
{ id: 'N5', label: '15' },
{ id: 'N3', label: '16' },
{ id: 'N6', label: '23' },
{ id: 'N7', label: '42' },
{ id: 'N8', label: 'End' }
]
let workflow: ComfyBoxWorkflow;
let journey: WritableJourneyStateStore
const edges = [
{ id: 'E1', source: 'N1', target: 'N2' },
{ id: 'E2', source: 'N2', target: 'N3' },
{ id: 'E3', source: 'N3', target: 'N6' },
{ id: 'E4', source: 'N2', target: 'N4' },
{ id: 'E5', source: 'N4', target: 'N5' },
{ id: 'E6', source: 'N5', target: 'N4', label: '2' },
{ id: 'E7', source: 'N5', target: 'N6' },
{ id: 'E8', source: 'N6', target: 'N7' },
{ id: 'E9', source: 'N7', target: 'N7', label: '3' },
{ id: 'E10', source: 'N7', target: 'N8' }
]
$: workflow = $workflowState.activeWorkflow
$: journey = workflow?.journey
</script>
<div class="journey-view">
<Graph>
{#each nodes as node}
<GraphNode node={node}/>
{/each}
{#each edges as edge}
<GraphEdge edge={edge}/>
{/each}
</Graph>
<JourneyRenderer {workflow} {journey} />
</div>
<style lang="scss">

View File

@@ -0,0 +1,38 @@
<script lang="ts">
import type { WritableJourneyStateStore } from '$lib/stores/journeyStates';
import { ComfyBoxWorkflow } from '$lib/stores/workflowState';
import Graph from './graph/Graph.svelte'
import type { NodeDataDefinition, EdgeDataDefinition } from 'cytoscape';
import { NodeDataDefinition } from 'cytoscape';
export let workflow: ComfyBoxWorkflow | null = null
export let journey: WritableJourneyStateStore | null = ull
const nodes: NodeDataDefinition[] = [
{ id: 'N1', label: 'Start' },
{ id: 'N2', label: '4' },
{ id: 'N4', label: '8' },
{ id: 'N5', label: '15' },
{ id: 'N3', label: '16' },
{ id: 'N6', label: '23' },
{ id: 'N7', label: '42' },
{ id: 'N8', label: 'End' }
]
const edges: EdgeDataDefinition[] = [
{ id: 'E1', source: 'N1', target: 'N2' },
{ id: 'E2', source: 'N2', target: 'N3' },
{ id: 'E3', source: 'N3', target: 'N6' },
{ id: 'E4', source: 'N2', target: 'N4' },
{ id: 'E5', source: 'N4', target: 'N5' },
{ id: 'E6', source: 'N5', target: 'N4', label: '2' },
{ id: 'E7', source: 'N5', target: 'N6' },
{ id: 'E8', source: 'N6', target: 'N7' },
{ id: 'E9', source: 'N7', target: 'N7', label: '3' },
{ id: 'E10', source: 'N7', target: 'N8' }
]
</script>
{#if workflow && journey}
<Graph {nodes} {edges} style="background: var(--neutral-900)" />
{/if}

View File

@@ -7,49 +7,70 @@
</script>
<script lang="ts">
import { onMount, setContext } from "svelte"
import cytoscape from "cytoscape"
import dagre from "cytoscape-dagre"
import GraphStyles from "./GraphStyles"
import cytoscape from "cytoscape"
import dagre from "cytoscape-dagre"
import GraphStyles from "./GraphStyles"
import type { EdgeDataDefinition } from "cytoscape";
import type { NodeDataDefinition } from "cytoscape";
const graphContext: GraphContext = {
getCyInstance: () => cyInstance
export let nodes: ReadonlyArray<NodeDataDefinition>;
export let edges: ReadonlyArray<EdgeDataDefinition>;
export let style: string = ""
$: if (nodes != null && edges != null && refElement != null) {
rebuildGraph()
}
else {
cyInstance = null;
}
setContext(GRAPH_STATE, graphContext)
let refElement = null
let cyInstance: cytoscape.Core = null
let _nodes: any[];
let _edges: any[];
onMount(() => {
cytoscape.use(dagre)
function rebuildGraph() {
cytoscape.use(dagre)
cyInstance = cytoscape({
container: refElement,
style: GraphStyles,
cyInstance = cytoscape({
container: refElement,
style: GraphStyles,
wheelSensitivity: 0.1,
})
})
cyInstance.on("add", () => {
cyInstance
.makeLayout({
name: "dagre",
rankDir: "TB",
nodeSep: 150
})
.run()
})
cyInstance.on("add", () => {
cyInstance
.makeLayout({
name: "dagre",
rankDir: "TB",
nodeSep: 150
})
.run()
})
})
for (const node of nodes) {
cyInstance.add({
group: 'nodes',
data: { ...node }
})
}
for (const edge of edges) {
cyInstance.add({
group: 'edges',
data: { ...edge }
})
}
}
let refElement = null
let cyInstance: cytoscape.Core = null
</script>
<div class="graph" bind:this={refElement}>
{#if cyInstance}
<slot></slot>
{/if}
<div class="cy-graph" {style} bind:this={refElement}>
</div>
<style lang="scss">
.graph {
.cy-graph {
background: white;
width: 100%;
height: 100%;

View File

@@ -1,15 +1,15 @@
<script lang="ts">
import { onMount, getContext } from 'svelte'
import { GRAPH_STATE, type GraphContext } from './Graph.svelte';
import type { EdgeDataDefinition } from 'cytoscape';
export let edge
export let edge: EdgeDataDefinition
const { getCyInstance } = getContext(GRAPH_STATE) as GraphContext;
const cyInstance = getCyInstance()
cyInstance.add({
group: 'edges',
id: edge.id,
data: { ...edge }
})
</script>

View File

@@ -1,15 +1,15 @@
<script lang="ts">
import { onMount, getContext } from 'svelte'
import { GRAPH_STATE, type GraphContext } from './Graph.svelte';
import { onMount, getContext } from 'svelte'
import { GRAPH_STATE, type GraphContext } from './Graph.svelte';
import type { NodeDataDefinition } from 'cytoscape';
export let node
export let node: NodeDataDefinition
const { getCyInstance } = getContext(GRAPH_STATE) as GraphContext
const cyInstance = getCyInstance()
const { getCyInstance } = getContext(GRAPH_STATE) as GraphContext
const cyInstance = getCyInstance()
cyInstance.add({
group: 'nodes',
id: node.id,
data: { ...node }
})
cyInstance.add({
group: 'nodes',
data: { ...node }
})
</script>

View File

@@ -1,27 +1,30 @@
export default [
import type { Stylesheet } from "cytoscape";
const styles: Stylesheet[] = [
{
selector: 'node',
style: {
'width': '50',
'height': '50',
'font-family': 'Arial',
'font-size': '18',
'font-weight': 'bold',
'font-weight': 'normal',
'content': `data(label)`,
'text-valign': 'center',
'text-wrap': 'wrap',
'text-max-width': '140',
'background-color': 'gold',
'border-color': 'orange',
'background-color': '#60a5fa',
'border-color': '#2563eb',
'border-width': '3',
'color': 'darkred'
'color': '#1d3660'
}
},
{
selector: 'node:selected',
style: {
'background-color': 'darkred',
'background-color': '#f97316',
color: 'white',
'border-color': 'darkred',
'border-color': '#ea580c',
'line-color': '#0e76ba',
'target-arrow-color': '#0e76ba'
}
@@ -36,8 +39,8 @@ export default [
'text-background-padding': '3',
'width': '3',
'target-arrow-shape': 'triangle',
'line-color': 'darkred',
'target-arrow-color': 'darkred',
'line-color': '#1d4ed8',
'target-arrow-color': '#1d4ed8',
'font-weight': 'bold'
}
},
@@ -55,3 +58,4 @@ export default [
}
}
]
export default styles;

View File

@@ -0,0 +1,74 @@
import { writable } from 'svelte/store';
import type { Readable, Writable } from 'svelte/store';
import type { DragItemID, IDragItem } from './layoutStates';
import type { LGraphNode, NodeID } from '@litegraph-ts/core';
/*
* A "journey" is like browser history for prompts, except organized in a
* tree-like graph. It lets you save incremental changes to your workflow and
* jump between past and present sets of parameters.
*/
export type JourneyState = {
/*
* Selected drag items.
* NOTE: Order is important, for node grouping actions.
*/
currentJourney: DragItemID[],
/*
* Hovered drag items.
*/
currentHovered: Set<DragItemID>,
/*
* Selected LGraphNodes inside the litegraph canvas.
* NOTE: Order is important, for node grouping actions.
*/
currentJourneyNodes: LGraphNode[],
/*
* Currently hovered nodes.
*/
currentHoveredNodes: Set<NodeID>
}
type JourneyStateOps = {
clear: () => void,
}
export type WritableJourneyStateStore = Writable<JourneyState> & JourneyStateOps;
function create() {
const store: Writable<JourneyState> = writable(
{
currentJourney: [],
currentJourneyNodes: [],
currentHovered: new Set(),
currentHoveredNodes: new Set(),
})
function clear() {
store.set({
currentJourney: [],
currentJourneyNodes: [],
currentHovered: new Set(),
currentHoveredNodes: new Set(),
})
}
return {
...store,
clear
}
}
export type JourneyStateStaticOps = {
create: () => WritableJourneyStateStore
}
// These will be attached to workflows.
const ops: JourneyStateStaticOps = {
create
}
export default ops

View File

@@ -12,6 +12,8 @@ import type { SerializedAppState, SerializedPrompt } from '$lib/components/Comfy
import type ComfyReceiveOutputNode from '$lib/nodes/actions/ComfyReceiveOutputNode';
import type { ComfyBoxPromptExtraData, PromptID } from '$lib/api';
import type { ComfyAPIPromptErrorResponse, ComfyExecutionError } from '$lib/apiErrors';
import type { WritableJourneyStateStore } from './journeyState';
import journeyStates from './journeyStates';
type ActiveCanvas = {
canvas: LGraphCanvas | null;
@@ -115,7 +117,12 @@ export class ComfyBoxWorkflow {
/*
* Completed queue entry ID that holds the last validation/execution error.
*/
lastError?: PromptID
lastError?: PromptID;
/*
* Saved prompt history ("journey") for this workflow
*/
journey: WritableJourneyStateStore;
get layout(): WritableLayoutStateStore | null {
return layoutStates.getLayout(this.id)
@@ -133,6 +140,7 @@ export class ComfyBoxWorkflow {
title,
}
this.graph = new ComfyGraph(this.id);
this.journey = journeyStates.create();
}
notifyModified() {