Appearance
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.vueApplication-Frontend/src/pages/Chatbots/components/CavaiFlow/BrandingToolbar.vueApplication-Frontend/src/pages/Chatbots/components/CavaiFlow/LabelToolbar.vueApplication-Frontend/src/pages/Chatbots/components/CavaiFlow/NodeLabel.vueApplication-Frontend/src/pages/Chatbots/components/CavaiFlow/brandingToolbarConstants.tsApplication-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:
- Branding toolbar (floating)
- Label toolbar (floating)
- 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:
activeColorsis 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 nodesopen-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.labelis set - Inline editing directly on the node badge (textarea)
- Auto-resize textarea and keep it visually above the node
Events emitted:
edit-startsave-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: fromAVAILABLE_COLORSfocusedComponents: array of selected node keysshowBrandingToolbar: whether the color toolbar is visiblecolorToolbarX,colorToolbarY: toolbar positionshowLabelToolbar: whether the label toolbar is visiblelabelToolbarX,labelToolbarY: label toolbar position
Indicator panel state:
highlightedColor: string | nullcolorNodesMap: 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.brandingexists Vue.set(operator.properties.body.branding, 'color', color)- Calls
updateColorNodesMap()(for the indicator panel) - Emits
emitChangeand saves checkpoint
Showing and positioning the BrandingToolbar
Configuration constants live in brandingToolbarConstants.ts, including:
TOOLBAR_SHOW_DELAYTOOLBAR_POSITION_UPDATE_DELAYTOOLBAR_OFFSET_YAVAILABLE_COLORScalculateToolbarWidth(colorCount)
Positioning logic:
updateColorToolbarPosition(immediate = false)- Calculates bounding box of all selected nodes using
getBoundingClientRect()ondocument.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 tosaveInlineLabel(label, opKey)- Ensures
brandingexists, thenVue.set(..., 'label', label)
Color indicator panel & “highlighting”
The panel renders when there are colors present in the flow:
activeColors()computed returnsObject.keys(this.colorNodesMap)- Each color is shown as a clickable indicator
Interaction:
- Clicking a color calls
highlightNodesByColor(color) - This toggles
highlightedColorbetweennulland the selected color
Important current behavior:
highlightedColorcurrently 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:
opListwatcher callsupdateColorNodesMap()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.coloris 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
highlightedColorprop - 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.