* Add note component
* save note size and position
* move add note button up
* nit
* Add markdown support
* wip
* fix add sticky note button
* fix text update
* Add sticky note to saved flow data
* add note color picker
* Introduce node multiselect
* Add group notes
* Adapt layout to group node
* create a note manager class
* clean reactivity
* clean
* improve adaptive layout to group note
* modify layout based on cached text height
* fined grained graph rendering for notes
* separate noteManager into editor and render
* separate noteManager into editor and render
* create a note change observer
* render note node from context
* simplify note state managment
* show note in flow viewer
* clean dirty changes
* clean selection manager
* fix layout check
* improve bg surface select
* Handle z-index for stacked group notes
* clean selection manager
* exclude notes from rect select
* Allow switch between selection modes with keyboard keys
* improve selection box styling
* prevent dragging note when editing
* nit
* Simplify selection using svelte flow built in feature
* handle note selection separately
* Add min size for notes
* improve selection toggle
* improve mode switch
* make size and position optional for group notes
* Improve initial viewport position
* Add context menu for the canevas
* nit
* Add node context menu
* improve note select
* use clickoutside for note deselect
* use pointerdown outside to close context menu
* nit
* fix selection issues
* make edges non selectable
* improve color palette
* fix backend
* fix backend check
* cargo lock restore
* Add toggle to display notes
* fix note selection
* nit
* account for css offset in for loop
* fix multiple selection pannel styling
* clear flow selection when creating note
* Improve placeholder and note default text
* Escape note edit mode when pressing Esc
* Allow note edition in local dev
* clean
* Handle subflow selection
* prevent group note resizing
* nit
* allow notes in flow expand
* Improve multi select panel
* Allow context menu in note mode
* Add event listenner to fix pane click deselect
* prevent zoom in text area in notes
* improve bounding box styling
* Use control for box selection for non mac users
* nit
* clean notes groups
* nit
* use portal for note actions
* handle assets node when computing note layout
* Simplify layout compute for notes
* use smart color choice for notes
* Switch display note when adding a new note
* clean code
* improve group note bound size calculation
* simplify AI tool nodes and asset handling
* nit
* nit
* improve flow centering
* create group note button
* Improve selection of nodes
* Revert "Improve selection of nodes"
This reverts commit d2c40d82b1.
* refert backend changes
* nit
* improve graph selection
* clean
* make backend work except job runs
* fix notSelectable
---------
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
type FlowNode = { id: string; parentIds?: string[] }
|
|
|
|
/**
|
|
* Use a simple algorithm to complete a group and split it into connected components
|
|
*/
|
|
export function completeAndSplitGroup(groupNodes: string[], flowNodes: FlowNode[]): string[][] {
|
|
if (groupNodes.length <= 1) {
|
|
return groupNodes.length === 1 ? [groupNodes] : []
|
|
}
|
|
|
|
// Build parent map for upward traversal only
|
|
const parents = new Map<string, string[]>()
|
|
for (const node of flowNodes) {
|
|
parents.set(node.id, node.parentIds || [])
|
|
}
|
|
|
|
const groupSet = new Set(groupNodes)
|
|
const assignedComponent = new Map<string, number>()
|
|
const components: Array<Set<string>> = []
|
|
|
|
const mergeComponents = (fromIdx: number, toIdx: number): void => {
|
|
if (fromIdx === toIdx) return
|
|
const target = components[toIdx]
|
|
const source = components[fromIdx]
|
|
|
|
source.forEach((node) => target.add(node))
|
|
source.clear()
|
|
|
|
for (const [nodeId, idx] of assignedComponent.entries()) {
|
|
if (idx === fromIdx) {
|
|
assignedComponent.set(nodeId, toIdx)
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const startNode of groupNodes) {
|
|
if (assignedComponent.has(startNode)) continue
|
|
|
|
const componentIdx = components.length
|
|
components.push(new Set([startNode]))
|
|
assignedComponent.set(startNode, componentIdx)
|
|
|
|
const stack: { nodeId: string; path: string[]; seen: Set<string> }[] = [
|
|
{ nodeId: startNode, path: [startNode], seen: new Set([startNode]) }
|
|
]
|
|
|
|
while (stack.length > 0) {
|
|
const { nodeId, path, seen } = stack.pop()!
|
|
const parentIds = parents.get(nodeId) || []
|
|
|
|
for (const parentId of parentIds) {
|
|
if (seen.has(parentId)) continue
|
|
|
|
const newPath = [...path, parentId]
|
|
const newSeen = new Set(seen)
|
|
newSeen.add(parentId)
|
|
|
|
if (groupSet.has(parentId)) {
|
|
const existingIdx = assignedComponent.get(parentId)
|
|
if (existingIdx === undefined) {
|
|
assignedComponent.set(parentId, componentIdx)
|
|
components[componentIdx].add(parentId)
|
|
stack.push({ nodeId: parentId, path: [parentId], seen: new Set([parentId]) })
|
|
} else if (existingIdx !== componentIdx) {
|
|
mergeComponents(existingIdx, componentIdx)
|
|
}
|
|
|
|
for (const node of newPath) {
|
|
components[componentIdx].add(node)
|
|
}
|
|
} else {
|
|
stack.push({ nodeId: parentId, path: newPath, seen: newSeen })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return components
|
|
.filter((component) => component.size > 0)
|
|
.map((component) =>
|
|
Array.from(component)
|
|
.filter((nodeId) => !nodeId.startsWith('subflow:'))
|
|
.sort()
|
|
)
|
|
.filter((component) => component.length > 0)
|
|
}
|