Skip to content

CavaiFlow node branding & color indicator system

This document describes the current CavaiFlow “branding” system used for organizing nodes (operators) by:

  • Color (branding.color)
  • Label (branding.label)

It also documents the color indicator panel that shows all active colors in the flow.

Files:

  • Application-Frontend/src/pages/Chatbots/components/CavaiFlow/CavaiFlow.vue
  • Application-Frontend/src/pages/Chatbots/components/CavaiFlow/BrandingToolbar.vue
  • Application-Frontend/src/pages/Chatbots/components/CavaiFlow/LabelToolbar.vue
  • Application-Frontend/src/pages/Chatbots/components/CavaiFlow/NodeLabel.vue
  • Application-Frontend/src/pages/Chatbots/components/CavaiFlow/brandingToolbarConstants.ts
  • Application-Frontend/src/pages/Chatbots/components/CavaiFlow/OperatorHelper.ts (initial defaults)
  • Application-Frontend/src/pages/Chatbots/components/CavaiFlow/flowactors/OperatorBase.vue (renders branding)

Data model

Branding lives on each operator at:

operator.properties.body.branding

Shape:

  • color: string (empty string means “none”)
  • label: string

Defaults for new operators are defined in OperatorHelper.getOperatorProps():

  • branding: { color: '', label: '' }

User-facing UI

There are three UI parts:

  1. Branding toolbar (floating)
  2. Label toolbar (floating)
  3. Color indicator panel (fixed panel showing all used colors)

BrandingToolbar (floating)

Component: BrandingToolbar.vue

Responsibilities:

  • Show a row of color swatches (AVAILABLE_COLORS)
  • Indicate “active” swatch when all selected nodes share the same color
  • Provide an “Add label” button that opens the label toolbar

Important implementation detail:

  • activeColors is an array of the selected nodes’ colors.
  • A swatch is considered active when:
    • activeColors.every((color) => color === colorValue)
    • This means multi-select shows active state only if selection is uniform.

Events emitted:

  • select-color → parent applies chosen color to all selected nodes
  • open-label → parent opens label UI for selection

LabelToolbar (floating)

Component: LabelToolbar.vue

Responsibilities:

  • Provide a single text input for setting label
  • Auto-focus/select when opened
  • Save on:
    • Enter
    • Blur
  • Cancel on:
    • Esc

Events emitted:

  • save-label (string)
  • cancel

NodeLabel (inline on node)

Component: NodeLabel.vue

Responsibilities:

  • Render the label badge above a node when branding.label is set
  • Inline editing directly on the node badge (textarea)
  • Auto-resize textarea and keep it visually above the node

Events emitted:

  • edit-start
  • save-label (string)

Note:

  • The badge background uses branding.color (so color and label are visually tied).

CavaiFlow orchestration

Component: CavaiFlow.vue

State

The relevant state in data():

  • availableColors: from AVAILABLE_COLORS
  • focusedComponents: array of selected node keys
  • showBrandingToolbar: whether the color toolbar is visible
  • colorToolbarX, colorToolbarY: toolbar position
  • showLabelToolbar: whether the label toolbar is visible
  • labelToolbarX, labelToolbarY: label toolbar position

Indicator panel state:

  • highlightedColor: string | null
  • colorNodesMap: Record<string, string[]>

Applying a color to selected nodes

When a swatch is clicked in BrandingToolbar, CavaiFlow runs:

  • handleColorSelection(color)

Behavior:

  • Iterates focusedComponents
  • Ensures operator.properties.body.branding exists
  • Vue.set(operator.properties.body.branding, 'color', color)
  • Calls updateColorNodesMap() (for the indicator panel)
  • Emits emitChange and saves checkpoint

Showing and positioning the BrandingToolbar

Configuration constants live in brandingToolbarConstants.ts, including:

  • TOOLBAR_SHOW_DELAY
  • TOOLBAR_POSITION_UPDATE_DELAY
  • TOOLBAR_OFFSET_Y
  • AVAILABLE_COLORS
  • calculateToolbarWidth(colorCount)

Positioning logic:

  • updateColorToolbarPosition(immediate = false)
  • Calculates bounding box of all selected nodes using getBoundingClientRect() on document.querySelector([drag-select-attribute="<nodeKey>"])
  • Places toolbar above the highest node and centered horizontally
  • Uses a delayed show (TOOLBAR_SHOW_DELAY) to avoid jitter

Visibility/UX rules (current):

  • Toolbar is hidden while zooming/panning (panCanvasByWheel)
  • Toolbar is hidden during drag (performance/UX)

Label toolbar orchestration

Open via openLabelToolbar():

  • Uses first focused node as currentEditingOpKey
  • If all selected nodes share the same label, it pre-fills currentLabel, otherwise it starts empty
  • Positions label toolbar above the color toolbar (LABEL_TOOLBAR_OFFSET_Y)

Saving label:

  • handleLabelSave(label) delegates to saveInlineLabel(label, opKey)
  • Ensures branding exists, then Vue.set(..., 'label', label)

Color indicator panel & “highlighting”

The panel renders when there are colors present in the flow:

  • activeColors() computed returns Object.keys(this.colorNodesMap)
  • Each color is shown as a clickable indicator

Interaction:

  • Clicking a color calls highlightNodesByColor(color)
  • This toggles highlightedColor between null and the selected color

Important current behavior:

  • highlightedColor currently only affects the indicator panel UI state (which indicator is “active”).
  • There is no current fade/highlight effect applied to nodes based on highlightedColor.

colorNodesMap is built in updateColorNodesMap() by iterating operatorList and grouping operator keys by branding.color.

Performance consideration:

  • opList watcher calls updateColorNodesMap() only when !isDragging (to avoid re-computation during drag).

Rendering on the node (OperatorBase)

Component: flowactors/OperatorBase.vue

Current branding rendering:

  • The node gets an outline when branding.color is set (and the node is not “first”):
    • outline: 2px dashed <branding.color>
  • The label badge itself is rendered by NodeLabel.vue.

Extension point: enable real node highlight/fade

CavaiFlow.vue currently passes :highlighted-color="highlightedColor" into OperatorBase, but OperatorBase.vue does not currently declare/use this prop.

If you want the original “highlight selected color, fade the rest” behavior, implement it in OperatorBase.vue:

  • Add a highlightedColor prop
  • Compute:
    • isColorHighlighted (node branding color matches)
    • isColorFaded (highlight active, but node color differs)
  • Bind CSS classes accordingly

This will keep CavaiFlow.vue as orchestrator and keep node-level styling local to OperatorBase.vue.

Internal documentation