meadow-integration 1.0.19 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/example-applications/mapping-demo/.quackage.json +10 -0
  2. package/example-applications/mapping-demo/README.md +99 -0
  3. package/example-applications/mapping-demo/data/books-sample.csv +21 -0
  4. package/example-applications/mapping-demo/generate-build-config.js +44 -0
  5. package/example-applications/mapping-demo/mappings/books-to-book.json +14 -0
  6. package/example-applications/mapping-demo/package.json +14 -0
  7. package/example-applications/mapping-demo/server.js +814 -0
  8. package/example-applications/mapping-demo/source/MappingDemoApp.js +52 -0
  9. package/example-applications/mapping-demo/source/views/MappingDemoEditorView.js +186 -0
  10. package/example-applications/mapping-demo/web/index.html +892 -0
  11. package/example-applications/mapping-demo/web/mapping-demo-editor.js +3195 -0
  12. package/example-applications/mapping-demo/web/mapping-demo-editor.js.map +1 -0
  13. package/example-applications/mapping-demo/web/mapping-demo-editor.min.js +2 -0
  14. package/example-applications/mapping-demo/web/mapping-demo-editor.min.js.map +1 -0
  15. package/example-applications/mapping-demo/web/pict.min.js +12 -0
  16. package/package.json +11 -5
  17. package/source/Meadow-Integration-Browser.js +31 -0
  18. package/source/Meadow-Integration.js +30 -1
  19. package/source/services/certainty/Service-CertaintyAccumulator.js +402 -0
  20. package/source/services/clone/Meadow-Service-Sync-Entity-Initial.js +16 -3
  21. package/source/services/clone/Meadow-Service-Sync-Entity-Ongoing.js +15 -2
  22. package/source/services/clone/Meadow-Service-Sync.js +21 -0
  23. package/source/services/parser/Service-FileParser-CSV.js +263 -0
  24. package/source/services/parser/Service-FileParser-FixedWidth.js +158 -0
  25. package/source/services/parser/Service-FileParser-JSON.js +255 -0
  26. package/source/services/parser/Service-FileParser-XLSX.js +194 -0
  27. package/source/services/parser/Service-FileParser-XML.js +190 -0
  28. package/source/services/parser/Service-FileParser.js +142 -0
  29. package/source/views/MappingEditor-SchemaUtils.js +71 -0
  30. package/source/views/PictView-MeadowMappingEditor.js +1299 -0
  31. package/source/views/flow-cards/FlowCard-MappingSource.js +50 -0
  32. package/source/views/flow-cards/FlowCard-MappingTarget.js +49 -0
  33. package/source/views/flow-cards/FlowCard-SolverExpression.js +78 -0
  34. package/source/views/flow-cards/FlowCard-TemplateExpression.js +77 -0
  35. package/test/Meadow-Integration-CloneDeleteSync_test.js +809 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["mapping-demo-editor.min.js","source/MappingDemoApp.js","../../node_modules/browser-pack/_prelude.js","source/views/MappingDemoEditorView.js","../../node_modules/fable-serviceproviderbase/package.json","../../node_modules/fable-serviceproviderbase/source/Fable-ServiceProviderBase.js","../../node_modules/pict-application/package.json","../../node_modules/pict-application/source/Pict-Application.js","../../node_modules/pict-provider/package.json","../../node_modules/pict-provider/source/Pict-Provider.js","../../node_modules/pict-section-flow/source/Pict-Section-Flow.js","../../node_modules/pict-section-flow/source/PictFlowCard.js","../../node_modules/pict-section-flow/source/PictFlowCardPropertiesPanel.js","../../node_modules/pict-section-flow/source/panels/FlowCardPropertiesPanel-Form.js","../../node_modules/pict-section-flow/source/panels/FlowCardPropertiesPanel-Markdown.js","../../node_modules/pict-section-flow/source/panels/FlowCardPropertiesPanel-Template.js","../../node_modules/pict-section-flow/source/panels/FlowCardPropertiesPanel-View.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-CSS.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-ConnectorShapes.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-EventHandler.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-Geometry.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-Icons.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-Layouts.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-NodeTypes.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-Noise.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-PanelChrome.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-SVGHelpers.js","../../node_modules/pict-section-flow/source/providers/PictProvider-Flow-Theme.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-ConnectionHandleManager.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-ConnectionRenderer.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-DataManager.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-InteractionManager.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-Layout.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-PanelManager.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-PathGenerator.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-PortRenderer.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-RenderManager.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-SelectionManager.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-Tether.js","../../node_modules/pict-section-flow/source/services/PictService-Flow-ViewportManager.js","../../node_modules/pict-section-flow/source/views/PictView-Flow-FloatingToolbar.js","../../node_modules/pict-section-flow/source/views/PictView-Flow-Node.js","../../node_modules/pict-section-flow/source/views/PictView-Flow-PropertiesPanel.js","../../node_modules/pict-section-flow/source/views/PictView-Flow-Toolbar.js","../../node_modules/pict-section-flow/source/views/PictView-Flow.js","../../node_modules/pict-view/package.json","../../node_modules/pict-view/source/Pict-View.js","../../source/views/MappingEditor-SchemaUtils.js","../../source/views/PictView-MeadowMappingEditor.js","../../source/views/flow-cards/FlowCard-MappingSource.js","../../source/views/flow-cards/FlowCard-MappingTarget.js","../../source/views/flow-cards/FlowCard-SolverExpression.js","../../source/views/flow-cards/FlowCard-TemplateExpression.js"],"names":["_regenerator","e","t","r","Symbol","n","iterator","o","toStringTag","i","c","prototype","Generator","u","Object","create","_regeneratorDefine2","f","p","y","G","v","a","d","bind","length","l","TypeError","call","done","value","GeneratorFunction","GeneratorFunctionPrototype","getPrototypeOf","this","setPrototypeOf","__proto__","displayName","w","m","defineProperty","_invoke","enumerable","configurable","writable","asyncGeneratorStep","Promise","resolve","then","_asyncToGenerator","arguments","apply","_next","_throw","_createForOfIteratorHelper","Array","isArray","_unsupportedIterableToArray","_n","F","s","next","_arrayLikeToArray","toString","slice","constructor","name","from","test","_defineProperty","_toPropertyKey","_classCallCheck","_defineProperties","key","_createClass","_toPrimitive","_typeof","toPrimitive","String","Number","_callSuper","_getPrototypeOf","_possibleConstructorReturn","_isNativeReflectConstruct","Reflect","construct","_assertThisInitialized","ReferenceError","Boolean","valueOf","_superPropGet","_get","get","_superPropBase","getOwnPropertyDescriptor","hasOwnProperty","_inherits","_setPrototypeOf","exports","module","define","amd","window","global","self","MappingDemoApplication","require","Error","code","libPictApplication","libMappingDemoEditorView","_libPictApplication","pFable","pOptions","pServiceHash","_this","pict","addView","default_configuration","fCallback","openMappingEditor","tmpPlaceholder","document","getElementById","style","display","_Pict","views","editMappings","Name","Hash","AutoSolveAfterInitialize","libMeadowMappingEditorView","_ViewConfiguration","ViewIdentifier","DefaultRenderable","DefaultDestinationAddress","AutoRender","CSS","Templates","Template","Renderables","RenderableHash","TemplateHash","ContentDestinationAddress","RenderMethod","MappingDemoEditorView","_libMeadowMappingEdit","pContextID","fetch","json","Stores","pMappingID","method","pSourceID","pRecordLimit","pData","headers","body","JSON","stringify","tmpEditor","classList","remove","dispatchEvent","CustomEvent","version","description","main","scripts","start","tests","coverage","build","types","check","mocha","diff","extension","package","reporter","slow","timeout","ui","repository","type","url","keywords","author","license","bugs","homepage","devDependencies","fable","quackage","typescript","libPackage","FableServiceProviderBase","UUID","options","services","servicesMap","isFable","connectFable","_PackageFableServiceProvider","getUUID","concat","Math","floor","random","serviceType","tmpErrorMessage","console","log","Logging","CoreServiceProviderBase","lint","eslint","dependencies","libFableServiceBase","defaultPictSettings","MainViewportViewIdentifier","MainViewportRenderableHash","MainViewportDestinationAddress","MainViewportDefaultDataAddress","AutoRenderMainViewportViewAfterInitialize","AutoRenderViewsAfterInitialize","AutoLoginAfterInitialize","AutoLoadDataAfterLogin","ConfigurationOnlyViews","Manifests","IdentifierAddressPrefix","PictApplication","_libFableServiceBase","_this2","tmpCarryOverConfiguration","settings","PictApplicationConfiguration","assign","parse","_Package","AppData","Bundle","initializeTimestamp","lastSolvedTimestamp","lastLoginTimestamp","lastMarshalFromViewsTimestamp","lastMarshalToViewsTimestamp","lastAutoRenderTimestamp","lastLoadDataTimestamp","tmpManifestKeys","keys","tmpManifestKey","instantiateServiceProvider","LogNoisiness","trace","onPreSolve","onBeforeSolve","onSolve","tmpLoadedProviders","providers","tmpProvidersToSolve","tmpProvider","AutoSolveWithApp","push","sort","b","AutoSolveOrdinal","solve","tmpLoadedViews","tmpViewsToSolve","tmpView","AutoInitialize","AutoInitializeOrdinal","onAfterSolve","getTimeStamp","_this3","tmpAnticipate","instantiateServiceProviderWithoutRegistration","anticipate","onBeforeSolveAsync","tmpCallback","warn","pError","error","solveAsync","onSolveAsync","onAfterSolveAsync","wait","_this4","onBeforeLoginAsync","onLoginAsync","onAfterLoginAsync","fNext","isLoggedIn","loadDataAsync","_this5","onBeforeLoadDataAsync","tmpProvidersToLoadData","AutoLoadDataWithApp","AutoLoadDataOrdinal","_i7","_tmpProvidersToLoadDa","onLoadDataAsync","_i8","_tmpProvidersToLoadDa2","onAfterLoadDataAsync","_i9","_tmpProvidersToLoadDa3","_this6","onBeforeSaveDataAsync","tmpProvidersToSaveData","AutoSaveDataWithApp","AutoSaveDataOrdinal","_i0","_tmpProvidersToSaveDa","onSaveDataAsync","_i1","_tmpProvidersToSaveDa2","onAfterSaveDataAsync","_i10","_tmpProvidersToSaveDa3","lastSaveDataTimestamp","onBeforeInitialize","onInitialize","LogControlFlow","tmpViewIdentifier","info","tmpProvidersToInitialize","initialize","tmpViewsToInitialize","onAfterInitialize","render","onCompletionOfInitialize","_this7","onCompletionOfInitializeAsync","onBeforeInitializeAsync","onInitializeAsync","initializeAsync","onAfterInitializeAsync","loginAsync","renderMainViewportAsync","message","stack","onBeforeMarshalFromViews","onMarshalFromViews","tmpViewsToMarshalFromViews","marshalFromView","onAfterMarshalFromViews","_this8","onBeforeMarshalFromViewsAsync","marshalFromViewAsync","onMarshalFromViewsAsync","onAfterMarshalFromViewsAsync","onBeforeMarshalToViews","onMarshalToViews","tmpViewsToMarshalToViews","marshalToView","onAfterMarshalToViews","_this9","onBeforeMarshalToViewsAsync","marshalToViewAsync","onMarshalToViewsAsync","onAfterMarshalToViewsAsync","onBeforeRender","pViewIdentifier","pRenderableHash","pRenderDestinationAddress","pTemplateDataAddress","tmpRenderableHash","tmpRenderDestinationAddress","tmpTemplateDataAddress","PictView","onRender","onAfterRender","_this0","tmpRenderAnticipate","newAnticipate","onBeforeRenderAsync","onRenderAsync","renderAsync","onAfterRenderAsync","_this1","AutoRenderOrdinal","_this10","defaultPictProviderSettings","ProviderIdentifier","PictProvider","_libFableServiceBase2","_this11","tmpDefaultTemplate","Source","TemplateProvider","addDefaultTemplate","Prefix","Postfix","_this12","Stack","onPreRender","PictViewFlowNode","PictViewFlowToolbar","PictViewFlowFloatingToolbar","PictServiceFlowInteractionManager","PictServiceFlowConnectionRenderer","PictServiceFlowTether","PictServiceFlowLayout","PictServiceFlowPathGenerator","PictServiceFlowViewportManager","PictServiceFlowSelectionManager","PictServiceFlowPanelManager","PictServiceFlowDataManager","PictServiceFlowConnectionHandleManager","PictServiceFlowRenderManager","PictServiceFlowPortRenderer","PictProviderFlowNodeTypes","PictProviderFlowEventHandler","PictProviderFlowLayouts","PictProviderFlowSVGHelpers","PictProviderFlowGeometry","PictProviderFlowPanelChrome","PictProviderFlowCSS","PictProviderFlowIcons","PictProviderFlowConnectorShapes","PictFlowCard","PictFlowCardPropertiesPanel","FlowCardPropertiesPanelTemplate","FlowCardPropertiesPanelMarkdown","FlowCardPropertiesPanelForm","FlowCardPropertiesPanelView","PictViewFlowPropertiesPanel","_libFableServiceProvi","_this13","tmpOptions","cardTitle","Title","cardName","cardCode","Code","cardDescription","Description","cardIcon","Icon","cardPreviewImage","PreviewImage","cardDocumentation","Documentation","cardTooltip","Tooltip","cardHelp","Help","cardEnabled","Enabled","cardTitleBarColor","TitleBarColor","cardBodyStyle","BodyStyle","cardWidth","Width","cardHeight","Height","cardCategory","Category","cardInputs","Inputs","cardOutputs","Outputs","cardPropertiesPanel","PropertiesPanel","cardBodyContent","BodyContent","cardShowTypeLabel","ShowTypeLabel","cardPortLabelsOnHover","PortLabelsOnHover","cardPortLabelsVertical","PortLabelsVertical","cardPortLabelPadding","PortLabelPadding","cardPortLabelsOutside","PortLabelsOutside","cardLabelsInFront","LabelsInFront","tmpPorts","tmpInput","tmpPort","Direction","Side","Label","MinimumInputCount","MaximumInputCount","PortType","DataType","tmpOutput","tmpOutPort","tmpResult","DefaultWidth","DefaultHeight","DefaultPorts","CardMetadata","RenderCallback","pFlowView","_NodeTypeProvider","tmpConfig","getNodeTypeConfiguration","registerNodeType","_libFableServiceProvi2","_this14","panelType","PanelType","panelTitle","panelWidth","panelHeight","_FlowView","_NodeData","_ContentContainer","_Configuration","Configuration","pContainer","pNodeData","libPictFlowCardPropertiesPanel","_libPictFlowCardPrope","_this15","_Metacontroller","_InjectedSectionHash","Manifest","tmpContainerID","innerHTML","Record","tmpServiceType","AutoPopulateAfterRender","AutoSolveBeforeRender","injectManifestAndRender","tmpFormContainerID","tmpContainerEl","querySelector","tmpManifest","injectManifest","tmpViewsToRender","tmpInnerHTML","tmpDestID","charAt","substring","_libPictFlowCardPrope2","_this16","_ContentProvider","_renderMarkdown","tmpMarkdown","MarkdownAddress","manifest","getValueByHash","Markdown","parseMarkdown","tmpHTML","renderKaTeXEquations","renderMermaidDiagrams","_escapeHTML","pText","tmpDiv","createElement","textContent","_libPictFlowCardPrope3","_this17","_TemplatesRegistered","tmpTemplates","addTemplate","_renderTemplate","tmpTemplateHash","tmpRecord","parseTemplateByHash","_libPictFlowCardPrope4","_this18","_OriginalDestination","_ViewInstance","ViewHash","tmpViewHash","tmpPict","libFableServiceProviderBase","_ProviderConfiguration","_libFableServiceProvi3","_this19","FlowView","tmpBaseCSS","getContainerCSS","getNodeCSS","getBodyContentCSS","getNodeVariantCSS","getPortCSS","getConnectionCSS","getHandleCSS","getTetherCSS","getPanelCSS","getInfoPanelCSS","getNodePropsEditorCSS","getPanelTabsCSS","getBracketNodeCSS","getFullscreenCSS","getToolbarCSS","getPaletteCSS","getPopupCSS","getCollapsedToolbarCSS","getFloatingToolbarCSS","getIconCSS","_ThemeProvider","tmpTheme","getActiveTheme","CSSVariables","tmpOverrides","tmpKey","AdditionalCSS","CSSMap","removeCSS","addCSS","generateCSS","injectCSS","_libFableServiceProvi4","_this20","_DefaultShapes","port","ElementType","Attributes","ClassName","rx","ry","MarkerWidth","MarkerHeight","RefX","RefY","Points","Fill","_OriginalShapes","pOverrides","pShapeKey","pConfig","pPortData","pPosition","pNodeHash","tmpElement","_SVGHelperProvider","createSVGElement","tmpClassName","setAttribute","x","pX","pY","pWidth","pHeight","pPath","pConnectionHash","pIsSelected","tmpMarkerId","pOwnerHash","pHandleType","pLayer","pElementType","pOwnerAttrName","tmpHandle","createHandleElement","appendChild","pPanelHash","tmpConnectionMarker","tmpSelectedMarker","tmpTetherMarker","tmpMarkup","tmpPortTypeColors","setting","tmpType","libPictProvider","_libPictProvider","_this21","_Handlers","pEventName","pHandler","pHandlerHash","tmpHash","Handler","tmpIndex","findIndex","pH","splice","pEventData","tmpEntry","_libFableServiceProvi5","_this22","pSide","getEdgeFromSide","dx","dy","pRectData","X","Y","pIndex","pTotal","pTitleBarHeight","pPortCountsBySide","tmpEdge","tmpZone","_computeAdaptiveZone","_getZoneFromSide","tmpFixedZone","tmpAlignment","tmpX","tmpBodyHeight","tmpZoneStart","tmpSlack","end","tmpAlignOffset","tmpY","pEdge","tmpZoneKeys","_getZoneKeysForEdge","tmpTotalSpace","tmpSpaceByZone","tmpCount","tmpSpace","tmpCumulativeStart","tmpFraction","pPorts","tmpCounts","tmpSide","tmpCountBySide","buildPortCountsBySide","tmpSpacePerEdge","tmpZoneSpace","tmpMinHeight","tmpRequired","ceil","_DefaultIcons","ITE","SW","EACH","FREAD","FWRITE","LOG","GET","SET","fullscreen","close","search","cards","layout","collapse","expand","grip","plus","trash","save","dock","restore","default","_libFableServiceProvi6","_this23","_Icons","AdditionalIcons","tmpKeys","getTemplate","pIconValue","charCodeAt","pCardMetadata","isEmojiIcon","pIconKey","pSize","tmpSize","replace","pParentGroup","tmpScale","tmpTempSVG","createElementNS","tmpInnerSVG","tmpGroup","childNodes","pSVGMarkup","DefaultIcons","_libPictProvider2","_this24","undefined","StorageKey","_StorageKey","pLayouts","localStorage","setItem","tmpRaw","getItem","tmpParsed","removeItem","_this25","storageRead","_FlowData","tmpExisting","SavedLayouts","tmpExistingHashes","tmpAdded","pName","_this26","tmpFlowData","tmpLayoutHash","tmpNodePositions","tmpPanelPositions","Nodes","tmpNode","Style","OpenPanels","tmpPanel","NodeHash","tmpLayout","CreatedAt","Date","toISOString","NodePositions","PanelPositions","ViewState","PanX","PanY","Zoom","storageWrite","_EventHandlerProvider","fireEvent","pLayoutHash","find","pLayout","tmpMatchedNodes","tmpUnmatchedNodes","tmpSaved","tmpSavedPanel","_LayoutService","autoLayoutSubset","Connections","renderFlow","_this27","tmpRemovedLayout","_DefaultNodeTypes","fill","stroke","halt","decision","_libPictProvider3","_this28","IncludeDefaultNodeTypes","_NodeTypes","AdditionalNodeTypes","tmpAdditionalKeys","tmpOriginal","pTypeHash","pNodeTypeConfig","tmpCards","getEnabledCards","tmpCategories","tmpCategory","DefaultNodeTypes","PictProviderFlowNoise","_libFableServiceProvi7","_this29","pStr","pSeed","tmpSeed","imul","pAmplitude","pRNG","pSerifLength","pSeedString","_this30","tmpRNG","seededRandom","hashString","tmpS","tmpW","tmpH","tmpJ","jitterPoint","tmpTL_serif","tmpTL_corner","tmpBL_corner","tmpBL_serif","tmpTR_serif","tmpTR_corner","tmpBR_corner","tmpBR_serif","tmpPath","toFixed","pPathString","tmpTokens","match","tmpNumericIndices","tmpXIdx","tmpYIdx","tmpLocalAmplitude","parseFloat","tmpJittered","_libFableServiceProvi8","_this31","pPanelData","pPanelsLayer","tmpFO","tmpTitle","tmpChromeHTML","tmpCloseIcon","_IconProvider","getIconSVGMarkup","tmpContent","addEventListener","pEvent","stopPropagation","tmpTabbar","_libFableServiceProvi9","_this32","pTagName","PictProviderFlowTheme","_libFableServiceProvi0","_this33","_ActiveThemeKey","_NoiseLevel","_Themes","_registerBuiltInThemes","Key","NodeBodyMode","BracketConfig","ConnectionConfig","StrokeDashArray","StrokeWidth","ArrowheadStyle","NoiseConfig","DefaultLevel","MaxJitterPx","AffectsNodes","AffectsConnections","ShapeOverrides","SerifLength","TitleSeparator","pThemeKey","_ConnectorShapesProvider","resetToDefaults","applyThemeOverrides","pLevel","max","min","pKey","pThemeDefinition","tmpAmplitude","_NoiseProvider","jitterPath","_libFableServiceProvi1","_this34","tmpConnection","getConnection","Data","HandleCustomized","startsWith","parseInt","isNaN","BezierHandles","_renderSingleConnection","BezierHandleX","BezierHandleY","OrthoCorner1X","OrthoCorner1Y","OrthoCorner2X","OrthoCorner2Y","tmpSourcePos","getPortPosition","SourceNodeHash","SourcePortHash","tmpTargetPos","TargetNodeHash","TargetPortHash","tmpGeom","_ConnectionRenderer","_computeDirectionalGeometry","tmpStartDir","startDir","abs","tmpAutoMidX","departX","approachX","OrthoMidOffset","tmpAutoMidY","departY","approachY","LineMode","tmpInsertIndex","computeInsertionIndex","tmpConn","_TetherService","resetHandlesForNode","pPanel","resetHandlePositions","_libFableServiceProvi10","_this35","pConnection","pConnectionsLayer","tmpSourcePortType","tmpSourceNode","getNode","Ports","tmpData","tmpCorners","corner1","corner2","_generateOrthogonalPath","tmpHandles","_getBezierHandles","_generateMultiHandleBezierPath","_generateDirectionalPath","processPathString","tmpStrokeDashArray","tmpActiveTheme","tmpArrowMarkerId","tmpConnTypeClass","tmpShapeProvider","tmpHitArea","createConnectionHitAreaElement","tmpPathElement","createConnectionPathElement","getAttribute","_renderHandles","pStart","pEnd","_PathGenerator","computeDirectionalGeometry","tmpGeo","buildBezierPathString","cp1X","cp1Y","cp2X","cp2Y","pHandles","buildMultiBezierPathString","endDir","pClickPoint","tmpWaypoints","tmpBestDist","Infinity","tmpBestIndex","tmpDist","_distanceToSegment","pPX","pPY","pAX","pAY","pBX","pBY","distanceToSegment","getAutoMidpoint","pCorners","pMidOffset","tmpCorner1","tmpCorner2","tmpDA","computeDepartApproach","tmpAutoCorners","computeAutoOrthogonalCorners","fromDir","toDir","buildOrthogonalPathString","midpoint","tmpGeometry","getOrthogonalGeometry","_createHandle","tmpMidpoint","pClassName","tmpShapeKey","createFullHandle","tmpStart","side","tmpEnd","pStartX","pStartY","pEndX","pEndY","pStartSide","createDragConnectionElement","pPathElement","_libFableServiceProvi11","_this36","FlowDataAddress","tmpAddressSpace","Fable","Pict","Options","setFlowData","setValueByHash","pFlowData","SelectedNodeHash","SelectedConnectionHash","SelectedTetherHash","_LayoutProvider","loadPersistedLayouts","initialRenderComplete","pType","pTitle","DefaultNodeType","tmpNodeTypeConfig","getNodeType","Type","DefaultNodeWidth","DefaultNodeHeight","tmpNodeIndex","pNode","tmpRemovedNode","filter","closePanelForNode","pSourceNodeHash","pSourcePortHash","pTargetNodeHash","pTargetPortHash","tmpTargetNode","tmpSourcePort","pPort","tmpTargetPort","pConn","tmpConnectionIndex","tmpRemovedConnection","INTERACTION_STATES","IDLE","DRAGGING_NODE","DRAGGING_PANEL","DRAGGING_HANDLE","CONNECTING","PANNING","RESIZING_PANEL","_libFableServiceProvi12","_this37","_SVGElement","_ViewportElement","_State","_DragNodeHash","_DragStartX","_DragStartY","_DragNodeStartX","_DragNodeStartY","_DragPanelHash","_DragPanelStartX","_DragPanelStartY","_DragPanelDataStartX","_DragPanelDataStartY","_DragHandleConnectionHash","_DragHandlePanelHash","_DragHandleType","_DragHandleIsTether","_PanStartX","_PanStartY","_PanStartPanX","_PanStartPanY","_ConnectSourceNodeHash","_ConnectSourcePortHash","_ConnectDragLine","_LastClickTime","_LastClickNodeHash","_DoubleClickThreshold","_ResizePanelHash","_ResizeStartY","_ResizePanelStartHeight","_LastConnectionClickTime","_LastConnectionClickHash","_LastTetherClickTime","_LastTetherClickHash","_LastHandleClickTime","_LastHandleClickHash","_LastHandleClickType","_boundOnPointerDown","_onPointerDown","_boundOnPointerMove","_onPointerMove","_boundOnPointerUp","_onPointerUp","_boundOnWheel","_onWheel","_boundOnKeyDown","_onKeyDown","pSVGElement","pViewportElement","_this38","passive","preventDefault","tmpTarget","target","_getElementType","_addBezierHandle","_removeBezierHandle","_addTetherBezierHandle","_removeTetherBezierHandle","removeEventListener","tmpElementType","closest","button","setPointerCapture","pointerId","_startConnection","tmpNodeHash","_getNodeHash","tmpNow","now","togglePanel","_startNodeDrag","_startPanelDrag","_startPanelResize","tmpPanelHash","_getPanelHash","closePanel","tmpConnectionHash","_getConnectionHash","tmpHandleType","_toggleConnectionLineMode","_startHandleDrag","_selectTether","_toggleTetherLineMode","_selectConnection","EnablePanning","_startPanning","_onNodeDrag","_onPanelDrag","_onHandleDrag","_onConnectionDrag","_onPanelResize","_onPan","hasPointerCapture","releasePointerCapture","_endNodeDrag","_endPanelDrag","_endHandleDrag","_endPanelResize","_endConnection","_endPanning","EnableZooming","tmpDelta","deltaY","ZoomStep","tmpNewZoom","viewState","tmpRect","getBoundingClientRect","tmpMouseX","clientX","left","tmpMouseY","clientY","top","setZoom","tagName","deleteSelected","_cancelConnection","_IsFullscreen","exitFullscreen","_ToolbarView","tmpFlowViewIdentifier","tmpBtnElements","ContentAssignment","getElement","deselectAll","pTarget","EnableNodeDragging","selectNode","add","tmpNodeGroup","_NodesLayer","tmpVS","tmpDX","tmpDY","tmpNewX","tmpNewY","updateNodePosition","flowData","updatePanelPosition","_this39","_this40","tmpNewHeight","_PanelsLayer","pIsTether","tmpCoords","screenToSVGCoords","updateTetherHandle","updateConnectionHandle","_this41","selectConnection","addConnectionHandle","removeConnectionHandle","selectTether","addTetherHandle","removeTetherHandle","tmpCurrentMode","toggleLineMode","EnableConnectionCreation","tmpPortHash","tmpPortDirection","tmpPortPos","tmpEndCoords","parentNode","removeChild","elementFromPoint","tmpTargetPortHash","tmpTargetNodeHash","tmpTargetPortDirection","addConnection","updateViewportTransform","tmpParent","parentElement","tmpDepth","_libFableServiceProvi13","_this42","_HorizontalSpacing","_VerticalSpacing","_StartX","_StartY","pValue","pGridSize","round","pNodes","pConnections","tmpNodeMap","tmpInDegree","tmpOutEdges","tmpLayers","tmpQueue","tmpAssigned","tmpCurrentLayer","tmpNextQueue","tmpEdges","j","tmpTargetHash","tmpRemainingNodes","tmpCurrentX","tmpLayerIndex","tmpLayer","tmpMaxWidth","tmpCurrentY","tmpWidth","tmpHeight","pNodesToLayout","pFixedNodes","tmpStartX","tmpStartY","tmpMaxX","tmpRight","tmpNodeSet","tmpSourceInSubset","tmpTargetInSubset","pCenterX","pCenterY","tmpMinX","tmpMinY","tmpMaxY","tmpOffsetX","tmpOffsetY","_libFableServiceProvi14","_this43","tmpPanelType","tmpPanelConfig","tmpPanelData","tmpRemovedPanel","_PropertiesPanelView","destroyPanel","tmpPanelsToClose","indexOf","openPanel","_resetHandlesForPanel","_renderTethersForNode","_libFableServiceProvi15","_this44","pFrom","pTo","pDepartDist","_GeometryProvider","tmpFromDir","sideDirection","tmpToDir","pDepartX","pDepartY","pApproachX","pApproachY","pFromDir","pToDir","tmpOffset","tmpFromHoriz","tmpToHoriz","tmpMidX","tmpMidY","pP0","pP1","pP2","pP3","pT","tmpOMT","tmpOMT2","tmpOMT3","tmpT2","tmpT3","pDepart","pCP1","pCP2","pApproach","pCP1a","pCP1b","pHandle","pCP2a","pCP2b","pStartDir","pEndDir","tmpFrom","tmpTo","tmpSegDX","tmpSegDY","tmpSegLen","sqrt","tmpTanFromX","tmpTanFromY","tmpPrev","tmpNext","tmpTanLen","tmpTanToX","tmpTanToY","tmpCP1X","tmpCP1Y","tmpCP2X","tmpCP2Y","pCorner1","pCorner2","tmpCurveOffset","tmpEndDir","tmpDepartX","tmpDepartY","tmpApproachX","tmpApproachY","tmpBaseOffset","tmpSameAxis","tmpFacingEachOther","tmpInlineDist","tmpLenSq","tmpDPX","tmpDPY","tmpT","tmpDistX","tmpDistY","evaluateCubicBezier","tmpSpanX","tmpSpanY","tmpCPDist","tmpP0","tmpP1","tmpP2","tmpP3","_libFableServiceProvi16","_this45","pGroup","pNodeTitleBarHeight","tmpGeometryProvider","tmpPortsBySide","tmpPortCountsBySide","tmpPosition","getPortLocalPosition","tmpLabelElement","tmpBorderColor","tmpBadgeHeight","tmpTextLen","tmpBadgeX","tmpBadgeY","tmpBadgeWidth","tmpTextX","tmpTextAnchor","tmpStripeX","tmpStripeY","tmpStripeW","tmpStripeH","tmpBorderPath","tmpPortRadius","tmpBadgeBorderW","tmpBadgePadH","tmpBgRect","tmpBorderPathEl","tmpStripe","tmpCircle","createPortElement","tmpPortClass","_libFableServiceProvi17","_this46","_ConnectionsLayer","firstChild","tmpIsSelected","renderConnection","tmpDefault","_NodeView","renderNode","_TethersLayer","renderPanels","querySelectorAll","tmpNodeData","renderTether","EnableGridSnap","snapToGrid","GridSnapSize","_resetHandlesForNode","renderConnectionsForNode","renderTethersForNode","tmpAffectedConnections","tmpAffectedPanels","tmpDefs","tmpExistingMarkers","tmpMarkerMarkup","generateMarkerDefs","_libFableServiceProvi18","_this47","tmpPreviousSelection","removeNode","removeConnection","_libFableServiceProvi19","_this48","tmpNodeSide","tmpPanelSide","tmpNodeCX","tmpNodeCY","tmpNodeAnchor","getEdgeCenter","tmpPanelAnchor","nodeAnchor","panelAnchor","pHandleX","pHandleY","tmpCP1aX","tmpCP1aY","tmpCP2aX","tmpCP2aY","tmpTangentX","tmpTangentY","tmpTangentLen","tmpCP1bX","tmpCP1bY","tmpCP2bX","tmpCP2bY","buildSplitBezierPathString","getAutoMidpointSimple","TetherHandleCustomized","TetherOrthoCorner1X","TetherOrthoMidOffset","TetherOrthoCorner1Y","TetherOrthoCorner2X","TetherOrthoCorner2Y","TetherLineMode","generateOrthogonalPath","_getTetherBezierHandles","generateMultiBezierPath","tmpHandleX","TetherBezierHandleX","tmpHandleY","TetherBezierHandleY","generateBezierPath","TetherBezierHandles","tmpDepart","tmpApproach","_TetherMidDragX","_TetherMidDragY","pOpenPanels","pTethersLayer","tmpAnchors","getSmartAnchors","generatePath","createTetherHitAreaElement","createTetherPathElement","getOrthoGeometry","tmpMid","_libFableServiceProvi20","_this49","pZoom","pFocusX","pFocusY","MinZoom","MaxZoom","tmpOldZoom","tmpFlowWidth","tmpPadding","tmpFlowHeight","tmpSVGRect","tmpScaleX","width","tmpScaleY","height","tmpZoom","tmpCenterX","tmpCenterY","pScreenX","pScreenY","tmpPoint","createSVGPoint","tmpCTM","getScreenCTM","tmpInverse","inverse","tmpTransformed","matrixTransform","tmpContainerElements","tmpContainer","libPictView","_DefaultConfiguration","FlowViewIdentifier","DestinationAddress","_libPictView","_this50","_IsCollapsed","_IsDragging","_DragStartLeft","_DragStartTop","_BoundMouseMove","_BoundMouseUp","pTemplateRecordAddress","pRenderable","pRecord","pContent","_this51","tmpFloatingToolbar","tmpButton","tmpAction","_handleToolbarAction","_setToolbarMode","tmpGrip","_startDrag","_toggleCollapse","_populateIcons","EnableAddNode","tmpAddNodeBtn","EnableCardPalette","tmpCardsBtn","tmpIconProvider","tmpIconMap","tmpElementId","tmpElements","tmpToolbar","_this52","tmpEl","offsetLeft","offsetTop","pMoveEvent","_onDragMove","_onDragEnd","tmpDeltaX","tmpDeltaY","tmpNewLeft","tmpNewTop","tmpMaxLeft","clientWidth","offsetWidth","tmpMaxTop","clientHeight","offsetHeight","_FloatingPosition","NodeTitleBarHeight","_libPictView2","_this53","pNodesLayer","_this54","tmpClassList","tmpTitleBarHeight","tmpGeomProvider","computeMinimumNodeHeight","tmpNodeBodyMode","_renderBracketNodeBody","_renderRectNodeBody","tmpHasTitleIcon","tmpTitleIconSize","tmpMeta","tmpTitleIconMarginLeft","tmpShowTypeLabel","tmpRenderTypeLabels","tmpTypeLabel","tmpTitleIconRendered","tmpIconY","tmpResolvedKey","resolveIconKey","tmpIconGroup","renderIconIntoSVGGroup","tmpIconText","tmpIconX","tmpBodyCenterY","tmpCodeText","tmpSVGTitle","_renderBodyContent","_renderPorts","tmpIndicator","tmpIndicatorSize","tmpIndicatorX","tmpIndicatorY","createPanelIndicatorElement","tmpIndicatorTitle","_PortRenderer","renderPorts","tmpBodyContent","tmpContentType","ContentType","Padding","tmpBodyBounds","_registeredBodyTemplates","Set","tmpTpl","has","_renderBodyContentSVG","_renderBodyContentHTML","_renderBodyContentCanvas","pBodyContent","pBounds","pPict","tmpContentGroup","tmpRenderedContent","_resolveBodyTemplate","tmpCanvas","parseTemplate","tmpBody","tmpStyleKey","tmpInlineStyles","BodyFill","BodyStroke","BodyStrokeWidth","join","tmpTitleBar","tmpTitleBarBottom","tmpBodyFill","tmpTitleFill","tmpBracketConfig","getNodeNoiseAmplitude","tmpBracketD","generateBracketPath","tmpBracketPath","_libPictView3","_this55","_PanelInstances","pSelectedTetherHash","tmpOpenPanels","tmpExistingPanelHashes","tmpExistingForeignObjects","tmpDesiredPanelHashes","destroy","_createPanelForeignObject","_renderTether","tmpPanelChromeProvider","_PanelChromeProvider","createPanelForeignObject","_renderPanelContent","_renderAppearanceTab","_renderHelpTab","_wireTabSwitching","pBodyContainer","tmpServiceName","tmpInstance","_renderPortSummary","_renderInfoPanelContent","tmpInputs","tmpOutputs","tmpLabel","tmpContentParts","tmpIconMarkup","tmpDefaultMarkup","tmpBadgesContent","BadgesContent","tmpPortsContent","tmpConstraint","_getPortConstraintHTML","Constraint","PortsContent","PanelContent","tmpEventInputs","tmpEventOutputs","tmpStateOutputs","tmpPortType","tmpSummaryParts","SectionTitle","tmpDataType","DataTypeText","tmpSummaryDiv","className","tmpMin","tmpMax","tmpConstraintText","ConstraintText","pForeignObject","_this56","tmpAppearancePane","tmpStyle","tmpDefaultTitleBarColor","tmpDefaultBodyFill","tmpDefaultBodyStroke","BodyFillValue","BodyStrokeValue","BodyStrokeWidthValue","TitleBarColorValue","_loop","tmpProp","_applyNodePropChange","tmpHelpText","tmpHelpTabButton","tmpHelpPane","tmpTabs","tmpPanes","currentTarget","tmpTargetPane","pPropPath","pInputType","tmpValue","tmpTetherService","marshalFromPanel","EnablePalette","_libPictView4","_this57","_ToolbarMode","_ActivePopup","_DocumentClickHandler","_FloatingToolbarView","_this58","tmpToolbarBar","tmpExpandBtn","_populateToolbarIcons","tmpFullscreenIcon","tmpCardsChevron","tmpLayoutChevron","_this59","_closePopup","tmpAnchor","tmpPopup","_buildAddNodePopup","_buildCardsPopup","_buildLayoutPopup","_buildSettingsPopup","_positionPopup","setTimeout","contains","tmpSearch","focus","pPopupDiv","tmpTriggerSelector","tmpTriggerElements","tmpTriggerRect","tmpAnchorRect","tmpLeft","_this60","tmpSearchWrapper","tmpSearchIcon","tmpSearchInput","tmpListDiv","_populateNodeList","pListDiv","pFilter","_this61","tmpTypes","getNodeTypes","tmpTypeKeys","tmpFilter","toLowerCase","trim","tmpMatchCount","_loop2","tmpTypeConfig","tmpCode","tmpRow","tmpIconSpan","tmpLabelSpan","tmpCodeSpan","_addNodeAtCenter","tmpEmpty","_this62","_renderPalette","_this63","getCardsByCategory","tmpCategoryKeys","tmpTotalMatchCount","tmpCategoryName","tmpMatchingCards","tmpCardConfig","tmpCategoryDiv","padding","tmpCategoryLabel","tmpCardsDiv","_loop3","tmpCardEl","tmpSwatch","backgroundColor","tmpTitleSpan","_addCardFromPalette","_this64","tmpSaveSection","tmpSaveInputRow","tmpSaveInput","tmpSaveConfirmBtn","title","tmpSaveRow","tmpSaveIcon","tmpSaveText","tmpDoSave","tmpName","saveLayout","tmpDivider","tmpLayouts","getLayouts","_loop4","tmpNameSpan","tmpDeleteBtn","restoreLayout","deleteLayout","_this65","tmpThemeProvider","tmpThemeSection","tmpThemeLabel","tmpThemeSelect","tmpThemeKeys","getThemeKeys","tmpActiveKey","getActiveThemeKey","tmpOption","selected","setTheme","_refreshNoiseSlider","tmpNoiseSection","tmpNoiseLabel","tmpNoiseRow","tmpNoiseSlider","getNoiseLevel","tmpNoiseValue","tmpLevel","setNoiseLevel","tmpSlider","tmpValueLabel","pMode","tmpBar","tmpCollapsed","hide","_showFloatingToolbar","show","pNodeType","tmpSVGContainer","tmpNodeCount","addNode","pCardType","pAction","_openPopup","zoomToFit","autoLayout","tmpIsFullscreen","toggleFullscreen","tmpIconElements","tmpFullscreenBtn","libPictServiceFlowInteractionManager","libPictServiceFlowConnectionRenderer","libPictServiceFlowTether","libPictServiceFlowLayout","libPictServiceFlowPathGenerator","libPictServiceFlowViewportManager","libPictServiceFlowSelectionManager","libPictServiceFlowPanelManager","libPictServiceFlowDataManager","libPictServiceFlowConnectionHandleManager","libPictServiceFlowRenderManager","libPictServiceFlowPortRenderer","libPictProviderFlowNodeTypes","libPictProviderFlowEventHandler","libPictProviderFlowLayouts","libPictProviderFlowSVGHelpers","libPictProviderFlowGeometry","libPictProviderFlowPanelChrome","libPictProviderFlowCSS","libPictProviderFlowIcons","libPictProviderFlowConnectorShapes","libPictProviderFlowTheme","libPictProviderFlowNoise","libPictViewFlowNode","libPictViewFlowToolbar","libPictViewFlowFloatingToolbar","libPictViewFlowPropertiesPanel","libPanelTemplate","libPanelMarkdown","libPanelForm","libPanelView","TargetElementAddress","EnableToolbar","PictViewFlow","_libPictView5","_this66","_ServiceRegistry","ServiceType","Library","Property","NoFlowView","PostInit","ExtraOptions","NodeTypes","RegisterOnly","_registerServiceTypes","_DataManager","_ConnectionHandleManager","_RenderManager","_InteractionManager","_ViewportManager","_SelectionManager","_PanelManager","_CSSProvider","addServiceType","pRootRenderable","Theme","NoiseLevel","_instantiateServices","onAfterInitialRender","tmpSVGElements","tmpViewportElements","tmpNodesElements","tmpConnectionsElements","tmpTethersElements","tmpPanelsElements","getFlowData","registerCSS","_reinjectMarkerDefs","reinjectMarkerDefs","resetHandlesForPanel","addHandle","removeHandle","updateHandlePosition","_renderSingleTether","pPortHash","tmpSameSidePorts","tmpPortIndex","tmpPortCount","tmpLocal","renderSingleConnection","renderSingleTether","defaultPictViewSettings","DefaultTemplateRecordAddress","CSSHash","CSSProvider","CSSPriority","DefaultTemplates","_libFableServiceBase3","_this67","tmpHashIsUUID","lastRenderedTimestamp","lastMarshalFromViewTimestamp","lastMarshalToViewTimestamp","instantiateServiceProviderIfNotExists","tmpTemplate","tmpCSSHash","tmpCSSProvider","renderables","tmpRenderable","addRenderable","pTemplateHash","pDefaultTemplateRecordAddress","pDefaultDestinationAddress","pRenderMethod","_this68","onBeforeProject","tmpRenderOptions","Valid","Renderable","RecordAddress","DataProvider","getDataByAddress","projectContent","TestAddress","renderWithScope","pScope","tmpRecordAddress","TransactionHash","RootRenderableViewHash","TransactionTracking","registerTransaction","Content","RootRenderable","onProject","onAfterProject","renderWithScopeAsync","_this69","fOnBeforeRenderCallback","fAsyncTemplateCallback","onBeforeProjectAsync","onProjectAsync","onAfterProjectAsync","basicRenderWithScope","buildRenderOptions","assignRenderContent","basicRenderWithScopeAsync","_this70","pushToTransactionQueue","_step","_iterator","clearTransactionQueue","tmpEvent","err","_this71","_step2","_iterator2","_loop5","event","_this72","onBeforeMarshalFromView","onMarshalFromView","onAfterMarshalFromView","_this73","onBeforeMarshalFromViewAsync","onMarshalFromViewAsync","onAfterMarshalFromViewAsync","onBeforeMarshalToView","onMarshalToView","onAfterMarshalToView","_this74","onBeforeMarshalToViewAsync","onMarshalToViewAsync","onAfterMarshalToViewAsync","MICRODDL_TYPE_MAP","HasSize","$","DATATYPE_TO_SYMBOL","tmpSymbol","columnsToMicroDDL","pColumns","pTableName","tmpLines","tmpCol","tmpLine","Size","microDDLToColumns","pDDL","split","tmpColumns","tmpParts","libSchemaUtils","MeadowMappingEditorView","_libPictView6","_this75","_EditingContextID","_EditingName","_CurrentMappings","_SelectedMappingID","_DiscoveredFields","_MapEditorMode","_MappingSources","_MappingStores","_CurrentTargetSchema","Mappings","SchemaDefinition","Mapping","Headers","SampleSize","pMessage","tmpModal","toast","confirm","_this76","scrollIntoView","behavior","block","tmpMappingListWrap","tmpMappingDetail","all","_doLoadMappings","_doLoadSources","_doLoadStores","_doLoadTargetSchema","pResults","tmpSrc","Columns","IDSource","tmpSchema","refreshMappingList","_onClose","tmpViewID","tmpHtml","tmpMap","tmpSourceName","IDProjectionMapping","Active","_this77","tmpNameInput","tmpAutoSourceID","_populateSourceDropdown","_populateStoreChecklist","tmpJSONTextarea","tmpNewEntityName","tmpNewGUIDCol","tmpNewIDCol","tmpNewMappings","Entity","GUIDTemplate","GUIDName","Solvers","ManyfestAddresses","tmpFlowContainer","switchMapMode","initFlowView","pSchema","_rebuildFlowNodes","_this78","_doLoadMapping","pResponse","tmpMapping","tmpTargetStores","tmpParsedConfig","MappingConfiguration","TargetStores","IDProjectionStore","sourceColumns","tmpFlowState","FlowDiagramState","pParseError","_doToast","_deleteMapping","_callee","_this79","_context","_doConfirm","confirmLabel","dangerous","_doDeleteMapping","pResult","_x","tmpFlowWrap","tmpJSONWrap","tmpFlowTab","tmpJSONTab","flowToMappingConfig","pSelectedIDSource","tmpSelect","tmpSelected","pSelectedStoreIDs","tmpSelectedSet","tmpStore","tmpChecked","TargetTableName","Status","tmpIDs","_this80","tmpSourceSelect","tmpIDSource","_doDiscoverSourceFields","tmpHeaders","duration","tmpFields","tmpSchemaColumns","_getSchemaColumns","tmpSourceTitle","selectedIndex","text","tmpTargetTitle","tmpSourcePorts","tmpEntityName","tmpGUIDColumnName","tmpIDColumnName","tmpTargetPorts","tmpSrcNode","tmpTgtNode","tmpMergedSrcPorts","tmpSrcPortHashes","tmpExistingPorts","tmpInternalNodes","tmpMergedTgtPorts","tmpTgtPortHashes","tmpExistingTgtPorts","tmpDDL","tmpParsedColumns","libPictSectionFlow","libFlowCardSource","libFlowCardTarget","libFlowCardTemplate","libFlowCardSolver","tmpSourceCard","tmpTargetCard","tmpTemplateCard","tmpSolverCard","registerWithFlowView","pFlowError","tmpPortMap","NodeType","tmpSolverEntries","tmpTargetColumn","tmpSourceField","tmpExpression","TemplateExpression","SolverExpression","expression","outputs","tmpSolverKeys","_this81","tmpCheckedStoreIDs","_getCheckedStoreIDs","tmpIDProjectionStore","tmpMappingConfig","tmpJSON","tmpSavedColumns","_doUpdateMapping","_doCreateMapping","FlowCardMappingSource","_libPictFlowCard","_this82","FlowCardMappingTarget","_libPictFlowCard2","_this83","FlowCardSolverExpression","_libPictFlowCard3","_this84","FlowCardTemplateExpression","_libPictFlowCard4","_this85"],"mappings":"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","file":"mapping-demo-editor.min.js","sourcesContent":["(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.MappingDemoApplication = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){\n'use strict';\n\nconst libPictApplication = require('pict-application');\nconst libMappingDemoEditorView = require('./views/MappingDemoEditorView.js');\n\n/**\n * MappingDemoApplication\n *\n * Minimal pict application that hosts the MappingDemoEditorView.\n * Loaded client-side by Pict.safeLoadPictApplication(MappingDemoApplication, 2).\n *\n * After initialization it exposes window.openMappingEditor() so the static\n * HTML pipeline UI can activate the visual editor from a plain onclick handler.\n */\nclass MappingDemoApplication extends libPictApplication\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.pict.addView(\n\t\t\t'MappingDemoEditor',\n\t\t\tlibMappingDemoEditorView.default_configuration,\n\t\t\tlibMappingDemoEditorView);\n\t}\n\n\tonAfterInitializeAsync(fCallback)\n\t{\n\t\t// Expose a global hook so the outer HTML can open the editor\n\t\twindow.openMappingEditor = () =>\n\t\t{\n\t\t\tlet tmpPlaceholder = document.getElementById('mapping-editor-placeholder');\n\t\t\tif (tmpPlaceholder)\n\t\t\t{\n\t\t\t\ttmpPlaceholder.style.display = 'none';\n\t\t\t}\n\n\t\t\twindow._Pict.views['MappingDemoEditor'].editMappings(1, 'Book');\n\t\t};\n\n\t\treturn super.onAfterInitializeAsync(fCallback);\n\t}\n}\n\nmodule.exports = MappingDemoApplication;\n\nmodule.exports.default_configuration =\n{\n\tName: 'MappingDemoApp',\n\tHash: 'MappingDemo',\n\tAutoSolveAfterInitialize: true\n};\n\n},{\"./views/MappingDemoEditorView.js\":2,\"pict-application\":6}],2:[function(require,module,exports){\n'use strict';\n\n// Path: mapping-demo/source/views/ → ../../../../source/views/PictView-MeadowMappingEditor.js\nconst libMeadowMappingEditorView = require('../../../../source/views/PictView-MeadowMappingEditor.js');\n\n// ── View configuration ────────────────────────────────────────────────────────\n// Same template HTML as the generic base editor, but with 'MappingDemoEditor'\n// in all onclick handlers so pict resolves this registered view name.\n// DOM IDs stay as MeadowMap-* so inherited JS methods work without changes.\n\nconst _ViewConfiguration =\n{\n\tViewIdentifier: 'MappingDemoEditor',\n\n\tDefaultRenderable: 'MappingDemoEditor-Content',\n\tDefaultDestinationAddress: '#MeadowMap-Editor-Container',\n\n\tAutoRender: false,\n\n\tCSS: libMeadowMappingEditorView.default_configuration.CSS,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'MappingDemoEditor-Template',\n\t\t\tTemplate: /*html*/`\n<div>\n\t<div id=\"MeadowMap-Editor\" class=\"meadow-mapping-editor\">\n\t\t<div class=\"meadow-mapping-header\">\n\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-secondary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MappingDemoEditor'].closeMappingEditor()\">&larr; Back</button>\n\t\t\t<h3 id=\"MeadowMap-Title\">Mapping Editor</h3>\n\t\t\t<div class=\"meadow-schema-mode-tabs\">\n\t\t\t\t<button class=\"meadow-schema-mode-tab active\" id=\"MeadowMap-Mode-Flow\" onclick=\"{~P~}.views['MappingDemoEditor'].switchMapMode('flow')\">Visual Mapper</button>\n\t\t\t\t<button class=\"meadow-schema-mode-tab\" id=\"MeadowMap-Mode-JSON\" onclick=\"{~P~}.views['MappingDemoEditor'].switchMapMode('json')\">JSON Config</button>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div id=\"MeadowMap-List-Wrap\">\n\t\t\t<div style=\"display:flex; align-items:center; justify-content:space-between; margin-bottom:0.75em;\">\n\t\t\t\t<div class=\"meadow-section-title\" style=\"margin:0;\">Existing Mappings</div>\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-primary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MappingDemoEditor'].newMapping()\">+ New Mapping</button>\n\t\t\t</div>\n\t\t\t<div id=\"MeadowMap-List\"></div>\n\t\t</div>\n\n\t\t<div id=\"MeadowMap-Detail\" style=\"display:none;\">\n\t\t\t<div style=\"display:flex; gap:0.5em; align-items:center; margin-bottom:0.75em;\">\n\t\t\t\t<label style=\"font-size:0.78em; font-weight:600;\">Mapping Name</label>\n\t\t\t\t<input type=\"text\" id=\"MeadowMap-Name\" placeholder=\"Mapping name\" style=\"flex:1; padding:0.3em 0.5em; font-size:0.85em; border:1px solid var(--border); border-radius:4px; background:var(--bg-card); color:var(--text);\">\n\t\t\t</div>\n\n\t\t\t<div style=\"display:flex; gap:0.5em; align-items:center; margin-bottom:0.75em;\">\n\t\t\t\t<label style=\"font-size:0.78em; font-weight:600;\">Source</label>\n\t\t\t\t<select id=\"MeadowMap-Source\" style=\"flex:1; padding:0.3em 0.5em; font-size:0.85em; border:1px solid var(--border); border-radius:4px;\"></select>\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-secondary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MappingDemoEditor'].discoverSourceFields()\">Discover Fields</button>\n\t\t\t</div>\n\n\t\t\t<div id=\"MeadowMap-Flow-Wrap\">\n\t\t\t\t<div id=\"MeadowMap-Flow-Container\" class=\"meadow-flow-container\"></div>\n\t\t\t</div>\n\n\t\t\t<div id=\"MeadowMap-JSON-Wrap\" style=\"display:none;\">\n\t\t\t\t<textarea class=\"meadow-mapping-json-editor\" id=\"MeadowMap-JSON\" placeholder='{\"Entity\":\"Book\",\"GUIDTemplate\":\"{~D:Record.id~}\",\"Mappings\":{},\"Solvers\":[],\"ManyfestAddresses\":false}'></textarea>\n\t\t\t</div>\n\n\t\t\t<div style=\"margin-top:0.75em;\">\n\t\t\t\t<div style=\"font-size:0.72em; font-weight:600; text-transform:uppercase; letter-spacing:0.5px; color:var(--text-dim); margin-bottom:0.35em;\">Target Stores</div>\n\t\t\t\t<div id=\"MeadowMap-Stores\" class=\"meadow-mapping-store-checklist\"></div>\n\t\t\t</div>\n\n\t\t\t<div style=\"margin-top:0.75em; display:flex; gap:0.5em; flex-wrap:wrap; align-items:center;\">\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-primary\" onclick=\"{~P~}.views['MappingDemoEditor'].saveMapping()\">Save Mapping</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: 'MappingDemoEditor-Content',\n\t\t\tTemplateHash: 'MappingDemoEditor-Template',\n\t\t\tContentDestinationAddress: '#MeadowMap-Editor-Container',\n\t\t\tRenderMethod: 'replace'\n\t\t}\n\t]\n};\n\n/**\n * MappingDemoEditorView\n *\n * Extends MeadowMappingEditorView and wires the _do* data methods to the\n * mapping-demo server's REST API endpoints. All visual/canvas/serialization\n * logic lives in the base class; this subclass only supplies data access.\n */\nclass MappingDemoEditorView extends libMeadowMappingEditorView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\t}\n\n\t// ── Data methods ─────────────────────────────────────────────────────────\n\n\t_doLoadMappings(pContextID)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping').then(function(r) { return r.json(); });\n\t}\n\n\t_doLoadSources()\n\t{\n\t\treturn fetch('/1.0/Demo/Sources').then(function(r) { return r.json(); });\n\t}\n\n\t_doLoadStores(pContextID)\n\t{\n\t\treturn Promise.resolve({ Stores: [] });\n\t}\n\n\t_doLoadTargetSchema(pContextID)\n\t{\n\t\treturn fetch('/1.0/Demo/TargetSchema').then(function(r) { return r.json(); });\n\t}\n\n\t_doLoadMapping(pMappingID)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping/' + pMappingID).then(function(r) { return r.json(); });\n\t}\n\n\t_doDeleteMapping(pMappingID)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping/' + pMappingID,\n\t\t\t{ method: 'DELETE' }).then(function(r) { return r.json(); });\n\t}\n\n\t_doDiscoverSourceFields(pContextID, pSourceID, pRecordLimit)\n\t{\n\t\treturn fetch('/1.0/Demo/SourceSchema').then(function(r) { return r.json(); });\n\t}\n\n\t_doCreateMapping(pContextID, pData)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping',\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify(pData)\n\t\t\t}).then(function(r) { return r.json(); });\n\t}\n\n\t_doUpdateMapping(pMappingID, pData)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping/' + pMappingID,\n\t\t\t{\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify(pData)\n\t\t\t}).then(function(r) { return r.json(); });\n\t}\n\n\t_onClose()\n\t{\n\t\t// Hide the editor and restore the step placeholder\n\t\tlet tmpEditor = document.getElementById('MeadowMap-Editor');\n\t\tif (tmpEditor)\n\t\t{\n\t\t\ttmpEditor.classList.remove('active');\n\t\t}\n\n\t\tlet tmpPlaceholder = document.getElementById('mapping-editor-placeholder');\n\t\tif (tmpPlaceholder)\n\t\t{\n\t\t\ttmpPlaceholder.style.display = '';\n\t\t}\n\n\t\t// Dispatch an event so the outer UI can mark this step done\n\t\tdocument.dispatchEvent(new CustomEvent('mapping-editor-closed'));\n\t}\n}\n\nmodule.exports = MappingDemoEditorView;\n\nmodule.exports.default_configuration = _ViewConfiguration;\n\n},{\"../../../../source/views/PictView-MeadowMappingEditor.js\":47}],3:[function(require,module,exports){\nmodule.exports={\n \"name\": \"fable-serviceproviderbase\",\n \"version\": \"3.0.19\",\n \"description\": \"Simple base classes for fable services.\",\n \"main\": \"source/Fable-ServiceProviderBase.js\",\n \"scripts\": {\n \"start\": \"node source/Fable-ServiceProviderBase.js\",\n \"test\": \"npx quack test\",\n \"tests\": \"npx quack test -g\",\n \"coverage\": \"npx quack coverage\",\n \"build\": \"npx quack build\",\n \"types\": \"tsc -p ./tsconfig.build.json\",\n \"check\": \"tsc -p . --noEmit\"\n },\n \"types\": \"types/source/Fable-ServiceProviderBase.d.ts\",\n \"mocha\": {\n \"diff\": true,\n \"extension\": [\n \"js\"\n ],\n \"package\": \"./package.json\",\n \"reporter\": \"spec\",\n \"slow\": \"75\",\n \"timeout\": \"5000\",\n \"ui\": \"tdd\",\n \"watch-files\": [\n \"source/**/*.js\",\n \"test/**/*.js\"\n ],\n \"watch-ignore\": [\n \"lib/vendor\"\n ]\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/stevenvelozo/fable-serviceproviderbase.git\"\n },\n \"keywords\": [\n \"entity\",\n \"behavior\"\n ],\n \"author\": \"Steven Velozo <steven@velozo.com> (http://velozo.com/)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/stevenvelozo/fable-serviceproviderbase/issues\"\n },\n \"homepage\": \"https://github.com/stevenvelozo/fable-serviceproviderbase\",\n \"devDependencies\": {\n \"@types/mocha\": \"^10.0.10\",\n \"fable\": \"^3.1.62\",\n \"quackage\": \"^1.0.58\",\n \"typescript\": \"^5.9.3\"\n }\n}\n\n},{}],4:[function(require,module,exports){\n/**\n* Fable Service Base\n* @author <steven@velozo.com>\n*/\n\nconst libPackage = require('../package.json');\n\nclass FableServiceProviderBase\n{\n\t/**\n\t * The constructor can be used in two ways:\n\t * 1) With a fable, options object and service hash (the options object and service hash are optional)a\n\t * 2) With an object or nothing as the first parameter, where it will be treated as the options object\n\t *\n\t * @param {import('fable')|Record<string, any>} [pFable] - (optional) The fable instance, or the options object if there is no fable\n\t * @param {Record<string, any>|string} [pOptions] - (optional) The options object, or the service hash if there is no fable\n\t * @param {string} [pServiceHash] - (optional) The service hash to identify this service instance\n\t */\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\t/** @type {import('fable')} */\n\t\tthis.fable;\n\t\t/** @type {string} */\n\t\tthis.UUID;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.options;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.services;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.servicesMap;\n\n\t\t// Check if a fable was passed in; connect it if so\n\t\tif ((typeof(pFable) === 'object') && pFable.isFable)\n\t\t{\n\t\t\tthis.connectFable(pFable);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.fable = false;\n\t\t}\n\n\t\t// Initialize the services map if it wasn't passed in\n\t\t/** @type {Record<string, any>} */\n\t\tthis._PackageFableServiceProvider = libPackage;\n\n\t\t// initialize options and UUID based on whether the fable was passed in or not.\n\t\tif (this.fable)\n\t\t{\n\t\t\tthis.UUID = pFable.getUUID();\n\t\t\tthis.options = (typeof(pOptions) === 'object') ? pOptions\n\t\t\t\t\t\t\t: {};\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// With no fable, check to see if there was an object passed into either of the first two\n\t\t\t// Parameters, and if so, treat it as the options object\n\t\t\tthis.options = ((typeof(pFable) === 'object') && !pFable.isFable) ? pFable\n\t\t\t\t\t\t\t: (typeof(pOptions) === 'object') ? pOptions\n\t\t\t\t\t\t\t: {};\n\t\t\tthis.UUID = `CORE-SVC-${Math.floor((Math.random() * (99999 - 10000)) + 10000)}`\n\t\t}\n\n\t\t// It's expected that the deriving class will set this\n\t\tthis.serviceType = `Unknown-${this.UUID}`;\n\n\t\t// The service hash is used to identify the specific instantiation of the service in the services map\n\t\tthis.Hash = (typeof(pServiceHash) === 'string') ? pServiceHash \n\t\t\t\t\t: (!this.fable && (typeof(pOptions) === 'string')) ? pOptions\n\t\t\t\t\t: `${this.UUID}`;\n\t}\n\n\t/**\n\t * @param {import('fable')} pFable\n\t */\n\tconnectFable(pFable)\n\t{\n\t\tif ((typeof(pFable) !== 'object') || (!pFable.isFable))\n\t\t{\n\t\t\tlet tmpErrorMessage = `Fable Service Provider Base: Cannot connect to Fable, invalid Fable object passed in. The pFable parameter was a [${typeof(pFable)}].}`;\n\t\t\tconsole.log(tmpErrorMessage);\n\t\t\treturn new Error(tmpErrorMessage);\n\t\t}\n\n\t\tif (!this.fable)\n\t\t{\n\t\t\tthis.fable = pFable;\n\t\t}\n\n\t\tif (!this.log)\n\t\t{\n\t\t\tthis.log = this.fable.Logging;\n\t\t}\n\t\tif (!this.services)\n\t\t{\n\t\t\tthis.services = this.fable.services;\n\t\t}\n\n\t\tif (!this.servicesMap)\n\t\t{\n\t\t\tthis.servicesMap = this.fable.servicesMap;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tstatic isFableService = true;\n}\n\nmodule.exports = FableServiceProviderBase;\n\n// This is left here in case we want to go back to having different code/base class for \"core\" services\nmodule.exports.CoreServiceProviderBase = FableServiceProviderBase;\n\n},{\"../package.json\":3}],5:[function(require,module,exports){\nmodule.exports={\n \"name\": \"pict-application\",\n \"version\": \"1.0.33\",\n \"description\": \"Application base class for a pict view-based application\",\n \"main\": \"source/Pict-Application.js\",\n \"scripts\": {\n \"test\": \"npx quack test\",\n \"start\": \"node source/Pict-Application.js\",\n \"coverage\": \"npx quack coverage\",\n \"build\": \"npx quack build\",\n \"docker-dev-build\": \"docker build ./ -f Dockerfile_LUXURYCode -t pict-application-image:local\",\n \"docker-dev-run\": \"docker run -it -d --name pict-application-dev -p 30001:8080 -p 38086:8086 -v \\\"$PWD/.config:/home/coder/.config\\\" -v \\\"$PWD:/home/coder/pict-application\\\" -u \\\"$(id -u):$(id -g)\\\" -e \\\"DOCKER_USER=$USER\\\" pict-application-image:local\",\n \"docker-dev-shell\": \"docker exec -it pict-application-dev /bin/bash\",\n \"tests\": \"npx quack test -g\",\n \"lint\": \"eslint source/**\",\n \"types\": \"tsc -p .\"\n },\n \"types\": \"types/source/Pict-Application.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/stevenvelozo/pict-application.git\"\n },\n \"author\": \"steven velozo <steven@velozo.com>\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/stevenvelozo/pict-application/issues\"\n },\n \"homepage\": \"https://github.com/stevenvelozo/pict-application#readme\",\n \"devDependencies\": {\n \"@eslint/js\": \"^9.28.0\",\n \"browser-env\": \"^3.3.0\",\n \"eslint\": \"^9.28.0\",\n \"pict\": \"^1.0.348\",\n \"pict-provider\": \"^1.0.10\",\n \"pict-view\": \"^1.0.66\",\n \"quackage\": \"^1.0.58\",\n \"typescript\": \"^5.9.3\"\n },\n \"mocha\": {\n \"diff\": true,\n \"extension\": [\n \"js\"\n ],\n \"package\": \"./package.json\",\n \"reporter\": \"spec\",\n \"slow\": \"75\",\n \"timeout\": \"5000\",\n \"ui\": \"tdd\",\n \"watch-files\": [\n \"source/**/*.js\",\n \"test/**/*.js\"\n ],\n \"watch-ignore\": [\n \"lib/vendor\"\n ]\n },\n \"dependencies\": {\n \"fable-serviceproviderbase\": \"^3.0.19\"\n }\n}\n\n},{}],6:[function(require,module,exports){\nconst libFableServiceBase = require('fable-serviceproviderbase')\n\nconst libPackage = require('../package.json');\n\nconst defaultPictSettings = (\n\t{\n\t\tName: 'DefaultPictApplication',\n\n\t\t// The main \"viewport\" is the view that is used to host our application\n\t\tMainViewportViewIdentifier: 'Default-View',\n\t\tMainViewportRenderableHash: false,\n\t\tMainViewportDestinationAddress: false,\n\t\tMainViewportDefaultDataAddress: false,\n\n\t\t// Whether or not we should automatically render the main viewport and other autorender views after we initialize the pict application\n\t\tAutoSolveAfterInitialize: true,\n\t\tAutoRenderMainViewportViewAfterInitialize: true,\n\t\tAutoRenderViewsAfterInitialize: false,\n\t\tAutoLoginAfterInitialize: false,\n\t\tAutoLoadDataAfterLogin: false,\n\n\t\tConfigurationOnlyViews: [],\n\n\t\tManifests: {},\n\t\t// The prefix to prepend on all template destination hashes\n\t\tIdentifierAddressPrefix: 'PICT-'\n\t});\n\n/**\n * Base class for pict applications.\n */\nclass PictApplication extends libFableServiceBase\n{\n\t/**\n\t * @param {import('fable')} pFable\n\t * @param {Record<string, any>} [pOptions]\n\t * @param {string} [pServiceHash]\n\t */\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpCarryOverConfiguration = (typeof(pFable.settings.PictApplicationConfiguration) === 'object') ? pFable.settings.PictApplicationConfiguration : {};\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(defaultPictSettings)), tmpCarryOverConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\t/** @type {any} */\n\t\tthis.options;\n\t\t/** @type {any} */\n\t\tthis.log;\n\t\t/** @type {import('pict') & import('fable')} */\n\t\tthis.fable;\n\t\t/** @type {string} */\n\t\tthis.UUID;\n\t\t/** @type {string} */\n\t\tthis.Hash;\n\t\t/**\n\t\t * @type {{ [key: string]: any }}\n\t\t */\n\t\tthis.servicesMap;\n\n\t\tthis.serviceType = 'PictApplication';\n\t\t/** @type {Record<string, any>} */\n\t\tthis._Package = libPackage;\n\n\t\t// Convenience and consistency naming\n\t\tthis.pict = this.fable;\n\t\t// Wire in the essential Pict state\n\t\t/** @type {Record<string, any>} */\n\t\tthis.AppData = this.fable.AppData;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.Bundle = this.fable.Bundle;\n\n\t\t/** @type {number} */\n\t\tthis.initializeTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastSolvedTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastLoginTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastMarshalFromViewsTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastMarshalToViewsTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastAutoRenderTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastLoadDataTimestamp;\n\n\t\t// Load all the manifests for the application\n\t\tlet tmpManifestKeys = Object.keys(this.options.Manifests);\n\t\tif (tmpManifestKeys.length > 0)\n\t\t{\n\t\t\tfor (let i = 0; i < tmpManifestKeys.length; i++)\n\t\t\t{\n\t\t\t\t// Load each manifest\n\t\t\t\tlet tmpManifestKey = tmpManifestKeys[i];\n\t\t\t\tthis.fable.instantiateServiceProvider('Manifest', this.options.Manifests[tmpManifestKey], tmpManifestKey);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Solve All Views */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonPreSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onPreSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonPreSolveAsync(fCallback)\n\t{\n\t\tthis.onPreSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeSolveAsync(fCallback)\n\t{\n\t\tthis.onBeforeSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonSolveAsync(fCallback)\n\t{\n\t\tthis.onSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tsolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} executing solve() function...`)\n\t\t}\n\n\t\t// Walk through any loaded providers and solve them as well.\n\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\tlet tmpProvidersToSolve = [];\n\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t{\n\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\tif (tmpProvider.options.AutoSolveWithApp)\n\t\t\t{\n\t\t\t\ttmpProvidersToSolve.push(tmpProvider);\n\t\t\t}\n\t\t}\n\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpProvidersToSolve.sort((a, b) => { return a.options.AutoSolveOrdinal - b.options.AutoSolveOrdinal; });\n\t\tfor (let i = 0; i < tmpProvidersToSolve.length; i++)\n\t\t{\n\t\t\ttmpProvidersToSolve[i].solve(tmpProvidersToSolve[i]);\n\t\t}\n\n\t\tthis.onBeforeSolve();\n\t\t// Now walk through any loaded views and initialize them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToSolve = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\tif (tmpView.options.AutoInitialize)\n\t\t\t{\n\t\t\t\ttmpViewsToSolve.push(tmpView);\n\t\t\t}\n\t\t}\n\t\t// Sort the views by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpViewsToSolve.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\tfor (let i = 0; i < tmpViewsToSolve.length; i++)\n\t\t{\n\t\t\ttmpViewsToSolve[i].solve();\n\t\t}\n\t\tthis.onSolve();\n\t\tthis.onAfterSolve();\n\t\tthis.lastSolvedTimestamp = this.fable.log.getTimeStamp();\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tsolveAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\ttmpAnticipate.anticipate(this.onBeforeSolveAsync.bind(this));\n\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : false;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\t\t// Walk through any loaded providers and solve them as well.\n\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\tlet tmpProvidersToSolve = [];\n\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t{\n\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\tif (tmpProvider.options.AutoSolveWithApp)\n\t\t\t{\n\t\t\t\ttmpProvidersToSolve.push(tmpProvider);\n\t\t\t}\n\t\t}\n\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpProvidersToSolve.sort((a, b) => { return a.options.AutoSolveOrdinal - b.options.AutoSolveOrdinal; });\n\t\tfor (let i = 0; i < tmpProvidersToSolve.length; i++)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvidersToSolve[i].solveAsync.bind(tmpProvidersToSolve[i]));\n\t\t}\n\n\t\t// Walk through any loaded views and solve them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToSolve = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\tif (tmpView.options.AutoSolveWithApp)\n\t\t\t{\n\t\t\t\ttmpViewsToSolve.push(tmpView);\n\t\t\t}\n\t\t}\n\t\t// Sort the views by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpViewsToSolve.sort((a, b) => { return a.options.AutoSolveOrdinal - b.options.AutoSolveOrdinal; });\n\t\tfor (let i = 0; i < tmpViewsToSolve.length; i++)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpViewsToSolve[i].solveAsync.bind(tmpViewsToSolve[i]));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onSolveAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterSolveAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastSolvedTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterSolveAsync(fCallback)\n\t{\n\t\tthis.onAfterSolve();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Application Login */\n\t/* -------------------------------------------------------------------------- */\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeLoginAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeLoginAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonLoginAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onLoginAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tloginAsync(fCallback)\n\t{\n\t\tconst tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\tlet tmpCallback = fCallback;\n\n\t\tif (typeof(tmpCallback) !== 'function')\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loginAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loginAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeLoginAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onLoginAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterLoginAsync.bind(this));\n\n\t\t// check and see if we should automatically trigger a data load\n\t\tif (this.options.AutoLoadDataAfterLogin)\n\t\t{\n\t\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t\t{\n\t\t\t\tif (!this.isLoggedIn())\n\t\t\t\t{\n\t\t\t\t\treturn fNext();\n\t\t\t\t}\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto loading data after login...`);\n\t\t\t\t}\n\t\t\t\t//TODO: should data load errors funnel here? this creates a weird coupling between login and data load callbacks\n\t\t\t\tthis.loadDataAsync((pError) =>\n\t\t\t\t{\n\t\t\t\t\tfNext(pError);\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loginAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastLoginTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * Check if the application state is logged in. Defaults to true. Override this method in your application based on login requirements.\n\t *\n\t * @return {boolean}\n\t */\n\tisLoggedIn()\n\t{\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterLoginAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterLoginAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Application LoadData */\n\t/* -------------------------------------------------------------------------- */\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeLoadDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeLoadDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonLoadDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onLoadDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tloadDataAsync(fCallback)\n\t{\n\t\tconst tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\tlet tmpCallback = fCallback;\n\n\t\tif (typeof(tmpCallback) !== 'function')\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loadDataAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loadDataAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeLoadDataAsync.bind(this));\n\n\t\t// Walk through any loaded providers and load their data as well.\n\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\tlet tmpProvidersToLoadData = [];\n\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t{\n\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\tif (tmpProvider.options.AutoLoadDataWithApp)\n\t\t\t{\n\t\t\t\ttmpProvidersToLoadData.push(tmpProvider);\n\t\t\t}\n\t\t}\n\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpProvidersToLoadData.sort((a, b) => { return a.options.AutoLoadDataOrdinal - b.options.AutoLoadDataOrdinal; });\n\n\t\tfor (const tmpProvider of tmpProvidersToLoadData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onBeforeLoadDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onLoadDataAsync.bind(this));\n\n\t\t//TODO: think about ways to parallelize these\n\t\tfor (const tmpProvider of tmpProvidersToLoadData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onLoadDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onAfterLoadDataAsync.bind(this));\n\n\t\tfor (const tmpProvider of tmpProvidersToLoadData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onAfterLoadDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.wait(\n\t\t\t/** @param {Error} [pError] */\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loadDataAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastLoadDataTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterLoadDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterLoadDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Application SaveData */\n\t/* -------------------------------------------------------------------------- */\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeSaveDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeSaveDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonSaveDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onSaveDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tsaveDataAsync(fCallback)\n\t{\n\t\tconst tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\tlet tmpCallback = fCallback;\n\n\t\tif (typeof(tmpCallback) !== 'function')\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} saveDataAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} saveDataAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeSaveDataAsync.bind(this));\n\n\t\t// Walk through any loaded providers and load their data as well.\n\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\tlet tmpProvidersToSaveData = [];\n\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t{\n\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\tif (tmpProvider.options.AutoSaveDataWithApp)\n\t\t\t{\n\t\t\t\ttmpProvidersToSaveData.push(tmpProvider);\n\t\t\t}\n\t\t}\n\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpProvidersToSaveData.sort((a, b) => { return a.options.AutoSaveDataOrdinal - b.options.AutoSaveDataOrdinal; });\n\n\t\tfor (const tmpProvider of tmpProvidersToSaveData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onBeforeSaveDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onSaveDataAsync.bind(this));\n\n\t\t//TODO: think about ways to parallelize these\n\t\tfor (const tmpProvider of tmpProvidersToSaveData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onSaveDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onAfterSaveDataAsync.bind(this));\n\n\t\tfor (const tmpProvider of tmpProvidersToSaveData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onAfterSaveDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.wait(\n\t\t\t/** @param {Error} [pError] */\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} saveDataAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastSaveDataTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterSaveDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterSaveDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Initialize Application */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeInitializeAsync(fCallback)\n\t{\n\t\tthis.onBeforeInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonInitializeAsync(fCallback)\n\t{\n\t\tthis.onInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tinitialize()\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} initialize:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tthis.onBeforeInitialize();\n\n\t\t\tif ('ConfigurationOnlyViews' in this.options)\n\t\t\t{\n\t\t\t\t// Load all the configuration only views\n\t\t\t\tfor (let i = 0; i < this.options.ConfigurationOnlyViews.length; i++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpViewIdentifier = (typeof(this.options.ConfigurationOnlyViews[i].ViewIdentifier) === 'undefined') ? `AutoView-${this.fable.getUUID()}`\n\t\t\t\t\t\t\t\t\t\t\t: this.options.ConfigurationOnlyViews[i].ViewIdentifier;\n\t\t\t\t\tthis.log.info(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} adding configuration only view: ${tmpViewIdentifier}`);\n\t\t\t\t\tthis.pict.addView(tmpViewIdentifier, this.options.ConfigurationOnlyViews[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.onInitialize();\n\n\t\t\t// Walk through any loaded providers and initialize them as well.\n\t\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\t\tlet tmpProvidersToInitialize = [];\n\t\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\t\tif (tmpProvider.options.AutoInitialize)\n\t\t\t\t{\n\t\t\t\t\ttmpProvidersToInitialize.push(tmpProvider);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\t\ttmpProvidersToInitialize.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\t\tfor (let i = 0; i < tmpProvidersToInitialize.length; i++)\n\t\t\t{\n\t\t\t\ttmpProvidersToInitialize[i].initialize();\n\t\t\t}\n\n\t\t\t// Now walk through any loaded views and initialize them as well.\n\t\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\t\tlet tmpViewsToInitialize = [];\n\t\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\t\tif (tmpView.options.AutoInitialize)\n\t\t\t\t{\n\t\t\t\t\ttmpViewsToInitialize.push(tmpView);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the views by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\t\ttmpViewsToInitialize.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\t\tfor (let i = 0; i < tmpViewsToInitialize.length; i++)\n\t\t\t{\n\t\t\t\ttmpViewsToInitialize[i].initialize();\n\t\t\t}\n\n\t\t\tthis.onAfterInitialize();\n\t\t\tif (this.options.AutoSolveAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto solving after initialization...`);\n\t\t\t\t}\n\t\t\t\t// Solve the template synchronously\n\t\t\t\tthis.solve();\n\t\t\t}\n\t\t\t// Now check and see if we should automatically render as well\n\t\t\tif (this.options.AutoRenderMainViewportViewAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto rendering after initialization...`);\n\t\t\t\t}\n\t\t\t\t// Render the template synchronously\n\t\t\t\tthis.render();\n\t\t\t}\n\t\t\tthis.initializeTimestamp = this.fable.log.getTimeStamp();\n\t\t\tthis.onCompletionOfInitialize();\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initialize called but initialization is already completed. Aborting.`);\n\t\t\treturn false;\n\t\t}\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tinitializeAsync(fCallback)\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} initializeAsync:`);\n\t\t}\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : false;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initializeAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initializeAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t\tif (this.pict.LogNoisiness > 3)\n\t\t\t{\n\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} beginning initialization...`);\n\t\t\t}\n\n\t\t\tif ('ConfigurationOnlyViews' in this.options)\n\t\t\t{\n\t\t\t\t// Load all the configuration only views\n\t\t\t\tfor (let i = 0; i < this.options.ConfigurationOnlyViews.length; i++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpViewIdentifier = (typeof(this.options.ConfigurationOnlyViews[i].ViewIdentifier) === 'undefined') ? `AutoView-${this.fable.getUUID()}`\n\t\t\t\t\t\t\t\t\t\t\t: this.options.ConfigurationOnlyViews[i].ViewIdentifier;\n\t\t\t\t\tthis.log.info(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} adding configuration only view: ${tmpViewIdentifier}`);\n\t\t\t\t\tthis.pict.addView(tmpViewIdentifier, this.options.ConfigurationOnlyViews[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttmpAnticipate.anticipate(this.onBeforeInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onInitializeAsync.bind(this));\n\n\t\t\t// Walk through any loaded providers and solve them as well.\n\t\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\t\tlet tmpProvidersToInitialize = [];\n\t\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\t\tif (tmpProvider.options.AutoInitialize)\n\t\t\t\t{\n\t\t\t\t\ttmpProvidersToInitialize.push(tmpProvider);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\t\ttmpProvidersToInitialize.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\t\tfor (let i = 0; i < tmpProvidersToInitialize.length; i++)\n\t\t\t{\n\t\t\t\ttmpAnticipate.anticipate(tmpProvidersToInitialize[i].initializeAsync.bind(tmpProvidersToInitialize[i]));\n\t\t\t}\n\n\t\t\t// Now walk through any loaded views and initialize them as well.\n\t\t\t// TODO: Some optimization cleverness could be gained by grouping them into a parallelized async operation, by ordinal.\n\t\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\t\tlet tmpViewsToInitialize = [];\n\t\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\t\tif (tmpView.options.AutoInitialize)\n\t\t\t\t{\n\t\t\t\t\ttmpViewsToInitialize.push(tmpView);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the views by their priority\n\t\t\t// If they are all the default priority 0, it will end up being add order due to JSON Object Property Key order stuff\n\t\t\ttmpViewsToInitialize.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\t\tfor (let i = 0; i < tmpViewsToInitialize.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpView = tmpViewsToInitialize[i];\n\t\t\t\ttmpAnticipate.anticipate(tmpView.initializeAsync.bind(tmpView));\n\t\t\t}\n\n\t\t\ttmpAnticipate.anticipate(this.onAfterInitializeAsync.bind(this));\n\n\t\t\tif (this.options.AutoLoginAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto login (asynchronously) after initialization...`);\n\t\t\t\t}\n\t\t\t\ttmpAnticipate.anticipate(this.loginAsync.bind(this));\n\t\t\t}\n\n\t\t\tif (this.options.AutoSolveAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto solving (asynchronously) after initialization...`);\n\t\t\t\t}\n\t\t\t\ttmpAnticipate.anticipate(this.solveAsync.bind(this));\n\t\t\t}\n\n\t\t\tif (this.options.AutoRenderMainViewportViewAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto rendering (asynchronously) after initialization...`);\n\t\t\t\t}\n\t\t\t\ttmpAnticipate.anticipate(this.renderMainViewportAsync.bind(this));\n\t\t\t}\n\n\t\t\ttmpAnticipate.wait(\n\t\t\t\t(pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initializeAsync Error: ${pError.message || pError}`, { stack: pError.stack });\n\t\t\t\t\t}\n\t\t\t\t\tthis.initializeTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initialization complete.`);\n\t\t\t\t\t}\n\t\t\t\t\treturn tmpCallback();\n\t\t\t\t});\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} async initialize called but initialization is already completed. Aborting.`);\n\t\t\t// TODO: Should this be an error?\n\t\t\treturn this.onCompletionOfInitializeAsync(tmpCallback);\n\t\t}\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterInitializeAsync(fCallback)\n\t{\n\t\tthis.onAfterInitialize();\n\t\treturn fCallback();\n\t}\n\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonCompletionOfInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onCompletionOfInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonCompletionOfInitializeAsync(fCallback)\n\t{\n\t\tthis.onCompletionOfInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Marshal Data From All Views */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeMarshalFromViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeMarshalFromViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeMarshalFromViewsAsync(fCallback)\n\t{\n\t\tthis.onBeforeMarshalFromViews();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonMarshalFromViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onMarshalFromViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonMarshalFromViewsAsync(fCallback)\n\t{\n\t\tthis.onMarshalFromViews();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tmarshalFromViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} executing marshalFromViews() function...`)\n\t\t}\n\t\tthis.onBeforeMarshalFromViews();\n\t\t// Now walk through any loaded views and initialize them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToMarshalFromViews = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\ttmpViewsToMarshalFromViews.push(tmpView);\n\t\t}\n\t\tfor (let i = 0; i < tmpViewsToMarshalFromViews.length; i++)\n\t\t{\n\t\t\ttmpViewsToMarshalFromViews[i].marshalFromView();\n\t\t}\n\t\tthis.onMarshalFromViews();\n\t\tthis.onAfterMarshalFromViews();\n\t\tthis.lastMarshalFromViewsTimestamp = this.fable.log.getTimeStamp();\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tmarshalFromViewsAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : false;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewsAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewsAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeMarshalFromViewsAsync.bind(this));\n\t\t// Walk through any loaded views and marshalFromViews them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToMarshalFromViews = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\ttmpViewsToMarshalFromViews.push(tmpView);\n\t\t}\n\t\tfor (let i = 0; i < tmpViewsToMarshalFromViews.length; i++)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpViewsToMarshalFromViews[i].marshalFromViewAsync.bind(tmpViewsToMarshalFromViews[i]));\n\t\t}\n\t\ttmpAnticipate.anticipate(this.onMarshalFromViewsAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterMarshalFromViewsAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewsAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastMarshalFromViewsTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterMarshalFromViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterMarshalFromViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterMarshalFromViewsAsync(fCallback)\n\t{\n\t\tthis.onAfterMarshalFromViews();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Marshal Data To All Views */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeMarshalToViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeMarshalToViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeMarshalToViewsAsync(fCallback)\n\t{\n\t\tthis.onBeforeMarshalToViews();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonMarshalToViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onMarshalToViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonMarshalToViewsAsync(fCallback)\n\t{\n\t\tthis.onMarshalToViews();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tmarshalToViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} executing marshalToViews() function...`)\n\t\t}\n\t\tthis.onBeforeMarshalToViews();\n\t\t// Now walk through any loaded views and initialize them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToMarshalToViews = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\ttmpViewsToMarshalToViews.push(tmpView);\n\t\t}\n\t\tfor (let i = 0; i < tmpViewsToMarshalToViews.length; i++)\n\t\t{\n\t\t\ttmpViewsToMarshalToViews[i].marshalToView();\n\t\t}\n\t\tthis.onMarshalToViews();\n\t\tthis.onAfterMarshalToViews();\n\t\tthis.lastMarshalToViewsTimestamp = this.fable.log.getTimeStamp();\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tmarshalToViewsAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : false;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewsAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewsAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeMarshalToViewsAsync.bind(this));\n\t\t// Walk through any loaded views and marshalToViews them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToMarshalToViews = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\ttmpViewsToMarshalToViews.push(tmpView);\n\t\t}\n\t\tfor (let i = 0; i < tmpViewsToMarshalToViews.length; i++)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpViewsToMarshalToViews[i].marshalToViewAsync.bind(tmpViewsToMarshalToViews[i]));\n\t\t}\n\t\ttmpAnticipate.anticipate(this.onMarshalToViewsAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterMarshalToViewsAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewsAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastMarshalToViewsTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterMarshalToViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterMarshalToViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterMarshalToViewsAsync(fCallback)\n\t{\n\t\tthis.onAfterMarshalToViews();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Render View */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeRender()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeRenderAsync(fCallback)\n\t{\n\t\tthis.onBeforeRender();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {string} [pViewIdentifier] - The hash of the view to render. By default, the main viewport view is rendered.\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string} [pTemplateDataAddress] - The address where the data for the template is stored.\n\t *\n\t * TODO: Should we support objects for pTemplateDataAddress for parity with pict-view?\n\t */\n\trender(pViewIdentifier, pRenderableHash, pRenderDestinationAddress, pTemplateDataAddress)\n\t{\n\t\tlet tmpViewIdentifier = (typeof(pViewIdentifier) !== 'string') ? this.options.MainViewportViewIdentifier : pViewIdentifier;\n\t\tlet tmpRenderableHash = (typeof(pRenderableHash) !== 'string') ? this.options.MainViewportRenderableHash : pRenderableHash;\n\t\tlet tmpRenderDestinationAddress = (typeof(pRenderDestinationAddress) !== 'string') ? this.options.MainViewportDestinationAddress : pRenderDestinationAddress;\n\t\tlet tmpTemplateDataAddress = (typeof(pTemplateDataAddress) !== 'string') ? this.options.MainViewportDefaultDataAddress : pTemplateDataAddress;\n\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} VIEW Renderable[${tmpRenderableHash}] Destination[${tmpRenderDestinationAddress}] TemplateDataAddress[${tmpTemplateDataAddress}] render:`);\n\t\t}\n\n\t\tthis.onBeforeRender();\n\n\t\t// Now get the view (by hash) from the loaded views\n\t\tlet tmpView = (typeof (tmpViewIdentifier) === 'string') ? this.servicesMap.PictView[tmpViewIdentifier] : false;\n\t\tif (!tmpView)\n\t\t{\n\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} could not render from View ${tmpViewIdentifier} because it is not a valid view.`);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.onRender();\n\n\t\ttmpView.render(tmpRenderableHash, tmpRenderDestinationAddress, tmpTemplateDataAddress);\n\n\t\tthis.onAfterRender();\n\n\t\treturn true;\n\t}\n\t/**\n\t * @return {boolean}\n\t */\n\tonRender()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonRenderAsync(fCallback)\n\t{\n\t\tthis.onRender();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {string|((error?: Error) => void)} pViewIdentifier - The hash of the view to render. By default, the main viewport view is rendered. (or the callback)\n\t * @param {string|((error?: Error) => void)} [pRenderableHash] - The hash of the renderable to render. (or the callback)\n\t * @param {string|((error?: Error) => void)} [pRenderDestinationAddress] - The address where the renderable will be rendered. (or the callback)\n\t * @param {string|((error?: Error) => void)} [pTemplateDataAddress] - The address where the data for the template is stored. (or the callback)\n\t * @param {(error?: Error) => void} [fCallback] - The callback, if all other parameters are provided.\n\t *\n\t * TODO: Should we support objects for pTemplateDataAddress for parity with pict-view?\n\t */\n\trenderAsync(pViewIdentifier, pRenderableHash, pRenderDestinationAddress, pTemplateDataAddress, fCallback)\n\t{\n\t\tlet tmpViewIdentifier = (typeof(pViewIdentifier) !== 'string') ? this.options.MainViewportViewIdentifier : pViewIdentifier;\n\t\tlet tmpRenderableHash = (typeof(pRenderableHash) !== 'string') ? this.options.MainViewportRenderableHash : pRenderableHash;\n\t\tlet tmpRenderDestinationAddress = (typeof(pRenderDestinationAddress) !== 'string') ? this.options.MainViewportDestinationAddress : pRenderDestinationAddress;\n\t\tlet tmpTemplateDataAddress = (typeof(pTemplateDataAddress) !== 'string') ? this.options.MainViewportDefaultDataAddress : pTemplateDataAddress;\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback :\n\t\t\t\t\t\t\t(typeof(pTemplateDataAddress) === 'function') ? pTemplateDataAddress :\n\t\t\t\t\t\t\t(typeof(pRenderDestinationAddress) === 'function') ? pRenderDestinationAddress :\n\t\t\t\t\t\t\t(typeof(pRenderableHash) === 'function') ? pRenderableHash :\n\t\t\t\t\t\t\t(typeof(pViewIdentifier) === 'function') ? pViewIdentifier :\n\t\t\t\t\t\t\tfalse;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} VIEW Renderable[${tmpRenderableHash}] Destination[${tmpRenderDestinationAddress}] TemplateDataAddress[${tmpTemplateDataAddress}] renderAsync:`);\n\t\t}\n\n\t\tlet tmpRenderAnticipate = this.fable.newAnticipate();\n\n\t\ttmpRenderAnticipate.anticipate(this.onBeforeRenderAsync.bind(this));\n\n\t\tlet tmpView = (typeof (tmpViewIdentifier) === 'string') ? this.servicesMap.PictView[tmpViewIdentifier] : false;\n\t\tif (!tmpView)\n\t\t{\n\t\t\tlet tmpErrorMessage = `PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} could not asynchronously render from View ${tmpViewIdentifier} because it is not a valid view.`;\n\t\t\tif (this.pict.LogNoisiness > 3)\n\t\t\t{\n\t\t\t\tthis.log.error(tmpErrorMessage);\n\t\t\t}\n\t\t\treturn tmpCallback(new Error(tmpErrorMessage));\n\t\t}\n\n\t\ttmpRenderAnticipate.anticipate(this.onRenderAsync.bind(this));\n\n\t\ttmpRenderAnticipate.anticipate(\n\t\t\t(fNext) =>\n\t\t\t{\n\t\t\t\ttmpView.renderAsync.call(tmpView, tmpRenderableHash, tmpRenderDestinationAddress, tmpTemplateDataAddress, fNext);\n\t\t\t});\n\n\t\ttmpRenderAnticipate.anticipate(this.onAfterRenderAsync.bind(this));\n\n\t\treturn tmpRenderAnticipate.wait(tmpCallback);\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterRender()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterRenderAsync(fCallback)\n\t{\n\t\tthis.onAfterRender();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\trenderMainViewport()\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderMainViewport:`);\n\t\t}\n\n\t\treturn this.render();\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\trenderMainViewportAsync(fCallback)\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderMainViewportAsync:`);\n\t\t}\n\n\t\treturn this.renderAsync(fCallback);\n\t}\n\t/**\n\t * @return {void}\n\t */\n\trenderAutoViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} beginning renderAutoViews...`);\n\t\t}\n\t\t// Now walk through any loaded views and sort them by the AutoRender ordinal\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\t// Sort the views by their priority\n\t\t// If they are all the default priority 0, it will end up being add order due to JSON Object Property Key order stuff\n\t\ttmpLoadedViews.sort((a, b) =>\n\t\t{\n\t\t\treturn this.pict.views[a].options.AutoRenderOrdinal - this.pict.views[b].options.AutoRenderOrdinal;\n\t\t});\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\tif (tmpView.options.AutoRender)\n\t\t\t{\n\t\t\t\ttmpView.render();\n\t\t\t}\n\t\t}\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAutoViewsAsync complete.`);\n\t\t}\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\trenderAutoViewsAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback :\n\t\t\t\t\t\t\tfalse;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAutoViewsAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAutoViewsAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} beginning renderAutoViewsAsync...`);\n\t\t}\n\n\t\t// Now walk through any loaded views and sort them by the AutoRender ordinal\n\t\t// TODO: Some optimization cleverness could be gained by grouping them into a parallelized async operation, by ordinal.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\t// Sort the views by their priority\n\t\t// If they are all the default priority 0, it will end up being add order due to JSON Object Property Key order stuff\n\t\ttmpLoadedViews.sort((a, b) =>\n\t\t{\n\t\t\treturn this.pict.views[a].options.AutoRenderOrdinal - this.pict.views[b].options.AutoRenderOrdinal;\n\t\t});\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\tif (tmpView.options.AutoRender)\n\t\t\t{\n\t\t\t\ttmpAnticipate.anticipate(tmpView.renderAsync.bind(tmpView));\n\t\t\t}\n\t\t}\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tthis.lastAutoRenderTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAutoViewsAsync complete.`);\n\t\t\t\t}\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tget isPictApplication()\n\t{\n\t\treturn true;\n\t}\n}\n\nmodule.exports = PictApplication;\n\n},{\"../package.json\":5,\"fable-serviceproviderbase\":4}],7:[function(require,module,exports){\nmodule.exports={\n \"name\": \"pict-provider\",\n \"version\": \"1.0.12\",\n \"description\": \"Pict Provider Base Class\",\n \"main\": \"source/Pict-Provider.js\",\n \"scripts\": {\n \"start\": \"node source/Pict-Provider.js\",\n \"test\": \"npx quack test\",\n \"tests\": \"npx quack test -g\",\n \"coverage\": \"npx quack coverage\",\n \"build\": \"npx quack build\",\n \"docker-dev-build\": \"docker build ./ -f Dockerfile_LUXURYCode -t pict-provider-image:local\",\n \"docker-dev-run\": \"docker run -it -d --name pict-provider-dev -p 24125:8080 -p 30027:8086 -v \\\"$PWD/.config:/home/coder/.config\\\" -v \\\"$PWD:/home/coder/pict-provider\\\" -u \\\"$(id -u):$(id -g)\\\" -e \\\"DOCKER_USER=$USER\\\" pict-provider-image:local\",\n \"docker-dev-shell\": \"docker exec -it pict-provider-dev /bin/bash\",\n \"lint\": \"eslint source/**\",\n \"types\": \"tsc -p .\"\n },\n \"types\": \"types/source/Pict-Provider.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/stevenvelozo/pict-provider.git\"\n },\n \"author\": \"steven velozo <steven@velozo.com>\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/stevenvelozo/pict-provider/issues\"\n },\n \"homepage\": \"https://github.com/stevenvelozo/pict-provider#readme\",\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.1\",\n \"eslint\": \"^9.39.1\",\n \"pict\": \"^1.0.351\",\n \"quackage\": \"^1.0.58\",\n \"typescript\": \"^5.9.3\"\n },\n \"dependencies\": {\n \"fable-serviceproviderbase\": \"^3.0.19\"\n },\n \"mocha\": {\n \"diff\": true,\n \"extension\": [\n \"js\"\n ],\n \"package\": \"./package.json\",\n \"reporter\": \"spec\",\n \"slow\": \"75\",\n \"timeout\": \"5000\",\n \"ui\": \"tdd\",\n \"watch-files\": [\n \"source/**/*.js\",\n \"test/**/*.js\"\n ],\n \"watch-ignore\": [\n \"lib/vendor\"\n ]\n }\n}\n\n},{}],8:[function(require,module,exports){\nconst libFableServiceBase = require('fable-serviceproviderbase');\n\nconst libPackage = require('../package.json');\n\nconst defaultPictProviderSettings = (\n\t{\n\t\tProviderIdentifier: false,\n\n\t\t// If this is set to true, when the App initializes this will.\n\t\t// After the App initializes, initialize will be called as soon as it's added.\n\t\tAutoInitialize: true,\n\t\tAutoInitializeOrdinal: 0,\n\n\t\tAutoLoadDataWithApp: true,\n\t\tAutoLoadDataOrdinal: 0,\n\n\t\tAutoSolveWithApp: true,\n\t\tAutoSolveOrdinal: 0,\n\n\t\tManifests: {},\n\n\t\tTemplates: []\n\t});\n\nclass PictProvider extends libFableServiceBase\n{\n\t/**\n\t * @param {import('fable')} pFable - The Fable instance.\n\t * @param {Record<string, any>} [pOptions] - The options for the provider.\n\t * @param {string} [pServiceHash] - The service hash for the provider.\n\t */\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\t// Intersect default options, parent constructor, service information\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(defaultPictProviderSettings)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\t/** @type {import('fable') & import('pict') & { instantiateServiceProviderWithoutRegistration(pServiceType: string, pOptions?: Record<string, any>, pCustomServiceHash?: string): any }} */\n\t\tthis.fable;\n\t\t/** @type {import('fable') & import('pict') & { instantiateServiceProviderWithoutRegistration(pServiceType: string, pOptions?: Record<string, any>, pCustomServiceHash?: string): any }} */\n\t\tthis.pict;\n\t\t/** @type {any} */\n\t\tthis.log;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.options;\n\t\t/** @type {string} */\n\t\tthis.UUID;\n\t\t/** @type {string} */\n\t\tthis.Hash;\n\n\t\tif (!this.options.ProviderIdentifier)\n\t\t{\n\t\t\tthis.options.ProviderIdentifier = `AutoProviderID-${this.fable.getUUID()}`;\n\t\t}\n\n\t\tthis.serviceType = 'PictProvider';\n\t\t/** @type {Record<string, any>} */\n\t\tthis._Package = libPackage;\n\n\t\t// Convenience and consistency naming\n\t\tthis.pict = this.fable;\n\n\t\t// Wire in the essential Pict application state\n\t\t/** @type {Record<string, any>} */\n\t\tthis.AppData = this.pict.AppData;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.Bundle = this.pict.Bundle;\n\n\t\tthis.initializeTimestamp = false;\n\t\tthis.lastSolvedTimestamp = false;\n\n\t\tfor (let i = 0; i < this.options.Templates.length; i++)\n\t\t{\n\t\t\tlet tmpDefaultTemplate = this.options.Templates[i];\n\n\t\t\tif (!tmpDefaultTemplate.hasOwnProperty('Postfix') || !tmpDefaultTemplate.hasOwnProperty('Template'))\n\t\t\t{\n\t\t\t\tthis.log.error(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} could not load Default Template ${i} in the options array.`, tmpDefaultTemplate);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!tmpDefaultTemplate.Source)\n\t\t\t\t{\n\t\t\t\t\ttmpDefaultTemplate.Source = `PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} options object.`;\n\t\t\t\t}\n\t\t\t\tthis.pict.TemplateProvider.addDefaultTemplate(tmpDefaultTemplate.Prefix, tmpDefaultTemplate.Postfix, tmpDefaultTemplate.Template, tmpDefaultTemplate.Source);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Initialization */\n\t/* -------------------------------------------------------------------------- */\n\tonBeforeInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onBeforeInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after pre-pinitialization.\n\t *\n\t * @return {void}\n\t */\n\tonBeforeInitializeAsync(fCallback)\n\t{\n\t\tthis.onBeforeInitialize();\n\t\treturn fCallback();\n\t}\n\n\tonInitialize()\n\t{\n\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after initialization.\n\t *\n\t * @return {void}\n\t */\n\tonInitializeAsync(fCallback)\n\t{\n\t\tthis.onInitialize();\n\t\treturn fCallback();\n\t}\n\n\tinitialize()\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow PROVIDER [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initialize:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tthis.onBeforeInitialize();\n\t\t\tthis.onInitialize();\n\t\t\tthis.onAfterInitialize();\n\t\t\tthis.initializeTimestamp = this.pict.log.getTimeStamp();\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initialize called but initialization is already completed. Aborting.`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after initialization.\n\t *\n\t * @return {void}\n\t */\n\tinitializeAsync(fCallback)\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow PROVIDER [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initializeAsync:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t{\n\t\t\t\tthis.log.info(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} beginning initialization...`);\n\t\t\t}\n\n\t\t\ttmpAnticipate.anticipate(this.onBeforeInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onAfterInitializeAsync.bind(this));\n\n\t\t\ttmpAnticipate.wait(\n\t\t\t\t(pError) =>\n\t\t\t\t{\n\t\t\t\t\tthis.initializeTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initialization failed: ${pError.message || pError}`, { Stack: pError.stack });\n\t\t\t\t\t}\n\t\t\t\t\telse if (this.pict.LogNoisiness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.info(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initialization complete.`);\n\t\t\t\t\t}\n\t\t\t\t\treturn fCallback();\n\t\t\t\t})\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} async initialize called but initialization is already completed. Aborting.`);\n\t\t\t// TODO: Should this be an error?\n\t\t\treturn fCallback();\n\t\t}\n\t}\n\n\tonAfterInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onAfterInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after initialization.\n\t *\n\t * @return {void}\n\t */\n\tonAfterInitializeAsync(fCallback)\n\t{\n\t\tthis.onAfterInitialize();\n\t\treturn fCallback();\n\t}\n\n\tonPreRender()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onPreRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after pre-render.\n\t *\n\t * @return {void}\n\t */\n\tonPreRenderAsync(fCallback)\n\t{\n\t\tthis.onPreRender();\n\t\treturn fCallback();\n\t}\n\n\trender()\n\t{\n\t\treturn this.onPreRender();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after render.\n\t *\n\t * @return {void}\n\t */\n\trenderAsync(fCallback)\n\t{\n\t\tthis.onPreRender();\n\t\treturn fCallback();\n\t}\n\n\tonPreSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onPreSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after pre-solve.\n\t *\n\t * @return {void}\n\t */\n\tonPreSolveAsync(fCallback)\n\t{\n\t\tthis.onPreSolve();\n\t\treturn fCallback();\n\t}\n\n\tsolve()\n\t{\n\t\treturn this.onPreSolve();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after solve.\n\t *\n\t * @return {void}\n\t */\n\tsolveAsync(fCallback)\n\t{\n\t\tthis.onPreSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data pre-load.\n\t */\n\tonBeforeLoadDataAsync(fCallback)\n\t{\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Hook to allow the provider to load data during application data load.\n\t *\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data load.\n\t */\n\tonLoadDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onLoadDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data post-load.\n\t */\n\tonAfterLoadDataAsync(fCallback)\n\t{\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data pre-load.\n\t *\n\t * @return {void}\n\t */\n\tonBeforeSaveDataAsync(fCallback)\n\t{\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Hook to allow the provider to load data during application data load.\n\t *\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data load.\n\t *\n\t * @return {void}\n\t */\n\tonSaveDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onSaveDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data post-load.\n\t *\n\t * @return {void}\n\t */\n\tonAfterSaveDataAsync(fCallback)\n\t{\n\t\treturn fCallback();\n\t}\n}\n\nmodule.exports = PictProvider;\n\n},{\"../package.json\":7,\"fable-serviceproviderbase\":4}],9:[function(require,module,exports){\n// The container for all the Pict-Section-Flow related code.\n\n// The main flow diagram view class\nmodule.exports = require('./views/PictView-Flow.js');\n\n// Node rendering view\nmodule.exports.PictViewFlowNode = require('./views/PictView-Flow-Node.js');\n\n// Toolbar views\nmodule.exports.PictViewFlowToolbar = require('./views/PictView-Flow-Toolbar.js');\nmodule.exports.PictViewFlowFloatingToolbar = require('./views/PictView-Flow-FloatingToolbar.js');\n\n// Services\nmodule.exports.PictServiceFlowInteractionManager = require('./services/PictService-Flow-InteractionManager.js');\nmodule.exports.PictServiceFlowConnectionRenderer = require('./services/PictService-Flow-ConnectionRenderer.js');\nmodule.exports.PictServiceFlowTether = require('./services/PictService-Flow-Tether.js');\nmodule.exports.PictServiceFlowLayout = require('./services/PictService-Flow-Layout.js');\nmodule.exports.PictServiceFlowPathGenerator = require('./services/PictService-Flow-PathGenerator.js');\nmodule.exports.PictServiceFlowViewportManager = require('./services/PictService-Flow-ViewportManager.js');\nmodule.exports.PictServiceFlowSelectionManager = require('./services/PictService-Flow-SelectionManager.js');\nmodule.exports.PictServiceFlowPanelManager = require('./services/PictService-Flow-PanelManager.js');\nmodule.exports.PictServiceFlowDataManager = require('./services/PictService-Flow-DataManager.js');\nmodule.exports.PictServiceFlowConnectionHandleManager = require('./services/PictService-Flow-ConnectionHandleManager.js');\nmodule.exports.PictServiceFlowRenderManager = require('./services/PictService-Flow-RenderManager.js');\nmodule.exports.PictServiceFlowPortRenderer = require('./services/PictService-Flow-PortRenderer.js');\n\n// Providers\nmodule.exports.PictProviderFlowNodeTypes = require('./providers/PictProvider-Flow-NodeTypes.js');\nmodule.exports.PictProviderFlowEventHandler = require('./providers/PictProvider-Flow-EventHandler.js');\nmodule.exports.PictProviderFlowLayouts = require('./providers/PictProvider-Flow-Layouts.js');\nmodule.exports.PictProviderFlowSVGHelpers = require('./providers/PictProvider-Flow-SVGHelpers.js');\nmodule.exports.PictProviderFlowGeometry = require('./providers/PictProvider-Flow-Geometry.js');\nmodule.exports.PictProviderFlowPanelChrome = require('./providers/PictProvider-Flow-PanelChrome.js');\nmodule.exports.PictProviderFlowCSS = require('./providers/PictProvider-Flow-CSS.js');\nmodule.exports.PictProviderFlowIcons = require('./providers/PictProvider-Flow-Icons.js');\nmodule.exports.PictProviderFlowConnectorShapes = require('./providers/PictProvider-Flow-ConnectorShapes.js');\n\n// FlowCard base class\nmodule.exports.PictFlowCard = require('./PictFlowCard.js');\n\n// FlowCardPropertiesPanel base class and panel types\nmodule.exports.PictFlowCardPropertiesPanel = require('./PictFlowCardPropertiesPanel.js');\nmodule.exports.FlowCardPropertiesPanelTemplate = require('./panels/FlowCardPropertiesPanel-Template.js');\nmodule.exports.FlowCardPropertiesPanelMarkdown = require('./panels/FlowCardPropertiesPanel-Markdown.js');\nmodule.exports.FlowCardPropertiesPanelForm = require('./panels/FlowCardPropertiesPanel-Form.js');\nmodule.exports.FlowCardPropertiesPanelView = require('./panels/FlowCardPropertiesPanel-View.js');\n\n// Properties panel renderer view\nmodule.exports.PictViewFlowPropertiesPanel = require('./views/PictView-Flow-PropertiesPanel.js');\n\n},{\"./PictFlowCard.js\":10,\"./PictFlowCardPropertiesPanel.js\":11,\"./panels/FlowCardPropertiesPanel-Form.js\":12,\"./panels/FlowCardPropertiesPanel-Markdown.js\":13,\"./panels/FlowCardPropertiesPanel-Template.js\":14,\"./panels/FlowCardPropertiesPanel-View.js\":15,\"./providers/PictProvider-Flow-CSS.js\":16,\"./providers/PictProvider-Flow-ConnectorShapes.js\":17,\"./providers/PictProvider-Flow-EventHandler.js\":18,\"./providers/PictProvider-Flow-Geometry.js\":19,\"./providers/PictProvider-Flow-Icons.js\":20,\"./providers/PictProvider-Flow-Layouts.js\":21,\"./providers/PictProvider-Flow-NodeTypes.js\":22,\"./providers/PictProvider-Flow-PanelChrome.js\":24,\"./providers/PictProvider-Flow-SVGHelpers.js\":25,\"./services/PictService-Flow-ConnectionHandleManager.js\":27,\"./services/PictService-Flow-ConnectionRenderer.js\":28,\"./services/PictService-Flow-DataManager.js\":29,\"./services/PictService-Flow-InteractionManager.js\":30,\"./services/PictService-Flow-Layout.js\":31,\"./services/PictService-Flow-PanelManager.js\":32,\"./services/PictService-Flow-PathGenerator.js\":33,\"./services/PictService-Flow-PortRenderer.js\":34,\"./services/PictService-Flow-RenderManager.js\":35,\"./services/PictService-Flow-SelectionManager.js\":36,\"./services/PictService-Flow-Tether.js\":37,\"./services/PictService-Flow-ViewportManager.js\":38,\"./views/PictView-Flow-FloatingToolbar.js\":39,\"./views/PictView-Flow-Node.js\":40,\"./views/PictView-Flow-PropertiesPanel.js\":41,\"./views/PictView-Flow-Toolbar.js\":42,\"./views/PictView-Flow.js\":43}],10:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictFlowCard - Base class for flow diagram cards.\n *\n * Developers create subclasses of PictFlowCard to define reusable node types\n * that appear in the flow palette. Each card describes a discrete operation\n * (e.g. \"If-Then-Else\", \"File Read\") with configurable inputs, outputs, and\n * metadata. Pict-Section-Flow uses registered cards to build a palette for\n * the user to drag onto the graph.\n *\n * Configurable properties:\n * - Title (string, required) - Display name shown on the node\n * - Name (string, optional) - Longer descriptive name\n * - Code (string, required) - Short identifier (e.g. \"ITE\", \"SW\")\n * - Description (string, optional) - Brief explanation of what the card does\n * - Icon (string, optional) - Icon identifier or emoji\n * - PreviewImage (string, optional) - URL to a preview/thumbnail image\n * - Documentation (string, optional) - URL or inline documentation text\n * - Tooltip (string, optional) - Hover tooltip text\n * - Inputs (array) - Named input ports, each with:\n * - Name (string) - Port label\n * - Side (string) - Port side ('left', 'top', etc.)\n * - MinimumInputCount (number) - Minimum connections accepted (default 0)\n * - MaximumInputCount (number) - Maximum connections accepted (default -1, unlimited)\n * - Outputs (array) - Named output ports\n * - Enabled (boolean) - Whether this card is available in the palette\n * - PropertiesPanel (object, optional) - Configuration for the on-graph properties panel\n * - PanelType (string) - 'Template', 'Markdown', 'Form', or 'View'\n * - DefaultWidth (number) - Panel width (default 300)\n * - DefaultHeight (number) - Panel height (default 200)\n * - Title (string) - Panel title bar text\n * - Configuration (object) - Panel-type-specific configuration\n * - BodyContent (object, optional) - Custom content rendered in the node body area\n * - ContentType (string) - 'svg', 'html', or 'canvas'\n * - Template (string) - Pict template string (html/svg only)\n * - TemplateHash (string) - Registered template hash (takes precedence over Template)\n * - Templates (array) - Template definitions to register [{Hash, Template}]\n * - RenderCallback (function)- Imperative render callback (required for canvas)\n * - Padding (number) - Inner padding in pixels (default 2)\n * - ShowTypeLabel (boolean, default true) - Whether to show the type label/code badge on hover\n * - PortLabelsOnHover (boolean, default false) - When true, port labels only appear on hover\n * - PortLabelsVertical (boolean, default false) - When true, port labels render vertically\n * - PortLabelPadding (boolean, default false) - When true, port labels have extra padding to avoid overlapping body content\n * - LabelsInFront (boolean, default true) - When true, labels render in front of body content; when false, behind\n *\n * Usage:\n * class MyCard extends PictFlowCard {\n * constructor(pFable, pOptions, pServiceHash) {\n * super(pFable, pOptions, pServiceHash);\n * this.cardTitle = 'My Card';\n * this.cardCode = 'MC';\n * this.cardInputs = [{ Name: 'Data', Side: 'left', MinimumInputCount: 1, MaximumInputCount: 1 }];\n * this.cardOutputs = [{ Name: 'Result', Side: 'right' }];\n * }\n * }\n */\nclass PictFlowCard extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, PictFlowCard.default_configuration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCard';\n\n\t\t// --- Card metadata ---\n\t\tthis.cardTitle = (tmpOptions.Title) ? tmpOptions.Title : 'Card';\n\t\tthis.cardName = (tmpOptions.Name) ? tmpOptions.Name : false;\n\t\tthis.cardCode = (tmpOptions.Code) ? tmpOptions.Code : '';\n\t\tthis.cardDescription = (tmpOptions.Description) ? tmpOptions.Description : false;\n\t\tthis.cardIcon = (tmpOptions.Icon) ? tmpOptions.Icon : false;\n\t\tthis.cardPreviewImage = (tmpOptions.PreviewImage) ? tmpOptions.PreviewImage : false;\n\t\tthis.cardDocumentation = (tmpOptions.Documentation) ? tmpOptions.Documentation : false;\n\t\tthis.cardTooltip = (tmpOptions.Tooltip) ? tmpOptions.Tooltip : false;\n\t\tthis.cardHelp = (tmpOptions.Help) ? tmpOptions.Help : false;\n\n\t\t// --- Card enabled state ---\n\t\tthis.cardEnabled = (typeof tmpOptions.Enabled === 'boolean') ? tmpOptions.Enabled : true;\n\n\t\t// --- Card appearance ---\n\t\tthis.cardTitleBarColor = (tmpOptions.TitleBarColor) ? tmpOptions.TitleBarColor : '#2c3e50';\n\t\tthis.cardBodyStyle = (tmpOptions.BodyStyle) ? tmpOptions.BodyStyle : {};\n\t\tthis.cardWidth = (typeof tmpOptions.Width === 'number') ? tmpOptions.Width : 180;\n\t\tthis.cardHeight = (typeof tmpOptions.Height === 'number') ? tmpOptions.Height : 80;\n\t\tthis.cardCategory = (tmpOptions.Category) ? tmpOptions.Category : 'General';\n\n\t\t// --- Input and Output port definitions ---\n\t\t// Inputs: [{ Name: 'In', Side: 'left', MinimumInputCount: 0, MaximumInputCount: -1 }]\n\t\t// Outputs: [{ Name: 'Out', Side: 'right' }]\n\t\tthis.cardInputs = Array.isArray(tmpOptions.Inputs) ? tmpOptions.Inputs : [];\n\t\tthis.cardOutputs = Array.isArray(tmpOptions.Outputs) ? tmpOptions.Outputs : [];\n\n\t\t// --- Properties panel configuration ---\n\t\tthis.cardPropertiesPanel = (tmpOptions.PropertiesPanel && typeof tmpOptions.PropertiesPanel === 'object')\n\t\t\t? tmpOptions.PropertiesPanel\n\t\t\t: null;\n\n\t\t// --- Body content configuration ---\n\t\tthis.cardBodyContent = (tmpOptions.BodyContent && typeof tmpOptions.BodyContent === 'object')\n\t\t\t? tmpOptions.BodyContent\n\t\t\t: null;\n\n\t\t// --- Label display booleans ---\n\t\tthis.cardShowTypeLabel = (typeof tmpOptions.ShowTypeLabel === 'boolean') ? tmpOptions.ShowTypeLabel : true;\n\t\tthis.cardPortLabelsOnHover = (typeof tmpOptions.PortLabelsOnHover === 'boolean') ? tmpOptions.PortLabelsOnHover : false;\n\t\tthis.cardPortLabelsVertical = (typeof tmpOptions.PortLabelsVertical === 'boolean') ? tmpOptions.PortLabelsVertical : false;\n\t\tthis.cardPortLabelPadding = (typeof tmpOptions.PortLabelPadding === 'boolean') ? tmpOptions.PortLabelPadding : false;\n\t\tthis.cardPortLabelsOutside = (typeof tmpOptions.PortLabelsOutside === 'boolean') ? tmpOptions.PortLabelsOutside : false;\n\t\tthis.cardLabelsInFront = (typeof tmpOptions.LabelsInFront === 'boolean') ? tmpOptions.LabelsInFront : true;\n\t}\n\n\t/**\n\t * Generate the node type configuration object for the NodeTypes provider.\n\t * This converts the card's properties into the format expected by\n\t * PictProviderFlowNodeTypes.registerNodeType().\n\t *\n\t * @returns {Object} Node type configuration\n\t */\n\tgetNodeTypeConfiguration()\n\t{\n\t\tlet tmpPorts = [];\n\n\t\t// Build input ports\n\t\tfor (let i = 0; i < this.cardInputs.length; i++)\n\t\t{\n\t\t\tlet tmpInput = this.cardInputs[i];\n\t\t\tlet tmpPort =\n\t\t\t\t{\n\t\t\t\t\tHash: null,\n\t\t\t\t\tDirection: 'input',\n\t\t\t\t\tSide: tmpInput.Side || 'left',\n\t\t\t\t\tLabel: tmpInput.Name || `In ${i + 1}`,\n\t\t\t\t\tMinimumInputCount: (typeof tmpInput.MinimumInputCount === 'number') ? tmpInput.MinimumInputCount : 0,\n\t\t\t\t\tMaximumInputCount: (typeof tmpInput.MaximumInputCount === 'number') ? tmpInput.MaximumInputCount : -1\n\t\t\t\t};\n\t\t\tif (tmpInput.PortType)\n\t\t\t{\n\t\t\t\ttmpPort.PortType = tmpInput.PortType;\n\t\t\t}\n\t\t\tif (tmpInput.DataType)\n\t\t\t{\n\t\t\t\ttmpPort.DataType = tmpInput.DataType;\n\t\t\t}\n\t\t\ttmpPorts.push(tmpPort);\n\t\t}\n\n\t\t// Build output ports\n\t\tfor (let i = 0; i < this.cardOutputs.length; i++)\n\t\t{\n\t\t\tlet tmpOutput = this.cardOutputs[i];\n\t\t\tlet tmpOutPort =\n\t\t\t\t{\n\t\t\t\t\tHash: null,\n\t\t\t\t\tDirection: 'output',\n\t\t\t\t\tSide: tmpOutput.Side || 'right',\n\t\t\t\t\tLabel: tmpOutput.Name || `Out ${i + 1}`\n\t\t\t\t};\n\t\t\tif (tmpOutput.PortType)\n\t\t\t{\n\t\t\t\ttmpOutPort.PortType = tmpOutput.PortType;\n\t\t\t}\n\t\t\tif (tmpOutput.DataType)\n\t\t\t{\n\t\t\t\ttmpOutPort.DataType = tmpOutput.DataType;\n\t\t\t}\n\t\t\ttmpPorts.push(tmpOutPort);\n\t\t}\n\n\t\t// If no ports were defined, provide sensible defaults\n\t\tif (tmpPorts.length === 0)\n\t\t{\n\t\t\ttmpPorts.push({ Hash: null, Direction: 'input', Side: 'left', Label: 'In' });\n\t\t\ttmpPorts.push({ Hash: null, Direction: 'output', Side: 'right', Label: 'Out' });\n\t\t}\n\n\t\tlet tmpResult =\n\t\t{\n\t\t\tHash: this.cardCode,\n\t\t\tLabel: this.cardTitle,\n\t\t\tDefaultWidth: this.cardWidth,\n\t\t\tDefaultHeight: this.cardHeight,\n\t\t\tDefaultPorts: tmpPorts,\n\t\t\tTitleBarColor: this.cardTitleBarColor,\n\t\t\tBodyStyle: JSON.parse(JSON.stringify(this.cardBodyStyle)),\n\t\t\t// Extended FlowCard metadata stored alongside the type\n\t\t\tCardMetadata:\n\t\t\t{\n\t\t\t\tName: this.cardName,\n\t\t\t\tCode: this.cardCode,\n\t\t\t\tDescription: this.cardDescription,\n\t\t\t\tIcon: this.cardIcon,\n\t\t\t\tPreviewImage: this.cardPreviewImage,\n\t\t\t\tDocumentation: this.cardDocumentation,\n\t\t\t\tTooltip: this.cardTooltip,\n\t\t\t\tEnabled: this.cardEnabled,\n\t\t\t\tCategory: this.cardCategory,\n\t\t\t\tHelp: this.cardHelp\n\t\t\t}\n\t\t};\n\n\t\t// Include label display booleans\n\t\ttmpResult.ShowTypeLabel = this.cardShowTypeLabel;\n\t\ttmpResult.PortLabelsOnHover = this.cardPortLabelsOnHover;\n\t\ttmpResult.PortLabelsVertical = this.cardPortLabelsVertical;\n\t\ttmpResult.PortLabelPadding = this.cardPortLabelPadding;\n\t\ttmpResult.PortLabelsOutside = this.cardPortLabelsOutside;\n\t\ttmpResult.LabelsInFront = this.cardLabelsInFront;\n\n\t\t// Include properties panel config if defined\n\t\tif (this.cardPropertiesPanel)\n\t\t{\n\t\t\ttmpResult.PropertiesPanel = JSON.parse(JSON.stringify(this.cardPropertiesPanel));\n\t\t}\n\n\t\t// Include body content config if defined\n\t\tif (this.cardBodyContent)\n\t\t{\n\t\t\ttmpResult.BodyContent = JSON.parse(JSON.stringify(this.cardBodyContent));\n\t\t\t// RenderCallback cannot be serialized — preserve the original function reference\n\t\t\tif (typeof this.options.BodyContent.RenderCallback === 'function')\n\t\t\t{\n\t\t\t\ttmpResult.BodyContent.RenderCallback = this.options.BodyContent.RenderCallback;\n\t\t\t}\n\t\t}\n\n\t\treturn tmpResult;\n\t}\n\n\t/**\n\t * Register this card with a FlowView's node type provider.\n\t *\n\t * @param {Object} pFlowView - The PictViewFlow instance\n\t * @returns {boolean} Whether registration succeeded\n\t */\n\tregisterWithFlowView(pFlowView)\n\t{\n\t\tif (!pFlowView || !pFlowView._NodeTypeProvider)\n\t\t{\n\t\t\tthis.log.warn('PictFlowCard registerWithFlowView: no valid FlowView or NodeTypeProvider');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpConfig = this.getNodeTypeConfiguration();\n\t\treturn pFlowView._NodeTypeProvider.registerNodeType(tmpConfig);\n\t}\n}\n\nmodule.exports = PictFlowCard;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Card',\n\tName: false,\n\tCode: '',\n\tDescription: false,\n\tIcon: false,\n\tPreviewImage: false,\n\tDocumentation: false,\n\tTooltip: false,\n\tInputs: [],\n\tOutputs: [],\n\tEnabled: true,\n\tTitleBarColor: '#2c3e50',\n\tBodyStyle: {},\n\tWidth: 180,\n\tHeight: 80,\n\tCategory: 'General',\n\tPropertiesPanel: null,\n\tBodyContent: null,\n\tShowTypeLabel: true,\n\tPortLabelsOnHover: false,\n\tPortLabelsVertical: false,\n\tPortLabelPadding: false,\n\tPortLabelsOutside: false,\n\tLabelsInFront: true\n};\n\n},{\"fable-serviceproviderbase\":4}],11:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictFlowCardPropertiesPanel - Base class for flow card property panels.\n *\n * Developers create subclasses to define what UI appears when a user opens\n * the properties panel for a node on the flow graph. Four built-in panel\n * types are provided:\n *\n * - Template - Renders pict templates\n * - Markdown - Renders markdown via pict-section-content\n * - Form - Creates an ephemeral pict-section-form section\n * - View - Renders an existing registered pict-view\n *\n * Configurable properties:\n * - PanelType (string) - Panel type identifier\n * - Title (string) - Panel title bar text\n * - Width (number) - Default panel width in pixels\n * - Height (number) - Default panel height in pixels\n * - Configuration (object) - Panel-type-specific configuration\n */\nclass PictFlowCardPropertiesPanel extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, PictFlowCardPropertiesPanel.default_configuration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel';\n\n\t\t// Panel metadata\n\t\tthis.panelType = tmpOptions.PanelType || 'Base';\n\t\tthis.panelTitle = tmpOptions.Title || 'Properties';\n\t\tthis.panelWidth = (typeof tmpOptions.Width === 'number') ? tmpOptions.Width : 300;\n\t\tthis.panelHeight = (typeof tmpOptions.Height === 'number') ? tmpOptions.Height : 200;\n\n\t\t// Reference to the flow view (set when panel is activated)\n\t\tthis._FlowView = null;\n\n\t\t// The node data this panel is operating on (set when panel is opened)\n\t\tthis._NodeData = null;\n\n\t\t// The DOM container element for panel content (set during render)\n\t\tthis._ContentContainer = null;\n\n\t\t// The panel configuration (panel-type-specific)\n\t\tthis._Configuration = tmpOptions.Configuration || {};\n\t}\n\n\t/**\n\t * Render the panel's content into a DOM container element.\n\t * Subclasses MUST override this.\n\t *\n\t * @param {HTMLElement} pContainer - The DOM element to render into\n\t * @param {Object} pNodeData - The node data object (has .Data property)\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tthis._ContentContainer = pContainer;\n\t\tthis._NodeData = pNodeData;\n\t}\n\n\t/**\n\t * Marshal data FROM the node's Data object INTO the panel UI.\n\t * Called when the panel opens or when data changes externally.\n\t *\n\t * @param {Object} pNodeData\n\t */\n\tmarshalToPanel(pNodeData)\n\t{\n\t\tthis._NodeData = pNodeData;\n\t}\n\n\t/**\n\t * Marshal data FROM the panel UI INTO the node's Data object.\n\t * Called before saving or when the panel is about to close.\n\t *\n\t * @param {Object} pNodeData\n\t */\n\tmarshalFromPanel(pNodeData)\n\t{\n\t\t// Subclasses override\n\t}\n\n\t/**\n\t * Called when the panel is being destroyed (closed).\n\t * Subclasses should clean up resources.\n\t */\n\tdestroy()\n\t{\n\t\tthis._ContentContainer = null;\n\t\tthis._NodeData = null;\n\t}\n}\n\nmodule.exports = PictFlowCardPropertiesPanel;\n\nmodule.exports.default_configuration =\n{\n\tPanelType: 'Base',\n\tTitle: 'Properties',\n\tWidth: 300,\n\tHeight: 200,\n\tConfiguration: {}\n};\n\n},{\"fable-serviceproviderbase\":4}],12:[function(require,module,exports){\nconst libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\n\n/**\n * FlowCardPropertiesPanel-Form\n *\n * Creates an ephemeral pict-section-form section to edit the node's Data object.\n * Uses PictViewFormMetacontroller.injectManifest() to dynamically create form\n * sections at runtime.\n *\n * Note: pict-section-form must be available in the consuming application\n * (it is an optional/peer dependency, not bundled with pict-section-flow).\n *\n * Configuration:\n * {\n * PanelType: 'Form',\n * Configuration: {\n * Manifest: {\n * Sections: [...],\n * Descriptors: {...}\n * }\n * }\n * }\n */\nclass FlowCardPropertiesPanelForm extends libPictFlowCardPropertiesPanel\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel-Form';\n\n\t\tthis._Metacontroller = null;\n\t\tthis._InjectedSectionHash = null;\n\t}\n\n\t/**\n\t * Render the form into the panel body.\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tsuper.render(pContainer, pNodeData);\n\n\t\tif (!this._Configuration || !this._Configuration.Manifest)\n\t\t{\n\t\t\tpContainer.innerHTML = '<em>No form manifest configured</em>';\n\t\t\treturn;\n\t\t}\n\n\t\t// Create a unique container ID for the form section\n\t\tlet tmpContainerID = `pict-flow-panel-form-${pNodeData.Hash}`;\n\t\tpContainer.innerHTML = `<div id=\"${tmpContainerID}\"></div>`;\n\n\t\t// Bind the node data to AppData.Record so the form descriptors\n\t\t// (which use addresses like Record.Data.SearchString) resolve against\n\t\t// the actual node object.\n\t\tthis.fable.AppData.Record = pNodeData;\n\n\t\ttry\n\t\t{\n\t\t\t// Look for an existing metacontroller or create one\n\t\t\tif (!this._Metacontroller)\n\t\t\t{\n\t\t\t\t// Try to find PictFormMetacontroller service type (check both common names)\n\t\t\t\tlet tmpServiceType = null;\n\t\t\t\tif (this.fable.servicesMap.hasOwnProperty('PictFormMetacontroller'))\n\t\t\t\t{\n\t\t\t\t\ttmpServiceType = 'PictFormMetacontroller';\n\t\t\t\t}\n\t\t\t\telse if (this.fable.servicesMap.hasOwnProperty('PictViewFormMetacontroller'))\n\t\t\t\t{\n\t\t\t\t\ttmpServiceType = 'PictViewFormMetacontroller';\n\t\t\t\t}\n\n\t\t\t\tif (tmpServiceType)\n\t\t\t\t{\n\t\t\t\t\tthis._Metacontroller = this.fable.instantiateServiceProviderWithoutRegistration(tmpServiceType,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tViewIdentifier: `FlowPanelForm-${pNodeData.Hash}`,\n\t\t\t\t\t\t\tDefaultDestinationAddress: `#${tmpContainerID}`,\n\t\t\t\t\t\t\tAutoRender: false,\n\t\t\t\t\t\t\tAutoPopulateAfterRender: true,\n\t\t\t\t\t\t\tAutoSolveBeforeRender: false\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this._Metacontroller && typeof this._Metacontroller.injectManifestAndRender === 'function')\n\t\t\t{\n\t\t\t\t// Create the FormContainer div that the metacontroller's\n\t\t\t\t// updateMetatemplateInDOM() expects when adding section wrappers.\n\t\t\t\t// Normally this is created when the metacontroller renders its own\n\t\t\t\t// metatemplate, but we skip that step since we only need the\n\t\t\t\t// injected section views, not the metacontroller's own renderable.\n\t\t\t\tlet tmpFormContainerID = `Pict-${this._Metacontroller.UUID}-FormContainer`;\n\t\t\t\tlet tmpContainerEl = pContainer.querySelector(`#${tmpContainerID}`);\n\t\t\t\tif (tmpContainerEl)\n\t\t\t\t{\n\t\t\t\t\ttmpContainerEl.innerHTML = `<div id=\"${tmpFormContainerID}\" class=\"pict-form\"></div>`;\n\t\t\t\t}\n\n\t\t\t\t// Deep clone the manifest so each panel gets its own copy\n\t\t\t\tlet tmpManifest = JSON.parse(JSON.stringify(this._Configuration.Manifest));\n\t\t\t\tthis._InjectedSectionHash = tmpManifest.Hash || null;\n\n\t\t\t\t// Use injectManifestAndRender which properly creates section views,\n\t\t\t\t// updates the metatemplate in the DOM, and renders each section view\n\t\t\t\tthis._Metacontroller.injectManifestAndRender(tmpManifest);\n\t\t\t}\n\t\t\telse if (this._Metacontroller && typeof this._Metacontroller.injectManifest === 'function')\n\t\t\t{\n\t\t\t\t// Fallback for older pict-section-form versions: inject the\n\t\t\t\t// manifest and render each section view individually\n\t\t\t\tlet tmpManifest = JSON.parse(JSON.stringify(this._Configuration.Manifest));\n\t\t\t\tlet tmpViewsToRender = this._Metacontroller.injectManifest(tmpManifest);\n\t\t\t\tthis._InjectedSectionHash = tmpManifest.Hash || null;\n\n\t\t\t\t// Create container divs for each section view and render them\n\t\t\t\tlet tmpContainerEl = pContainer.querySelector(`#${tmpContainerID}`);\n\t\t\t\tif (tmpContainerEl && tmpViewsToRender.length > 0)\n\t\t\t\t{\n\t\t\t\t\tlet tmpInnerHTML = '';\n\t\t\t\t\tfor (let i = 0; i < tmpViewsToRender.length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tlet tmpDestID = tmpViewsToRender[i].options.DefaultDestinationAddress;\n\t\t\t\t\t\tif (tmpDestID && tmpDestID.charAt(0) === '#')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttmpDestID = tmpDestID.substring(1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttmpInnerHTML += `<div id=\"${tmpDestID}\" class=\"pict-form-view\"></div>`;\n\t\t\t\t\t}\n\t\t\t\t\ttmpContainerEl.innerHTML = tmpInnerHTML;\n\n\t\t\t\t\tfor (let i = 0; i < tmpViewsToRender.length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpViewsToRender[i].render();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpContainer.innerHTML = '<em>pict-section-form is not available. Install it in your application to use Form panels.</em>';\n\t\t\t}\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`FlowCardPropertiesPanel-Form render error: ${pError.message}`);\n\t\t\tpContainer.innerHTML = `<em>Form render error: ${pError.message}</em>`;\n\t\t}\n\t}\n\n\tmarshalFromPanel(pNodeData)\n\t{\n\t\tif (this._Metacontroller && typeof this._Metacontroller.marshalFromView === 'function')\n\t\t{\n\t\t\tthis._Metacontroller.marshalFromView();\n\t\t}\n\t}\n\n\tdestroy()\n\t{\n\t\tthis._Metacontroller = null;\n\t\tthis._InjectedSectionHash = null;\n\t\tsuper.destroy();\n\t}\n}\n\nmodule.exports = FlowCardPropertiesPanelForm;\n\nmodule.exports.default_configuration = Object.assign(\n\t{},\n\tlibPictFlowCardPropertiesPanel.default_configuration,\n\t{\n\t\tPanelType: 'Form',\n\t\tConfiguration:\n\t\t{\n\t\t\tManifest: null\n\t\t}\n\t}\n);\n\n},{\"../PictFlowCardPropertiesPanel.js\":11}],13:[function(require,module,exports){\nconst libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\n\n/**\n * FlowCardPropertiesPanel-Markdown\n *\n * Renders markdown content into the panel body using pict-section-content's\n * PictContentProvider service. When pict-section-content is installed and its\n * PictContentProvider service type has been registered with the pict instance,\n * full markdown rendering is available (headings, lists, tables, code blocks\n * with syntax highlighting, KaTeX equations, Mermaid diagrams, etc.).\n *\n * If PictContentProvider is not available, the panel falls back to displaying\n * the raw markdown as pre-formatted text.\n *\n * Configuration:\n * {\n * PanelType: 'Markdown',\n * Configuration: {\n * Markdown: '# Title\\nSome **markdown** content'\n * }\n * }\n *\n * Or use an address to pull markdown from the node's data:\n * {\n * PanelType: 'Markdown',\n * Configuration: {\n * MarkdownAddress: 'Data.MarkdownContent'\n * }\n * }\n */\nclass FlowCardPropertiesPanelMarkdown extends libPictFlowCardPropertiesPanel\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel-Markdown';\n\n\t\tthis._ContentProvider = null;\n\t}\n\n\t/**\n\t * Render markdown into the panel.\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tsuper.render(pContainer, pNodeData);\n\n\t\tthis._renderMarkdown();\n\t}\n\n\tmarshalToPanel(pNodeData)\n\t{\n\t\tsuper.marshalToPanel(pNodeData);\n\t\tthis._renderMarkdown();\n\t}\n\n\t_renderMarkdown()\n\t{\n\t\tif (!this._ContentContainer || !this._Configuration) return;\n\n\t\tlet tmpMarkdown = '';\n\n\t\tif (this._Configuration.MarkdownAddress && this._NodeData)\n\t\t{\n\t\t\t// Resolve markdown from node data using manyfest\n\t\t\ttmpMarkdown = this.fable.manifest.getValueByHash(this._NodeData, this._Configuration.MarkdownAddress) || '';\n\t\t}\n\t\telse if (this._Configuration.Markdown)\n\t\t{\n\t\t\ttmpMarkdown = this._Configuration.Markdown;\n\t\t}\n\n\t\tif (!tmpMarkdown)\n\t\t{\n\t\t\tthis._ContentContainer.innerHTML = '<em>No content</em>';\n\t\t\treturn;\n\t\t}\n\n\t\t// Use pict-section-content's PictContentProvider for markdown rendering.\n\t\t// The consuming application must register the PictContentProvider service\n\t\t// type (from pict-section-content) for this to work.\n\t\ttry\n\t\t{\n\t\t\tif (!this._ContentProvider)\n\t\t\t{\n\t\t\t\tif (this.fable.servicesMap.hasOwnProperty('PictContentProvider'))\n\t\t\t\t{\n\t\t\t\t\tthis._ContentProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictContentProvider', {});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this._ContentProvider && typeof this._ContentProvider.parseMarkdown === 'function')\n\t\t\t{\n\t\t\t\tlet tmpHTML = this._ContentProvider.parseMarkdown(tmpMarkdown);\n\t\t\t\tthis._ContentContainer.innerHTML = tmpHTML;\n\n\t\t\t\t// Post-render hooks for equations and diagrams\n\t\t\t\tif (typeof this._ContentProvider.renderKaTeXEquations === 'function')\n\t\t\t\t{\n\t\t\t\t\tthis._ContentProvider.renderKaTeXEquations(this._ContentContainer);\n\t\t\t\t}\n\t\t\t\tif (typeof this._ContentProvider.renderMermaidDiagrams === 'function')\n\t\t\t\t{\n\t\t\t\t\tthis._ContentProvider.renderMermaidDiagrams(this._ContentContainer);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// PictContentProvider not registered — render as pre-formatted text\n\t\t\t\tthis._ContentContainer.innerHTML = `<pre style=\"white-space: pre-wrap; font-family: inherit;\">${this._escapeHTML(tmpMarkdown)}</pre>`;\n\t\t\t}\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`FlowCardPropertiesPanel-Markdown render error: ${pError.message}`);\n\t\t\tthis._ContentContainer.innerHTML = `<pre style=\"white-space: pre-wrap; font-family: inherit;\">${this._escapeHTML(tmpMarkdown)}</pre>`;\n\t\t}\n\t}\n\n\t_escapeHTML(pText)\n\t{\n\t\tlet tmpDiv = document.createElement('div');\n\t\ttmpDiv.textContent = pText;\n\t\treturn tmpDiv.innerHTML;\n\t}\n\n\tdestroy()\n\t{\n\t\tthis._ContentProvider = null;\n\t\tsuper.destroy();\n\t}\n}\n\nmodule.exports = FlowCardPropertiesPanelMarkdown;\n\nmodule.exports.default_configuration = Object.assign(\n\t{},\n\tlibPictFlowCardPropertiesPanel.default_configuration,\n\t{\n\t\tPanelType: 'Markdown',\n\t\tConfiguration:\n\t\t{\n\t\t\tMarkdown: '',\n\t\t\tMarkdownAddress: ''\n\t\t}\n\t}\n);\n\n},{\"../PictFlowCardPropertiesPanel.js\":11}],14:[function(require,module,exports){\nconst libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\n\n/**\n * FlowCardPropertiesPanel-Template\n *\n * Renders pict templates into the panel body.\n *\n * Configuration:\n * {\n * PanelType: 'Template',\n * Configuration: {\n * Templates: [\n * { Hash: 'my-template', Template: '<div>{~D:Record.Data.SomeValue~}</div>' }\n * ],\n * TemplateHash: 'my-template'\n * }\n * }\n */\nclass FlowCardPropertiesPanelTemplate extends libPictFlowCardPropertiesPanel\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel-Template';\n\n\t\tthis._TemplatesRegistered = false;\n\t}\n\n\t/**\n\t * Register templates with the pict template provider and render.\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tsuper.render(pContainer, pNodeData);\n\n\t\tif (!this._Configuration || !this._Configuration.Templates) return;\n\n\t\t// Register templates with pict (only once)\n\t\tif (!this._TemplatesRegistered)\n\t\t{\n\t\t\tlet tmpTemplates = this._Configuration.Templates;\n\t\t\tfor (let i = 0; i < tmpTemplates.length; i++)\n\t\t\t{\n\t\t\t\tif (tmpTemplates[i].Hash && tmpTemplates[i].Template)\n\t\t\t\t{\n\t\t\t\t\tthis.fable.TemplateProvider.addTemplate(tmpTemplates[i].Hash, tmpTemplates[i].Template);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._TemplatesRegistered = true;\n\t\t}\n\n\t\tthis._renderTemplate();\n\t}\n\n\tmarshalToPanel(pNodeData)\n\t{\n\t\tsuper.marshalToPanel(pNodeData);\n\t\tthis._renderTemplate();\n\t}\n\n\t_renderTemplate()\n\t{\n\t\tif (!this._ContentContainer || !this._NodeData) return;\n\n\t\tlet tmpTemplateHash = this._Configuration.TemplateHash;\n\t\tif (!tmpTemplateHash) return;\n\n\t\tlet tmpRecord = this._NodeData;\n\t\tlet tmpHTML = this.fable.parseTemplateByHash(tmpTemplateHash, tmpRecord, null, [tmpRecord]);\n\t\tthis._ContentContainer.innerHTML = tmpHTML;\n\t}\n}\n\nmodule.exports = FlowCardPropertiesPanelTemplate;\n\nmodule.exports.default_configuration = Object.assign(\n\t{},\n\tlibPictFlowCardPropertiesPanel.default_configuration,\n\t{\n\t\tPanelType: 'Template',\n\t\tConfiguration:\n\t\t{\n\t\t\tTemplates: [],\n\t\t\tTemplateHash: ''\n\t\t}\n\t}\n);\n\n},{\"../PictFlowCardPropertiesPanel.js\":11}],15:[function(require,module,exports){\nconst libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\n\n/**\n * FlowCardPropertiesPanel-View\n *\n * Renders an existing registered pict-view into the panel body.\n * The view's destination is temporarily overridden to render inside\n * the panel container.\n *\n * Configuration:\n * {\n * PanelType: 'View',\n * Configuration: {\n * ViewHash: 'MyCustomViewIdentifier'\n * }\n * }\n */\nclass FlowCardPropertiesPanelView extends libPictFlowCardPropertiesPanel\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel-View';\n\n\t\tthis._OriginalDestination = null;\n\t\tthis._ViewInstance = null;\n\t}\n\n\t/**\n\t * Render the referenced pict-view into the panel body.\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tsuper.render(pContainer, pNodeData);\n\n\t\tif (!this._Configuration || !this._Configuration.ViewHash)\n\t\t{\n\t\t\tpContainer.innerHTML = '<em>No ViewHash configured</em>';\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpViewHash = this._Configuration.ViewHash;\n\n\t\t// Create a unique container ID\n\t\tlet tmpContainerID = `pict-flow-panel-view-${pNodeData.Hash}`;\n\t\tpContainer.innerHTML = `<div id=\"${tmpContainerID}\"></div>`;\n\n\t\ttry\n\t\t{\n\t\t\t// Look up the view in the pict instance\n\t\t\tlet tmpPict = this.pict || this.fable;\n\t\t\tif (tmpPict.views && tmpPict.views[tmpViewHash])\n\t\t\t{\n\t\t\t\tthis._ViewInstance = tmpPict.views[tmpViewHash];\n\n\t\t\t\t// Save original destination\n\t\t\t\tthis._OriginalDestination = this._ViewInstance.options.DefaultDestinationAddress;\n\n\t\t\t\t// Override destination to our panel container\n\t\t\t\tthis._ViewInstance.options.DefaultDestinationAddress = `#${tmpContainerID}`;\n\n\t\t\t\tif (typeof this._ViewInstance.render === 'function')\n\t\t\t\t{\n\t\t\t\t\tthis._ViewInstance.render();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpContainer.innerHTML = `<em>View \"${tmpViewHash}\" not found</em>`;\n\t\t\t}\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`FlowCardPropertiesPanel-View render error: ${pError.message}`);\n\t\t\tpContainer.innerHTML = `<em>View render error: ${pError.message}</em>`;\n\t\t}\n\t}\n\n\tmarshalFromPanel(pNodeData)\n\t{\n\t\tif (this._ViewInstance && typeof this._ViewInstance.marshalFromView === 'function')\n\t\t{\n\t\t\tthis._ViewInstance.marshalFromView();\n\t\t}\n\t}\n\n\tdestroy()\n\t{\n\t\t// Restore original destination\n\t\tif (this._ViewInstance && this._OriginalDestination)\n\t\t{\n\t\t\tthis._ViewInstance.options.DefaultDestinationAddress = this._OriginalDestination;\n\t\t}\n\n\t\tthis._ViewInstance = null;\n\t\tthis._OriginalDestination = null;\n\t\tsuper.destroy();\n\t}\n}\n\nmodule.exports = FlowCardPropertiesPanelView;\n\nmodule.exports.default_configuration = Object.assign(\n\t{},\n\tlibPictFlowCardPropertiesPanel.default_configuration,\n\t{\n\t\tPanelType: 'View',\n\t\tConfiguration:\n\t\t{\n\t\t\tViewHash: ''\n\t\t}\n\t}\n);\n\n},{\"../PictFlowCardPropertiesPanel.js\":11}],16:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-CSS\n *\n * Centralized CSS provider for the flow diagram.\n * All flow-related CSS is organized into domain-specific getter methods,\n * providing a single source of truth and enabling future theming.\n */\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowCSS'\n};\n\nclass PictProviderFlowCSS extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowCSS';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t// ── Container ──────────────────────────────────────────────────────────\n\t/**\n\t * CSS for the flow container, SVG container, panning/connecting cursors, and grid pattern.\n\t * @returns {string}\n\t */\n\tgetContainerCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-container {\n\t\t\t/* ── Design Tokens ─────────────────────────────────────\n\t\t\t Override these custom properties to theme the flow diagram.\n\t\t\t Node-type classes (.pict-flow-node-{type}) can scope-override\n\t\t\t any variable for per-type variation. */\n\n\t\t\t/* Text */\n\t\t\t--pf-text-primary: #2c3e50;\n\t\t\t--pf-text-heading: #1a252f;\n\t\t\t--pf-text-secondary: #7f8c8d;\n\t\t\t--pf-text-tertiary: #8e99a4;\n\t\t\t--pf-text-placeholder: #95a5a6;\n\n\t\t\t/* Node */\n\t\t\t--pf-node-body-fill: #ffffff;\n\t\t\t--pf-node-body-stroke: #d0d4d8;\n\t\t\t--pf-node-body-stroke-hover: #b0b8c0;\n\t\t\t--pf-node-body-stroke-width: 1;\n\t\t\t--pf-node-body-radius: 8px;\n\t\t\t--pf-node-shadow: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.10));\n\t\t\t--pf-node-shadow-hover: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.15));\n\t\t\t--pf-node-shadow-selected: drop-shadow(0 2px 8px rgba(52, 152, 219, 0.25));\n\t\t\t--pf-node-shadow-dragging: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.20));\n\t\t\t--pf-node-title-fill: #ffffff;\n\t\t\t--pf-node-title-size: 11.5px;\n\t\t\t--pf-node-title-weight: 600;\n\t\t\t--pf-node-title-bar-color: #2c3e50;\n\t\t\t--pf-node-type-label-fill: #a0a8b0;\n\t\t\t--pf-node-selected-stroke: #3498db;\n\n\t\t\t/* Node Variants */\n\t\t\t--pf-node-start-fill: #eafaf1;\n\t\t\t--pf-node-start-stroke: #27ae60;\n\t\t\t--pf-node-end-fill: #e8f8f5;\n\t\t\t--pf-node-end-stroke: #1abc9c;\n\t\t\t--pf-node-halt-fill: #fdedec;\n\t\t\t--pf-node-halt-stroke: #e74c3c;\n\t\t\t--pf-node-decision-fill: #fff9e6;\n\t\t\t--pf-node-decision-stroke: #f39c12;\n\n\t\t\t/* Ports */\n\t\t\t--pf-port-input-fill: #3498db;\n\t\t\t--pf-port-output-fill: #2ecc71;\n\t\t\t--pf-port-stroke: #ffffff;\n\t\t\t--pf-port-stroke-width: 2;\n\t\t\t--pf-port-label-bg: rgba(255, 253, 240, 0.5);\n\t\t\t--pf-port-label-text: #2c3e50;\n\n\t\t\t/* Port Type Colors */\n\t\t\t--pf-port-event-in-fill: #3498db;\n\t\t\t--pf-port-event-out-fill: #2ecc71;\n\t\t\t--pf-port-setting-fill: #e67e22;\n\t\t\t--pf-port-value-fill: #f1c40f;\n\t\t\t--pf-port-error-fill: #e74c3c;\n\n\t\t\t/* Connection Type Colors (match source port) */\n\t\t\t--pf-connection-event-in-stroke: #3498db;\n\t\t\t--pf-connection-event-out-stroke: #2ecc71;\n\t\t\t--pf-connection-setting-stroke: #e67e22;\n\t\t\t--pf-connection-value-stroke: #f1c40f;\n\t\t\t--pf-connection-error-stroke: #e74c3c;\n\n\t\t\t/* Connections */\n\t\t\t--pf-connection-stroke: #95a5a6;\n\t\t\t--pf-connection-stroke-hover: #7f8c8d;\n\t\t\t--pf-connection-selected-stroke: #3498db;\n\n\t\t\t/* Panels */\n\t\t\t--pf-panel-bg: #ffffff;\n\t\t\t--pf-panel-border: #d0d4d8;\n\t\t\t--pf-panel-radius: 8px;\n\t\t\t--pf-panel-shadow: 0 4px 12px rgba(0,0,0,0.10), 0 1px 3px rgba(0,0,0,0.06);\n\t\t\t--pf-panel-titlebar-bg: #f7f8fa;\n\t\t\t--pf-panel-titlebar-border: #e8eaed;\n\t\t\t--pf-panel-title-color: #2c3e50;\n\n\t\t\t/* Tabs */\n\t\t\t--pf-tab-text: #8e99a4;\n\t\t\t--pf-tab-text-hover: #5a6a7a;\n\t\t\t--pf-tab-active-border: var(--pf-node-selected-stroke);\n\t\t\t--pf-resize-handle-hover: #e0e3e6;\n\n\t\t\t/* Forms & Inputs */\n\t\t\t--pf-input-border: #d5d8dc;\n\t\t\t--pf-input-border-focus: #3498db;\n\t\t\t--pf-divider-light: #ecf0f1;\n\t\t\t--pf-divider-medium: #e8eaed;\n\n\t\t\t/* Buttons */\n\t\t\t--pf-button-border: #bdc3c7;\n\t\t\t--pf-button-hover-border: #95a5a6;\n\t\t\t--pf-button-hover-bg: #ecf0f1;\n\t\t\t--pf-button-active-bg: #d5dbdb;\n\t\t\t--pf-button-danger-text: #e74c3c;\n\t\t\t--pf-button-danger-hover-bg: #fdedec;\n\t\t\t--pf-button-close-color: #b0b8c0;\n\n\t\t\t/* Badges */\n\t\t\t--pf-badge-category-bg: #f0f2f4;\n\t\t\t--pf-badge-category-text: #6b7b8d;\n\t\t\t--pf-badge-code-bg: #eaf2f8;\n\t\t\t--pf-badge-code-text: #2980b9;\n\n\t\t\t/* Info Panel */\n\t\t\t--pf-port-item-bg: #f8f9fa;\n\n\t\t\t/* Toolbar */\n\t\t\t--pf-toolbar-bg: #ffffff;\n\t\t\t--pf-toolbar-border: #e0e0e0;\n\n\t\t\t/* Palette Cards */\n\t\t\t--pf-card-border: #d5d8dc;\n\t\t\t--pf-card-hover-bg: #eaf2f8;\n\t\t\t--pf-card-hover-shadow: 0 1px 3px rgba(52, 152, 219, 0.15);\n\n\t\t\t/* Canvas */\n\t\t\t--pf-canvas-bg: #fafafa;\n\t\t\t--pf-grid-stroke: #e8e8e8;\n\n\t\t\tposition: relative;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tmin-height: 400px;\n\t\t\toverflow: hidden;\n\t\t\tbackground-color: var(--pf-canvas-bg);\n\t\t\tborder: 1px solid var(--pf-toolbar-border);\n\t\t\tborder-radius: 4px;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t}\n\t\t.pict-flow-svg-container {\n\t\t\tflex: 1;\n\t\t\tmin-height: 0;\n\t\t\tposition: relative;\n\t\t}\n\t\t.pict-flow-svg {\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tcursor: grab;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t}\n\t\t.pict-flow-svg.panning {\n\t\t\tcursor: grabbing;\n\t\t}\n\t\t.pict-flow-svg.connecting {\n\t\t\tcursor: crosshair;\n\t\t}\n\t\t.pict-flow-grid-pattern line {\n\t\t\tstroke: var(--pf-grid-stroke);\n\t\t\tstroke-width: 0.5;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Nodes ──────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for base node styling: body, hover/selected/dragging states, title bar, title text, type label.\n\t * @returns {string}\n\t */\n\tgetNodeCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node {\n\t\t\tcursor: pointer;\n\t\t\tfilter: var(--pf-node-shadow);\n\t\t\ttransition: filter 0.2s;\n\t\t}\n\t\t.pict-flow-node:hover {\n\t\t\tfilter: var(--pf-node-shadow-hover);\n\t\t}\n\t\t.pict-flow-node:hover .pict-flow-node-body {\n\t\t\tstroke: var(--pf-node-body-stroke-hover);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t.pict-flow-node.selected {\n\t\t\tfilter: var(--pf-node-shadow-selected);\n\t\t}\n\t\t.pict-flow-node.selected .pict-flow-node-body {\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t}\n\t\t.pict-flow-node.dragging {\n\t\t\topacity: 0.9;\n\t\t\tcursor: grabbing;\n\t\t\tfilter: var(--pf-node-shadow-dragging);\n\t\t}\n\t\t.pict-flow-node-body {\n\t\t\tfill: var(--pf-node-body-fill);\n\t\t\tstroke: var(--pf-node-body-stroke);\n\t\t\tstroke-width: var(--pf-node-body-stroke-width);\n\t\t\trx: var(--pf-node-body-radius);\n\t\t\try: var(--pf-node-body-radius);\n\t\t\ttransition: stroke 0.2s, stroke-width 0.2s;\n\t\t}\n\t\t.pict-flow-node-title-bar {\n\t\t\tfill: var(--pf-node-title-bar-color);\n\t\t\trx: var(--pf-node-body-radius);\n\t\t\try: var(--pf-node-body-radius);\n\t\t}\n\t\t.pict-flow-node-title-bar-bottom {\n\t\t\tfill: var(--pf-node-title-bar-color);\n\t\t}\n\t\t.pict-flow-node-title {\n\t\t\tfill: var(--pf-node-title-fill);\n\t\t\tfont-size: var(--pf-node-title-size);\n\t\t\tfont-weight: var(--pf-node-title-weight);\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tletter-spacing: 0.2px;\n\t\t\tpointer-events: none;\n\t\t}\n\t\t.pict-flow-node-type-label {\n\t\t\tfill: var(--pf-node-type-label-fill);\n\t\t\tfont-size: 9.5px;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.3px;\n\t\t\tpointer-events: none;\n\t\t\topacity: 0;\n\t\t\ttransition: opacity 0.2s;\n\t\t}\n\t\t.pict-flow-node:hover .pict-flow-node-type-label {\n\t\t\topacity: 1;\n\t\t}\n\t\t.pict-flow-node-card-code {\n\t\t\topacity: 0;\n\t\t\ttransition: opacity 0.2s;\n\t\t}\n\t\t.pict-flow-node:hover .pict-flow-node-card-code {\n\t\t\topacity: 1;\n\t\t}\n\t\t/* Title-bar icon: invert SVG paths to white for dark title bars */\n\t\t.pict-flow-node-title-icon {\n\t\t\tfilter: brightness(0) invert(1);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Body Content ──────────────────────────────────────────────────────\n\t/**\n\t * CSS for custom body content in nodes: SVG group, foreignObject, HTML container, canvas.\n\t * @returns {string}\n\t */\n\tgetBodyContentCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node-body-content {\n\t\t\tpointer-events: none;\n\t\t}\n\t\t.pict-flow-node-body-content-fo {\n\t\t\toverflow: hidden;\n\t\t}\n\t\t.pict-flow-node-body-content-html {\n\t\t\toverflow: hidden;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tbox-sizing: border-box;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tfont-size: 11px;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tpointer-events: auto;\n\t\t}\n\t\t.pict-flow-node-body-content-canvas {\n\t\t\tdisplay: block;\n\t\t\tpointer-events: auto;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Node Variants ──────────────────────────────────────────────────────\n\t/**\n\t * CSS overrides for specific node types: start, end, halt, decision.\n\t * @returns {string}\n\t */\n\tgetNodeVariantCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node-decision .pict-flow-node-body {\n\t\t\tfill: var(--pf-node-decision-fill);\n\t\t\tstroke: var(--pf-node-decision-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t.pict-flow-node-start .pict-flow-node-body {\n\t\t\tfill: var(--pf-node-start-fill);\n\t\t\tstroke: var(--pf-node-start-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t.pict-flow-node-end .pict-flow-node-body {\n\t\t\tfill: var(--pf-node-end-fill);\n\t\t\tstroke: var(--pf-node-end-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t.pict-flow-node-halt .pict-flow-node-body {\n\t\t\tfill: var(--pf-node-halt-fill);\n\t\t\tstroke: var(--pf-node-halt-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Ports ──────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for port circles: input/output coloring, hover states, labels.\n\t * @returns {string}\n\t */\n\tgetPortCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-port {\n\t\t\tcursor: crosshair;\n\t\t\ttransition: r 0.15s, filter 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.12));\n\t\t}\n\t\t.pict-flow-port.input {\n\t\t\tfill: var(--pf-port-input-fill);\n\t\t\tstroke: var(--pf-port-stroke);\n\t\t\tstroke-width: var(--pf-port-stroke-width);\n\t\t}\n\t\t.pict-flow-port.output {\n\t\t\tfill: var(--pf-port-output-fill);\n\t\t\tstroke: var(--pf-port-stroke);\n\t\t\tstroke-width: var(--pf-port-stroke-width);\n\t\t}\n\t\t.pict-flow-port:hover {\n\t\t\tr: 7;\n\t\t\tfilter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.20));\n\t\t}\n\t\t/* Port type color overrides */\n\t\t.pict-flow-port.port-type-event-in {\n\t\t\tfill: var(--pf-port-event-in-fill);\n\t\t}\n\t\t.pict-flow-port.port-type-event-out {\n\t\t\tfill: var(--pf-port-event-out-fill);\n\t\t}\n\t\t.pict-flow-port.port-type-setting {\n\t\t\tfill: var(--pf-port-setting-fill);\n\t\t}\n\t\t.pict-flow-port.port-type-value {\n\t\t\tfill: var(--pf-port-value-fill);\n\t\t}\n\t\t.pict-flow-port.port-type-error {\n\t\t\tfill: var(--pf-port-error-fill);\n\t\t}\n\t\t.pict-flow-port-label {\n\t\t\tfont-size: 8px;\n\t\t\tfont-weight: 600;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tpointer-events: none;\n\t\t}\n\t\t/* Port label badge background */\n\t\t.pict-flow-port-label-bg {\n\t\t\tpointer-events: none;\n\t\t}\n\t\t/* Port labels on hover: hidden by default, revealed on node hover */\n\t\t.pict-flow-node-port-labels-hover .pict-flow-port-label,\n\t\t.pict-flow-node-port-labels-hover .pict-flow-port-label-bg {\n\t\t\topacity: 0;\n\t\t\ttransition: opacity 0.2s;\n\t\t}\n\t\t.pict-flow-node-port-labels-hover:hover .pict-flow-port-label,\n\t\t.pict-flow-node-port-labels-hover:hover .pict-flow-port-label-bg {\n\t\t\topacity: 1;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Connections ────────────────────────────────────────────────────────\n\t/**\n\t * CSS for connection paths: base, hover/selected states, hitarea, drag-connection.\n\t * @returns {string}\n\t */\n\tgetConnectionCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-connection {\n\t\t\tfill: none;\n\t\t\tstroke: var(--pf-connection-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: pointer;\n\t\t\ttransition: stroke 0.15s;\n\t\t}\n\t\t.pict-flow-connection:hover {\n\t\t\tstroke: var(--pf-connection-stroke-hover);\n\t\t\tstroke-width: 3;\n\t\t}\n\t\t.pict-flow-connection.selected {\n\t\t\tstroke: var(--pf-connection-selected-stroke);\n\t\t\tstroke-width: 3;\n\t\t}\n\t\t/* Connection type color overrides (based on source port type) */\n\t\t.pict-flow-connection.conn-type-event-in {\n\t\t\tstroke: var(--pf-connection-event-in-stroke);\n\t\t}\n\t\t.pict-flow-connection.conn-type-event-out {\n\t\t\tstroke: var(--pf-connection-event-out-stroke);\n\t\t}\n\t\t.pict-flow-connection.conn-type-setting {\n\t\t\tstroke: var(--pf-connection-setting-stroke);\n\t\t}\n\t\t.pict-flow-connection.conn-type-value {\n\t\t\tstroke: var(--pf-connection-value-stroke);\n\t\t}\n\t\t.pict-flow-connection.conn-type-error {\n\t\t\tstroke: var(--pf-connection-error-stroke);\n\t\t}\n\t\t.pict-flow-connection-hitarea {\n\t\t\tfill: none;\n\t\t\tstroke: transparent;\n\t\t\tstroke-width: 12;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-drag-connection {\n\t\t\tfill: none;\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tstroke-dasharray: 6 3;\n\t\t\tpointer-events: none;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Connection Handles ─────────────────────────────────────────────────\n\t/**\n\t * CSS for connection waypoint handles and midpoint handles.\n\t * @returns {string}\n\t */\n\tgetHandleCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-connection-handle {\n\t\t\tfill: var(--pf-panel-bg);\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: grab;\n\t\t\ttransition: r 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));\n\t\t}\n\t\t.pict-flow-connection-handle:hover {\n\t\t\tr: 8;\n\t\t\tstroke-width: 2.5;\n\t\t}\n\t\t.pict-flow-connection-handle-midpoint {\n\t\t\tfill: var(--pf-panel-bg);\n\t\t\tstroke: var(--pf-port-setting-fill);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: grab;\n\t\t\ttransition: r 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));\n\t\t}\n\t\t.pict-flow-connection-handle-midpoint:hover {\n\t\t\tr: 8;\n\t\t\tstroke-width: 2.5;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Tethers ────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for tether lines, hitareas, handles, and midpoint handles.\n\t * @returns {string}\n\t */\n\tgetTetherCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-tether-line {\n\t\t\tfill: none;\n\t\t\tstroke: var(--pf-connection-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t\tstroke-dasharray: 6 4;\n\t\t\tpointer-events: visibleStroke;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-tether-line.selected {\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t}\n\t\t.pict-flow-tether-hitarea {\n\t\t\tfill: none;\n\t\t\tstroke: transparent;\n\t\t\tstroke-width: 10;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-tether-handle {\n\t\t\tfill: var(--pf-panel-bg);\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: grab;\n\t\t\ttransition: r 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));\n\t\t}\n\t\t.pict-flow-tether-handle:hover {\n\t\t\tr: 8;\n\t\t\tstroke-width: 2.5;\n\t\t}\n\t\t.pict-flow-tether-handle-midpoint {\n\t\t\tfill: var(--pf-panel-bg);\n\t\t\tstroke: var(--pf-port-setting-fill);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: grab;\n\t\t\ttransition: r 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));\n\t\t}\n\t\t.pict-flow-tether-handle-midpoint:hover {\n\t\t\tr: 8;\n\t\t\tstroke-width: 2.5;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Panels ─────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for property panels: foreign object, panel container, titlebar, close button, body, indicator.\n\t * @returns {string}\n\t */\n\tgetPanelCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node-panel-indicator {\n\t\t\tfill: var(--pf-node-selected-stroke);\n\t\t\tstroke: none;\n\t\t\topacity: 0.6;\n\t\t\tcursor: pointer;\n\t\t\ttransition: opacity 0.15s;\n\t\t}\n\t\t.pict-flow-node-panel-indicator:hover {\n\t\t\topacity: 1.0;\n\t\t}\n\t\t.pict-flow-panel-foreign-object {\n\t\t\toverflow: visible;\n\t\t}\n\t\t.pict-flow-panel {\n\t\t\tbackground: var(--pf-panel-bg);\n\t\t\tborder: 1px solid var(--pf-panel-border);\n\t\t\tborder-radius: var(--pf-panel-radius);\n\t\t\tbox-shadow: var(--pf-panel-shadow);\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tfont-size: 13px;\n\t\t\toverflow: hidden;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.pict-flow-panel-titlebar {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: space-between;\n\t\t\tpadding: 8px 12px;\n\t\t\tbackground: var(--pf-panel-titlebar-bg);\n\t\t\tborder-bottom: 1px solid var(--pf-panel-titlebar-border);\n\t\t\tcursor: grab;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.pict-flow-panel-titlebar.dragging {\n\t\t\tcursor: grabbing;\n\t\t}\n\t\t.pict-flow-panel-title-text {\n\t\t\tfont-weight: 600;\n\t\t\tfont-size: 12px;\n\t\t\tcolor: var(--pf-panel-title-color);\n\t\t\twhite-space: nowrap;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t\tletter-spacing: 0.1px;\n\t\t}\n\t\t.pict-flow-panel-close-btn {\n\t\t\tcursor: pointer;\n\t\t\tcolor: var(--pf-button-close-color);\n\t\t\tfont-size: 14px;\n\t\t\tline-height: 1;\n\t\t\tpadding: 4px;\n\t\t\tborder: none;\n\t\t\tbackground: none;\n\t\t\tborder-radius: 4px;\n\t\t\ttransition: background-color 0.15s, color 0.15s;\n\t\t}\n\t\t.pict-flow-panel-close-btn:hover {\n\t\t\tcolor: var(--pf-button-danger-text);\n\t\t\tbackground-color: rgba(231, 76, 60, 0.08);\n\t\t}\n\t\t.pict-flow-panel-content {\n\t\t\tflex: 1;\n\t\t\toverflow-y: auto;\n\t\t\tmin-height: 0;\n\t\t\tpadding: 0;\n\t\t}\n\t\t.pict-flow-panel-tab-pane {\n\t\t\tpadding: 10px 12px;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Info Panels ────────────────────────────────────────────────────────\n\t/**\n\t * CSS for the info/hover panel and all sub-elements: header, description, badges, sections, ports.\n\t * @returns {string}\n\t */\n\tgetInfoPanelCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-info-panel {\n\t\t\tpadding: 2px 0;\n\t\t\tfont-size: 12px;\n\t\t\tline-height: 1.5;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t}\n\t\t.pict-flow-info-panel-header {\n\t\t\tfont-size: 13px;\n\t\t\tfont-weight: 600;\n\t\t\tmargin-bottom: 6px;\n\t\t\tcolor: var(--pf-text-heading);\n\t\t}\n\t\t.pict-flow-info-panel-header.with-icon {\n\t\t\tfont-size: 14px;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 6px;\n\t\t}\n\t\t.pict-flow-info-panel-description {\n\t\t\tfont-size: 11px;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\tmargin-bottom: 10px;\n\t\t\tline-height: 1.45;\n\t\t}\n\t\t.pict-flow-info-panel-badges {\n\t\t\tmargin-bottom: 10px;\n\t\t\tdisplay: flex;\n\t\t\tflex-wrap: wrap;\n\t\t\tgap: 4px;\n\t\t}\n\t\t.pict-flow-info-panel-badge {\n\t\t\tdisplay: inline-block;\n\t\t\tpadding: 2px 8px;\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 10px;\n\t\t}\n\t\t.pict-flow-info-panel-badge.category {\n\t\t\tbackground: var(--pf-badge-category-bg);\n\t\t\tcolor: var(--pf-badge-category-text);\n\t\t}\n\t\t.pict-flow-info-panel-badge.code {\n\t\t\tbackground: var(--pf-badge-code-bg);\n\t\t\tcolor: var(--pf-badge-code-text);\n\t\t\tfont-family: \"SF Mono\", \"Fira Code\", monospace;\n\t\t}\n\t\t.pict-flow-info-panel-section {\n\t\t\tmargin-bottom: 8px;\n\t\t}\n\t\t.pict-flow-info-panel-section-title {\n\t\t\tfont-size: 10px;\n\t\t\tfont-weight: 600;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.5px;\n\t\t\tcolor: var(--pf-text-tertiary);\n\t\t\tmargin-bottom: 4px;\n\t\t\tpadding-bottom: 2px;\n\t\t\tborder-bottom: 1px solid var(--pf-divider-light);\n\t\t}\n\t\t.pict-flow-info-panel-port {\n\t\t\tpadding: 3px 8px;\n\t\t\tbackground: var(--pf-port-item-bg);\n\t\t\tmargin-bottom: 3px;\n\t\t\tfont-size: 11px;\n\t\t\tborder-radius: 3px;\n\t\t}\n\t\t.pict-flow-info-panel-port.input {\n\t\t\tborder-left: 3px solid var(--pf-port-input-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.output {\n\t\t\tborder-left: 3px solid var(--pf-port-output-fill);\n\t\t}\n\t\t/* Info panel port type color overrides */\n\t\t.pict-flow-info-panel-port.port-type-event-in {\n\t\t\tborder-left-color: var(--pf-port-event-in-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.port-type-event-out {\n\t\t\tborder-left-color: var(--pf-port-event-out-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.port-type-setting {\n\t\t\tborder-left-color: var(--pf-port-setting-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.port-type-value {\n\t\t\tborder-left-color: var(--pf-port-value-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.port-type-error {\n\t\t\tborder-left-color: var(--pf-port-error-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port-constraint {\n\t\t\tcolor: var(--pf-text-tertiary);\n\t\t\tfont-size: 10px;\n\t\t}\n\t\t/* Port summary section appended below form panels */\n\t\t.pict-flow-port-summary {\n\t\t\tmargin-top: 12px;\n\t\t\tpadding-top: 8px;\n\t\t\tborder-top: 1px solid var(--pf-divider-medium);\n\t\t}\n\t\t.pict-flow-info-panel-port.event {\n\t\t\tborder-left: 3px solid var(--pf-port-event-in-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.value {\n\t\t\tborder-left: 3px solid var(--pf-port-value-fill);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Node Properties Editor ────────────────────────────────────────────\n\t/**\n\t * CSS for the node properties editor fields used in the Appearance tab.\n\t * @returns {string}\n\t */\n\tgetNodePropsEditorCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node-props-fields {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tgap: 6px;\n\t\t}\n\t\t.pict-flow-node-props-field {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 8px;\n\t\t}\n\t\t.pict-flow-node-props-label {\n\t\t\tfont-size: 11px;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\tmin-width: 72px;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.pict-flow-node-props-input {\n\t\t\tflex: 1;\n\t\t\tpadding: 3px 6px;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 3px;\n\t\t\tfont-size: 11px;\n\t\t\toutline: none;\n\t\t\tbox-sizing: border-box;\n\t\t\tmin-width: 0;\n\t\t}\n\t\t.pict-flow-node-props-input:focus {\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-node-props-color {\n\t\t\twidth: 28px;\n\t\t\theight: 24px;\n\t\t\tpadding: 1px;\n\t\t\tcursor: pointer;\n\t\t\tflex: 0 0 28px;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Panel Tabs & Resize ───────────────────────────────────────────────\n\t/**\n\t * CSS for the tab bar, tab panes, resize handle, and help content.\n\t * @returns {string}\n\t */\n\tgetPanelTabsCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-panel-resize-handle {\n\t\t\theight: 6px;\n\t\t\tcursor: ns-resize;\n\t\t\tbackground: transparent;\n\t\t\tflex-shrink: 0;\n\t\t\ttransition: background-color 0.15s;\n\t\t\tborder-top: 1px solid var(--pf-panel-titlebar-border);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t}\n\t\t.pict-flow-panel-resize-handle::after {\n\t\t\tcontent: '';\n\t\t\twidth: 24px;\n\t\t\theight: 2px;\n\t\t\tborder-radius: 1px;\n\t\t\tbackground: var(--pf-resize-handle-hover);\n\t\t\ttransition: background-color 0.15s, width 0.15s;\n\t\t}\n\t\t.pict-flow-panel-resize-handle:hover::after {\n\t\t\tbackground: var(--pf-button-hover-border);\n\t\t\twidth: 32px;\n\t\t}\n\t\t.pict-flow-panel-tabbar {\n\t\t\tdisplay: flex;\n\t\t\tflex-shrink: 0;\n\t\t\tborder-top: 1px solid var(--pf-panel-titlebar-border);\n\t\t\tbackground: var(--pf-panel-titlebar-bg);\n\t\t}\n\t\t.pict-flow-panel-tab {\n\t\t\tflex: 1;\n\t\t\tpadding: 5px 8px;\n\t\t\tfont-size: 11px;\n\t\t\ttext-align: center;\n\t\t\tcursor: pointer;\n\t\t\tcolor: var(--pf-tab-text);\n\t\t\tborder-top: 2px solid transparent;\n\t\t\ttransition: color 0.15s, border-top-color 0.15s;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t}\n\t\t.pict-flow-panel-tab:hover {\n\t\t\tcolor: var(--pf-tab-text-hover);\n\t\t}\n\t\t.pict-flow-panel-tab.active {\n\t\t\tborder-top-color: var(--pf-node-selected-stroke);\n\t\t\tcolor: var(--pf-panel-title-color);\n\t\t\tfont-weight: 600;\n\t\t}\n\t\t.pict-flow-panel-help-content {\n\t\t\tfont-size: 12px;\n\t\t\tline-height: 1.5;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t}\n\t\t.pict-flow-panel-help-content p {\n\t\t\tmargin: 0 0 8px 0;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Fullscreen ─────────────────────────────────────────────────────────\n\t/**\n\t * CSS for fullscreen mode.\n\t * @returns {string}\n\t */\n\tgetFullscreenCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-fullscreen {\n\t\t\tposition: fixed;\n\t\t\ttop: 0;\n\t\t\tleft: 0;\n\t\t\twidth: 100vw;\n\t\t\theight: 100vh;\n\t\t\tz-index: 9999;\n\t\t\tborder-radius: 0;\n\t\t\tborder: none;\n\t\t\tmin-height: 100vh;\n\t\t}\n\t\t.pict-flow-fullscreen .pict-flow-svg {\n\t\t\tmin-height: calc(100vh - 50px);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Toolbar ────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for the toolbar: buttons, groups, labels, selects.\n\t * @returns {string}\n\t */\n\tgetToolbarCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-toolbar {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.5em;\n\t\t\tpadding: 0.5em 0.75em;\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tborder-bottom: 1px solid var(--pf-toolbar-border);\n\t\t\tflex-wrap: wrap;\n\t\t}\n\t\t.pict-flow-toolbar-group {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.25em;\n\t\t\tpadding-right: 0.75em;\n\t\t\tborder-right: 1px solid var(--pf-toolbar-border);\n\t\t}\n\t\t.pict-flow-toolbar-group:last-child {\n\t\t\tborder-right: none;\n\t\t\tpadding-right: 0;\n\t\t}\n\t\t.pict-flow-toolbar-btn {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: 0.35em;\n\t\t\tpadding: 0.35em 0.65em;\n\t\t\tborder: 1px solid var(--pf-button-border);\n\t\t\tborder-radius: 4px;\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tfont-size: 0.85em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.15s, border-color 0.15s;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t}\n\t\t.pict-flow-toolbar-btn:focus {\n\t\t\toutline: none;\n\t\t}\n\t\t.pict-flow-toolbar-btn:hover {\n\t\t\tbackground-color: var(--pf-button-hover-bg);\n\t\t\tborder-color: var(--pf-button-hover-border);\n\t\t}\n\t\t.pict-flow-toolbar-btn:active {\n\t\t\tbackground-color: var(--pf-button-active-bg);\n\t\t}\n\t\t.pict-flow-toolbar-btn-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-toolbar-btn-icon svg {\n\t\t\tdisplay: block;\n\t\t}\n\t\t.pict-flow-toolbar-btn-text {\n\t\t\twhite-space: nowrap;\n\t\t}\n\t\t.pict-flow-toolbar-btn-chevron {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tmargin-left: 0.15em;\n\t\t}\n\t\t.pict-flow-toolbar-right {\n\t\t\tmargin-left: auto;\n\t\t\tborder-right: none;\n\t\t\tpadding-right: 0;\n\t\t}\n\t\t.pict-flow-toolbar-label {\n\t\t\tfont-size: 0.8em;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\tmargin-right: 0.25em;\n\t\t}\n\t\t.pict-flow-toolbar-select {\n\t\t\tpadding: 0.3em 0.5em;\n\t\t\tborder: 1px solid var(--pf-button-border);\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 0.85em;\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tcolor: var(--pf-text-primary);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Card Palette ───────────────────────────────────────────────────────\n\t/**\n\t * CSS for the card palette: toggle, body, categories, cards, swatches.\n\t * @returns {string}\n\t */\n\tgetPaletteCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-palette-category {\n\t\t\tmargin-bottom: 0.5em;\n\t\t}\n\t\t.pict-flow-palette-category:last-child {\n\t\t\tmargin-bottom: 0;\n\t\t}\n\t\t.pict-flow-palette-category-label {\n\t\t\tfont-size: 0.7em;\n\t\t\tfont-weight: 700;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.05em;\n\t\t\tcolor: var(--pf-text-placeholder);\n\t\t\tmargin-bottom: 0.35em;\n\t\t\tpadding-bottom: 0.2em;\n\t\t\tborder-bottom: 1px solid var(--pf-divider-light);\n\t\t}\n\t\t.pict-flow-palette-cards {\n\t\t\tdisplay: flex;\n\t\t\tflex-wrap: wrap;\n\t\t\tgap: 0.35em;\n\t\t}\n\t\t.pict-flow-palette-card {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.35em;\n\t\t\tpadding: 0.35em 0.6em;\n\t\t\tborder: 1px solid var(--pf-card-border);\n\t\t\tborder-radius: 4px;\n\t\t\tbackground-color: var(--pf-panel-bg);\n\t\t\tfont-size: 0.8em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t\tposition: relative;\n\t\t}\n\t\t.pict-flow-palette-card:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t\tborder-color: var(--pf-node-selected-stroke);\n\t\t\tbox-shadow: var(--pf-card-hover-shadow);\n\t\t}\n\t\t.pict-flow-palette-card.disabled {\n\t\t\topacity: 0.45;\n\t\t\tpointer-events: none;\n\t\t\tcursor: default;\n\t\t}\n\t\t.pict-flow-palette-card-icon {\n\t\t\tfont-size: 1.1em;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-palette-card-swatch {\n\t\t\twidth: 10px;\n\t\t\theight: 10px;\n\t\t\tborder-radius: 2px;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.pict-flow-palette-card-title {\n\t\t\tfont-weight: 500;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\twhite-space: nowrap;\n\t\t}\n\t\t.pict-flow-palette-card-code {\n\t\t\tfont-size: 0.8em;\n\t\t\tcolor: var(--pf-text-placeholder);\n\t\t\tfont-family: monospace;\n\t\t}\n\t\t.pict-flow-toolbar-select.layout-select {\n\t\t\tmin-width: 120px;\n\t\t\tmax-width: 200px;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Popups ────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for toolbar dropdown popups (Add Node, Cards, Layout).\n\t * @returns {string}\n\t */\n\tgetPopupCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-toolbar-popup-anchor {\n\t\t\tposition: relative;\n\t\t}\n\t\t.pict-flow-toolbar-popup {\n\t\t\tposition: absolute;\n\t\t\tz-index: 1000;\n\t\t\tbackground: var(--pf-panel-bg);\n\t\t\tborder: 1px solid var(--pf-card-border);\n\t\t\tborder-radius: 6px;\n\t\t\tbox-shadow: 0 4px 16px rgba(0,0,0,0.12);\n\t\t\tmin-width: 240px;\n\t\t\tmax-height: 340px;\n\t\t\toverflow-y: auto;\n\t\t\tpadding: 0.35em 0;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tfont-size: 13px;\n\t\t}\n\t\t.pict-flow-popup-search-wrapper {\n\t\t\tposition: relative;\n\t\t\tpadding: 0.4em 0.5em;\n\t\t\tborder-bottom: 1px solid var(--pf-divider-light);\n\t\t}\n\t\t.pict-flow-popup-search-icon {\n\t\t\tposition: absolute;\n\t\t\tleft: 0.85em;\n\t\t\ttop: 50%;\n\t\t\ttransform: translateY(-50%);\n\t\t\tpointer-events: none;\n\t\t\tline-height: 1;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t}\n\t\t.pict-flow-popup-search {\n\t\t\twidth: 100%;\n\t\t\tpadding: 0.4em 0.5em 0.4em 2em;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 0.9em;\n\t\t\toutline: none;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.pict-flow-popup-search:focus {\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-popup-list-item {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.5em;\n\t\t\tpadding: 0.45em 0.75em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.1s;\n\t\t}\n\t\t.pict-flow-popup-list-item:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t}\n\t\t.pict-flow-popup-list-item-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tflex-shrink: 0;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-popup-list-item-label {\n\t\t\tflex: 1;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tfont-weight: 500;\n\t\t}\n\t\t.pict-flow-popup-list-item-code {\n\t\t\tfont-size: 0.8em;\n\t\t\tcolor: var(--pf-text-placeholder);\n\t\t\tfont-family: monospace;\n\t\t\tbackground: var(--pf-badge-category-bg);\n\t\t\tpadding: 0.1em 0.4em;\n\t\t\tborder-radius: 3px;\n\t\t}\n\t\t.pict-flow-popup-divider {\n\t\t\theight: 1px;\n\t\t\tbackground: var(--pf-divider-light);\n\t\t\tmargin: 0.25em 0;\n\t\t}\n\t\t.pict-flow-popup-list-empty {\n\t\t\ttext-align: center;\n\t\t\tcolor: var(--pf-text-placeholder);\n\t\t\tpadding: 1.5em 0.75em;\n\t\t\tfont-size: 0.9em;\n\t\t}\n\t\t.pict-flow-popup-layout-save {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.5em;\n\t\t\tpadding: 0.45em 0.75em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.1s;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tfont-weight: 500;\n\t\t}\n\t\t.pict-flow-popup-layout-save:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t}\n\t\t.pict-flow-popup-layout-save-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tflex-shrink: 0;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-popup-layout-save-input-row {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.35em;\n\t\t\tpadding: 0.35em 0.5em;\n\t\t}\n\t\t.pict-flow-popup-layout-save-input {\n\t\t\tflex: 1;\n\t\t\tpadding: 0.35em 0.5em;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 0.9em;\n\t\t\toutline: none;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.pict-flow-popup-layout-save-input:focus {\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-popup-layout-save-confirm {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\twidth: 28px;\n\t\t\theight: 28px;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 4px;\n\t\t\tbackground: var(--pf-panel-bg);\n\t\t\tcursor: pointer;\n\t\t\tflex-shrink: 0;\n\t\t\ttransition: background-color 0.15s, border-color 0.15s;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-popup-layout-save-confirm:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-popup-layout-row {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tpadding: 0.45em 0.75em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.1s;\n\t\t}\n\t\t.pict-flow-popup-layout-row:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t}\n\t\t.pict-flow-popup-layout-name {\n\t\t\tflex: 1;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t}\n\t\t.pict-flow-popup-layout-delete {\n\t\t\tdisplay: none;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tborder: none;\n\t\t\tbackground: none;\n\t\t\tcolor: var(--pf-button-danger-text);\n\t\t\tcursor: pointer;\n\t\t\tpadding: 2px 4px;\n\t\t\tborder-radius: 3px;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-popup-layout-row:hover .pict-flow-popup-layout-delete {\n\t\t\tdisplay: inline-flex;\n\t\t}\n\t\t.pict-flow-popup-layout-delete:hover {\n\t\t\tbackground-color: var(--pf-button-danger-hover-bg);\n\t\t}\n\t\t.pict-flow-popup-settings-section {\n\t\t\tpadding: 0.5em 0.75em;\n\t\t}\n\t\t.pict-flow-popup-settings-label {\n\t\t\tdisplay: block;\n\t\t\tfont-size: 0.8em;\n\t\t\tfont-weight: 600;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.05em;\n\t\t\tmargin-bottom: 0.35em;\n\t\t}\n\t\t.pict-flow-popup-settings-select {\n\t\t\twidth: 100%;\n\t\t\tpadding: 0.4em 0.5em;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 0.9em;\n\t\t\tbackground: var(--pf-panel-bg);\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tcursor: pointer;\n\t\t\toutline: none;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.pict-flow-popup-settings-select:focus {\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-popup-settings-slider-row {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.5em;\n\t\t}\n\t\t.pict-flow-popup-settings-slider {\n\t\t\tflex: 1;\n\t\t\t-webkit-appearance: none;\n\t\t\tappearance: none;\n\t\t\theight: 4px;\n\t\t\tbackground: var(--pf-input-border);\n\t\t\tborder-radius: 2px;\n\t\t\toutline: none;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-popup-settings-slider::-webkit-slider-thumb {\n\t\t\t-webkit-appearance: none;\n\t\t\tappearance: none;\n\t\t\twidth: 14px;\n\t\t\theight: 14px;\n\t\t\tbackground: var(--pf-node-selected-stroke);\n\t\t\tborder-radius: 50%;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-popup-settings-slider::-moz-range-thumb {\n\t\t\twidth: 14px;\n\t\t\theight: 14px;\n\t\t\tbackground: var(--pf-node-selected-stroke);\n\t\t\tborder-radius: 50%;\n\t\t\tcursor: pointer;\n\t\t\tborder: none;\n\t\t}\n\t\t.pict-flow-popup-settings-slider-value {\n\t\t\tfont-size: 0.85em;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\tmin-width: 2.5em;\n\t\t\ttext-align: right;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Collapsed Toolbar ─────────────────────────────────────────────────\n\t/**\n\t * CSS for the collapsed toolbar state (small expand button in corner).\n\t * @returns {string}\n\t */\n\tgetCollapsedToolbarCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-toolbar-collapsed {\n\t\t\tposition: absolute;\n\t\t\ttop: 8px;\n\t\t\tright: 8px;\n\t\t\tz-index: 100;\n\t\t\tdisplay: none;\n\t\t}\n\t\t.pict-flow-toolbar-collapsed.visible {\n\t\t\tdisplay: block;\n\t\t}\n\t\t.pict-flow-toolbar-expand-btn {\n\t\t\twidth: 36px;\n\t\t\theight: 36px;\n\t\t\tborder-radius: 6px;\n\t\t\tborder: 1px solid var(--pf-card-border);\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tbox-shadow: 0 2px 6px rgba(0,0,0,0.1);\n\t\t\tcursor: pointer;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\ttransition: background-color 0.15s, box-shadow 0.15s;\n\t\t}\n\t\t.pict-flow-toolbar-expand-btn:hover {\n\t\t\tbackground-color: var(--pf-button-hover-bg);\n\t\t\tbox-shadow: 0 2px 8px rgba(0,0,0,0.15);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Floating Toolbar ──────────────────────────────────────────────────\n\t/**\n\t * CSS for the floating draggable toolbar.\n\t * @returns {string}\n\t */\n\tgetFloatingToolbarCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-floating-toolbar {\n\t\t\tposition: absolute;\n\t\t\tz-index: 100;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tgap: 2px;\n\t\t\tpadding: 4px;\n\t\t\tborder-radius: 8px;\n\t\t\tborder: 1px solid var(--pf-card-border);\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tbox-shadow: 0 4px 16px rgba(0,0,0,0.12);\n\t\t\tpointer-events: auto;\n\t\t}\n\t\t.pict-flow-floating-grip {\n\t\t\tcursor: grab;\n\t\t\tpadding: 4px;\n\t\t\tborder-radius: 4px;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\ttransition: background-color 0.15s;\n\t\t}\n\t\t.pict-flow-floating-grip:hover {\n\t\t\tbackground-color: var(--pf-button-hover-bg);\n\t\t}\n\t\t.pict-flow-floating-grip:active {\n\t\t\tcursor: grabbing;\n\t\t}\n\t\t.pict-flow-floating-btn {\n\t\t\twidth: 32px;\n\t\t\theight: 32px;\n\t\t\tborder: none;\n\t\t\tborder-radius: 4px;\n\t\t\tbackground-color: transparent;\n\t\t\tcursor: pointer;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\ttransition: background-color 0.15s;\n\t\t}\n\t\t.pict-flow-floating-btn:focus {\n\t\t\toutline: none;\n\t\t}\n\t\t.pict-flow-floating-btn:hover {\n\t\t\tbackground-color: var(--pf-button-hover-bg);\n\t\t}\n\t\t.pict-flow-floating-separator {\n\t\t\theight: 1px;\n\t\t\tbackground-color: var(--pf-divider-light);\n\t\t\tmargin: 2px 4px;\n\t\t}\n\t\t/* Collapsed floating toolbar — grip-only draggable square */\n\t\t.pict-flow-floating-toolbar.collapsed .pict-flow-floating-btn,\n\t\t.pict-flow-floating-toolbar.collapsed .pict-flow-floating-separator {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.pict-flow-floating-toolbar.collapsed {\n\t\t\tpadding: 0;\n\t\t\tborder-radius: 6px;\n\t\t}\n\t\t.pict-flow-floating-toolbar.collapsed .pict-flow-floating-grip {\n\t\t\twidth: 32px;\n\t\t\theight: 32px;\n\t\t\tpadding: 0;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t}\n\t\t.pict-flow-floating-toolbar.collapsed .pict-flow-floating-grip span {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tline-height: 1;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Icons ─────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for inline SVG icons in palette cards, toolbar buttons, info panel headers, and panel close buttons.\n\t * @returns {string}\n\t */\n\tgetIconCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-icon-svg {\n\t\t\tpointer-events: none;\n\t\t}\n\t\t.pict-flow-palette-card-icon svg {\n\t\t\tdisplay: inline-block;\n\t\t\tvertical-align: middle;\n\t\t}\n\t\t.pict-flow-toolbar-btn-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-toolbar-btn-icon svg {\n\t\t\tdisplay: block;\n\t\t\tvertical-align: middle;\n\t\t}\n\t\t.pict-flow-info-panel-header.with-icon svg {\n\t\t\tdisplay: inline-block;\n\t\t\tvertical-align: middle;\n\t\t\tmargin-right: 4px;\n\t\t}\n\t\t.pict-flow-panel-close-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-panel-close-icon svg {\n\t\t\tdisplay: block;\n\t\t}\n\t\t.pict-flow-palette-toggle-arrow svg {\n\t\t\tdisplay: block;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Bracket Node CSS ──────────────────────────────────────────────────\n\t/**\n\t * CSS for bracket-style node bodies used by sketch/blueprint themes.\n\t *\n\t * The bracket fill rects share class `.pict-flow-node-body` for fill\n\t * inheritance, so these rules must use parent-qualified selectors\n\t * (specificity ≥ 0,2,0) to override the base, variant, hover, and\n\t * selected rules that set stroke and rx/ry on `.pict-flow-node-body`.\n\t *\n\t * @returns {string}\n\t */\n\tgetBracketNodeCSS()\n\t{\n\t\treturn /*css*/`\n\t\t/* Bracket outline path */\n\t\t.pict-flow-node-bracket {\n\t\t\tfill: none;\n\t\t\tstroke: var(--pf-node-body-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tstroke-linecap: round;\n\t\t\tstroke-linejoin: round;\n\t\t}\n\t\t.pict-flow-node.selected .pict-flow-node-bracket {\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t}\n\t\t.pict-flow-node:hover .pict-flow-node-bracket {\n\t\t\tstroke: var(--pf-node-body-stroke-hover);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\n\t\t/* Bracket fill rects: no stroke, no rounded corners.\n\t\t Uses parent-qualified selectors to beat variant rules\n\t\t (e.g. .pict-flow-node-start .pict-flow-node-body). */\n\t\t.pict-flow-node .pict-flow-node-bracket-fill,\n\t\t.pict-flow-node .pict-flow-node-bracket-title-fill {\n\t\t\tstroke: none;\n\t\t\tstroke-width: 0;\n\t\t\trx: 0;\n\t\t\try: 0;\n\t\t}\n\t\t/* Beat hover rule: .pict-flow-node:hover .pict-flow-node-body */\n\t\t.pict-flow-node:hover .pict-flow-node-bracket-fill,\n\t\t.pict-flow-node:hover .pict-flow-node-bracket-title-fill {\n\t\t\tstroke: none;\n\t\t\tstroke-width: 0;\n\t\t}\n\t\t/* Beat selected rule: .pict-flow-node.selected .pict-flow-node-body */\n\t\t.pict-flow-node.selected .pict-flow-node-bracket-fill,\n\t\t.pict-flow-node.selected .pict-flow-node-bracket-title-fill {\n\t\t\tstroke: none;\n\t\t\tstroke-width: 0;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Aggregate Methods ──────────────────────────────────────────────────\n\n\t/**\n\t * Concatenate all domain CSS getters into a single CSS string.\n\t * Includes theme overrides if a theme provider is active.\n\t * @returns {string}\n\t */\n\tgenerateCSS()\n\t{\n\t\tlet tmpBaseCSS = (\n\t\t\tthis.getContainerCSS() +\n\t\t\tthis.getNodeCSS() +\n\t\t\tthis.getBodyContentCSS() +\n\t\t\tthis.getNodeVariantCSS() +\n\t\t\tthis.getPortCSS() +\n\t\t\tthis.getConnectionCSS() +\n\t\t\tthis.getHandleCSS() +\n\t\t\tthis.getTetherCSS() +\n\t\t\tthis.getPanelCSS() +\n\t\t\tthis.getInfoPanelCSS() +\n\t\t\tthis.getNodePropsEditorCSS() +\n\t\t\tthis.getPanelTabsCSS() +\n\t\t\tthis.getBracketNodeCSS() +\n\t\t\tthis.getFullscreenCSS() +\n\t\t\tthis.getToolbarCSS() +\n\t\t\tthis.getPaletteCSS() +\n\t\t\tthis.getPopupCSS() +\n\t\t\tthis.getCollapsedToolbarCSS() +\n\t\t\tthis.getFloatingToolbarCSS() +\n\t\t\tthis.getIconCSS()\n\t\t);\n\n\t\t// Apply theme overrides if a theme provider exists\n\t\tif (this._FlowView && this._FlowView._ThemeProvider)\n\t\t{\n\t\t\tlet tmpTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\t\tif (tmpTheme && tmpTheme.CSSVariables && Object.keys(tmpTheme.CSSVariables).length > 0)\n\t\t\t{\n\t\t\t\tlet tmpOverrides = '.pict-flow-container {\\n';\n\t\t\t\tfor (let tmpKey in tmpTheme.CSSVariables)\n\t\t\t\t{\n\t\t\t\t\ttmpOverrides += '\\t' + tmpKey + ': ' + tmpTheme.CSSVariables[tmpKey] + ';\\n';\n\t\t\t\t}\n\t\t\t\ttmpOverrides += '}\\n';\n\t\t\t\ttmpBaseCSS += tmpOverrides;\n\t\t\t}\n\t\t\tif (tmpTheme && tmpTheme.AdditionalCSS)\n\t\t\t{\n\t\t\t\ttmpBaseCSS += tmpTheme.AdditionalCSS;\n\t\t\t}\n\t\t}\n\n\t\treturn tmpBaseCSS;\n\t}\n\n\t/**\n\t * Register all flow CSS with pict's CSSMap service.\n\t * Uses correct parameter ordering: (hash, content, priority, provider).\n\t * Removes existing CSS first to allow theme re-registration,\n\t * then re-injects into the DOM.\n\t */\n\tregisterCSS()\n\t{\n\t\tif (this.fable && this.fable.CSSMap)\n\t\t{\n\t\t\t// Remove existing CSS first so we can re-register with updated theme overrides\n\t\t\tthis.fable.CSSMap.removeCSS('PictSectionFlow-CSS');\n\t\t\tthis.fable.CSSMap.addCSS('PictSectionFlow-CSS', this.generateCSS(), 500, 'PictProviderFlowCSS');\n\t\t\t// Re-inject into the DOM to apply the updated CSS\n\t\t\tthis.fable.CSSMap.injectCSS();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowCSS: CSSMap not available; CSS not registered.');\n\t\t}\n\t}\n}\n\nmodule.exports = PictProviderFlowCSS;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n\n},{\"fable-serviceproviderbase\":4}],17:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nclass PictProviderFlowConnectorShapes extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowConnectorShapes';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Default shape configurations — each keyed by a shape identifier\n\t\t// _OriginalShapes stores a deep copy for reset when switching themes.\n\t\tthis._DefaultShapes =\n\t\t{\n\t\t\t'port':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '5' },\n\t\t\t\tClassName: 'pict-flow-port'\n\t\t\t},\n\t\t\t'panel-indicator':\n\t\t\t{\n\t\t\t\tElementType: 'rect',\n\t\t\t\tAttributes: { rx: '2', ry: '2' },\n\t\t\t\tClassName: 'pict-flow-node-panel-indicator'\n\t\t\t},\n\t\t\t'connection-path':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-connection'\n\t\t\t},\n\t\t\t'connection-hitarea':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-connection-hitarea'\n\t\t\t},\n\t\t\t'connection-handle':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '6' },\n\t\t\t\tClassName: 'pict-flow-connection-handle'\n\t\t\t},\n\t\t\t'connection-handle-midpoint':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '6' },\n\t\t\t\tClassName: 'pict-flow-connection-handle-midpoint'\n\t\t\t},\n\t\t\t'drag-connection':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-drag-connection'\n\t\t\t},\n\t\t\t'tether-path':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-tether-line'\n\t\t\t},\n\t\t\t'tether-hitarea':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-tether-hitarea'\n\t\t\t},\n\t\t\t'tether-handle':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '6' },\n\t\t\t\tClassName: 'pict-flow-tether-handle'\n\t\t\t},\n\t\t\t'tether-handle-midpoint':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '6' },\n\t\t\t\tClassName: 'pict-flow-tether-handle-midpoint'\n\t\t\t},\n\t\t\t'arrowhead-connection':\n\t\t\t{\n\t\t\t\tMarkerWidth: '5',\n\t\t\t\tMarkerHeight: '7',\n\t\t\t\tRefX: '7.5',\n\t\t\t\tRefY: '3.5',\n\t\t\t\tPoints: '0 0, 5 3.5, 0 7',\n\t\t\t\tFill: '#95a5a6'\n\t\t\t},\n\t\t\t'arrowhead-connection-selected':\n\t\t\t{\n\t\t\t\tMarkerWidth: '5',\n\t\t\t\tMarkerHeight: '7',\n\t\t\t\tRefX: '7.5',\n\t\t\t\tRefY: '3.5',\n\t\t\t\tPoints: '0 0, 5 3.5, 0 7',\n\t\t\t\tFill: '#3498db'\n\t\t\t},\n\t\t\t'arrowhead-tether':\n\t\t\t{\n\t\t\t\tMarkerWidth: '4',\n\t\t\t\tMarkerHeight: '6',\n\t\t\t\tRefX: '6',\n\t\t\t\tRefY: '3',\n\t\t\t\tPoints: '0 0, 4 3, 0 6',\n\t\t\t\tFill: '#95a5a6'\n\t\t\t}\n\t\t};\n\n\t\t// Store a deep copy for resetting when switching themes\n\t\tthis._OriginalShapes = JSON.parse(JSON.stringify(this._DefaultShapes));\n\t}\n\n\t// ── Theme Override Methods ─────────────────────────────────────────────\n\n\t/**\n\t * Apply theme-specific shape overrides.\n\t * Merges the overrides into the active shape configs.\n\t * @param {Object} pOverrides - Map of shape key to partial config\n\t */\n\tapplyThemeOverrides(pOverrides)\n\t{\n\t\tif (!pOverrides || typeof pOverrides !== 'object')\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tfor (let tmpKey in pOverrides)\n\t\t{\n\t\t\tif (this._DefaultShapes.hasOwnProperty(tmpKey))\n\t\t\t{\n\t\t\t\tObject.assign(this._DefaultShapes[tmpKey], pOverrides[tmpKey]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tthis._DefaultShapes[tmpKey] = pOverrides[tmpKey];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Reset all shape configs to their original defaults.\n\t * Called before applying new theme overrides to prevent accumulation.\n\t */\n\tresetToDefaults()\n\t{\n\t\tthis._DefaultShapes = JSON.parse(JSON.stringify(this._OriginalShapes));\n\t}\n\n\t/**\n\t * Get a shape configuration by key.\n\t * @param {string} pShapeKey\n\t * @returns {Object|null}\n\t */\n\tgetShapeConfig(pShapeKey)\n\t{\n\t\tif (this._DefaultShapes.hasOwnProperty(pShapeKey))\n\t\t{\n\t\t\treturn this._DefaultShapes[pShapeKey];\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Override or add a shape configuration.\n\t * Consumers can call this to customize connector shapes without subclassing.\n\t * @param {string} pShapeKey\n\t * @param {Object} pConfig\n\t */\n\tsetShapeConfig(pShapeKey, pConfig)\n\t{\n\t\tthis._DefaultShapes[pShapeKey] = pConfig;\n\t}\n\n\t/**\n\t * Get all registered shape keys.\n\t * @returns {string[]}\n\t */\n\tgetShapeKeys()\n\t{\n\t\treturn Object.keys(this._DefaultShapes);\n\t}\n\n\t// ---- Factory Methods ----\n\n\t/**\n\t * Create a port SVG element.\n\t * @param {Object} pPortData - Port data with Hash, Direction\n\t * @param {{x: number, y: number}} pPosition - Local position within the node group\n\t * @param {string} pNodeHash - The owning node hash\n\t * @returns {SVGCircleElement}\n\t */\n\tcreatePortElement(pPortData, pPosition, pNodeHash)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['port'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\tlet tmpClassName = tmpConfig.ClassName + ' ' + pPortData.Direction;\n\t\tif (pPortData.PortType)\n\t\t{\n\t\t\ttmpClassName += ' port-type-' + pPortData.PortType;\n\t\t}\n\t\ttmpElement.setAttribute('class', tmpClassName);\n\t\ttmpElement.setAttribute('cx', String(pPosition.x));\n\t\ttmpElement.setAttribute('cy', String(pPosition.y));\n\t\t// Apply config attributes (r, etc.)\n\t\tfor (let tmpKey in tmpConfig.Attributes)\n\t\t{\n\t\t\ttmpElement.setAttribute(tmpKey, tmpConfig.Attributes[tmpKey]);\n\t\t}\n\t\ttmpElement.setAttribute('data-port-hash', pPortData.Hash);\n\t\ttmpElement.setAttribute('data-node-hash', pNodeHash);\n\t\ttmpElement.setAttribute('data-port-direction', pPortData.Direction);\n\t\tif (pPortData.PortType)\n\t\t{\n\t\t\ttmpElement.setAttribute('data-port-type', pPortData.PortType);\n\t\t}\n\t\ttmpElement.setAttribute('data-element-type', 'port');\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a panel indicator SVG element.\n\t * @param {string} pNodeHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @returns {SVGRectElement}\n\t */\n\tcreatePanelIndicatorElement(pNodeHash, pX, pY, pWidth, pHeight)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['panel-indicator'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('x', String(pX));\n\t\ttmpElement.setAttribute('y', String(pY));\n\t\ttmpElement.setAttribute('width', String(pWidth));\n\t\ttmpElement.setAttribute('height', String(pHeight));\n\t\t// Apply config attributes (rx, ry, etc.)\n\t\tfor (let tmpKey in tmpConfig.Attributes)\n\t\t{\n\t\t\ttmpElement.setAttribute(tmpKey, tmpConfig.Attributes[tmpKey]);\n\t\t}\n\t\ttmpElement.setAttribute('data-node-hash', pNodeHash);\n\t\ttmpElement.setAttribute('data-element-type', 'panel-indicator');\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a visible connection path SVG element.\n\t * @param {string} pPath - The SVG path d-string\n\t * @param {string} pConnectionHash\n\t * @param {boolean} pIsSelected\n\t * @param {string} pViewIdentifier\n\t * @returns {SVGPathElement}\n\t */\n\tcreateConnectionPathElement(pPath, pConnectionHash, pIsSelected, pViewIdentifier)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['connection-path'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName + (pIsSelected ? ' selected' : ''));\n\t\ttmpElement.setAttribute('d', pPath);\n\t\ttmpElement.setAttribute('data-connection-hash', pConnectionHash);\n\t\ttmpElement.setAttribute('data-element-type', 'connection');\n\n\t\t// Arrow marker\n\t\tlet tmpMarkerConfig = pIsSelected\n\t\t\t? this._DefaultShapes['arrowhead-connection-selected']\n\t\t\t: this._DefaultShapes['arrowhead-connection'];\n\t\t// The marker id follows the naming convention used in generateMarkerDefs\n\t\tlet tmpMarkerId = pIsSelected\n\t\t\t? ('flow-arrowhead-selected-' + pViewIdentifier)\n\t\t\t: ('flow-arrowhead-' + pViewIdentifier);\n\t\ttmpElement.setAttribute('marker-end', 'url(#' + tmpMarkerId + ')');\n\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a connection hit area SVG element (wider invisible path for click targeting).\n\t * @param {string} pPath - The SVG path d-string\n\t * @param {string} pConnectionHash\n\t * @returns {SVGPathElement}\n\t */\n\tcreateConnectionHitAreaElement(pPath, pConnectionHash)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['connection-hitarea'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('d', pPath);\n\t\ttmpElement.setAttribute('data-connection-hash', pConnectionHash);\n\t\ttmpElement.setAttribute('data-element-type', 'connection-hitarea');\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a drag handle circle element.\n\t * Works for both connection handles and tether handles.\n\t * @param {string} pOwnerHash - Connection hash or panel hash\n\t * @param {string} pHandleType - e.g. 'ortho-corner1', 'bezier-midpoint'\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {string} pShapeKey - 'connection-handle', 'connection-handle-midpoint', 'tether-handle', 'tether-handle-midpoint'\n\t * @returns {SVGCircleElement}\n\t */\n\tcreateHandleElement(pOwnerHash, pHandleType, pX, pY, pShapeKey)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes[pShapeKey] || this._DefaultShapes['connection-handle'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('cx', String(pX));\n\t\ttmpElement.setAttribute('cy', String(pY));\n\t\t// Apply config attributes (r, etc.)\n\t\tfor (let tmpKey in tmpConfig.Attributes)\n\t\t{\n\t\t\ttmpElement.setAttribute(tmpKey, tmpConfig.Attributes[tmpKey]);\n\t\t}\n\t\ttmpElement.setAttribute('data-handle-type', pHandleType);\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a complete drag handle element with all data attributes set and\n\t * append it to the given layer. Unifies the handle-creation logic that\n\t * was previously duplicated in ConnectionRenderer and TetherService.\n\t *\n\t * @param {SVGGElement} pLayer - The SVG group to append the handle to\n\t * @param {string} pOwnerHash - Connection hash or panel hash\n\t * @param {string} pHandleType - e.g. 'ortho-corner1', 'bezier-midpoint'\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {string} pShapeKey - Shape config key ('connection-handle', 'tether-handle-midpoint', etc.)\n\t * @param {string} pElementType - data-element-type value ('connection-handle' or 'tether-handle')\n\t * @param {string} pOwnerAttrName - data attribute name for the owner ('data-connection-hash' or 'data-panel-hash')\n\t * @returns {SVGElement}\n\t */\n\tcreateFullHandle(pLayer, pOwnerHash, pHandleType, pX, pY, pShapeKey, pElementType, pOwnerAttrName)\n\t{\n\t\tlet tmpHandle = this.createHandleElement(pOwnerHash, pHandleType, pX, pY, pShapeKey);\n\t\ttmpHandle.setAttribute('data-element-type', pElementType);\n\t\ttmpHandle.setAttribute(pOwnerAttrName, pOwnerHash);\n\t\tpLayer.appendChild(tmpHandle);\n\t\treturn tmpHandle;\n\t}\n\n\t/**\n\t * Create a temporary drag connection path element.\n\t * @param {string} pPath - The SVG path d-string\n\t * @returns {SVGPathElement}\n\t */\n\tcreateDragConnectionElement(pPath)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['drag-connection'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('d', pPath);\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a visible tether path SVG element.\n\t * @param {string} pPath - The SVG path d-string\n\t * @param {string} pPanelHash\n\t * @param {boolean} pIsSelected\n\t * @param {string} pViewIdentifier\n\t * @returns {SVGPathElement}\n\t */\n\tcreateTetherPathElement(pPath, pPanelHash, pIsSelected, pViewIdentifier)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['tether-path'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName + (pIsSelected ? ' selected' : ''));\n\t\ttmpElement.setAttribute('d', pPath);\n\t\ttmpElement.setAttribute('marker-end', 'url(#flow-tether-arrowhead-' + pViewIdentifier + ')');\n\t\ttmpElement.setAttribute('data-element-type', 'tether');\n\t\ttmpElement.setAttribute('data-panel-hash', pPanelHash);\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a tether hit area SVG element.\n\t * @param {string} pPath - The SVG path d-string\n\t * @param {string} pPanelHash\n\t * @returns {SVGPathElement}\n\t */\n\tcreateTetherHitAreaElement(pPath, pPanelHash)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['tether-hitarea'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('d', pPath);\n\t\ttmpElement.setAttribute('data-element-type', 'tether-hitarea');\n\t\ttmpElement.setAttribute('data-panel-hash', pPanelHash);\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Generate SVG marker definition markup for all arrowhead types.\n\t * Returns raw SVG markup to be injected into the <defs> section.\n\t * @param {string} pViewIdentifier\n\t * @returns {string}\n\t */\n\tgenerateMarkerDefs(pViewIdentifier)\n\t{\n\t\tlet tmpConnectionMarker = this._DefaultShapes['arrowhead-connection'];\n\t\tlet tmpSelectedMarker = this._DefaultShapes['arrowhead-connection-selected'];\n\t\tlet tmpTetherMarker = this._DefaultShapes['arrowhead-tether'];\n\n\t\tlet tmpMarkup = '';\n\n\t\t// Normal connection arrowhead (default gray)\n\t\ttmpMarkup += '<marker id=\"flow-arrowhead-' + pViewIdentifier + '\"'\n\t\t\t+ ' markerWidth=\"' + tmpConnectionMarker.MarkerWidth + '\"'\n\t\t\t+ ' markerHeight=\"' + tmpConnectionMarker.MarkerHeight + '\"'\n\t\t\t+ ' refX=\"' + tmpConnectionMarker.RefX + '\"'\n\t\t\t+ ' refY=\"' + tmpConnectionMarker.RefY + '\"'\n\t\t\t+ ' orient=\"auto\" markerUnits=\"strokeWidth\">'\n\t\t\t+ '<polygon points=\"' + tmpConnectionMarker.Points + '\" fill=\"' + tmpConnectionMarker.Fill + '\" />'\n\t\t\t+ '</marker>';\n\n\t\t// Per-port-type connection arrowheads\n\t\tlet tmpPortTypeColors =\n\t\t{\n\t\t\t'event-in': '#3498db',\n\t\t\t'event-out': '#2ecc71',\n\t\t\t'setting': '#e67e22',\n\t\t\t'value': '#f1c40f',\n\t\t\t'error': '#e74c3c'\n\t\t};\n\n\t\tfor (let tmpType in tmpPortTypeColors)\n\t\t{\n\t\t\ttmpMarkup += '<marker id=\"flow-arrowhead-' + tmpType + '-' + pViewIdentifier + '\"'\n\t\t\t\t+ ' markerWidth=\"' + tmpConnectionMarker.MarkerWidth + '\"'\n\t\t\t\t+ ' markerHeight=\"' + tmpConnectionMarker.MarkerHeight + '\"'\n\t\t\t\t+ ' refX=\"' + tmpConnectionMarker.RefX + '\"'\n\t\t\t\t+ ' refY=\"' + tmpConnectionMarker.RefY + '\"'\n\t\t\t\t+ ' orient=\"auto\" markerUnits=\"strokeWidth\">'\n\t\t\t\t+ '<polygon points=\"' + tmpConnectionMarker.Points + '\" fill=\"' + tmpPortTypeColors[tmpType] + '\" />'\n\t\t\t\t+ '</marker>';\n\t\t}\n\n\t\t// Selected connection arrowhead\n\t\ttmpMarkup += '<marker id=\"flow-arrowhead-selected-' + pViewIdentifier + '\"'\n\t\t\t+ ' markerWidth=\"' + tmpSelectedMarker.MarkerWidth + '\"'\n\t\t\t+ ' markerHeight=\"' + tmpSelectedMarker.MarkerHeight + '\"'\n\t\t\t+ ' refX=\"' + tmpSelectedMarker.RefX + '\"'\n\t\t\t+ ' refY=\"' + tmpSelectedMarker.RefY + '\"'\n\t\t\t+ ' orient=\"auto\" markerUnits=\"strokeWidth\">'\n\t\t\t+ '<polygon points=\"' + tmpSelectedMarker.Points + '\" fill=\"' + tmpSelectedMarker.Fill + '\" />'\n\t\t\t+ '</marker>';\n\n\t\t// Tether arrowhead\n\t\ttmpMarkup += '<marker id=\"flow-tether-arrowhead-' + pViewIdentifier + '\"'\n\t\t\t+ ' markerWidth=\"' + tmpTetherMarker.MarkerWidth + '\"'\n\t\t\t+ ' markerHeight=\"' + tmpTetherMarker.MarkerHeight + '\"'\n\t\t\t+ ' refX=\"' + tmpTetherMarker.RefX + '\"'\n\t\t\t+ ' refY=\"' + tmpTetherMarker.RefY + '\"'\n\t\t\t+ ' orient=\"auto\" markerUnits=\"strokeWidth\">'\n\t\t\t+ '<polygon points=\"' + tmpTetherMarker.Points + '\" fill=\"' + tmpTetherMarker.Fill + '\" />'\n\t\t\t+ '</marker>';\n\n\t\treturn tmpMarkup;\n\t}\n}\n\nmodule.exports = PictProviderFlowConnectorShapes;\n\n},{\"fable-serviceproviderbase\":4}],18:[function(require,module,exports){\nconst libPictProvider = require('pict-provider');\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowEventHandler'\n};\n\n/**\n * Event handler provider for the flow diagram.\n * Provides hook points for extensibility - consumers can register handlers\n * for flow events like node selection, movement, connection creation, etc.\n *\n * Supported events:\n * - onNodeSelected(node) - A node was selected (or null for deselection)\n * - onNodeAdded(node) - A new node was added\n * - onNodeRemoved(node) - A node was removed\n * - onNodeMoved(node) - A node was moved to a new position\n * - onConnectionSelected(conn) - A connection was selected\n * - onConnectionCreated(conn) - A new connection was created\n * - onConnectionRemoved(conn) - A connection was removed\n * - onConnectionHandleMoved(conn) - A connection's drag handle was repositioned\n * - onConnectionModeChanged(conn) - A connection's line mode was toggled (bezier/orthogonal)\n * - onPanelOpened(panelData) - A properties panel was opened\n * - onPanelClosed(panelData) - A properties panel was closed\n * - onPanelMoved(panelData) - A properties panel was moved\n * - onTetherSelected(panelData) - A tether line was selected (or null for deselection)\n * - onTetherHandleMoved(panelData) - A tether's drag handle was repositioned\n * - onTetherModeChanged(panelData) - A tether's line mode was toggled (bezier/orthogonal)\n * - onLayoutSaved(layoutData) - A layout snapshot was saved\n * - onLayoutRestored(layoutData) - A saved layout was restored\n * - onLayoutDeleted(layoutData) - A saved layout was deleted\n * - onFlowChanged(flowData) - The flow data changed (catch-all)\n */\nclass PictProviderFlowEventHandler extends libPictProvider\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowEventHandler';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Event handler registry\n\t\tthis._Handlers = {};\n\t}\n\n\t/**\n\t * Register an event handler\n\t * @param {string} pEventName - The event name (e.g., 'onNodeSelected')\n\t * @param {Function} pHandler - The handler function\n\t * @param {string} [pHandlerHash] - Optional unique identifier for this handler (for later removal)\n\t * @returns {string} The handler hash\n\t */\n\tregisterHandler(pEventName, pHandler, pHandlerHash)\n\t{\n\t\tif (typeof pHandler !== 'function')\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowEventHandler registerHandler: handler for '${pEventName}' is not a function`);\n\t\t\treturn null;\n\t\t}\n\n\t\tif (!this._Handlers[pEventName])\n\t\t{\n\t\t\tthis._Handlers[pEventName] = [];\n\t\t}\n\n\t\tlet tmpHash = pHandlerHash || `handler-${this.fable.getUUID()}`;\n\n\t\tthis._Handlers[pEventName].push({\n\t\t\tHash: tmpHash,\n\t\t\tHandler: pHandler\n\t\t});\n\n\t\tthis.log.trace(`PictProviderFlowEventHandler registered handler '${tmpHash}' for event '${pEventName}'`);\n\n\t\treturn tmpHash;\n\t}\n\n\t/**\n\t * Remove a specific event handler\n\t * @param {string} pEventName - The event name\n\t * @param {string} pHandlerHash - The handler hash to remove\n\t * @returns {boolean}\n\t */\n\tremoveHandler(pEventName, pHandlerHash)\n\t{\n\t\tif (!this._Handlers[pEventName]) return false;\n\n\t\tlet tmpIndex = this._Handlers[pEventName].findIndex((pH) => pH.Hash === pHandlerHash);\n\t\tif (tmpIndex >= 0)\n\t\t{\n\t\t\tthis._Handlers[pEventName].splice(tmpIndex, 1);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Remove all handlers for a specific event\n\t * @param {string} pEventName\n\t */\n\tremoveAllHandlers(pEventName)\n\t{\n\t\tif (pEventName)\n\t\t{\n\t\t\tthis._Handlers[pEventName] = [];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._Handlers = {};\n\t\t}\n\t}\n\n\t/**\n\t * Fire an event, calling all registered handlers\n\t * @param {string} pEventName - The event name\n\t * @param {*} pEventData - The event data to pass to handlers\n\t */\n\tfireEvent(pEventName, pEventData)\n\t{\n\t\tthis.log.trace(`PictProviderFlowEventHandler firing event '${pEventName}'`);\n\n\t\tif (!this._Handlers[pEventName] || this._Handlers[pEventName].length === 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tfor (let i = 0; i < this._Handlers[pEventName].length; i++)\n\t\t{\n\t\t\tlet tmpEntry = this._Handlers[pEventName][i];\n\t\t\ttry\n\t\t\t{\n\t\t\t\ttmpEntry.Handler(pEventData, this._FlowView);\n\t\t\t}\n\t\t\tcatch (pError)\n\t\t\t{\n\t\t\t\tthis.log.error(`PictProviderFlowEventHandler error in handler '${tmpEntry.Hash}' for event '${pEventName}': ${pError.message}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check if any handlers are registered for a specific event\n\t * @param {string} pEventName\n\t * @returns {boolean}\n\t */\n\thasHandlers(pEventName)\n\t{\n\t\treturn !!(this._Handlers[pEventName] && this._Handlers[pEventName].length > 0);\n\t}\n\n\t/**\n\t * Get the count of handlers for a specific event\n\t * @param {string} pEventName\n\t * @returns {number}\n\t */\n\tgetHandlerCount(pEventName)\n\t{\n\t\tif (!this._Handlers[pEventName]) return 0;\n\t\treturn this._Handlers[pEventName].length;\n\t}\n}\n\nmodule.exports = PictProviderFlowEventHandler;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n\n},{\"pict-provider\":8}],19:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-Geometry\n *\n * Shared geometry utilities for the flow diagram.\n * Provides direction vectors and edge center calculations used by\n * connections, tethers, and other flow components.\n *\n * Port Side values (12 positions):\n *\n * Top edge: 'top-left' 'top' 'top-right'\n * Left edge: 'left-top' 'left' 'left-bottom'\n * Right edge: 'right-top' 'right' 'right-bottom'\n * Bottom edge: 'bottom-left' 'bottom' 'bottom-right'\n *\n * The old 4-value sides ('left', 'right', 'top', 'bottom') map to\n * the middle position on each edge for backward compatibility.\n */\nclass PictProviderFlowGeometry extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowGeometry';\n\t}\n\n\t/**\n\t * Extract the edge name from a Side value.\n\t *\n\t * Maps all 12 positions (and the 4 legacy values) back to\n\t * the edge they sit on: 'left', 'right', 'top', or 'bottom'.\n\t *\n\t * @param {string} pSide - Any valid Side value\n\t * @returns {string} The edge: 'left', 'right', 'top', or 'bottom'\n\t */\n\tgetEdgeFromSide(pSide)\n\t{\n\t\tswitch (pSide)\n\t\t{\n\t\t\tcase 'left-top':\n\t\t\tcase 'left':\n\t\t\tcase 'left-bottom':\n\t\t\t\treturn 'left';\n\n\t\t\tcase 'right-top':\n\t\t\tcase 'right':\n\t\t\tcase 'right-bottom':\n\t\t\t\treturn 'right';\n\n\t\t\tcase 'top-left':\n\t\t\tcase 'top':\n\t\t\tcase 'top-right':\n\t\t\t\treturn 'top';\n\n\t\t\tcase 'bottom-left':\n\t\t\tcase 'bottom':\n\t\t\tcase 'bottom-right':\n\t\t\t\treturn 'bottom';\n\n\t\t\tdefault:\n\t\t\t\treturn 'right';\n\t\t}\n\t}\n\n\t/**\n\t * Get the outward unit direction vector for a given side.\n\t *\n\t * All positions on the same edge share the same direction vector.\n\t *\n\t * @param {string} pSide - Any valid Side value (12 positions or 4 legacy)\n\t * @returns {{dx: number, dy: number}}\n\t */\n\tsideDirection(pSide)\n\t{\n\t\tswitch (this.getEdgeFromSide(pSide))\n\t\t{\n\t\t\tcase 'left': return { dx: -1, dy: 0 };\n\t\t\tcase 'right': return { dx: 1, dy: 0 };\n\t\t\tcase 'top': return { dx: 0, dy: -1 };\n\t\t\tcase 'bottom': return { dx: 0, dy: 1 };\n\t\t\tdefault: return { dx: 1, dy: 0 };\n\t\t}\n\t}\n\n\t/**\n\t * Get the center point of a rectangle's edge.\n\t * Works for any object with X, Y, Width, Height properties\n\t * (nodes, panels, or any rectangular element).\n\t *\n\t * @param {Object} pRectData - Object with X, Y, Width, Height\n\t * @param {string} pSide - 'left', 'right', 'top', 'bottom'\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetEdgeCenter(pRectData, pSide)\n\t{\n\t\tswitch (pSide)\n\t\t{\n\t\t\tcase 'left':\n\t\t\t\treturn { x: pRectData.X, y: pRectData.Y + pRectData.Height / 2 };\n\t\t\tcase 'right':\n\t\t\t\treturn { x: pRectData.X + pRectData.Width, y: pRectData.Y + pRectData.Height / 2 };\n\t\t\tcase 'top':\n\t\t\t\treturn { x: pRectData.X + pRectData.Width / 2, y: pRectData.Y };\n\t\t\tcase 'bottom':\n\t\t\t\treturn { x: pRectData.X + pRectData.Width / 2, y: pRectData.Y + pRectData.Height };\n\t\t\tdefault:\n\t\t\t\treturn { x: pRectData.X + pRectData.Width, y: pRectData.Y + pRectData.Height / 2 };\n\t\t}\n\t}\n\n\t/**\n\t * Calculate a port's local position relative to node origin.\n\t *\n\t * Supports 12 positions (3 zones per edge). For left/right edges,\n\t * the body area below the title bar is divided into vertical zones.\n\t * For top/bottom edges, the full width is divided into horizontal zones.\n\t *\n\t * When pPortCountsBySide is provided, zone fractions are computed\n\t * proportionally based on actual port counts (adaptive zones).\n\t * Otherwise, fixed 1/3 zones are used for backward compatibility.\n\t *\n\t * Multiple ports sharing the same Side value distribute evenly within\n\t * their zone.\n\t *\n\t * @param {string} pSide - Side value (any of 12 positions or 4 legacy)\n\t * @param {number} pIndex - Index of this port within its Side group\n\t * @param {number} pTotal - Total ports with this Side value\n\t * @param {number} pWidth - Node width\n\t * @param {number} pHeight - Node height\n\t * @param {number} pTitleBarHeight - Height of the node title bar\n\t * @param {Object} [pPortCountsBySide] - Optional map of Side → port count\n\t * for all ports on the node. Enables adaptive zone sizing.\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetPortLocalPosition(pSide, pIndex, pTotal, pWidth, pHeight, pTitleBarHeight, pPortCountsBySide)\n\t{\n\t\tlet tmpEdge = this.getEdgeFromSide(pSide);\n\t\tlet tmpZone = pPortCountsBySide\n\t\t\t? this._computeAdaptiveZone(pSide, pPortCountsBySide)\n\t\t\t: this._getZoneFromSide(pSide);\n\n\t\t// Use the fixed zone to decide alignment intent (start/center/end)\n\t\t// because adaptive zones shift boundaries when neighbouring zones\n\t\t// are empty, which would break alignment decisions.\n\t\tlet tmpFixedZone = this._getZoneFromSide(pSide);\n\n\t\t// Minimum spacing between port centers (px)\n\t\tlet tmpMinSpacing = 16;\n\n\t\t// Reserve space at the bottom of the body so that port badges\n\t\t// never overlap the panel-indicator icon (10×10 rect at bottom-right)\n\t\t// and always leave a visible gap above the node bottom edge.\n\t\tlet tmpBottomPad = 16;\n\n\t\t// Determine alignment from the fixed zone position:\n\t\t// start zone (0.000 – 0.333) → start-align (offset 0)\n\t\t// middle zone (0.333 – 0.667) → center\n\t\t// end zone (0.667 – 1.000) → end-align\n\t\tlet tmpAlignment = 'start';\n\t\tif (tmpFixedZone.start >= 0.5)\n\t\t{\n\t\t\ttmpAlignment = 'end';\n\t\t}\n\t\telse if (tmpFixedZone.start >= 0.17)\n\t\t{\n\t\t\ttmpAlignment = 'center';\n\t\t}\n\n\t\tif (tmpEdge === 'left' || tmpEdge === 'right')\n\t\t{\n\t\t\tlet tmpX = (tmpEdge === 'left') ? 0 : pWidth;\n\t\t\tlet tmpBodyHeight = pHeight - pTitleBarHeight - tmpBottomPad;\n\t\t\tlet tmpZoneStart = pTitleBarHeight + tmpBodyHeight * tmpZone.start;\n\t\t\tlet tmpZoneHeight = tmpBodyHeight * (tmpZone.end - tmpZone.start);\n\n\t\t\t// Use fixed spacing so port gaps stay consistent across cards\n\t\t\t// even when one edge drives the card height beyond what the\n\t\t\t// other needs.\n\t\t\tlet tmpSpacing = tmpMinSpacing;\n\t\t\tlet tmpGroupHeight = tmpSpacing * (pTotal + 1);\n\t\t\tlet tmpSlack = tmpZoneHeight - tmpGroupHeight;\n\t\t\tif (tmpSlack < 0)\n\t\t\t{\n\t\t\t\ttmpSlack = 0;\n\t\t\t}\n\n\t\t\tlet tmpAlignOffset = 0;\n\t\t\tif (tmpAlignment === 'end')\n\t\t\t{\n\t\t\t\ttmpAlignOffset = tmpSlack;\n\t\t\t}\n\t\t\telse if (tmpAlignment === 'center')\n\t\t\t{\n\t\t\t\ttmpAlignOffset = tmpSlack / 2;\n\t\t\t}\n\n\t\t\tlet tmpY = tmpZoneStart + tmpAlignOffset + tmpSpacing * (pIndex + 1);\n\t\t\treturn { x: tmpX, y: tmpY };\n\t\t}\n\n\t\t// top or bottom\n\t\tlet tmpY = (tmpEdge === 'top') ? 0 : pHeight;\n\t\tlet tmpZoneStart = pWidth * tmpZone.start;\n\t\tlet tmpZoneWidth = pWidth * (tmpZone.end - tmpZone.start);\n\n\t\tlet tmpSpacing = tmpMinSpacing;\n\t\tlet tmpGroupWidth = tmpSpacing * (pTotal + 1);\n\t\tlet tmpSlack = tmpZoneWidth - tmpGroupWidth;\n\t\tif (tmpSlack < 0)\n\t\t{\n\t\t\ttmpSlack = 0;\n\t\t}\n\n\t\tlet tmpAlignOffset = 0;\n\t\tif (tmpAlignment === 'end')\n\t\t{\n\t\t\ttmpAlignOffset = tmpSlack;\n\t\t}\n\t\telse if (tmpAlignment === 'center')\n\t\t{\n\t\t\ttmpAlignOffset = tmpSlack / 2;\n\t\t}\n\n\t\tlet tmpX = tmpZoneStart + tmpAlignOffset + tmpSpacing * (pIndex + 1);\n\t\treturn { x: tmpX, y: tmpY };\n\t}\n\n\t/**\n\t * Get the zone fraction (start, end) for a Side value.\n\t *\n\t * Each edge is divided into three zones of equal size:\n\t * start: 0.0 — 0.333\n\t * middle: 0.333 — 0.667\n\t * end: 0.667 — 1.0\n\t *\n\t * Used as fallback when adaptive zones are not available.\n\t *\n\t * @param {string} pSide\n\t * @returns {{start: number, end: number}}\n\t */\n\t_getZoneFromSide(pSide)\n\t{\n\t\tswitch (pSide)\n\t\t{\n\t\t\t// Left edge: top, middle, bottom\n\t\t\tcase 'left-top': return { start: 0.0, end: 0.333 };\n\t\t\tcase 'left': return { start: 0.333, end: 0.667 };\n\t\t\tcase 'left-bottom': return { start: 0.667, end: 1.0 };\n\n\t\t\t// Right edge: top, middle, bottom\n\t\t\tcase 'right-top': return { start: 0.0, end: 0.333 };\n\t\t\tcase 'right': return { start: 0.333, end: 0.667 };\n\t\t\tcase 'right-bottom': return { start: 0.667, end: 1.0 };\n\n\t\t\t// Top edge: left, middle, right\n\t\t\tcase 'top-left': return { start: 0.0, end: 0.333 };\n\t\t\tcase 'top': return { start: 0.333, end: 0.667 };\n\t\t\tcase 'top-right': return { start: 0.667, end: 1.0 };\n\n\t\t\t// Bottom edge: left, middle, right\n\t\t\tcase 'bottom-left': return { start: 0.0, end: 0.333 };\n\t\t\tcase 'bottom': return { start: 0.333, end: 0.667 };\n\t\t\tcase 'bottom-right': return { start: 0.667, end: 1.0 };\n\n\t\t\t// Fallback: full range (legacy behavior)\n\t\t\tdefault: return { start: 0.0, end: 1.0 };\n\t\t}\n\t}\n\n\t/**\n\t * Get the three zone Side keys for a given edge, in order.\n\t *\n\t * @param {string} pEdge - 'left', 'right', 'top', or 'bottom'\n\t * @returns {Array<string>} Three Side keys in start-to-end order\n\t */\n\t_getZoneKeysForEdge(pEdge)\n\t{\n\t\tswitch (pEdge)\n\t\t{\n\t\t\tcase 'left': return ['left-top', 'left', 'left-bottom'];\n\t\t\tcase 'right': return ['right-top', 'right', 'right-bottom'];\n\t\t\tcase 'top': return ['top-left', 'top', 'top-right'];\n\t\t\tcase 'bottom': return ['bottom-left', 'bottom', 'bottom-right'];\n\t\t\tdefault: return ['right-top', 'right', 'right-bottom'];\n\t\t}\n\t}\n\n\t/**\n\t * Compute an adaptive zone fraction for a Side value based on the\n\t * actual port distribution across all zones on the same edge.\n\t *\n\t * Instead of fixed 1/3 splits, zones are sized proportionally to the\n\t * space each zone needs (minSpacing * (portCount + 1)). Zones with\n\t * zero ports collapse to zero, giving occupied zones more room.\n\t *\n\t * @param {string} pSide - The Side value to compute a zone for\n\t * @param {Object} pPortCountsBySide - Map of Side → number of ports\n\t * @returns {{start: number, end: number}}\n\t */\n\t_computeAdaptiveZone(pSide, pPortCountsBySide)\n\t{\n\t\tlet tmpEdge = this.getEdgeFromSide(pSide);\n\t\tlet tmpZoneKeys = this._getZoneKeysForEdge(tmpEdge);\n\n\t\tlet tmpMinSpacing = 16;\n\n\t\t// Compute the space each zone needs: minSpacing * (count + 1)\n\t\t// The +1 provides padding at both ends of the zone.\n\t\tlet tmpTotalSpace = 0;\n\t\tlet tmpSpaceByZone = {};\n\t\tfor (let i = 0; i < tmpZoneKeys.length; i++)\n\t\t{\n\t\t\tlet tmpKey = tmpZoneKeys[i];\n\t\t\tlet tmpCount = pPortCountsBySide[tmpKey] || 0;\n\t\t\tlet tmpSpace = (tmpCount > 0) ? (tmpMinSpacing * (tmpCount + 1)) : 0;\n\t\t\ttmpSpaceByZone[tmpKey] = tmpSpace;\n\t\t\ttmpTotalSpace += tmpSpace;\n\t\t}\n\n\t\t// If no ports on this edge at all, fall back to fixed zones\n\t\tif (tmpTotalSpace === 0)\n\t\t{\n\t\t\treturn this._getZoneFromSide(pSide);\n\t\t}\n\n\t\t// Compute proportional start/end for the requested zone\n\t\tlet tmpCumulativeStart = 0;\n\t\tfor (let i = 0; i < tmpZoneKeys.length; i++)\n\t\t{\n\t\t\tlet tmpKey = tmpZoneKeys[i];\n\t\t\tlet tmpFraction = tmpSpaceByZone[tmpKey] / tmpTotalSpace;\n\t\t\tif (tmpKey === pSide)\n\t\t\t{\n\t\t\t\treturn { start: tmpCumulativeStart, end: tmpCumulativeStart + tmpFraction };\n\t\t\t}\n\t\t\ttmpCumulativeStart += tmpFraction;\n\t\t}\n\n\t\t// Should not reach here; fall back to fixed zones\n\t\treturn this._getZoneFromSide(pSide);\n\t}\n\n\t/**\n\t * Build a map of Side → port count from an array of port objects.\n\t *\n\t * Convenience method for callers that need to pass port counts\n\t * to getPortLocalPosition or computeMinimumNodeHeight.\n\t *\n\t * @param {Array} pPorts - Array of port objects with Side, Direction\n\t * @returns {Object} Map of Side value → count\n\t */\n\tbuildPortCountsBySide(pPorts)\n\t{\n\t\tlet tmpCounts = {};\n\t\tif (!pPorts || !Array.isArray(pPorts))\n\t\t{\n\t\t\treturn tmpCounts;\n\t\t}\n\t\tfor (let i = 0; i < pPorts.length; i++)\n\t\t{\n\t\t\tlet tmpSide = pPorts[i].Side || (pPorts[i].Direction === 'input' ? 'left' : 'right');\n\t\t\tif (!tmpCounts[tmpSide])\n\t\t\t{\n\t\t\t\ttmpCounts[tmpSide] = 0;\n\t\t\t}\n\t\t\ttmpCounts[tmpSide]++;\n\t\t}\n\t\treturn tmpCounts;\n\t}\n\n\t/**\n\t * Compute the minimum node height required so that all ports\n\t * (with their badges) fit within the node boundary.\n\t *\n\t * Uses adaptive zone sizing: instead of assuming each zone gets\n\t * a fixed 1/3 of the body, sums the space needed by all occupied\n\t * zones on each left/right edge. This produces compact cards\n\t * whose height scales linearly with total port count.\n\t *\n\t * @param {Array} pPorts - Array of port objects with Side, Direction\n\t * @param {number} pTitleBarHeight - Height of the title bar\n\t * @returns {number} Minimum node height in pixels (0 if no ports)\n\t */\n\tcomputeMinimumNodeHeight(pPorts, pTitleBarHeight)\n\t{\n\t\tif (!pPorts || !Array.isArray(pPorts) || pPorts.length === 0)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\n\t\tlet tmpMinSpacing = 16;\n\t\tlet tmpBottomPad = 16;\n\n\t\t// Count ports per Side value\n\t\tlet tmpCountBySide = this.buildPortCountsBySide(pPorts);\n\n\t\t// Sum the space needed per edge (left, right) across all zones.\n\t\t// Each zone needs minSpacing * (count + 1) pixels.\n\t\tlet tmpSpacePerEdge = {};\n\t\tfor (let tmpSide in tmpCountBySide)\n\t\t{\n\t\t\tlet tmpEdge = this.getEdgeFromSide(tmpSide);\n\n\t\t\t// Only left/right edge zones affect required height\n\t\t\tif (tmpEdge !== 'left' && tmpEdge !== 'right')\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet tmpCount = tmpCountBySide[tmpSide];\n\t\t\tlet tmpZoneSpace = tmpMinSpacing * (tmpCount + 1);\n\n\t\t\tif (!tmpSpacePerEdge[tmpEdge])\n\t\t\t{\n\t\t\t\ttmpSpacePerEdge[tmpEdge] = 0;\n\t\t\t}\n\t\t\ttmpSpacePerEdge[tmpEdge] += tmpZoneSpace;\n\t\t}\n\n\t\t// The minimum height is titleBar + bottomPad + max edge space\n\t\tlet tmpMinHeight = 0;\n\t\tfor (let tmpEdge in tmpSpacePerEdge)\n\t\t{\n\t\t\tlet tmpRequired = pTitleBarHeight + tmpBottomPad + tmpSpacePerEdge[tmpEdge];\n\t\t\tif (tmpRequired > tmpMinHeight)\n\t\t\t{\n\t\t\t\ttmpMinHeight = tmpRequired;\n\t\t\t}\n\t\t}\n\n\t\treturn Math.ceil(tmpMinHeight);\n\t}\n}\n\nmodule.exports = PictProviderFlowGeometry;\n\n},{\"fable-serviceproviderbase\":4}],20:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-Icons\n *\n * Centralized SVG icon provider for the flow diagram.\n * All icons use a duotone style: 2px outline (#2c3e50) with\n * subtle filled accent shapes (#d5e8f7).\n *\n * Each icon is registered as a pict template with hash `Flow-Icon-{key}`,\n * making them individually overridable by consumers.\n */\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowIcons'\n};\n\n// ── Default Icon SVG Markup ────────────────────────────────────────────────\n// All icons: viewBox=\"0 0 24 24\", duotone style\n// Accent fill: #d5e8f7, Stroke: #2c3e50, Stroke-width: 2\n//\n// The {FlowIconSize} placeholder is replaced at render time with the\n// requested pixel size. Each template is a self-contained <svg> element.\n\nconst _DefaultIcons =\n{\n\t// ── FlowCard Icons ─────────────────────────────────────────────────────\n\n\t'ITE': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"6\" r=\"3\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><circle cx=\"6\" cy=\"18\" r=\"2.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><circle cx=\"18\" cy=\"18\" r=\"2.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 9v2M9.5 12.5L6 15.5M14.5 12.5L18 15.5\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'SW': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M3 12h5M16 12h5M14.8 9.2l3.7-5.2M14.8 14.8l3.7 5.2\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'EACH': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'FREAD': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-7-7z\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M13 2v7h7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M9 13h6M9 17h4\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'FWRITE': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-7-7z\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M13 2v7h7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 13v5M9.5 15.5L12 13l2.5 2.5\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'LOG': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><circle cx=\"7.5\" cy=\"8\" r=\"1\" fill=\"#2c3e50\"/><circle cx=\"7.5\" cy=\"12\" r=\"1\" fill=\"#2c3e50\"/><circle cx=\"7.5\" cy=\"16\" r=\"1\" fill=\"#2c3e50\"/><path d=\"M11 8h5.5M11 12h5.5M11 16h3.5\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'GET': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M21 21l-5.15-5.15\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'SET': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4.5 1.5L4 16Z\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M14 6l3 3\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t// ── UI Icons ───────────────────────────────────────────────────────────\n\n\t'fullscreen': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 3h6v6\"/><path d=\"M9 21H3v-6\"/><path d=\"M21 3l-7 7\"/><path d=\"M3 21l7-7\"/></svg>',\n\n\t'exit-fullscreen': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 14h6v6\"/><path d=\"M20 10h-6V4\"/><path d=\"M14 10l7-7\"/><path d=\"M3 21l7-7\"/></svg>',\n\n\t'close': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>',\n\n\t'chevron-down': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>',\n\n\t// ── Toolbar & Popup Icons ─────────────────────────────────────────────\n\n\t'search': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"7\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M21 21l-4.35-4.35\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'cards': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"7\" width=\"16\" height=\"12\" rx=\"2\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M6 7V5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-2\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'layout': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"8\" height=\"10\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"13\" y=\"3\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"3\" y=\"15\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"13\" y=\"11\" width=\"8\" height=\"10\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'collapse': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M8 12h8\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'expand': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 8v8M8 12h8\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'grip': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"#2c3e50\"><circle cx=\"9\" cy=\"5\" r=\"1.5\"/><circle cx=\"15\" cy=\"5\" r=\"1.5\"/><circle cx=\"9\" cy=\"12\" r=\"1.5\"/><circle cx=\"15\" cy=\"12\" r=\"1.5\"/><circle cx=\"9\" cy=\"19\" r=\"1.5\"/><circle cx=\"15\" cy=\"19\" r=\"1.5\"/></svg>',\n\n\t'settings': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\" fill=\"#d5e8f7\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"/></svg>',\n\n\t'plus': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 8v8M8 12h8\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'trash': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"5\" y=\"6\" width=\"14\" height=\"14\" rx=\"2\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M10 11v6M14 11v6\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'save': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M17 21v-8H7v8\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M7 3v5h8\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'auto-layout': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"8\" y=\"2\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"2\" y=\"16\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"14\" y=\"16\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 8v4M6 16v-4h12v4\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'zoom-in': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"7\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M21 21l-4.35-4.35\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M11 8v6M8 11h6\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'zoom-out': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"7\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M21 21l-4.35-4.35\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M8 11h6\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'zoom-fit': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 7V3h4\"/><path d=\"M17 3h4v4\"/><path d=\"M21 17v4h-4\"/><path d=\"M7 21H3v-4\"/><rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" rx=\"1.5\" fill=\"#d5e8f7\"/></svg>',\n\n\t'dock': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"4\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 20V11M8 14l4-4 4 4\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'restore': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 12a9 9 0 1 0 3-6.7L3 8\"/><path d=\"M3 3v5h5\"/></svg>',\n\n\t'delete-node': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"5\" y=\"6\" width=\"14\" height=\"14\" rx=\"2\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M10 11v6M14 11v6\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t// ── Fallback ───────────────────────────────────────────────────────────\n\n\t'default': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"4\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><circle cx=\"12\" cy=\"12\" r=\"2.5\" fill=\"#2c3e50\"/></svg>'\n};\n\nclass PictProviderFlowIcons extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowIcons';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Deep copy the default icons\n\t\tthis._Icons = JSON.parse(JSON.stringify(_DefaultIcons));\n\n\t\t// Merge any additional icons passed via options\n\t\tif (pOptions && pOptions.AdditionalIcons && typeof pOptions.AdditionalIcons === 'object')\n\t\t{\n\t\t\tlet tmpKeys = Object.keys(pOptions.AdditionalIcons);\n\t\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t\t{\n\t\t\t\tthis._Icons[tmpKeys[i]] = pOptions.AdditionalIcons[tmpKeys[i]];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Register all icons as pict templates with hash Flow-Icon-{key}.\n\t * Consumers can override any icon by registering a template with\n\t * the same hash before the flow view renders.\n\t */\n\tregisterIconTemplates()\n\t{\n\t\tif (!this.fable || !this.fable.TemplateProvider)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowIcons: TemplateProvider not available; icon templates not registered.');\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpKeys = Object.keys(this._Icons);\n\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t{\n\t\t\tlet tmpHash = 'Flow-Icon-' + tmpKeys[i];\n\n\t\t\t// Only register if not already present (allow consumer overrides)\n\t\t\tif (!this.fable.TemplateProvider.getTemplate(tmpHash))\n\t\t\t{\n\t\t\t\tthis.fable.TemplateProvider.addTemplate(tmpHash, this._Icons[tmpKeys[i]]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Determine if the given icon string is an emoji (legacy) or an icon key.\n\t * Returns true if the string contains characters with code points above U+00FF,\n\t * indicating emoji or Unicode symbol characters.\n\t *\n\t * @param {string} pIconValue - The icon value to check\n\t * @returns {boolean}\n\t */\n\tisEmojiIcon(pIconValue)\n\t{\n\t\tif (!pIconValue || typeof pIconValue !== 'string')\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (let i = 0; i < pIconValue.length; i++)\n\t\t{\n\t\t\tif (pIconValue.charCodeAt(i) > 255)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Resolve the icon key to use for a given CardMetadata object.\n\t * Tries Icon field first, then Code field, then falls back to 'default'.\n\t *\n\t * @param {Object} pCardMetadata - The CardMetadata object\n\t * @returns {string} The icon key\n\t */\n\tresolveIconKey(pCardMetadata)\n\t{\n\t\tif (!pCardMetadata)\n\t\t{\n\t\t\treturn 'default';\n\t\t}\n\n\t\t// If Icon is a known key, use it\n\t\tif (pCardMetadata.Icon && this._Icons.hasOwnProperty(pCardMetadata.Icon))\n\t\t{\n\t\t\treturn pCardMetadata.Icon;\n\t\t}\n\n\t\t// If Icon is a non-emoji string, check if it matches a registered template\n\t\tif (pCardMetadata.Icon && !this.isEmojiIcon(pCardMetadata.Icon))\n\t\t{\n\t\t\treturn pCardMetadata.Icon;\n\t\t}\n\n\t\t// Fall back to Code field\n\t\tif (pCardMetadata.Code && this._Icons.hasOwnProperty(pCardMetadata.Code))\n\t\t{\n\t\t\treturn pCardMetadata.Code;\n\t\t}\n\n\t\treturn 'default';\n\t}\n\n\t/**\n\t * Get the raw SVG markup string for a given icon key, with size applied.\n\t *\n\t * @param {string} pIconKey - The icon key\n\t * @param {number} pSize - Pixel size (default 16)\n\t * @returns {string} The SVG markup string\n\t */\n\tgetIconSVGMarkup(pIconKey, pSize)\n\t{\n\t\tlet tmpSize = pSize || 16;\n\t\tlet tmpKey = pIconKey || 'default';\n\t\tlet tmpMarkup = this._Icons[tmpKey] || this._Icons['default'];\n\n\t\t// Replace the size placeholder\n\t\treturn tmpMarkup.replace(/\\{FlowIconSize\\}/g, String(tmpSize));\n\t}\n\n\t/**\n\t * Render an icon into an SVG canvas context using createElementNS.\n\t * Creates a <g> element containing the icon paths, positioned at (pX, pY)\n\t * with the given size via a scale transform.\n\t *\n\t * @param {string} pIconKey - The icon key\n\t * @param {SVGElement} pParentGroup - The parent SVG group to append to\n\t * @param {number} pX - X position (top-left of icon bounding box)\n\t * @param {number} pY - Y position (top-left of icon bounding box)\n\t * @param {number} pSize - Pixel size (default 16)\n\t * @returns {SVGGElement|null} The created group element, or null on failure\n\t */\n\trenderIconIntoSVGGroup(pIconKey, pParentGroup, pX, pY, pSize)\n\t{\n\t\tif (!pParentGroup)\n\t\t{\n\t\t\treturn null;\n\t\t}\n\n\t\tlet tmpSize = pSize || 16;\n\t\tlet tmpScale = tmpSize / 24;\n\t\tlet tmpKey = pIconKey || 'default';\n\t\tlet tmpMarkup = this._Icons[tmpKey] || this._Icons['default'];\n\n\t\t// Replace size placeholder (for consistency, though we scale via transform)\n\t\ttmpMarkup = tmpMarkup.replace(/\\{FlowIconSize\\}/g, '24');\n\n\t\ttry\n\t\t{\n\t\t\t// Create a temporary SVG element to parse the icon markup\n\t\t\tlet tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\t\t\ttmpTempSVG.innerHTML = tmpMarkup;\n\n\t\t\t// Find the inner SVG (the icon's root <svg> element)\n\t\t\tlet tmpInnerSVG = tmpTempSVG.querySelector('svg');\n\t\t\tif (!tmpInnerSVG)\n\t\t\t{\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Create a group to hold the icon content\n\t\t\tlet tmpGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n\t\t\ttmpGroup.setAttribute('transform', 'translate(' + pX + ',' + pY + ') scale(' + tmpScale + ')');\n\t\t\ttmpGroup.setAttribute('pointer-events', 'none');\n\t\t\ttmpGroup.setAttribute('class', 'pict-flow-icon-svg');\n\n\t\t\t// Move all children from the parsed SVG into the group\n\t\t\twhile (tmpInnerSVG.childNodes.length > 0)\n\t\t\t{\n\t\t\t\ttmpGroup.appendChild(tmpInnerSVG.childNodes[0]);\n\t\t\t}\n\n\t\t\tpParentGroup.appendChild(tmpGroup);\n\t\t\treturn tmpGroup;\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowIcons renderIconIntoSVGGroup error: ' + pError.message);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get all registered icon keys.\n\t * @returns {Array<string>}\n\t */\n\tgetIconKeys()\n\t{\n\t\treturn Object.keys(this._Icons);\n\t}\n\n\t/**\n\t * Check if a given key has a registered icon.\n\t * @param {string} pIconKey\n\t * @returns {boolean}\n\t */\n\thasIcon(pIconKey)\n\t{\n\t\treturn this._Icons.hasOwnProperty(pIconKey);\n\t}\n\n\t/**\n\t * Register a new icon or override an existing one.\n\t * @param {string} pIconKey - The icon key\n\t * @param {string} pSVGMarkup - The SVG markup string (must contain {FlowIconSize} placeholders)\n\t * @returns {boolean}\n\t */\n\tregisterIcon(pIconKey, pSVGMarkup)\n\t{\n\t\tif (!pIconKey || !pSVGMarkup)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._Icons[pIconKey] = pSVGMarkup;\n\n\t\t// Also update the pict template if TemplateProvider is available\n\t\tif (this.fable && this.fable.TemplateProvider)\n\t\t{\n\t\t\tthis.fable.TemplateProvider.addTemplate('Flow-Icon-' + pIconKey, pSVGMarkup);\n\t\t}\n\n\t\treturn true;\n\t}\n}\n\nmodule.exports = PictProviderFlowIcons;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\nmodule.exports.DefaultIcons = _DefaultIcons;\n\n},{\"fable-serviceproviderbase\":4}],21:[function(require,module,exports){\nconst libPictProvider = require('pict-provider');\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowLayouts'\n};\n\n/**\n * Provider for managing saved flow diagram layouts.\n *\n * Layouts capture the spatial arrangement of nodes and panels on the canvas\n * without storing any card content. When a layout is restored, nodes that\n * still exist are placed at their saved positions and any new nodes are\n * auto-laid-out to the right.\n *\n * ## Persistence\n *\n * By default, layouts are persisted to the browser's `localStorage` using a\n * key derived from the flow view identifier. This means layouts survive page\n * refreshes out of the box.\n *\n * Developers can override the storage backend (e.g., to use a REST API or\n * IndexedDB) by replacing the three storage hook methods on the instance or\n * in a subclass:\n *\n * - `storageWrite(pLayouts, fCallback)` — persist the full layout array\n * - `storageRead(fCallback)` — load the persisted layout array\n * - `storageDelete(fCallback)` — remove all persisted layouts\n *\n * Each callback follows the Node convention: `fCallback(pError, pResult)`.\n *\n * Saved layout data structure:\n * {\n * Hash: \"layout-<UUID>\",\n * Name: \"My Layout\",\n * CreatedAt: \"2026-02-26T12:00:00.000Z\",\n * NodePositions: { \"node-hash\": { X, Y, Width, Height } },\n * PanelPositions: { \"node-hash\": { X, Y, Width, Height } },\n * ViewState: { PanX, PanY, Zoom }\n * }\n */\nclass PictProviderFlowLayouts extends libPictProvider\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowLayouts';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Storage key for localStorage persistence.\n\t\t// Defaults to a key derived from the FlowView identifier, or can be\n\t\t// set via options.StorageKey. Pass `false` to disable localStorage.\n\t\tif (pOptions && pOptions.StorageKey !== undefined)\n\t\t{\n\t\t\tthis._StorageKey = pOptions.StorageKey;\n\t\t}\n\t\telse if (this._FlowView && this._FlowView.options && this._FlowView.options.ViewIdentifier)\n\t\t{\n\t\t\tthis._StorageKey = `pict-flow-layouts-${this._FlowView.options.ViewIdentifier}`;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._StorageKey = 'pict-flow-layouts';\n\t\t}\n\t}\n\n\t// ── Storage Hooks ─────────────────────────────────────────────────────\n\t// These three methods form the persistence contract. The default\n\t// implementation uses localStorage. Override them on the instance or\n\t// subclass to use a REST API, IndexedDB, or any other backend.\n\t//\n\t// All callbacks follow `fCallback(pError, pResult)`.\n\n\t/**\n\t * Persist the full array of layout objects.\n\t *\n\t * Default implementation writes JSON to `localStorage`.\n\t *\n\t * @param {Array} pLayouts - The array of layout objects to persist\n\t * @param {Function} fCallback - `function(pError)` called when done\n\t */\n\tstorageWrite(pLayouts, fCallback)\n\t{\n\t\tif (this._StorageKey === false)\n\t\t{\n\t\t\treturn fCallback(null);\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (typeof localStorage !== 'undefined')\n\t\t\t{\n\t\t\t\tlocalStorage.setItem(this._StorageKey, JSON.stringify(pLayouts));\n\t\t\t}\n\t\t\treturn fCallback(null);\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts storageWrite error: ${pError.message}`);\n\t\t\treturn fCallback(pError);\n\t\t}\n\t}\n\n\t/**\n\t * Load the persisted array of layout objects.\n\t *\n\t * Default implementation reads JSON from `localStorage`.\n\t *\n\t * @param {Function} fCallback - `function(pError, pLayouts)` where\n\t * pLayouts is an Array (or null/empty if nothing stored)\n\t */\n\tstorageRead(fCallback)\n\t{\n\t\tif (this._StorageKey === false)\n\t\t{\n\t\t\treturn fCallback(null, []);\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (typeof localStorage !== 'undefined')\n\t\t\t{\n\t\t\t\tlet tmpRaw = localStorage.getItem(this._StorageKey);\n\t\t\t\tif (tmpRaw)\n\t\t\t\t{\n\t\t\t\t\tlet tmpParsed = JSON.parse(tmpRaw);\n\t\t\t\t\tif (Array.isArray(tmpParsed))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn fCallback(null, tmpParsed);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fCallback(null, []);\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts storageRead error: ${pError.message}`);\n\t\t\treturn fCallback(pError, []);\n\t\t}\n\t}\n\n\t/**\n\t * Remove all persisted layout data.\n\t *\n\t * Default implementation removes the key from `localStorage`.\n\t *\n\t * @param {Function} fCallback - `function(pError)` called when done\n\t */\n\tstorageDelete(fCallback)\n\t{\n\t\tif (this._StorageKey === false)\n\t\t{\n\t\t\treturn fCallback(null);\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (typeof localStorage !== 'undefined')\n\t\t\t{\n\t\t\t\tlocalStorage.removeItem(this._StorageKey);\n\t\t\t}\n\t\t\treturn fCallback(null);\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts storageDelete error: ${pError.message}`);\n\t\t\treturn fCallback(pError);\n\t\t}\n\t}\n\n\t// ── Initialization ────────────────────────────────────────────────────\n\n\t/**\n\t * Load persisted layouts and merge them into _FlowData.SavedLayouts.\n\t * Layouts already present in _FlowData (e.g., from setFlowData) are\n\t * kept; persisted layouts with new hashes are appended.\n\t *\n\t * Call this after _FlowData is populated.\n\t */\n\tloadPersistedLayouts()\n\t{\n\t\tthis.storageRead((pError, pLayouts) =>\n\t\t{\n\t\t\tif (pError || !Array.isArray(pLayouts) || pLayouts.length === 0)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!this._FlowView || !this._FlowView._FlowData)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet tmpExisting = this._FlowView._FlowData.SavedLayouts;\n\t\t\tlet tmpExistingHashes = {};\n\t\t\tfor (let i = 0; i < tmpExisting.length; i++)\n\t\t\t{\n\t\t\t\ttmpExistingHashes[tmpExisting[i].Hash] = true;\n\t\t\t}\n\n\t\t\tlet tmpAdded = 0;\n\t\t\tfor (let i = 0; i < pLayouts.length; i++)\n\t\t\t{\n\t\t\t\tif (!tmpExistingHashes[pLayouts[i].Hash])\n\t\t\t\t{\n\t\t\t\t\ttmpExisting.push(pLayouts[i]);\n\t\t\t\t\ttmpAdded++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tmpAdded > 0)\n\t\t\t{\n\t\t\t\tthis.log.trace(`PictProviderFlowLayouts loaded ${tmpAdded} persisted layout(s)`);\n\t\t\t}\n\t\t});\n\t}\n\n\t// ── Public API ────────────────────────────────────────────────────────\n\n\t/**\n\t * Save the current node and panel positions as a named layout.\n\t * @param {string} pName - The display name for this layout\n\t * @returns {Object} The saved layout entry\n\t */\n\tsaveLayout(pName)\n\t{\n\t\tif (!this._FlowView)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowLayouts saveLayout: no FlowView reference');\n\t\t\treturn null;\n\t\t}\n\n\t\tlet tmpFlowData = this._FlowView._FlowData;\n\t\tlet tmpLayoutHash = `layout-${this.fable.getUUID()}`;\n\t\tlet tmpNodePositions = {};\n\t\tlet tmpPanelPositions = {};\n\n\t\t// Capture node positions and per-instance overrides (Title, Style)\n\t\tfor (let i = 0; i < tmpFlowData.Nodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = tmpFlowData.Nodes[i];\n\t\t\ttmpNodePositions[tmpNode.Hash] =\n\t\t\t{\n\t\t\t\tX: tmpNode.X,\n\t\t\t\tY: tmpNode.Y,\n\t\t\t\tWidth: tmpNode.Width,\n\t\t\t\tHeight: tmpNode.Height,\n\t\t\t\tTitle: tmpNode.Title\n\t\t\t};\n\n\t\t\t// Only include Style if it has been customized\n\t\t\tif (tmpNode.Style && Object.keys(tmpNode.Style).length > 0)\n\t\t\t{\n\t\t\t\ttmpNodePositions[tmpNode.Hash].Style = JSON.parse(JSON.stringify(tmpNode.Style));\n\t\t\t}\n\t\t}\n\n\t\t// Capture panel positions keyed by NodeHash (panels get new hashes on each open)\n\t\tfor (let i = 0; i < tmpFlowData.OpenPanels.length; i++)\n\t\t{\n\t\t\tlet tmpPanel = tmpFlowData.OpenPanels[i];\n\t\t\ttmpPanelPositions[tmpPanel.NodeHash] =\n\t\t\t{\n\t\t\t\tX: tmpPanel.X,\n\t\t\t\tY: tmpPanel.Y,\n\t\t\t\tWidth: tmpPanel.Width,\n\t\t\t\tHeight: tmpPanel.Height\n\t\t\t};\n\t\t}\n\n\t\tlet tmpLayout =\n\t\t{\n\t\t\tHash: tmpLayoutHash,\n\t\t\tName: pName || 'Untitled Layout',\n\t\t\tCreatedAt: new Date().toISOString(),\n\t\t\tNodePositions: tmpNodePositions,\n\t\t\tPanelPositions: tmpPanelPositions,\n\t\t\tViewState:\n\t\t\t{\n\t\t\t\tPanX: tmpFlowData.ViewState.PanX,\n\t\t\t\tPanY: tmpFlowData.ViewState.PanY,\n\t\t\t\tZoom: tmpFlowData.ViewState.Zoom\n\t\t\t}\n\t\t};\n\n\t\ttmpFlowData.SavedLayouts.push(tmpLayout);\n\t\tthis._FlowView.marshalFromView();\n\n\t\t// Persist to storage\n\t\tthis.storageWrite(tmpFlowData.SavedLayouts, (pError) =>\n\t\t{\n\t\t\tif (pError)\n\t\t\t{\n\t\t\t\tthis.log.warn(`PictProviderFlowLayouts: failed to persist after save: ${pError.message}`);\n\t\t\t}\n\t\t});\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onLayoutSaved', tmpLayout);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);\n\t\t}\n\n\t\tthis.log.trace(`PictProviderFlowLayouts saved layout '${tmpLayout.Name}' (${tmpLayout.Hash})`);\n\n\t\treturn tmpLayout;\n\t}\n\n\t/**\n\t * Restore a saved layout by hash.\n\t * Nodes present in the saved layout are placed at their saved positions.\n\t * Nodes not in the saved layout are auto-laid-out to the right of the\n\t * positioned nodes.\n\t * @param {string} pLayoutHash - The hash of the layout to restore\n\t * @returns {boolean} Whether the layout was restored\n\t */\n\trestoreLayout(pLayoutHash)\n\t{\n\t\tif (!this._FlowView)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowLayouts restoreLayout: no FlowView reference');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpFlowData = this._FlowView._FlowData;\n\t\tlet tmpLayout = tmpFlowData.SavedLayouts.find(\n\t\t\t(pLayout) => pLayout.Hash === pLayoutHash\n\t\t);\n\n\t\tif (!tmpLayout)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts restoreLayout: layout '${pLayoutHash}' not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpMatchedNodes = [];\n\t\tlet tmpUnmatchedNodes = [];\n\n\t\t// Apply saved positions to matched nodes; collect unmatched ones\n\t\tfor (let i = 0; i < tmpFlowData.Nodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = tmpFlowData.Nodes[i];\n\t\t\tlet tmpSaved = tmpLayout.NodePositions[tmpNode.Hash];\n\n\t\t\tif (tmpSaved)\n\t\t\t{\n\t\t\t\ttmpNode.X = tmpSaved.X;\n\t\t\t\ttmpNode.Y = tmpSaved.Y;\n\t\t\t\tif (typeof tmpSaved.Width === 'number') tmpNode.Width = tmpSaved.Width;\n\t\t\t\tif (typeof tmpSaved.Height === 'number') tmpNode.Height = tmpSaved.Height;\n\t\t\t\tif (typeof tmpSaved.Title === 'string') tmpNode.Title = tmpSaved.Title;\n\t\t\t\tif (tmpSaved.Style && typeof tmpSaved.Style === 'object')\n\t\t\t\t{\n\t\t\t\t\ttmpNode.Style = JSON.parse(JSON.stringify(tmpSaved.Style));\n\t\t\t\t}\n\t\t\t\ttmpMatchedNodes.push(tmpNode);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpUnmatchedNodes.push(tmpNode);\n\t\t\t}\n\t\t}\n\n\t\t// Apply saved panel positions (keyed by NodeHash)\n\t\tif (tmpLayout.PanelPositions)\n\t\t{\n\t\t\tfor (let i = 0; i < tmpFlowData.OpenPanels.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPanel = tmpFlowData.OpenPanels[i];\n\t\t\t\tlet tmpSavedPanel = tmpLayout.PanelPositions[tmpPanel.NodeHash];\n\n\t\t\t\tif (tmpSavedPanel)\n\t\t\t\t{\n\t\t\t\t\ttmpPanel.X = tmpSavedPanel.X;\n\t\t\t\t\ttmpPanel.Y = tmpSavedPanel.Y;\n\t\t\t\t\tif (typeof tmpSavedPanel.Width === 'number') tmpPanel.Width = tmpSavedPanel.Width;\n\t\t\t\t\tif (typeof tmpSavedPanel.Height === 'number') tmpPanel.Height = tmpSavedPanel.Height;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Auto-layout unmatched nodes to the right of positioned nodes\n\t\tif (tmpUnmatchedNodes.length > 0 && this._FlowView._LayoutService)\n\t\t{\n\t\t\tthis._FlowView._LayoutService.autoLayoutSubset(\n\t\t\t\ttmpUnmatchedNodes,\n\t\t\t\ttmpMatchedNodes,\n\t\t\t\ttmpFlowData.Connections\n\t\t\t);\n\t\t}\n\n\t\t// Restore view state (camera position)\n\t\tif (tmpLayout.ViewState)\n\t\t{\n\t\t\tif (typeof tmpLayout.ViewState.PanX === 'number')\n\t\t\t{\n\t\t\t\ttmpFlowData.ViewState.PanX = tmpLayout.ViewState.PanX;\n\t\t\t}\n\t\t\tif (typeof tmpLayout.ViewState.PanY === 'number')\n\t\t\t{\n\t\t\t\ttmpFlowData.ViewState.PanY = tmpLayout.ViewState.PanY;\n\t\t\t}\n\t\t\tif (typeof tmpLayout.ViewState.Zoom === 'number')\n\t\t\t{\n\t\t\t\ttmpFlowData.ViewState.Zoom = tmpLayout.ViewState.Zoom;\n\t\t\t}\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onLayoutRestored', tmpLayout);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);\n\t\t}\n\n\t\tthis.log.trace(`PictProviderFlowLayouts restored layout '${tmpLayout.Name}' (${tmpLayout.Hash})`);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delete a saved layout by hash.\n\t * @param {string} pLayoutHash - The hash of the layout to delete\n\t * @returns {boolean} Whether the layout was deleted\n\t */\n\tdeleteLayout(pLayoutHash)\n\t{\n\t\tif (!this._FlowView)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowLayouts deleteLayout: no FlowView reference');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpFlowData = this._FlowView._FlowData;\n\t\tlet tmpIndex = tmpFlowData.SavedLayouts.findIndex(\n\t\t\t(pLayout) => pLayout.Hash === pLayoutHash\n\t\t);\n\n\t\tif (tmpIndex < 0)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts deleteLayout: layout '${pLayoutHash}' not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpRemovedLayout = tmpFlowData.SavedLayouts.splice(tmpIndex, 1)[0];\n\t\tthis._FlowView.marshalFromView();\n\n\t\t// Persist to storage (with the layout removed)\n\t\tthis.storageWrite(tmpFlowData.SavedLayouts, (pError) =>\n\t\t{\n\t\t\tif (pError)\n\t\t\t{\n\t\t\t\tthis.log.warn(`PictProviderFlowLayouts: failed to persist after delete: ${pError.message}`);\n\t\t\t}\n\t\t});\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onLayoutDeleted', tmpRemovedLayout);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);\n\t\t}\n\n\t\tthis.log.trace(`PictProviderFlowLayouts deleted layout '${tmpRemovedLayout.Name}' (${tmpRemovedLayout.Hash})`);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get the list of saved layouts.\n\t * @returns {Array} Array of saved layout objects\n\t */\n\tgetLayouts()\n\t{\n\t\tif (!this._FlowView) return [];\n\t\treturn this._FlowView._FlowData.SavedLayouts;\n\t}\n\n\t/**\n\t * Get a specific saved layout by hash.\n\t * @param {string} pLayoutHash\n\t * @returns {Object|null}\n\t */\n\tgetLayout(pLayoutHash)\n\t{\n\t\tif (!this._FlowView) return null;\n\t\treturn this._FlowView._FlowData.SavedLayouts.find(\n\t\t\t(pLayout) => pLayout.Hash === pLayoutHash\n\t\t) || null;\n\t}\n}\n\nmodule.exports = PictProviderFlowLayouts;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n\n},{\"pict-provider\":8}],22:[function(require,module,exports){\nconst libPictProvider = require('pict-provider');\n\nconst _DefaultNodeTypes =\n{\n\t'default':\n\t{\n\t\tHash: 'default',\n\t\tLabel: 'Default',\n\t\tDefaultWidth: 180,\n\t\tDefaultHeight: 80,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' },\n\t\t\t{ Hash: null, Direction: 'output', Side: 'right', Label: 'Out' }\n\t\t],\n\t\tTitleBarColor: '#2c3e50',\n\t\tBodyStyle: {}\n\t},\n\t'start':\n\t{\n\t\tHash: 'start',\n\t\tLabel: 'Start',\n\t\tDefaultWidth: 140,\n\t\tDefaultHeight: 80,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'output', Side: 'right', Label: 'Out' }\n\t\t],\n\t\tTitleBarColor: '#27ae60',\n\t\tBodyStyle:\n\t\t{\n\t\t\t'fill': '#eafaf1',\n\t\t\t'stroke': '#27ae60'\n\t\t}\n\t},\n\t'end':\n\t{\n\t\tHash: 'end',\n\t\tLabel: 'End',\n\t\tDefaultWidth: 140,\n\t\tDefaultHeight: 80,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' }\n\t\t],\n\t\tTitleBarColor: '#1abc9c',\n\t\tBodyStyle:\n\t\t{\n\t\t\t'fill': '#e8f8f5',\n\t\t\t'stroke': '#1abc9c'\n\t\t}\n\t},\n\t'halt':\n\t{\n\t\tHash: 'halt',\n\t\tLabel: 'Halt',\n\t\tDefaultWidth: 140,\n\t\tDefaultHeight: 80,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' }\n\t\t],\n\t\tTitleBarColor: '#e74c3c',\n\t\tBodyStyle:\n\t\t{\n\t\t\t'fill': '#fdedec',\n\t\t\t'stroke': '#e74c3c'\n\t\t}\n\t},\n\t'decision':\n\t{\n\t\tHash: 'decision',\n\t\tLabel: 'Decision',\n\t\tDefaultWidth: 200,\n\t\tDefaultHeight: 100,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' },\n\t\t\t{ Hash: null, Direction: 'output', Side: 'right', Label: 'Yes' },\n\t\t\t{ Hash: null, Direction: 'output', Side: 'bottom', Label: 'No' }\n\t\t],\n\t\tTitleBarColor: '#f39c12',\n\t\tBodyStyle:\n\t\t{\n\t\t\t'fill': '#fff9e6',\n\t\t\t'stroke': '#f39c12'\n\t\t}\n\t}\n};\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowNodeTypes'\n};\n\nclass PictProviderFlowNodeTypes extends libPictProvider\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowNodeTypes';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Initialize with default node types unless explicitly disabled\n\t\tif (pOptions && pOptions.IncludeDefaultNodeTypes === false)\n\t\t{\n\t\t\tthis._NodeTypes = {};\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._NodeTypes = JSON.parse(JSON.stringify(_DefaultNodeTypes));\n\t\t}\n\n\t\t// Merge any additional node types passed in via options\n\t\tif (pOptions && pOptions.AdditionalNodeTypes && typeof pOptions.AdditionalNodeTypes === 'object')\n\t\t{\n\t\t\tlet tmpAdditionalKeys = Object.keys(pOptions.AdditionalNodeTypes);\n\t\t\tfor (let i = 0; i < tmpAdditionalKeys.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpOriginal = pOptions.AdditionalNodeTypes[tmpAdditionalKeys[i]];\n\t\t\t\tthis._NodeTypes[tmpAdditionalKeys[i]] = Object.assign(\n\t\t\t\t\t{},\n\t\t\t\t\tthis._NodeTypes[tmpAdditionalKeys[i]] || {},\n\t\t\t\t\tJSON.parse(JSON.stringify(tmpOriginal))\n\t\t\t\t);\n\t\t\t\t// Preserve BodyContent.RenderCallback (functions are stripped by JSON serialization)\n\t\t\t\tif (tmpOriginal.BodyContent && typeof tmpOriginal.BodyContent.RenderCallback === 'function')\n\t\t\t\t{\n\t\t\t\t\tif (!this._NodeTypes[tmpAdditionalKeys[i]].BodyContent)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._NodeTypes[tmpAdditionalKeys[i]].BodyContent = {};\n\t\t\t\t\t}\n\t\t\t\t\tthis._NodeTypes[tmpAdditionalKeys[i]].BodyContent.RenderCallback = tmpOriginal.BodyContent.RenderCallback;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get a node type configuration by hash\n\t * @param {string} pTypeHash - The node type hash\n\t * @returns {Object|null} The node type configuration\n\t */\n\tgetNodeType(pTypeHash)\n\t{\n\t\treturn this._NodeTypes[pTypeHash] || this._NodeTypes['default'];\n\t}\n\n\t/**\n\t * Register a new node type or override an existing one\n\t * @param {Object} pNodeTypeConfig - The node type configuration\n\t * @returns {boolean}\n\t */\n\tregisterNodeType(pNodeTypeConfig)\n\t{\n\t\tif (!pNodeTypeConfig || !pNodeTypeConfig.Hash)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowNodeTypes registerNodeType: invalid config (missing Hash)');\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._NodeTypes[pNodeTypeConfig.Hash] = Object.assign(\n\t\t\t{},\n\t\t\tthis._NodeTypes[pNodeTypeConfig.Hash] || {},\n\t\t\tpNodeTypeConfig\n\t\t);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Remove a node type\n\t * @param {string} pTypeHash\n\t * @returns {boolean}\n\t */\n\tremoveNodeType(pTypeHash)\n\t{\n\t\tif (pTypeHash === 'default')\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowNodeTypes: cannot remove the default node type');\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this._NodeTypes[pTypeHash])\n\t\t{\n\t\t\tdelete this._NodeTypes[pTypeHash];\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Get all registered node types\n\t * @returns {Object} Map of type hash to type configuration\n\t */\n\tgetNodeTypes()\n\t{\n\t\treturn JSON.parse(JSON.stringify(this._NodeTypes));\n\t}\n\n\t/**\n\t * Get a list of node type hashes\n\t * @returns {Array<string>}\n\t */\n\tgetNodeTypeList()\n\t{\n\t\treturn Object.keys(this._NodeTypes);\n\t}\n\n\t/**\n\t * Get all enabled node types that have FlowCard metadata.\n\t * Returns only types that are cards and whose Enabled flag is true.\n\t * @returns {Array<Object>} Array of node type configurations\n\t */\n\tgetEnabledCards()\n\t{\n\t\tlet tmpCards = [];\n\t\tlet tmpKeys = Object.keys(this._NodeTypes);\n\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t{\n\t\t\tlet tmpType = this._NodeTypes[tmpKeys[i]];\n\t\t\tif (tmpType.CardMetadata)\n\t\t\t{\n\t\t\t\tif (tmpType.CardMetadata.Enabled !== false)\n\t\t\t\t{\n\t\t\t\t\ttmpCards.push(JSON.parse(JSON.stringify(tmpType)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn tmpCards;\n\t}\n\n\t/**\n\t * Get all enabled cards grouped by category.\n\t * @returns {Object} Map of category name to array of node type configurations\n\t */\n\tgetCardsByCategory()\n\t{\n\t\tlet tmpCards = this.getEnabledCards();\n\t\tlet tmpCategories = {};\n\t\tfor (let i = 0; i < tmpCards.length; i++)\n\t\t{\n\t\t\tlet tmpCategory = (tmpCards[i].CardMetadata && tmpCards[i].CardMetadata.Category)\n\t\t\t\t? tmpCards[i].CardMetadata.Category\n\t\t\t\t: 'General';\n\t\t\tif (!tmpCategories[tmpCategory])\n\t\t\t{\n\t\t\t\ttmpCategories[tmpCategory] = [];\n\t\t\t}\n\t\t\ttmpCategories[tmpCategory].push(tmpCards[i]);\n\t\t}\n\t\treturn tmpCategories;\n\t}\n\n\t/**\n\t * Check whether a node type hash refers to a FlowCard (has CardMetadata).\n\t * @param {string} pTypeHash\n\t * @returns {boolean}\n\t */\n\tisFlowCard(pTypeHash)\n\t{\n\t\tlet tmpType = this._NodeTypes[pTypeHash];\n\t\treturn !!(tmpType && tmpType.CardMetadata);\n\t}\n}\n\nmodule.exports = PictProviderFlowNodeTypes;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\nmodule.exports.DefaultNodeTypes = _DefaultNodeTypes;\n\n},{\"pict-provider\":8}],23:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowNoise'\n};\n\n/**\n * PictProvider-Flow-Noise\n *\n * Deterministic noise/jitter generator for hand-drawn visual effects.\n *\n * Uses seeded pseudo-random number generation so that the same node or\n * connection always receives the same jitter values across re-renders,\n * preventing visual \"jumping\" while still looking organic.\n */\nclass PictProviderFlowNoise extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowNoise';\n\t}\n\n\t// ── Hashing / PRNG ────────────────────────────────────────────────────\n\n\t/**\n\t * Convert a string to a 32-bit integer hash (djb2 algorithm).\n\t * @param {string} pStr\n\t * @returns {number}\n\t */\n\thashString(pStr)\n\t{\n\t\tlet tmpHash = 5381;\n\t\tfor (let i = 0; i < pStr.length; i++)\n\t\t{\n\t\t\ttmpHash = ((tmpHash << 5) + tmpHash) + pStr.charCodeAt(i);\n\t\t\ttmpHash = tmpHash & tmpHash; // Convert to 32-bit integer\n\t\t}\n\t\treturn tmpHash >>> 0; // Ensure unsigned\n\t}\n\n\t/**\n\t * Create a seeded pseudo-random number generator (Mulberry32).\n\t * Returns a function that produces deterministic floats in [0, 1).\n\t * @param {number} pSeed - 32-bit integer seed\n\t * @returns {Function}\n\t */\n\tseededRandom(pSeed)\n\t{\n\t\tlet tmpSeed = pSeed | 0;\n\t\treturn function()\n\t\t{\n\t\t\ttmpSeed = tmpSeed + 0x6D2B79F5 | 0;\n\t\t\tlet t = Math.imul(tmpSeed ^ tmpSeed >>> 15, 1 | tmpSeed);\n\t\t\tt = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;\n\t\t\treturn ((t ^ t >>> 14) >>> 0) / 4294967296;\n\t\t};\n\t}\n\n\t// ── Point Jitter ──────────────────────────────────────────────────────\n\n\t/**\n\t * Apply random jitter to a point.\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {number} pAmplitude - Maximum offset in pixels\n\t * @param {Function} pRNG - Seeded random function\n\t * @returns {{x: number, y: number}}\n\t */\n\tjitterPoint(pX, pY, pAmplitude, pRNG)\n\t{\n\t\tif (pAmplitude <= 0)\n\t\t{\n\t\t\treturn { x: pX, y: pY };\n\t\t}\n\t\treturn {\n\t\t\tx: pX + pAmplitude * (pRNG() - 0.5) * 2,\n\t\t\ty: pY + pAmplitude * (pRNG() - 0.5) * 2\n\t\t};\n\t}\n\n\t// ── Bracket Path Generation ───────────────────────────────────────────\n\n\t/**\n\t * Generate an SVG path `d` string for a bracket-shaped node border.\n\t *\n\t * Draws true bracket shapes — `[` on the left and `]` on the right —\n\t * with NO top/bottom connecting lines. The serifs (horizontal turns)\n\t * extend inward from each corner, giving a distinctive hand-drawn\n\t * technical-diagram look that is immediately distinguishable from a\n\t * regular rectangle at any zoom level.\n\t *\n\t * The bracket consists of:\n\t * - Left bracket `[`: top serif → vertical left side → bottom serif\n\t * - Right bracket `]`: top serif → vertical right side → bottom serif\n\t * - Optional title divider line across the full width\n\t *\n\t * @param {number} pWidth - Node width\n\t * @param {number} pHeight - Node height\n\t * @param {number} pSerifLength - Length of corner serifs in px\n\t * @param {number} pTitleBarHeight - Height of title bar (0 to skip divider)\n\t * @param {number} pAmplitude - Noise amplitude (0 = precise)\n\t * @param {string} pSeedString - Hash string for deterministic noise\n\t * @returns {string} SVG path d attribute\n\t */\n\tgenerateBracketPath(pWidth, pHeight, pSerifLength, pTitleBarHeight, pAmplitude, pSeedString)\n\t{\n\t\tlet tmpRNG = this.seededRandom(this.hashString(pSeedString || 'default'));\n\t\tlet tmpS = pSerifLength || 18;\n\t\tlet tmpW = pWidth;\n\t\tlet tmpH = pHeight;\n\n\t\tlet tmpJ = (pX, pY) =>\n\t\t{\n\t\t\treturn this.jitterPoint(pX, pY, pAmplitude, tmpRNG);\n\t\t};\n\n\t\t// Left bracket `[`: top serif → down left side → bottom serif\n\t\tlet tmpTL_serif = tmpJ(tmpS, 0);\n\t\tlet tmpTL_corner = tmpJ(0, 0);\n\t\tlet tmpBL_corner = tmpJ(0, tmpH);\n\t\tlet tmpBL_serif = tmpJ(tmpS, tmpH);\n\n\t\t// Right bracket `]`: top serif → down right side → bottom serif\n\t\tlet tmpTR_serif = tmpJ(tmpW - tmpS, 0);\n\t\tlet tmpTR_corner = tmpJ(tmpW, 0);\n\t\tlet tmpBR_corner = tmpJ(tmpW, tmpH);\n\t\tlet tmpBR_serif = tmpJ(tmpW - tmpS, tmpH);\n\n\t\tlet tmpPath = '';\n\n\t\t// Left bracket `[`\n\t\ttmpPath += `M ${tmpTL_serif.x.toFixed(1)} ${tmpTL_serif.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpTL_corner.x.toFixed(1)} ${tmpTL_corner.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpBL_corner.x.toFixed(1)} ${tmpBL_corner.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpBL_serif.x.toFixed(1)} ${tmpBL_serif.y.toFixed(1)}`;\n\n\t\t// Right bracket `]`\n\t\ttmpPath += ` M ${tmpTR_serif.x.toFixed(1)} ${tmpTR_serif.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpTR_corner.x.toFixed(1)} ${tmpTR_corner.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpBR_corner.x.toFixed(1)} ${tmpBR_corner.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpBR_serif.x.toFixed(1)} ${tmpBR_serif.y.toFixed(1)}`;\n\n\t\t// No horizontal lines — the title bar fill rect provides visual\n\t\t// separation via its background color. The bracket outline is\n\t\t// purely the `[` and `]` shapes on the sides.\n\n\t\treturn tmpPath;\n\t}\n\n\t// ── Path Jitter (for connections) ─────────────────────────────────────\n\n\t/**\n\t * Apply jitter to an existing SVG path string by offsetting coordinate\n\t * pairs. The first M and last coordinate pair receive reduced jitter\n\t * to keep connections aligned with their port anchors.\n\t *\n\t * @param {string} pPathString - SVG path d attribute\n\t * @param {number} pAmplitude - Noise amplitude (0 = no change)\n\t * @param {string} pSeedString - Hash string for deterministic noise\n\t * @returns {string} Modified path string\n\t */\n\tjitterPath(pPathString, pAmplitude, pSeedString)\n\t{\n\t\tif (pAmplitude <= 0 || !pPathString)\n\t\t{\n\t\t\treturn pPathString;\n\t\t}\n\n\t\tlet tmpRNG = this.seededRandom(this.hashString(pSeedString || 'path'));\n\n\t\t// Parse path into tokens: commands and numbers\n\t\tlet tmpTokens = pPathString.match(/[MLCQZmlcqz]|[-+]?[0-9]*\\.?[0-9]+/g);\n\t\tif (!tmpTokens)\n\t\t{\n\t\t\treturn pPathString;\n\t\t}\n\n\t\t// Collect all numeric coordinate indices\n\t\tlet tmpNumericIndices = [];\n\t\tfor (let i = 0; i < tmpTokens.length; i++)\n\t\t{\n\t\t\tif (/^[-+]?[0-9]*\\.?[0-9]+$/.test(tmpTokens[i]))\n\t\t\t{\n\t\t\t\ttmpNumericIndices.push(i);\n\t\t\t}\n\t\t}\n\n\t\t// Process pairs of coordinates (x, y)\n\t\tfor (let i = 0; i < tmpNumericIndices.length - 1; i += 2)\n\t\t{\n\t\t\tlet tmpXIdx = tmpNumericIndices[i];\n\t\t\tlet tmpYIdx = tmpNumericIndices[i + 1];\n\n\t\t\t// Reduce jitter for first and last coordinate pairs (port anchors)\n\t\t\tlet tmpLocalAmplitude = pAmplitude;\n\t\t\tif (i === 0 || i >= tmpNumericIndices.length - 2)\n\t\t\t{\n\t\t\t\ttmpLocalAmplitude = pAmplitude * 0.15; // Minimal anchor jitter\n\t\t\t}\n\t\t\telse if (i === 2 || i >= tmpNumericIndices.length - 4)\n\t\t\t{\n\t\t\t\ttmpLocalAmplitude = pAmplitude * 0.5; // Reduced near anchors\n\t\t\t}\n\n\t\t\tlet tmpX = parseFloat(tmpTokens[tmpXIdx]);\n\t\t\tlet tmpY = parseFloat(tmpTokens[tmpYIdx]);\n\t\t\tlet tmpJittered = this.jitterPoint(tmpX, tmpY, tmpLocalAmplitude, tmpRNG);\n\n\t\t\ttmpTokens[tmpXIdx] = tmpJittered.x.toFixed(1);\n\t\t\ttmpTokens[tmpYIdx] = tmpJittered.y.toFixed(1);\n\t\t}\n\n\t\t// Reassemble path string with spaces\n\t\tlet tmpResult = '';\n\t\tfor (let i = 0; i < tmpTokens.length; i++)\n\t\t{\n\t\t\tif (i > 0 && /^[MLCQZmlcqz]$/.test(tmpTokens[i]))\n\t\t\t{\n\t\t\t\ttmpResult += ' ' + tmpTokens[i];\n\t\t\t}\n\t\t\telse if (i > 0)\n\t\t\t{\n\t\t\t\ttmpResult += ' ' + tmpTokens[i];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpResult += tmpTokens[i];\n\t\t\t}\n\t\t}\n\n\t\treturn tmpResult;\n\t}\n}\n\nmodule.exports = PictProviderFlowNoise;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n\n},{\"fable-serviceproviderbase\":4}],24:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-PanelChrome\n *\n * Template-based provider for creating the panel chrome (the foreignObject\n * wrapper, title bar, close button, and body container) for properties panels.\n *\n * Replaces the raw DOM API approach with a configuration template\n * (Flow-PanelChrome-Template) registered in the FlowView's template set.\n */\nclass PictProviderFlowPanelChrome extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowPanelChrome';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Create a foreignObject containing the panel chrome and empty body container.\n\t *\n\t * Uses the Flow-PanelChrome-Template registered in the FlowView configuration\n\t * to render the inner HTML (title bar, close button, body container), then\n\t * attaches event isolation listeners so pointer and wheel events inside the\n\t * panel body do not propagate to the SVG interaction layer.\n\t *\n\t * @param {Object} pPanelData - Panel data from OpenPanels (Hash, NodeHash, X, Y, Width, Height, Title)\n\t * @param {SVGGElement} pPanelsLayer - The SVG <g> for panel foreignObjects\n\t * @returns {HTMLDivElement} The panel body container element for content rendering\n\t */\n\tcreatePanelForeignObject(pPanelData, pPanelsLayer)\n\t{\n\t\tlet tmpSVGHelper = this._FlowView._SVGHelperProvider;\n\n\t\t// Create the SVG foreignObject wrapper\n\t\tlet tmpFO = tmpSVGHelper.createSVGElement('foreignObject');\n\t\ttmpFO.setAttribute('class', 'pict-flow-panel-foreign-object');\n\t\ttmpFO.setAttribute('data-panel-hash', pPanelData.Hash);\n\t\ttmpFO.setAttribute('data-node-hash', pPanelData.NodeHash);\n\t\ttmpFO.setAttribute('x', String(pPanelData.X));\n\t\ttmpFO.setAttribute('y', String(pPanelData.Y));\n\t\ttmpFO.setAttribute('width', String(pPanelData.Width));\n\t\ttmpFO.setAttribute('height', String(pPanelData.Height));\n\n\t\t// Render the panel chrome from the configuration template\n\t\tlet tmpPict = this._FlowView.pict || this._FlowView.fable;\n\t\tlet tmpTitle = pPanelData.Title || 'Properties';\n\t\tlet tmpChromeHTML = tmpPict.parseTemplateByHash('Flow-PanelChrome-Template',\n\t\t\t{ Hash: pPanelData.Hash, Title: tmpTitle });\n\n\t\ttmpFO.innerHTML = tmpChromeHTML;\n\n\t\t// Populate the close button icon\n\t\tlet tmpCloseIcon = tmpFO.querySelector('.pict-flow-panel-close-icon');\n\t\tif (tmpCloseIcon && this._FlowView && this._FlowView._IconProvider)\n\t\t{\n\t\t\ttmpCloseIcon.innerHTML = this._FlowView._IconProvider.getIconSVGMarkup('close', 12);\n\t\t}\n\t\telse if (tmpCloseIcon)\n\t\t{\n\t\t\ttmpCloseIcon.textContent = '\\u2715';\n\t\t}\n\n\t\t// Attach event isolation to the scrollable content area so\n\t\t// pointer/wheel events inside the panel do not trigger SVG interactions\n\t\tlet tmpContent = tmpFO.querySelector('.pict-flow-panel-content');\n\t\tif (tmpContent)\n\t\t{\n\t\t\ttmpContent.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\t\ttmpContent.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });\n\t\t}\n\n\t\t// Isolate events on the tab bar\n\t\tlet tmpTabbar = tmpFO.querySelector('.pict-flow-panel-tabbar');\n\t\tif (tmpTabbar)\n\t\t{\n\t\t\ttmpTabbar.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\t\ttmpTabbar.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });\n\t\t}\n\n\t\tpPanelsLayer.appendChild(tmpFO);\n\n\t\t// Return the properties tab pane as the body container for content rendering\n\t\tlet tmpPropertiesPane = tmpFO.querySelector('.pict-flow-panel-tab-pane[data-tab=\"properties\"]');\n\t\treturn tmpPropertiesPane;\n\t}\n}\n\nmodule.exports = PictProviderFlowPanelChrome;\n\n},{\"fable-serviceproviderbase\":4}],25:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-SVGHelpers\n *\n * Shared SVG element creation utility used by all flow components\n * that need to create SVG namespace elements (nodes, connections, tethers).\n */\nclass PictProviderFlowSVGHelpers extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowSVGHelpers';\n\t}\n\n\t/**\n\t * Create an SVG namespace element.\n\t *\n\t * @param {string} pTagName - The SVG element tag name (e.g., 'path', 'circle', 'g', 'rect', 'text')\n\t * @returns {SVGElement}\n\t */\n\tcreateSVGElement(pTagName)\n\t{\n\t\treturn document.createElementNS('http://www.w3.org/2000/svg', pTagName);\n\t}\n}\n\nmodule.exports = PictProviderFlowSVGHelpers;\n\n},{\"fable-serviceproviderbase\":4}],26:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowTheme'\n};\n\n/**\n * PictProvider-Flow-Theme\n *\n * Central orchestrator for the flow diagram theming system.\n *\n * Holds a registry of theme definitions, manages the active theme,\n * and provides hooks for node body rendering and path noise processing.\n *\n * ## Usage\n *\n * ```javascript\n * flowView.setTheme('sketch'); // Switch to hand-drawn style\n * flowView.setNoiseLevel(0.6); // Increase bracket/connection wobble\n * flowView.setTheme('default'); // Restore modern style\n * ```\n *\n * ## Custom Themes\n *\n * ```javascript\n * flowView._ThemeProvider.registerTheme('custom', {\n * Key: 'custom',\n * Label: 'My Custom Theme',\n * CSSVariables: { '--pf-canvas-bg': '#222' },\n * NodeBodyMode: 'rect',\n * NoiseConfig: { Enabled: false }\n * });\n * flowView.setTheme('custom');\n * ```\n */\nclass PictProviderFlowTheme extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowTheme';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\tthis._ActiveThemeKey = 'default';\n\t\tthis._NoiseLevel = 0;\n\t\tthis._Themes = {};\n\n\t\tthis._registerBuiltInThemes();\n\t}\n\n\t// ── Theme Registry ────────────────────────────────────────────────────\n\n\t_registerBuiltInThemes()\n\t{\n\t\t// ── 1. Default (Modern) ──────────────────────────────────────\n\t\tthis._Themes['default'] =\n\t\t{\n\t\t\tKey: 'default',\n\t\t\tLabel: 'Modern',\n\t\t\tCSSVariables: {},\n\t\t\tAdditionalCSS: '',\n\t\t\tNodeBodyMode: 'rect',\n\t\t\tBracketConfig: null,\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 2,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides: {}\n\t\t};\n\n\t\t// ── 2. Sketch (Hand-drawn) ───────────────────────────────────\n\t\tthis._Themes['sketch'] =\n\t\t{\n\t\t\tKey: 'sketch',\n\t\t\tLabel: 'Sketch',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': '#fffef5',\n\t\t\t\t'--pf-node-body-stroke': '#444444',\n\t\t\t\t'--pf-node-body-stroke-width': '1.5',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'none',\n\t\t\t\t'--pf-node-shadow-hover': 'none',\n\t\t\t\t'--pf-node-shadow-selected': 'none',\n\t\t\t\t'--pf-node-shadow-dragging': 'none',\n\t\t\t\t'--pf-node-title-fill': '#333333',\n\t\t\t\t'--pf-node-title-size': '12px',\n\t\t\t\t'--pf-node-title-weight': '400',\n\t\t\t\t'--pf-node-title-bar-color': '#f0ece0',\n\t\t\t\t'--pf-node-type-label-fill': '#888888',\n\t\t\t\t'--pf-node-selected-stroke': '#2255aa',\n\t\t\t\t'--pf-port-input-fill': '#5577bb',\n\t\t\t\t'--pf-port-output-fill': '#55aa77',\n\t\t\t\t'--pf-port-stroke': '#fffef5',\n\t\t\t\t'--pf-connection-stroke': '#555555',\n\t\t\t\t'--pf-connection-selected-stroke': '#2255aa',\n\t\t\t\t'--pf-canvas-bg': '#fffef5',\n\t\t\t\t'--pf-grid-stroke': '#e8e4d8',\n\t\t\t\t'--pf-panel-bg': '#fffef5',\n\t\t\t\t'--pf-panel-border': '#ccccaa',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': '2px 2px 0px rgba(0,0,0,0.08)',\n\t\t\t\t'--pf-panel-titlebar-bg': '#f0ece0',\n\t\t\t\t'--pf-panel-titlebar-border': '#ccccaa',\n\t\t\t\t'--pf-panel-title-color': '#333333'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: \"Courier New\", \"Courier\", monospace !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-panel-title-text,\n\t\t\t\t.pict-flow-panel-node-props-title,\n\t\t\t\t.pict-flow-info-panel {\n\t\t\t\t\tfont-family: \"Courier New\", \"Courier\", monospace !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) !important;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'bracket',\n\t\t\tBracketConfig:\n\t\t\t{\n\t\t\t\tSerifLength: 20,\n\t\t\t\tTitleSeparator: true\n\t\t\t},\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 1.5,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: true,\n\t\t\t\tDefaultLevel: 0.4,\n\t\t\t\tMaxJitterPx: 4,\n\t\t\t\tAffectsNodes: true,\n\t\t\t\tAffectsConnections: true\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#555555' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#2255aa' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 3. Blueprint (Technical) ─────────────────────────────────\n\t\tthis._Themes['blueprint'] =\n\t\t{\n\t\t\tKey: 'blueprint',\n\t\t\tLabel: 'Blueprint',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': 'rgba(255,255,255,0.05)',\n\t\t\t\t'--pf-node-body-stroke': '#ffffff',\n\t\t\t\t'--pf-node-body-stroke-width': '1',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'none',\n\t\t\t\t'--pf-node-shadow-hover': 'none',\n\t\t\t\t'--pf-node-shadow-selected': 'none',\n\t\t\t\t'--pf-node-shadow-dragging': 'none',\n\t\t\t\t'--pf-node-title-fill': '#ffffff',\n\t\t\t\t'--pf-node-title-size': '11px',\n\t\t\t\t'--pf-node-title-weight': '400',\n\t\t\t\t'--pf-node-title-bar-color': 'rgba(255,255,255,0.1)',\n\t\t\t\t'--pf-node-type-label-fill': 'rgba(255,255,255,0.5)',\n\t\t\t\t'--pf-node-selected-stroke': '#ffdd44',\n\t\t\t\t'--pf-port-input-fill': '#88bbff',\n\t\t\t\t'--pf-port-output-fill': '#88ffbb',\n\t\t\t\t'--pf-port-stroke': '#1a3a6a',\n\t\t\t\t'--pf-connection-stroke': 'rgba(255,255,255,0.6)',\n\t\t\t\t'--pf-connection-selected-stroke': '#ffdd44',\n\t\t\t\t'--pf-canvas-bg': '#1a3a6a',\n\t\t\t\t'--pf-grid-stroke': 'rgba(255,255,255,0.08)',\n\t\t\t\t'--pf-panel-bg': '#1a3a6a',\n\t\t\t\t'--pf-panel-border': 'rgba(255,255,255,0.3)',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': 'none',\n\t\t\t\t'--pf-panel-titlebar-bg': 'rgba(255,255,255,0.05)',\n\t\t\t\t'--pf-panel-titlebar-border': 'rgba(255,255,255,0.15)',\n\t\t\t\t'--pf-panel-title-color': '#ffffff'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: \"Courier New\", monospace !important;\n\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\tletter-spacing: 1px;\n\t\t\t\t}\n\t\t\t\t.pict-flow-container {\n\t\t\t\t\tborder-color: #0d2244;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) invert(1) !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar {\n\t\t\t\t\tbackground-color: #142e54;\n\t\t\t\t\tborder-bottom-color: rgba(255,255,255,0.15);\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn {\n\t\t\t\t\tbackground-color: rgba(255,255,255,0.05);\n\t\t\t\t\tborder-color: rgba(255,255,255,0.2);\n\t\t\t\t\tcolor: #ffffff;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn:hover {\n\t\t\t\t\tbackground-color: rgba(255,255,255,0.1);\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'bracket',\n\t\t\tBracketConfig:\n\t\t\t{\n\t\t\t\tSerifLength: 18,\n\t\t\t\tTitleSeparator: true\n\t\t\t},\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: '8 4',\n\t\t\t\tStrokeWidth: 1,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: 'rgba(255,255,255,0.6)' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#ffdd44' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 4. Mono (Black & White) ──────────────────────────────────\n\t\tthis._Themes['mono'] =\n\t\t{\n\t\t\tKey: 'mono',\n\t\t\tLabel: 'Monochrome',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': '#ffffff',\n\t\t\t\t'--pf-node-body-stroke': '#000000',\n\t\t\t\t'--pf-node-body-stroke-width': '1',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'none',\n\t\t\t\t'--pf-node-shadow-hover': 'none',\n\t\t\t\t'--pf-node-shadow-selected': 'none',\n\t\t\t\t'--pf-node-shadow-dragging': 'none',\n\t\t\t\t'--pf-node-title-fill': '#ffffff',\n\t\t\t\t'--pf-node-title-size': '11px',\n\t\t\t\t'--pf-node-title-weight': '600',\n\t\t\t\t'--pf-node-title-bar-color': '#000000',\n\t\t\t\t'--pf-node-type-label-fill': '#888888',\n\t\t\t\t'--pf-node-selected-stroke': '#444444',\n\t\t\t\t'--pf-port-input-fill': '#000000',\n\t\t\t\t'--pf-port-output-fill': '#666666',\n\t\t\t\t'--pf-port-stroke': '#ffffff',\n\t\t\t\t'--pf-connection-stroke': '#000000',\n\t\t\t\t'--pf-connection-selected-stroke': '#444444',\n\t\t\t\t'--pf-canvas-bg': '#ffffff',\n\t\t\t\t'--pf-grid-stroke': '#eeeeee',\n\t\t\t\t'--pf-panel-bg': '#ffffff',\n\t\t\t\t'--pf-panel-border': '#000000',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': 'none',\n\t\t\t\t'--pf-panel-titlebar-bg': '#f0f0f0',\n\t\t\t\t'--pf-panel-titlebar-border': '#000000',\n\t\t\t\t'--pf-panel-title-color': '#000000'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title {\n\t\t\t\t\tfont-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) invert(1) !important;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'rect',\n\t\t\tBracketConfig: null,\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 1,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#000000' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#444444' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 5. Retro 80s (Neon) ──────────────────────────────────────\n\t\tthis._Themes['retro-80s'] =\n\t\t{\n\t\t\tKey: 'retro-80s',\n\t\t\tLabel: '80s Retro',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': '#1a0a2e',\n\t\t\t\t'--pf-node-body-stroke': '#ff00ff',\n\t\t\t\t'--pf-node-body-stroke-width': '2',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'drop-shadow(0 0 8px rgba(255,0,255,0.4))',\n\t\t\t\t'--pf-node-shadow-hover': 'drop-shadow(0 0 12px rgba(255,0,255,0.6))',\n\t\t\t\t'--pf-node-shadow-selected': 'drop-shadow(0 0 16px rgba(0,255,255,0.5))',\n\t\t\t\t'--pf-node-shadow-dragging': 'drop-shadow(0 0 20px rgba(255,0,255,0.7))',\n\t\t\t\t'--pf-node-title-fill': '#00ffff',\n\t\t\t\t'--pf-node-title-size': '11px',\n\t\t\t\t'--pf-node-title-weight': '700',\n\t\t\t\t'--pf-node-title-bar-color': '#2a0a4e',\n\t\t\t\t'--pf-node-type-label-fill': '#ff66ff',\n\t\t\t\t'--pf-node-selected-stroke': '#00ffff',\n\t\t\t\t'--pf-port-input-fill': '#ff00ff',\n\t\t\t\t'--pf-port-output-fill': '#00ff66',\n\t\t\t\t'--pf-port-stroke': '#1a0a2e',\n\t\t\t\t'--pf-connection-stroke': '#ff00ff',\n\t\t\t\t'--pf-connection-selected-stroke': '#00ffff',\n\t\t\t\t'--pf-canvas-bg': '#0a0015',\n\t\t\t\t'--pf-grid-stroke': '#1a0a2e',\n\t\t\t\t'--pf-panel-bg': '#1a0a2e',\n\t\t\t\t'--pf-panel-border': '#ff00ff',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': '0 0 20px rgba(255,0,255,0.3)',\n\t\t\t\t'--pf-panel-titlebar-bg': '#2a0a4e',\n\t\t\t\t'--pf-panel-titlebar-border': '#ff00ff',\n\t\t\t\t'--pf-panel-title-color': '#00ffff'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: \"Courier New\", monospace !important;\n\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\tletter-spacing: 0.5px;\n\t\t\t\t}\n\t\t\t\t.pict-flow-connection {\n\t\t\t\t\tfilter: drop-shadow(0 0 3px rgba(255,0,255,0.4));\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) invert(1) hue-rotate(180deg) !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar {\n\t\t\t\t\tbackground-color: #1a0a2e;\n\t\t\t\t\tborder-bottom-color: #ff00ff;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn {\n\t\t\t\t\tbackground-color: #1a0a2e;\n\t\t\t\t\tborder-color: #ff00ff;\n\t\t\t\t\tcolor: #00ffff;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn:hover {\n\t\t\t\t\tbackground-color: #2a0a4e;\n\t\t\t\t}\n\t\t\t\t.pict-flow-container {\n\t\t\t\t\tborder-color: #ff00ff;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'rect',\n\t\t\tBracketConfig: null,\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 2,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#ff00ff' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#00ffff' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 6. Retro 90s (Windows) ───────────────────────────────────\n\t\tthis._Themes['retro-90s'] =\n\t\t{\n\t\t\tKey: 'retro-90s',\n\t\t\tLabel: '90s Retro',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': '#c0c0c0',\n\t\t\t\t'--pf-node-body-stroke': '#808080',\n\t\t\t\t'--pf-node-body-stroke-width': '1',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'drop-shadow(2px 2px 0px #404040)',\n\t\t\t\t'--pf-node-shadow-hover': 'drop-shadow(3px 3px 0px #404040)',\n\t\t\t\t'--pf-node-shadow-selected': 'drop-shadow(2px 2px 0px #008080)',\n\t\t\t\t'--pf-node-shadow-dragging': 'drop-shadow(4px 4px 0px #404040)',\n\t\t\t\t'--pf-node-title-fill': '#ffffff',\n\t\t\t\t'--pf-node-title-size': '11px',\n\t\t\t\t'--pf-node-title-weight': '700',\n\t\t\t\t'--pf-node-title-bar-color': '#000080',\n\t\t\t\t'--pf-node-type-label-fill': '#606060',\n\t\t\t\t'--pf-node-selected-stroke': '#008080',\n\t\t\t\t'--pf-port-input-fill': '#000080',\n\t\t\t\t'--pf-port-output-fill': '#008000',\n\t\t\t\t'--pf-port-stroke': '#c0c0c0',\n\t\t\t\t'--pf-connection-stroke': '#808080',\n\t\t\t\t'--pf-connection-selected-stroke': '#008080',\n\t\t\t\t'--pf-canvas-bg': '#008080',\n\t\t\t\t'--pf-grid-stroke': 'rgba(0,0,0,0.06)',\n\t\t\t\t'--pf-panel-bg': '#c0c0c0',\n\t\t\t\t'--pf-panel-border': '#808080',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': '2px 2px 0px #404040',\n\t\t\t\t'--pf-panel-titlebar-bg': '#000080',\n\t\t\t\t'--pf-panel-titlebar-border': '#c0c0c0',\n\t\t\t\t'--pf-panel-title-color': '#ffffff'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: \"MS Sans Serif\", \"Arial\", sans-serif !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) invert(1) !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar {\n\t\t\t\t\tbackground-color: #c0c0c0;\n\t\t\t\t\tborder-bottom: 2px solid #808080;\n\t\t\t\t\tborder-top: 1px solid #ffffff;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn {\n\t\t\t\t\tbackground-color: #c0c0c0;\n\t\t\t\t\tborder: 2px outset #c0c0c0;\n\t\t\t\t\tborder-radius: 0;\n\t\t\t\t\tcolor: #000000;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn:hover {\n\t\t\t\t\tbackground-color: #d0d0d0;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn:active {\n\t\t\t\t\tborder-style: inset;\n\t\t\t\t}\n\t\t\t\t.pict-flow-container {\n\t\t\t\t\tborder: 2px outset #c0c0c0;\n\t\t\t\t\tborder-radius: 0;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'rect',\n\t\t\tBracketConfig: null,\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 2,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#808080' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#008080' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 7. Whiteboard (Minimal brackets, no fills) ──────────────\n\t\tthis._Themes['whiteboard'] =\n\t\t{\n\t\t\tKey: 'whiteboard',\n\t\t\tLabel: 'Whiteboard',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': 'transparent',\n\t\t\t\t'--pf-node-body-stroke': '#555555',\n\t\t\t\t'--pf-node-body-stroke-width': '2',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'none',\n\t\t\t\t'--pf-node-shadow-hover': 'none',\n\t\t\t\t'--pf-node-shadow-selected': 'none',\n\t\t\t\t'--pf-node-shadow-dragging': 'none',\n\t\t\t\t'--pf-node-title-fill': '#333333',\n\t\t\t\t'--pf-node-title-size': '12px',\n\t\t\t\t'--pf-node-title-weight': '600',\n\t\t\t\t'--pf-node-title-bar-color': 'transparent',\n\t\t\t\t'--pf-node-type-label-fill': '#999999',\n\t\t\t\t'--pf-node-selected-stroke': '#2255aa',\n\t\t\t\t'--pf-port-input-fill': '#5577bb',\n\t\t\t\t'--pf-port-output-fill': '#55aa77',\n\t\t\t\t'--pf-port-stroke': '#ffffff',\n\t\t\t\t'--pf-connection-stroke': '#888888',\n\t\t\t\t'--pf-connection-selected-stroke': '#2255aa',\n\t\t\t\t'--pf-canvas-bg': '#ffffff',\n\t\t\t\t'--pf-grid-stroke': '#f0f0f0',\n\t\t\t\t'--pf-panel-bg': '#ffffff',\n\t\t\t\t'--pf-panel-border': '#cccccc',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': '2px 2px 0px rgba(0,0,0,0.06)',\n\t\t\t\t'--pf-panel-titlebar-bg': '#f8f8f8',\n\t\t\t\t'--pf-panel-titlebar-border': '#e0e0e0',\n\t\t\t\t'--pf-panel-title-color': '#333333'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif !important;\n\t\t\t\t}\n\t\t\t\t/* Node-type bracket colors — each type gets its own bracket color */\n\t\t\t\t.pict-flow-node-start .pict-flow-node-bracket { stroke: #27ae60; }\n\t\t\t\t.pict-flow-node-end .pict-flow-node-bracket { stroke: #1abc9c; }\n\t\t\t\t.pict-flow-node-halt .pict-flow-node-bracket { stroke: #e74c3c; }\n\t\t\t\t.pict-flow-node-decision .pict-flow-node-bracket { stroke: #f39c12; }\n\t\t\t\t.pict-flow-node-default .pict-flow-node-bracket { stroke: #3498db; }\n\t\t\t\t.pict-flow-node-action .pict-flow-node-bracket { stroke: #2c3e50; }\n\t\t\t\t/* Override variant rules: no fills/strokes on body rects in whiteboard */\n\t\t\t\t.pict-flow-node-decision .pict-flow-node-body,\n\t\t\t\t.pict-flow-node-start .pict-flow-node-body,\n\t\t\t\t.pict-flow-node-end .pict-flow-node-body,\n\t\t\t\t.pict-flow-node-halt .pict-flow-node-body {\n\t\t\t\t\tfill: transparent;\n\t\t\t\t\tstroke: transparent;\n\t\t\t\t\tstroke-width: 0;\n\t\t\t\t}\n\t\t\t\t/* Title bar fills transparent too */\n\t\t\t\t.pict-flow-node .pict-flow-node-bracket-title-fill {\n\t\t\t\t\tfill: transparent !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: none !important;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'bracket',\n\t\t\tBracketConfig:\n\t\t\t{\n\t\t\t\tSerifLength: 22,\n\t\t\t\tTitleSeparator: false\n\t\t\t},\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 1.5,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: true,\n\t\t\t\tDefaultLevel: 0.3,\n\t\t\t\tMaxJitterPx: 3,\n\t\t\t\tAffectsNodes: true,\n\t\t\t\tAffectsConnections: true\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#888888' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#2255aa' }\n\t\t\t}\n\t\t};\n\t}\n\n\t// ── Public API ────────────────────────────────────────────────────────\n\n\t/**\n\t * Get the active theme definition.\n\t * @returns {Object}\n\t */\n\tgetActiveTheme()\n\t{\n\t\treturn this._Themes[this._ActiveThemeKey] || this._Themes['default'];\n\t}\n\n\t/**\n\t * Get the active theme key.\n\t * @returns {string}\n\t */\n\tgetActiveThemeKey()\n\t{\n\t\treturn this._ActiveThemeKey;\n\t}\n\n\t/**\n\t * Switch the active theme.\n\t * This updates the internal key and applies shape overrides.\n\t * The caller (FlowView.setTheme) is responsible for re-registering\n\t * CSS and triggering a full re-render.\n\t *\n\t * @param {string} pThemeKey\n\t * @returns {boolean} Whether the theme was found and applied\n\t */\n\tsetTheme(pThemeKey)\n\t{\n\t\tif (!this._Themes[pThemeKey])\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowTheme: theme '${pThemeKey}' not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._ActiveThemeKey = pThemeKey;\n\t\tlet tmpTheme = this._Themes[pThemeKey];\n\n\t\t// Apply noise defaults from theme\n\t\tif (tmpTheme.NoiseConfig && typeof tmpTheme.NoiseConfig.DefaultLevel === 'number')\n\t\t{\n\t\t\tthis._NoiseLevel = tmpTheme.NoiseConfig.DefaultLevel;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._NoiseLevel = 0;\n\t\t}\n\n\t\t// Apply shape overrides\n\t\tif (this._FlowView && this._FlowView._ConnectorShapesProvider)\n\t\t{\n\t\t\tthis._FlowView._ConnectorShapesProvider.resetToDefaults();\n\t\t\tif (tmpTheme.ShapeOverrides && Object.keys(tmpTheme.ShapeOverrides).length > 0)\n\t\t\t{\n\t\t\t\tthis._FlowView._ConnectorShapesProvider.applyThemeOverrides(tmpTheme.ShapeOverrides);\n\t\t\t}\n\t\t}\n\n\t\tthis.log.trace(`PictProviderFlowTheme: switched to '${pThemeKey}'`);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get the current noise level (0 to 1).\n\t * @returns {number}\n\t */\n\tgetNoiseLevel()\n\t{\n\t\treturn this._NoiseLevel;\n\t}\n\n\t/**\n\t * Set the noise level (0 to 1).\n\t * @param {number} pLevel\n\t */\n\tsetNoiseLevel(pLevel)\n\t{\n\t\tthis._NoiseLevel = Math.max(0, Math.min(1, pLevel || 0));\n\t}\n\n\t/**\n\t * Register a custom theme.\n\t * @param {string} pKey\n\t * @param {Object} pThemeDefinition\n\t */\n\tregisterTheme(pKey, pThemeDefinition)\n\t{\n\t\tif (!pKey || !pThemeDefinition)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowTheme: registerTheme requires key and definition');\n\t\t\treturn;\n\t\t}\n\t\tpThemeDefinition.Key = pKey;\n\t\tthis._Themes[pKey] = pThemeDefinition;\n\t}\n\n\t/**\n\t * Get all registered theme keys.\n\t * @returns {Array<string>}\n\t */\n\tgetThemeKeys()\n\t{\n\t\treturn Object.keys(this._Themes);\n\t}\n\n\t// ── Rendering Hooks ───────────────────────────────────────────────────\n\n\t/**\n\t * Post-process an SVG path string to apply noise/jitter if the\n\t * active theme has noise enabled for connections.\n\t *\n\t * @param {string} pPathString - SVG path d attribute\n\t * @param {string} pSeedString - Hash for deterministic noise\n\t * @returns {string} Possibly-modified path string\n\t */\n\tprocessPathString(pPathString, pSeedString)\n\t{\n\t\tlet tmpTheme = this.getActiveTheme();\n\t\tif (!tmpTheme || !tmpTheme.NoiseConfig || !tmpTheme.NoiseConfig.Enabled || !tmpTheme.NoiseConfig.AffectsConnections)\n\t\t{\n\t\t\treturn pPathString;\n\t\t}\n\n\t\tlet tmpAmplitude = this._NoiseLevel * (tmpTheme.NoiseConfig.MaxJitterPx || 3);\n\t\tif (tmpAmplitude <= 0)\n\t\t{\n\t\t\treturn pPathString;\n\t\t}\n\n\t\tif (this._FlowView && this._FlowView._NoiseProvider)\n\t\t{\n\t\t\treturn this._FlowView._NoiseProvider.jitterPath(pPathString, tmpAmplitude, pSeedString);\n\t\t}\n\n\t\treturn pPathString;\n\t}\n\n\t/**\n\t * Get the noise amplitude for node bracket rendering.\n\t * Returns 0 if noise is not enabled for nodes in the active theme.\n\t * @returns {number}\n\t */\n\tgetNodeNoiseAmplitude()\n\t{\n\t\tlet tmpTheme = this.getActiveTheme();\n\t\tif (!tmpTheme || !tmpTheme.NoiseConfig || !tmpTheme.NoiseConfig.Enabled || !tmpTheme.NoiseConfig.AffectsNodes)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\treturn this._NoiseLevel * (tmpTheme.NoiseConfig.MaxJitterPx || 3);\n\t}\n}\n\nmodule.exports = PictProviderFlowTheme;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n\n},{\"fable-serviceproviderbase\":4}],27:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-ConnectionHandleManager\n *\n * Manages connection handle lifecycle: dragging, adding, removing,\n * and resetting bezier/orthogonal handles on connections and tethers.\n *\n * Extracted from PictView-Flow.js to isolate handle CRUD operations\n * from the main view.\n */\nclass PictServiceFlowConnectionHandleManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowConnectionHandleManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Update a connection handle position during drag (for real-time feedback).\n\t * @param {string} pConnectionHash\n\t * @param {string} pHandleType - 'bezier-midpoint', 'bezier-handle-N', 'ortho-corner1', 'ortho-corner2', 'ortho-midpoint'\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdateConnectionHandle(pConnectionHash, pHandleType, pX, pY)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection) return;\n\n\t\tif (!tmpConnection.Data) tmpConnection.Data = {};\n\t\ttmpConnection.Data.HandleCustomized = true;\n\n\t\t// Multi-handle bezier: handle type is 'bezier-handle-N'\n\t\tif (pHandleType && pHandleType.startsWith('bezier-handle-'))\n\t\t{\n\t\t\tlet tmpIndex = parseInt(pHandleType.replace('bezier-handle-', ''), 10);\n\t\t\tif (!isNaN(tmpIndex) && Array.isArray(tmpConnection.Data.BezierHandles)\n\t\t\t\t&& tmpIndex < tmpConnection.Data.BezierHandles.length)\n\t\t\t{\n\t\t\t\ttmpConnection.Data.BezierHandles[tmpIndex].x = pX;\n\t\t\t\ttmpConnection.Data.BezierHandles[tmpIndex].y = pY;\n\t\t\t}\n\t\t\tthis._FlowView._renderSingleConnection(pConnectionHash);\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (pHandleType)\n\t\t{\n\t\t\tcase 'bezier-midpoint':\n\t\t\t\t// Legacy single-handle: migrate to BezierHandles array\n\t\t\t\tif (!Array.isArray(tmpConnection.Data.BezierHandles)\n\t\t\t\t\t|| tmpConnection.Data.BezierHandles.length === 0)\n\t\t\t\t{\n\t\t\t\t\ttmpConnection.Data.BezierHandles = [{ x: pX, y: pY }];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttmpConnection.Data.BezierHandles[0].x = pX;\n\t\t\t\t\ttmpConnection.Data.BezierHandles[0].y = pY;\n\t\t\t\t}\n\t\t\t\t// Keep legacy fields in sync for backward compat\n\t\t\t\ttmpConnection.Data.BezierHandleX = pX;\n\t\t\t\ttmpConnection.Data.BezierHandleY = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-corner1':\n\t\t\t\ttmpConnection.Data.OrthoCorner1X = pX;\n\t\t\t\ttmpConnection.Data.OrthoCorner1Y = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-corner2':\n\t\t\t\ttmpConnection.Data.OrthoCorner2X = pX;\n\t\t\t\ttmpConnection.Data.OrthoCorner2Y = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-midpoint':\n\t\t\t{\n\t\t\t\t// Midpoint drag shifts the corridor offset\n\t\t\t\tlet tmpSourcePos = this._FlowView.getPortPosition(tmpConnection.SourceNodeHash, tmpConnection.SourcePortHash);\n\t\t\t\tlet tmpTargetPos = this._FlowView.getPortPosition(tmpConnection.TargetNodeHash, tmpConnection.TargetPortHash);\n\t\t\t\tif (tmpSourcePos && tmpTargetPos)\n\t\t\t\t{\n\t\t\t\t\tlet tmpGeom = this._FlowView._ConnectionRenderer._computeDirectionalGeometry(tmpSourcePos, tmpTargetPos);\n\t\t\t\t\tlet tmpStartDir = tmpGeom.startDir;\n\n\t\t\t\t\t// Compute offset along the corridor axis\n\t\t\t\t\tif (Math.abs(tmpStartDir.dx) > Math.abs(tmpStartDir.dy))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Horizontal departure — corridor is vertical, shift is along X\n\t\t\t\t\t\tlet tmpAutoMidX = (tmpGeom.departX + tmpGeom.approachX) / 2;\n\t\t\t\t\t\ttmpConnection.Data.OrthoMidOffset = pX - tmpAutoMidX;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Vertical departure — corridor is horizontal, shift is along Y\n\t\t\t\t\t\tlet tmpAutoMidY = (tmpGeom.departY + tmpGeom.approachY) / 2;\n\t\t\t\t\t\ttmpConnection.Data.OrthoMidOffset = pY - tmpAutoMidY;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis._FlowView._renderSingleConnection(pConnectionHash);\n\t}\n\n\t/**\n\t * Add a bezier handle to a connection at the specified position.\n\t * The handle is inserted at the correct index based on which\n\t * segment of the curve the click point is closest to.\n\t *\n\t * @param {string} pConnectionHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\taddConnectionHandle(pConnectionHash, pX, pY)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection) return;\n\n\t\tif (!tmpConnection.Data) tmpConnection.Data = {};\n\n\t\t// Ensure BezierHandles array exists (migrate from legacy if needed)\n\t\tif (!Array.isArray(tmpConnection.Data.BezierHandles))\n\t\t{\n\t\t\ttmpConnection.Data.BezierHandles = [];\n\t\t\tif (tmpConnection.Data.BezierHandleX != null && tmpConnection.Data.BezierHandleY != null)\n\t\t\t{\n\t\t\t\ttmpConnection.Data.BezierHandles.push({\n\t\t\t\t\tx: tmpConnection.Data.BezierHandleX,\n\t\t\t\t\ty: tmpConnection.Data.BezierHandleY\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Ensure bezier mode\n\t\ttmpConnection.Data.LineMode = 'bezier';\n\n\t\tlet tmpSourcePos = this._FlowView.getPortPosition(tmpConnection.SourceNodeHash, tmpConnection.SourcePortHash);\n\t\tlet tmpTargetPos = this._FlowView.getPortPosition(tmpConnection.TargetNodeHash, tmpConnection.TargetPortHash);\n\n\t\tlet tmpInsertIndex = 0;\n\t\tif (tmpSourcePos && tmpTargetPos && this._FlowView._ConnectionRenderer)\n\t\t{\n\t\t\ttmpInsertIndex = this._FlowView._ConnectionRenderer.computeInsertionIndex(\n\t\t\t\ttmpConnection.Data.BezierHandles,\n\t\t\t\t{ x: pX, y: pY },\n\t\t\t\ttmpSourcePos,\n\t\t\t\ttmpTargetPos\n\t\t\t);\n\t\t}\n\n\t\ttmpConnection.Data.BezierHandles.splice(tmpInsertIndex, 0, { x: pX, y: pY });\n\t\ttmpConnection.Data.HandleCustomized = true;\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Remove a bezier handle from a connection by index.\n\t *\n\t * @param {string} pConnectionHash\n\t * @param {number} pIndex - Index in the BezierHandles array\n\t */\n\tremoveConnectionHandle(pConnectionHash, pIndex)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection || !tmpConnection.Data) return;\n\n\t\tif (!Array.isArray(tmpConnection.Data.BezierHandles)) return;\n\t\tif (pIndex < 0 || pIndex >= tmpConnection.Data.BezierHandles.length) return;\n\n\t\ttmpConnection.Data.BezierHandles.splice(pIndex, 1);\n\n\t\tif (tmpConnection.Data.BezierHandles.length === 0)\n\t\t{\n\t\t\ttmpConnection.Data.HandleCustomized = false;\n\t\t\ttmpConnection.Data.BezierHandleX = null;\n\t\t\ttmpConnection.Data.BezierHandleY = null;\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Reset handle positions for all connections/tethers involving a node.\n\t * Called when a node moves. Preserves LineMode but resets handle coordinates to auto.\n\t * @param {string} pNodeHash\n\t */\n\tresetHandlesForNode(pNodeHash)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\t// Reset connection handles\n\t\tfor (let i = 0; i < this._FlowView._FlowData.Connections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = this._FlowView._FlowData.Connections[i];\n\t\t\tif (tmpConn.SourceNodeHash === pNodeHash || tmpConn.TargetNodeHash === pNodeHash)\n\t\t\t{\n\t\t\t\tif (tmpConn.Data && tmpConn.Data.HandleCustomized)\n\t\t\t\t{\n\t\t\t\t\ttmpConn.Data.HandleCustomized = false;\n\t\t\t\t\t// Clear multi-handle array (current format)\n\t\t\t\t\ttmpConn.Data.BezierHandles = [];\n\t\t\t\t\t// Clear legacy single-handle fields\n\t\t\t\t\ttmpConn.Data.BezierHandleX = null;\n\t\t\t\t\ttmpConn.Data.BezierHandleY = null;\n\t\t\t\t\ttmpConn.Data.OrthoCorner1X = null;\n\t\t\t\t\ttmpConn.Data.OrthoCorner1Y = null;\n\t\t\t\t\ttmpConn.Data.OrthoCorner2X = null;\n\t\t\t\t\ttmpConn.Data.OrthoCorner2Y = null;\n\t\t\t\t\ttmpConn.Data.OrthoMidOffset = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Reset tether handles for panels attached to this node\n\t\tif (this._FlowView._TetherService)\n\t\t{\n\t\t\tthis._FlowView._TetherService.resetHandlesForNode(this._FlowView._FlowData.OpenPanels, pNodeHash);\n\t\t}\n\t}\n\n\t/**\n\t * Reset tether handle positions for a specific panel.\n\t * Called when a panel is dragged.\n\t * @param {string} pPanelHash\n\t */\n\tresetHandlesForPanel(pPanelHash)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tif (this._FlowView._TetherService)\n\t\t{\n\t\t\tthis._FlowView._TetherService.resetHandlePositions(tmpPanel);\n\t\t}\n\t}\n}\n\nmodule.exports = PictServiceFlowConnectionHandleManager;\n\n},{\"fable-serviceproviderbase\":4}],28:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nclass PictServiceFlowConnectionRenderer extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowConnectionRenderer';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Render a connection as an SVG path with hit area and optional handles.\n\t * @param {Object} pConnection - The connection data\n\t * @param {SVGGElement} pConnectionsLayer - The SVG group to append to\n\t * @param {boolean} pIsSelected - Whether this connection is selected\n\t */\n\trenderConnection(pConnection, pConnectionsLayer, pIsSelected)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpSourcePos = this._FlowView.getPortPosition(pConnection.SourceNodeHash, pConnection.SourcePortHash);\n\t\tlet tmpTargetPos = this._FlowView.getPortPosition(pConnection.TargetNodeHash, pConnection.TargetPortHash);\n\n\t\t// Look up the source port's PortType for connection coloring\n\t\tlet tmpSourcePortType = null;\n\t\tlet tmpSourceNode = this._FlowView.getNode(pConnection.SourceNodeHash);\n\t\tif (tmpSourceNode && tmpSourceNode.Ports)\n\t\t{\n\t\t\tfor (let i = 0; i < tmpSourceNode.Ports.length; i++)\n\t\t\t{\n\t\t\t\tif (tmpSourceNode.Ports[i].Hash === pConnection.SourcePortHash)\n\t\t\t\t{\n\t\t\t\t\ttmpSourcePortType = tmpSourceNode.Ports[i].PortType || null;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!tmpSourcePos || !tmpTargetPos) return;\n\n\t\tlet tmpData = pConnection.Data || {};\n\t\tlet tmpLineMode = tmpData.LineMode || 'bezier';\n\t\tlet tmpPath;\n\n\t\tif (tmpLineMode === 'orthogonal')\n\t\t{\n\t\t\tlet tmpCorners = null;\n\t\t\tif (tmpData.HandleCustomized && tmpData.OrthoCorner1X != null)\n\t\t\t{\n\t\t\t\ttmpCorners =\n\t\t\t\t{\n\t\t\t\t\tcorner1: { x: tmpData.OrthoCorner1X, y: tmpData.OrthoCorner1Y },\n\t\t\t\t\tcorner2: { x: tmpData.OrthoCorner2X, y: tmpData.OrthoCorner2Y }\n\t\t\t\t};\n\t\t\t}\n\t\t\ttmpPath = this._generateOrthogonalPath(tmpSourcePos, tmpTargetPos, tmpCorners, tmpData.OrthoMidOffset || 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpHandles = this._getBezierHandles(tmpData);\n\t\t\tif (tmpHandles.length > 0)\n\t\t\t{\n\t\t\t\ttmpPath = this._generateMultiHandleBezierPath(tmpSourcePos, tmpTargetPos, tmpHandles);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpPath = this._generateDirectionalPath(tmpSourcePos, tmpTargetPos);\n\t\t\t}\n\t\t}\n\n\t\t// Apply theme noise post-processing to the path\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\ttmpPath = this._FlowView._ThemeProvider.processPathString(tmpPath, pConnection.Hash);\n\t\t}\n\n\t\t// Apply stroke-dasharray from theme's ConnectionConfig\n\t\tlet tmpStrokeDashArray = null;\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\tlet tmpActiveTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\t\tif (tmpActiveTheme && tmpActiveTheme.ConnectionConfig && tmpActiveTheme.ConnectionConfig.StrokeDashArray)\n\t\t\t{\n\t\t\t\ttmpStrokeDashArray = tmpActiveTheme.ConnectionConfig.StrokeDashArray;\n\t\t\t}\n\t\t}\n\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\n\t\t// Build the port-type CSS class suffix for connection coloring\n\t\tlet tmpConnTypeClass = tmpSourcePortType ? (' conn-type-' + tmpSourcePortType) : '';\n\n\t\t// Determine the arrowhead marker based on port type\n\t\tlet tmpArrowMarkerId;\n\t\tif (pIsSelected)\n\t\t{\n\t\t\ttmpArrowMarkerId = 'flow-arrowhead-selected-' + tmpViewIdentifier;\n\t\t}\n\t\telse if (tmpSourcePortType)\n\t\t{\n\t\t\ttmpArrowMarkerId = 'flow-arrowhead-' + tmpSourcePortType + '-' + tmpViewIdentifier;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpArrowMarkerId = 'flow-arrowhead-' + tmpViewIdentifier;\n\t\t}\n\n\t\t// Hit area (wider invisible path for easier selection)\n\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\tif (tmpShapeProvider)\n\t\t{\n\t\t\tlet tmpHitArea = tmpShapeProvider.createConnectionHitAreaElement(tmpPath, pConnection.Hash);\n\t\t\tpConnectionsLayer.appendChild(tmpHitArea);\n\n\t\t\tlet tmpPathElement = tmpShapeProvider.createConnectionPathElement(\n\t\t\t\ttmpPath, pConnection.Hash, pIsSelected, tmpViewIdentifier);\n\t\t\tif (tmpConnTypeClass)\n\t\t\t{\n\t\t\t\ttmpPathElement.setAttribute('class',\n\t\t\t\t\t(tmpPathElement.getAttribute('class') || '') + tmpConnTypeClass);\n\t\t\t}\n\t\t\t// Override the default arrowhead with the typed one\n\t\t\ttmpPathElement.setAttribute('marker-end', 'url(#' + tmpArrowMarkerId + ')');\n\t\t\tif (tmpStrokeDashArray)\n\t\t\t{\n\t\t\t\ttmpPathElement.setAttribute('stroke-dasharray', tmpStrokeDashArray);\n\t\t\t}\n\t\t\tpConnectionsLayer.appendChild(tmpPathElement);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpHitArea = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpHitArea.setAttribute('class', 'pict-flow-connection-hitarea');\n\t\t\ttmpHitArea.setAttribute('d', tmpPath);\n\t\t\ttmpHitArea.setAttribute('data-connection-hash', pConnection.Hash);\n\t\t\ttmpHitArea.setAttribute('data-element-type', 'connection-hitarea');\n\t\t\tpConnectionsLayer.appendChild(tmpHitArea);\n\n\t\t\tlet tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpPathElement.setAttribute('class', `pict-flow-connection${tmpConnTypeClass} ${pIsSelected ? 'selected' : ''}`);\n\t\t\ttmpPathElement.setAttribute('d', tmpPath);\n\t\t\ttmpPathElement.setAttribute('data-connection-hash', pConnection.Hash);\n\t\t\ttmpPathElement.setAttribute('data-element-type', 'connection');\n\t\t\ttmpPathElement.setAttribute('marker-end', 'url(#' + tmpArrowMarkerId + ')');\n\n\t\t\tif (tmpStrokeDashArray)\n\t\t\t{\n\t\t\t\ttmpPathElement.setAttribute('stroke-dasharray', tmpStrokeDashArray);\n\t\t\t}\n\n\t\t\tpConnectionsLayer.appendChild(tmpPathElement);\n\t\t}\n\n\t\t// Render drag handles when selected\n\t\tif (pIsSelected)\n\t\t{\n\t\t\tthis._renderHandles(pConnection, pConnectionsLayer, tmpSourcePos, tmpTargetPos);\n\t\t}\n\t}\n\n\t/**\n\t * Compute the departure and approach points plus control points\n\t * for a direction-aware bezier between two ports.\n\t *\n\t * This extracts the intermediate geometry from _generateDirectionalPath\n\t * so it can be reused by _getAutoMidpoint and _generateBezierPathWithHandle.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @returns {{departX, departY, approachX, approachY, cp1X, cp1Y, cp2X, cp2Y, startDir, endDir}}\n\t */\n\t_computeDirectionalGeometry(pStart, pEnd)\n\t{\n\t\treturn this._FlowView._PathGenerator.computeDirectionalGeometry(pStart, pEnd);\n\t}\n\n\t/**\n\t * Generate a direction-aware path between two ports.\n\t *\n\t * The path is composed of three segments:\n\t * 1. A short straight \"departure\" segment leaving the source port\n\t * in its outward direction.\n\t * 2. A cubic bezier curve connecting the departure point to an\n\t * \"approach\" point near the target port.\n\t * 3. A short straight \"approach\" segment arriving at the target\n\t * port aligned with its inward direction.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart - Start port position + side\n\t * @param {{x: number, y: number, side: string}} pEnd - End port position + side\n\t * @returns {string} SVG path d attribute\n\t */\n\t_generateDirectionalPath(pStart, pEnd)\n\t{\n\t\tlet tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);\n\n\t\treturn this._FlowView._PathGenerator.buildBezierPathString(\n\t\t\t{ x: pStart.x, y: pStart.y },\n\t\t\t{ x: tmpGeo.departX, y: tmpGeo.departY },\n\t\t\t{ x: tmpGeo.cp1X, y: tmpGeo.cp1Y },\n\t\t\t{ x: tmpGeo.cp2X, y: tmpGeo.cp2Y },\n\t\t\t{ x: tmpGeo.approachX, y: tmpGeo.approachY },\n\t\t\t{ x: pEnd.x, y: pEnd.y }\n\t\t);\n\t}\n\n\t/**\n\t * Get the bezier handles array from connection data, with backward\n\t * compatibility for the legacy BezierHandleX/Y single-handle format.\n\t *\n\t * @param {Object} pData - Connection.Data\n\t * @returns {Array<{x: number, y: number}>} Ordered handle waypoints (may be empty)\n\t */\n\t_getBezierHandles(pData)\n\t{\n\t\tif (!pData || !pData.HandleCustomized)\n\t\t{\n\t\t\treturn [];\n\t\t}\n\n\t\t// New multi-handle format\n\t\tif (Array.isArray(pData.BezierHandles) && pData.BezierHandles.length > 0)\n\t\t{\n\t\t\treturn pData.BezierHandles;\n\t\t}\n\n\t\t// Legacy single-handle format\n\t\tif (pData.BezierHandleX != null && pData.BezierHandleY != null)\n\t\t{\n\t\t\treturn [{ x: pData.BezierHandleX, y: pData.BezierHandleY }];\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Generate a multi-handle bezier path between two ports.\n\t * Delegates to PathGenerator.buildMultiBezierPathString for the\n\t * actual SVG path assembly with Catmull-Rom tangent continuity.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @param {Array<{x: number, y: number}>} pHandles - Ordered waypoints\n\t * @returns {string} SVG path d attribute\n\t */\n\t_generateMultiHandleBezierPath(pStart, pEnd, pHandles)\n\t{\n\t\tlet tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);\n\n\t\treturn this._FlowView._PathGenerator.buildMultiBezierPathString(\n\t\t\t{ x: pStart.x, y: pStart.y },\n\t\t\t{ x: tmpGeo.departX, y: tmpGeo.departY },\n\t\t\tpHandles,\n\t\t\t{ x: tmpGeo.approachX, y: tmpGeo.approachY },\n\t\t\t{ x: pEnd.x, y: pEnd.y },\n\t\t\ttmpGeo.startDir,\n\t\t\ttmpGeo.endDir\n\t\t);\n\t}\n\n\t/**\n\t * Find which segment of the multi-handle bezier the given click point\n\t * is closest to, returning the index at which a new handle should be\n\t * inserted into the BezierHandles array.\n\t *\n\t * Segments are: depart→handle[0], handle[0]→handle[1], ..., handle[N-1]→approach.\n\t * Returns 0 for before handle[0], 1 for between handle[0] and handle[1], etc.\n\t *\n\t * @param {Array<{x: number, y: number}>} pHandles - Current handles\n\t * @param {{x: number, y: number}} pClickPoint - Where the user right-clicked\n\t * @param {{x: number, y: number, side: string}} pStart - Source port position\n\t * @param {{x: number, y: number, side: string}} pEnd - Target port position\n\t * @returns {number} Insertion index\n\t */\n\tcomputeInsertionIndex(pHandles, pClickPoint, pStart, pEnd)\n\t{\n\t\tlet tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);\n\n\t\t// Build the waypoint chain: depart, handle[0..N-1], approach\n\t\tlet tmpWaypoints = [{ x: tmpGeo.departX, y: tmpGeo.departY }];\n\t\tfor (let i = 0; i < pHandles.length; i++)\n\t\t{\n\t\t\ttmpWaypoints.push(pHandles[i]);\n\t\t}\n\t\ttmpWaypoints.push({ x: tmpGeo.approachX, y: tmpGeo.approachY });\n\n\t\tlet tmpBestDist = Infinity;\n\t\tlet tmpBestIndex = 0;\n\n\t\tfor (let i = 0; i < tmpWaypoints.length - 1; i++)\n\t\t{\n\t\t\tlet tmpDist = this._distanceToSegment(\n\t\t\t\tpClickPoint.x, pClickPoint.y,\n\t\t\t\ttmpWaypoints[i].x, tmpWaypoints[i].y,\n\t\t\t\ttmpWaypoints[i + 1].x, tmpWaypoints[i + 1].y\n\t\t\t);\n\n\t\t\tif (tmpDist < tmpBestDist)\n\t\t\t{\n\t\t\t\ttmpBestDist = tmpDist;\n\t\t\t\ttmpBestIndex = i;\n\t\t\t}\n\t\t}\n\n\t\treturn tmpBestIndex;\n\t}\n\n\t/**\n\t * Distance from point (px,py) to line segment (ax,ay)-(bx,by).\n\t */\n\t_distanceToSegment(pPX, pPY, pAX, pAY, pBX, pBY)\n\t{\n\t\treturn this._FlowView._PathGenerator.distanceToSegment(pPX, pPY, pAX, pAY, pBX, pBY);\n\t}\n\n\t/**\n\t * Get the auto-calculated midpoint of the default bezier curve between two ports.\n\t * Evaluates the cubic bezier at t=0.5.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetAutoMidpoint(pStart, pEnd)\n\t{\n\t\treturn this._FlowView._PathGenerator.getAutoMidpoint(pStart, pEnd);\n\t}\n\n\t/**\n\t * Generate an orthogonal (90-degree angles only) path between two ports.\n\t *\n\t * Path format: M start L depart L corner1 L corner2 L approach L end\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @param {Object|null} pCorners - { corner1: {x,y}, corner2: {x,y} } or null for auto\n\t * @param {number} pMidOffset - Offset for the auto-calculated corridor position\n\t * @returns {string} SVG path d attribute\n\t */\n\t_generateOrthogonalPath(pStart, pEnd, pCorners, pMidOffset)\n\t{\n\t\tlet tmpDA = this._FlowView._PathGenerator.computeDepartApproach(pStart, pEnd, 20);\n\n\t\tlet tmpCorner1, tmpCorner2;\n\n\t\tif (pCorners && pCorners.corner1 && pCorners.corner2)\n\t\t{\n\t\t\ttmpCorner1 = pCorners.corner1;\n\t\t\ttmpCorner2 = pCorners.corner2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpAutoCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(\n\t\t\t\ttmpDA.departX, tmpDA.departY,\n\t\t\t\ttmpDA.approachX, tmpDA.approachY,\n\t\t\t\ttmpDA.fromDir, tmpDA.toDir,\n\t\t\t\tpMidOffset || 0\n\t\t\t);\n\t\t\ttmpCorner1 = tmpAutoCorners.corner1;\n\t\t\ttmpCorner2 = tmpAutoCorners.corner2;\n\t\t}\n\n\t\treturn this._FlowView._PathGenerator.buildOrthogonalPathString(\n\t\t\t{ x: pStart.x, y: pStart.y },\n\t\t\t{ x: tmpDA.departX, y: tmpDA.departY },\n\t\t\ttmpCorner1,\n\t\t\ttmpCorner2,\n\t\t\t{ x: tmpDA.approachX, y: tmpDA.approachY },\n\t\t\t{ x: pEnd.x, y: pEnd.y }\n\t\t);\n\t}\n\n\t/**\n\t * Get the full orthogonal geometry for a connection (for handle positioning).\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @param {Object} pData - Connection.Data\n\t * @returns {{corner1: {x,y}, corner2: {x,y}, midpoint: {x,y}}}\n\t */\n\tgetOrthogonalGeometry(pStart, pEnd, pData)\n\t{\n\t\tlet tmpDA = this._FlowView._PathGenerator.computeDepartApproach(pStart, pEnd, 20);\n\n\t\tif (pData && pData.HandleCustomized && pData.OrthoCorner1X != null)\n\t\t{\n\t\t\tlet tmpCorner1 = { x: pData.OrthoCorner1X, y: pData.OrthoCorner1Y };\n\t\t\tlet tmpCorner2 = { x: pData.OrthoCorner2X, y: pData.OrthoCorner2Y };\n\t\t\tlet tmpMidpoint =\n\t\t\t{\n\t\t\t\tx: (tmpCorner1.x + tmpCorner2.x) / 2,\n\t\t\t\ty: (tmpCorner1.y + tmpCorner2.y) / 2\n\t\t\t};\n\t\t\treturn { corner1: tmpCorner1, corner2: tmpCorner2, midpoint: tmpMidpoint };\n\t\t}\n\n\t\treturn this._FlowView._PathGenerator.computeAutoOrthogonalCorners(\n\t\t\ttmpDA.departX, tmpDA.departY,\n\t\t\ttmpDA.approachX, tmpDA.approachY,\n\t\t\ttmpDA.fromDir, tmpDA.toDir,\n\t\t\t(pData && pData.OrthoMidOffset) || 0\n\t\t);\n\t}\n\n\t/**\n\t * Render drag handles for the selected connection.\n\t *\n\t * @param {Object} pConnection\n\t * @param {SVGGElement} pLayer\n\t * @param {{x, y, side}} pStart - Source port position\n\t * @param {{x, y, side}} pEnd - Target port position\n\t */\n\t_renderHandles(pConnection, pLayer, pStart, pEnd)\n\t{\n\t\tlet tmpData = pConnection.Data || {};\n\t\tlet tmpLineMode = tmpData.LineMode || 'bezier';\n\n\t\tif (tmpLineMode === 'orthogonal')\n\t\t{\n\t\t\tlet tmpGeometry = this.getOrthogonalGeometry(pStart, pEnd, tmpData);\n\n\t\t\t// Corner 1 handle\n\t\t\tthis._createHandle(pLayer, pConnection.Hash, 'ortho-corner1',\n\t\t\t\ttmpGeometry.corner1.x, tmpGeometry.corner1.y, 'pict-flow-connection-handle');\n\n\t\t\t// Midpoint handle (between corners)\n\t\t\tthis._createHandle(pLayer, pConnection.Hash, 'ortho-midpoint',\n\t\t\t\ttmpGeometry.midpoint.x, tmpGeometry.midpoint.y, 'pict-flow-connection-handle-midpoint');\n\n\t\t\t// Corner 2 handle\n\t\t\tthis._createHandle(pLayer, pConnection.Hash, 'ortho-corner2',\n\t\t\t\ttmpGeometry.corner2.x, tmpGeometry.corner2.y, 'pict-flow-connection-handle');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Bezier handles — show one handle per waypoint, or a\n\t\t\t// single auto-midpoint when no custom handles exist.\n\t\t\tlet tmpHandles = this._getBezierHandles(tmpData);\n\n\t\t\tif (tmpHandles.length > 0)\n\t\t\t{\n\t\t\t\tfor (let i = 0; i < tmpHandles.length; i++)\n\t\t\t\t{\n\t\t\t\t\tthis._createHandle(pLayer, pConnection.Hash,\n\t\t\t\t\t\t'bezier-handle-' + i,\n\t\t\t\t\t\ttmpHandles[i].x, tmpHandles[i].y,\n\t\t\t\t\t\t'pict-flow-connection-handle');\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlet tmpMidpoint = this.getAutoMidpoint(pStart, pEnd);\n\t\t\t\tthis._createHandle(pLayer, pConnection.Hash, 'bezier-midpoint',\n\t\t\t\t\ttmpMidpoint.x, tmpMidpoint.y, 'pict-flow-connection-handle');\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a single SVG circle handle element.\n\t *\n\t * @param {SVGGElement} pLayer\n\t * @param {string} pConnectionHash\n\t * @param {string} pHandleType\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {string} pClassName\n\t */\n\t_createHandle(pLayer, pConnectionHash, pHandleType, pX, pY, pClassName)\n\t{\n\t\tif (!this._FlowView._ConnectorShapesProvider) return;\n\n\t\tlet tmpShapeKey = (pClassName === 'pict-flow-connection-handle-midpoint')\n\t\t\t? 'connection-handle-midpoint' : 'connection-handle';\n\n\t\tthis._FlowView._ConnectorShapesProvider.createFullHandle(\n\t\t\tpLayer, pConnectionHash, pHandleType, pX, pY,\n\t\t\ttmpShapeKey, 'connection-handle', 'data-connection-hash');\n\t}\n\n\t/**\n\t * Legacy bezier path for drag connections where we don't have side info.\n\t * @param {{x: number, y: number}} pStart\n\t * @param {{x: number, y: number}} pEnd\n\t * @returns {string}\n\t */\n\t_generateBezierPath(pStart, pEnd)\n\t{\n\t\t// During drag operations we may not have side info; default to right->left\n\t\tlet tmpStart = { x: pStart.x, y: pStart.y, side: pStart.side || 'right' };\n\t\tlet tmpEnd = { x: pEnd.x, y: pEnd.y, side: pEnd.side || 'left' };\n\t\treturn this._generateDirectionalPath(tmpStart, tmpEnd);\n\t}\n\n\t/**\n\t * Render a temporary drag connection line (used during connection creation)\n\t * @param {number} pStartX\n\t * @param {number} pStartY\n\t * @param {number} pEndX\n\t * @param {number} pEndY\n\t * @param {SVGGElement} pLayer - The layer to render into\n\t * @param {string} [pStartSide] - The side the source port is on\n\t * @returns {SVGPathElement} The created path element\n\t */\n\trenderDragConnection(pStartX, pStartY, pEndX, pEndY, pLayer, pStartSide)\n\t{\n\t\tlet tmpPath = this._generateDirectionalPath(\n\t\t\t{ x: pStartX, y: pStartY, side: pStartSide || 'right' },\n\t\t\t{ x: pEndX, y: pEndY, side: 'left' }\n\t\t);\n\n\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\tlet tmpPathElement;\n\n\t\tif (tmpShapeProvider)\n\t\t{\n\t\t\ttmpPathElement = tmpShapeProvider.createDragConnectionElement(tmpPath);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpPathElement.setAttribute('class', 'pict-flow-drag-connection');\n\t\t\ttmpPathElement.setAttribute('d', tmpPath);\n\t\t}\n\n\t\tpLayer.appendChild(tmpPathElement);\n\n\t\treturn tmpPathElement;\n\t}\n\n\t/**\n\t * Update a drag connection path\n\t * @param {SVGPathElement} pPathElement\n\t * @param {number} pStartX\n\t * @param {number} pStartY\n\t * @param {number} pEndX\n\t * @param {number} pEndY\n\t * @param {string} [pStartSide] - The side the source port is on\n\t */\n\tupdateDragConnection(pPathElement, pStartX, pStartY, pEndX, pEndY, pStartSide)\n\t{\n\t\tif (!pPathElement) return;\n\n\t\tlet tmpPath = this._generateDirectionalPath(\n\t\t\t{ x: pStartX, y: pStartY, side: pStartSide || 'right' },\n\t\t\t{ x: pEndX, y: pEndY, side: 'left' }\n\t\t);\n\n\t\tpPathElement.setAttribute('d', tmpPath);\n\t}\n}\n\nmodule.exports = PictServiceFlowConnectionRenderer;\n\n},{\"fable-serviceproviderbase\":4}],29:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-DataManager\n *\n * Manages flow data lifecycle: serialization, deserialization, and CRUD\n * operations for nodes and connections.\n *\n * Extracted from PictView-Flow.js to keep the view focused on\n * coordination and lifecycle rather than data manipulation.\n */\nclass PictServiceFlowDataManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowDataManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t// ---- Marshaling ----\n\n\t/**\n\t * Marshal data from AppData into the flow view\n\t */\n\tmarshalToView()\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tif (this._FlowView.options.FlowDataAddress)\n\t\t{\n\t\t\tconst tmpAddressSpace =\n\t\t\t{\n\t\t\t\tFable: this._FlowView.fable,\n\t\t\t\tPict: this._FlowView.pict || this._FlowView.fable,\n\t\t\t\tAppData: this._FlowView.pict ? this._FlowView.pict.AppData : this._FlowView.fable.AppData,\n\t\t\t\tBundle: this._FlowView.Bundle,\n\t\t\t\tOptions: this._FlowView.options\n\t\t\t};\n\t\t\tlet tmpData = this._FlowView.fable.manifest.getValueByHash(tmpAddressSpace, this._FlowView.options.FlowDataAddress);\n\t\t\tif (typeof tmpData === 'object' && tmpData !== null)\n\t\t\t{\n\t\t\t\tthis.setFlowData(tmpData);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Marshal data from the flow view back to AppData\n\t */\n\tmarshalFromView()\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tif (this._FlowView.options.FlowDataAddress)\n\t\t{\n\t\t\tconst tmpAddressSpace =\n\t\t\t{\n\t\t\t\tFable: this._FlowView.fable,\n\t\t\t\tPict: this._FlowView.pict || this._FlowView.fable,\n\t\t\t\tAppData: this._FlowView.pict ? this._FlowView.pict.AppData : this._FlowView.fable.AppData,\n\t\t\t\tBundle: this._FlowView.Bundle,\n\t\t\t\tOptions: this._FlowView.options\n\t\t\t};\n\t\t\tthis._FlowView.fable.manifest.setValueByHash(tmpAddressSpace, this._FlowView.options.FlowDataAddress, JSON.parse(JSON.stringify(this._FlowView._FlowData)));\n\t\t}\n\t}\n\n\t// ---- Flow Data Get/Set ----\n\n\t/**\n\t * Get the complete flow data object\n\t * @returns {Object} The flow data including nodes, connections, and view state\n\t */\n\tgetFlowData()\n\t{\n\t\tif (!this._FlowView) return {};\n\t\treturn JSON.parse(JSON.stringify(this._FlowView._FlowData));\n\t}\n\n\t/**\n\t * Set the complete flow data object and re-render\n\t * @param {Object} pFlowData - The flow data to set\n\t */\n\tsetFlowData(pFlowData)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tif (typeof pFlowData !== 'object' || pFlowData === null)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow setFlowData received invalid data');\n\t\t\treturn;\n\t\t}\n\n\t\tthis._FlowView._FlowData = {\n\t\t\tNodes: Array.isArray(pFlowData.Nodes) ? pFlowData.Nodes : [],\n\t\t\tConnections: Array.isArray(pFlowData.Connections) ? pFlowData.Connections : [],\n\t\t\tOpenPanels: Array.isArray(pFlowData.OpenPanels) ? pFlowData.OpenPanels : [],\n\t\t\tSavedLayouts: Array.isArray(pFlowData.SavedLayouts) ? pFlowData.SavedLayouts : [],\n\t\t\tViewState: Object.assign(\n\t\t\t\t{ PanX: 0, PanY: 0, Zoom: 1, SelectedNodeHash: null, SelectedConnectionHash: null, SelectedTetherHash: null },\n\t\t\t\tpFlowData.ViewState || {}\n\t\t\t)\n\t\t};\n\n\t\t// Merge any browser-persisted layouts into the newly loaded data\n\t\tif (this._FlowView._LayoutProvider)\n\t\t{\n\t\t\tthis._FlowView._LayoutProvider.loadPersistedLayouts();\n\t\t}\n\n\t\tif (this._FlowView.initialRenderComplete)\n\t\t{\n\t\t\tthis._FlowView.renderFlow();\n\t\t}\n\t}\n\n\t// ---- Node CRUD ----\n\n\t/**\n\t * Add a new node to the flow\n\t * @param {string} pType - The node type hash\n\t * @param {number} pX - X position\n\t * @param {number} pY - Y position\n\t * @param {string} [pTitle] - Optional title\n\t * @param {Object} [pData] - Optional additional data\n\t * @returns {Object} The created node\n\t */\n\taddNode(pType, pX, pY, pTitle, pData)\n\t{\n\t\tif (!this._FlowView) return null;\n\n\t\tlet tmpType = pType || this._FlowView.options.DefaultNodeType;\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpType);\n\n\t\tlet tmpNodeHash = `node-${this._FlowView.fable.getUUID()}`;\n\t\tlet tmpNode =\n\t\t{\n\t\t\tHash: tmpNodeHash,\n\t\t\tType: tmpType,\n\t\t\tX: pX || 100,\n\t\t\tY: pY || 100,\n\t\t\tWidth: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultWidth) || this._FlowView.options.DefaultNodeWidth,\n\t\t\tHeight: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultHeight) || this._FlowView.options.DefaultNodeHeight,\n\t\t\tTitle: pTitle || (tmpNodeTypeConfig && tmpNodeTypeConfig.Label) || 'New Node',\n\t\t\tPorts: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultPorts)\n\t\t\t\t? JSON.parse(JSON.stringify(tmpNodeTypeConfig.DefaultPorts))\n\t\t\t\t: [\n\t\t\t\t\t{ Hash: `port-in-${this._FlowView.fable.getUUID()}`, Direction: 'input', Side: 'left', Label: 'In' },\n\t\t\t\t\t{ Hash: `port-out-${this._FlowView.fable.getUUID()}`, Direction: 'output', Side: 'right', Label: 'Out' }\n\t\t\t\t],\n\t\t\tData: pData || {}\n\t\t};\n\n\t\t// Ensure each port has a unique hash\n\t\tfor (let i = 0; i < tmpNode.Ports.length; i++)\n\t\t{\n\t\t\tif (!tmpNode.Ports[i].Hash)\n\t\t\t{\n\t\t\t\ttmpNode.Ports[i].Hash = `port-${tmpNode.Ports[i].Direction}-${this._FlowView.fable.getUUID()}`;\n\t\t\t}\n\t\t}\n\n\t\tthis._FlowView._FlowData.Nodes.push(tmpNode);\n\t\tthis._FlowView.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onNodeAdded', tmpNode);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn tmpNode;\n\t}\n\n\t/**\n\t * Remove a node and all its connections\n\t * @param {string} pNodeHash - The hash of the node to remove\n\t * @returns {boolean} Whether the node was removed\n\t */\n\tremoveNode(pNodeHash)\n\t{\n\t\tif (!this._FlowView) return false;\n\n\t\tlet tmpNodeIndex = this._FlowView._FlowData.Nodes.findIndex((pNode) => pNode.Hash === pNodeHash);\n\t\tif (tmpNodeIndex < 0)\n\t\t{\n\t\t\tthis._FlowView.log.warn(`PictSectionFlow removeNode: node ${pNodeHash} not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpRemovedNode = this._FlowView._FlowData.Nodes.splice(tmpNodeIndex, 1)[0];\n\n\t\t// Remove all connections involving this node\n\t\tthis._FlowView._FlowData.Connections = this._FlowView._FlowData.Connections.filter((pConnection) =>\n\t\t{\n\t\t\treturn pConnection.SourceNodeHash !== pNodeHash && pConnection.TargetNodeHash !== pNodeHash;\n\t\t});\n\n\t\t// Close any open panels for this node\n\t\tthis._FlowView.closePanelForNode(pNodeHash);\n\n\t\t// Clear selection if this node was selected\n\t\tif (this._FlowView._FlowData.ViewState.SelectedNodeHash === pNodeHash)\n\t\t{\n\t\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = null;\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onNodeRemoved', tmpRemovedNode);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t// ---- Connection CRUD ----\n\n\t/**\n\t * Add a connection between two ports\n\t * @param {string} pSourceNodeHash\n\t * @param {string} pSourcePortHash\n\t * @param {string} pTargetNodeHash\n\t * @param {string} pTargetPortHash\n\t * @param {Object} [pData] - Optional additional data\n\t * @returns {Object|false} The created connection, or false if invalid\n\t */\n\taddConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData)\n\t{\n\t\tif (!this._FlowView) return false;\n\n\t\t// Validate that both nodes and ports exist\n\t\tlet tmpSourceNode = this._FlowView._FlowData.Nodes.find((pNode) => pNode.Hash === pSourceNodeHash);\n\t\tlet tmpTargetNode = this._FlowView._FlowData.Nodes.find((pNode) => pNode.Hash === pTargetNodeHash);\n\n\t\tif (!tmpSourceNode || !tmpTargetNode)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow addConnection: source or target node not found');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpSourcePort = tmpSourceNode.Ports.find((pPort) => pPort.Hash === pSourcePortHash);\n\t\tlet tmpTargetPort = tmpTargetNode.Ports.find((pPort) => pPort.Hash === pTargetPortHash);\n\n\t\tif (!tmpSourcePort || !tmpTargetPort)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow addConnection: source or target port not found');\n\t\t\treturn false;\n\t\t}\n\n\t\t// Prevent self-connections\n\t\tif (pSourceNodeHash === pTargetNodeHash)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow addConnection: cannot connect a node to itself');\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check for duplicate connections\n\t\tlet tmpDuplicate = this._FlowView._FlowData.Connections.find((pConn) =>\n\t\t{\n\t\t\treturn pConn.SourceNodeHash === pSourceNodeHash\n\t\t\t\t&& pConn.SourcePortHash === pSourcePortHash\n\t\t\t\t&& pConn.TargetNodeHash === pTargetNodeHash\n\t\t\t\t&& pConn.TargetPortHash === pTargetPortHash;\n\t\t});\n\t\tif (tmpDuplicate)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow addConnection: duplicate connection');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpConnection =\n\t\t{\n\t\t\tHash: `conn-${this._FlowView.fable.getUUID()}`,\n\t\t\tSourceNodeHash: pSourceNodeHash,\n\t\t\tSourcePortHash: pSourcePortHash,\n\t\t\tTargetNodeHash: pTargetNodeHash,\n\t\t\tTargetPortHash: pTargetPortHash,\n\t\t\tData: pData || {}\n\t\t};\n\n\t\tthis._FlowView._FlowData.Connections.push(tmpConnection);\n\t\tthis._FlowView.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionCreated', tmpConnection);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn tmpConnection;\n\t}\n\n\t/**\n\t * Remove a connection\n\t * @param {string} pConnectionHash - The hash of the connection to remove\n\t * @returns {boolean} Whether the connection was removed\n\t */\n\tremoveConnection(pConnectionHash)\n\t{\n\t\tif (!this._FlowView) return false;\n\n\t\tlet tmpConnectionIndex = this._FlowView._FlowData.Connections.findIndex((pConn) => pConn.Hash === pConnectionHash);\n\t\tif (tmpConnectionIndex < 0)\n\t\t{\n\t\t\tthis._FlowView.log.warn(`PictSectionFlow removeConnection: connection ${pConnectionHash} not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpRemovedConnection = this._FlowView._FlowData.Connections.splice(tmpConnectionIndex, 1)[0];\n\n\t\tif (this._FlowView._FlowData.ViewState.SelectedConnectionHash === pConnectionHash)\n\t\t{\n\t\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = null;\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionRemoved', tmpRemovedConnection);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn true;\n\t}\n}\n\nmodule.exports = PictServiceFlowDataManager;\n\n},{\"fable-serviceproviderbase\":4}],30:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * Interaction states for the flow diagram\n */\nconst INTERACTION_STATES =\n{\n\tIDLE: 'idle',\n\tDRAGGING_NODE: 'dragging-node',\n\tDRAGGING_PANEL: 'dragging-panel',\n\tDRAGGING_HANDLE: 'dragging-handle',\n\tCONNECTING: 'connecting',\n\tPANNING: 'panning',\n\tRESIZING_PANEL: 'resizing-panel'\n};\n\nclass PictServiceFlowInteractionManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowInteractionManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\tthis._SVGElement = null;\n\t\tthis._ViewportElement = null;\n\n\t\t// Interaction state\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\n\t\t// Drag state\n\t\tthis._DragNodeHash = null;\n\t\tthis._DragStartX = 0;\n\t\tthis._DragStartY = 0;\n\t\tthis._DragNodeStartX = 0;\n\t\tthis._DragNodeStartY = 0;\n\n\t\t// Panel drag state\n\t\tthis._DragPanelHash = null;\n\t\tthis._DragPanelStartX = 0;\n\t\tthis._DragPanelStartY = 0;\n\t\tthis._DragPanelDataStartX = 0;\n\t\tthis._DragPanelDataStartY = 0;\n\n\t\t// Handle drag state\n\t\tthis._DragHandleConnectionHash = null;\n\t\tthis._DragHandlePanelHash = null;\n\t\tthis._DragHandleType = null;\n\t\tthis._DragHandleIsTether = false;\n\n\t\t// Pan state\n\t\tthis._PanStartX = 0;\n\t\tthis._PanStartY = 0;\n\t\tthis._PanStartPanX = 0;\n\t\tthis._PanStartPanY = 0;\n\n\t\t// Connection drag state\n\t\tthis._ConnectSourceNodeHash = null;\n\t\tthis._ConnectSourcePortHash = null;\n\t\tthis._ConnectDragLine = null;\n\n\t\t// Double-click detection\n\t\tthis._LastClickTime = 0;\n\t\tthis._LastClickNodeHash = null;\n\t\tthis._DoubleClickThreshold = 400;\n\n\t\t// Panel resize state\n\t\tthis._ResizePanelHash = null;\n\t\tthis._ResizeStartY = 0;\n\t\tthis._ResizePanelStartHeight = 0;\n\n\t\t// Double-click detection for connections\n\t\tthis._LastConnectionClickTime = 0;\n\t\tthis._LastConnectionClickHash = null;\n\n\t\t// Double-click detection for tethers\n\t\tthis._LastTetherClickTime = 0;\n\t\tthis._LastTetherClickHash = null;\n\n\t\t// Double-click detection for handles\n\t\tthis._LastHandleClickTime = 0;\n\t\tthis._LastHandleClickHash = null;\n\t\tthis._LastHandleClickType = null;\n\n\t\t// Bound event handlers (for removeEventListener)\n\t\tthis._boundOnPointerDown = this._onPointerDown.bind(this);\n\t\tthis._boundOnPointerMove = this._onPointerMove.bind(this);\n\t\tthis._boundOnPointerUp = this._onPointerUp.bind(this);\n\t\tthis._boundOnWheel = this._onWheel.bind(this);\n\t\tthis._boundOnKeyDown = this._onKeyDown.bind(this);\n\t}\n\n\t/**\n\t * Initialize event listeners on the SVG element\n\t * @param {SVGSVGElement} pSVGElement\n\t * @param {SVGGElement} pViewportElement\n\t */\n\tinitialize(pSVGElement, pViewportElement)\n\t{\n\t\tthis._SVGElement = pSVGElement;\n\t\tthis._ViewportElement = pViewportElement;\n\n\t\tif (!this._SVGElement) return;\n\n\t\t// Use pointer events for unified mouse/touch handling\n\t\tthis._SVGElement.addEventListener('pointerdown', this._boundOnPointerDown);\n\t\tthis._SVGElement.addEventListener('pointermove', this._boundOnPointerMove);\n\t\tthis._SVGElement.addEventListener('pointerup', this._boundOnPointerUp);\n\t\tthis._SVGElement.addEventListener('pointerleave', this._boundOnPointerUp);\n\t\tthis._SVGElement.addEventListener('wheel', this._boundOnWheel, { passive: false });\n\n\t\t// Keyboard events for delete\n\t\tdocument.addEventListener('keydown', this._boundOnKeyDown);\n\n\t\t// Handle right-click: add/remove bezier handles on connections\n\t\tthis._SVGElement.addEventListener('contextmenu', (pEvent) =>\n\t\t{\n\t\t\tpEvent.preventDefault();\n\n\t\t\tlet tmpTarget = pEvent.target;\n\t\t\tlet tmpElementType = this._getElementType(tmpTarget);\n\n\t\t\tswitch (tmpElementType)\n\t\t\t{\n\t\t\t\tcase 'connection':\n\t\t\t\tcase 'connection-hitarea':\n\t\t\t\t\tthis._addBezierHandle(tmpTarget, pEvent);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'connection-handle':\n\t\t\t\t\tthis._removeBezierHandle(tmpTarget);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'tether':\n\t\t\t\tcase 'tether-hitarea':\n\t\t\t\t\tthis._addTetherBezierHandle(tmpTarget, pEvent);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'tether-handle':\n\t\t\t\t\tthis._removeTetherBezierHandle(tmpTarget);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Remove all event listeners\n\t */\n\tdestroy()\n\t{\n\t\tif (this._SVGElement)\n\t\t{\n\t\t\tthis._SVGElement.removeEventListener('pointerdown', this._boundOnPointerDown);\n\t\t\tthis._SVGElement.removeEventListener('pointermove', this._boundOnPointerMove);\n\t\t\tthis._SVGElement.removeEventListener('pointerup', this._boundOnPointerUp);\n\t\t\tthis._SVGElement.removeEventListener('pointerleave', this._boundOnPointerUp);\n\t\t\tthis._SVGElement.removeEventListener('wheel', this._boundOnWheel);\n\t\t}\n\n\t\tdocument.removeEventListener('keydown', this._boundOnKeyDown);\n\t}\n\n\t/**\n\t * Handle pointer down event\n\t * @param {PointerEvent} pEvent\n\t */\n\t_onPointerDown(pEvent)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpTarget = pEvent.target;\n\t\tlet tmpElementType = this._getElementType(tmpTarget);\n\n\t\t// Check if click is inside a panel content area — let HTML handle its own events\n\t\tif (tmpTarget.closest && tmpTarget.closest('.pict-flow-panel-content'))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Capture pointer for smooth dragging (left-click only — right-click\n\t\t// needs the real target for contextmenu handle add/remove)\n\t\tif (pEvent.button === 0)\n\t\t{\n\t\t\tthis._SVGElement.setPointerCapture(pEvent.pointerId);\n\t\t}\n\n\t\tswitch (tmpElementType)\n\t\t{\n\t\t\tcase 'port':\n\t\t\t\tthis._startConnection(pEvent, tmpTarget);\n\t\t\t\tbreak;\n\n\t\t\tcase 'node':\n\t\t\tcase 'node-body':\n\t\t\tcase 'panel-indicator':\n\t\t\t{\n\t\t\t\tlet tmpNodeHash = this._getNodeHash(tmpTarget);\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on same node\n\t\t\t\tif (tmpNodeHash && tmpNodeHash === this._LastClickNodeHash\n\t\t\t\t\t&& (tmpNow - this._LastClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\t// Double-click: toggle panel\n\t\t\t\t\tthis._LastClickTime = 0;\n\t\t\t\t\tthis._LastClickNodeHash = null;\n\t\t\t\t\tthis._FlowView.togglePanel(tmpNodeHash);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Single click: start node drag\n\t\t\t\t\tthis._LastClickTime = tmpNow;\n\t\t\t\t\tthis._LastClickNodeHash = tmpNodeHash;\n\t\t\t\t\tthis._startNodeDrag(pEvent, tmpTarget);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'panel-titlebar':\n\t\t\t\tthis._startPanelDrag(pEvent, tmpTarget);\n\t\t\t\tbreak;\n\n\t\t\tcase 'panel-resize':\n\t\t\t\tthis._startPanelResize(pEvent, tmpTarget);\n\t\t\t\tbreak;\n\n\t\t\tcase 'panel-close':\n\t\t\t{\n\t\t\t\tlet tmpPanelHash = this._getPanelHash(tmpTarget);\n\t\t\t\tif (tmpPanelHash)\n\t\t\t\t{\n\t\t\t\t\tthis._FlowView.closePanel(tmpPanelHash);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'connection-handle':\n\t\t\t{\n\t\t\t\tlet tmpConnectionHash = this._getConnectionHash(tmpTarget);\n\t\t\t\tlet tmpHandleType = tmpTarget.getAttribute('data-handle-type');\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on handle to toggle mode\n\t\t\t\tif (tmpConnectionHash === this._LastHandleClickHash\n\t\t\t\t\t&& tmpHandleType === this._LastHandleClickType\n\t\t\t\t\t&& (tmpNow - this._LastHandleClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\tthis._toggleConnectionLineMode(tmpConnectionHash);\n\t\t\t\t\tthis._LastHandleClickTime = 0;\n\t\t\t\t\tthis._LastHandleClickHash = null;\n\t\t\t\t\tthis._LastHandleClickType = null;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._LastHandleClickTime = tmpNow;\n\t\t\t\t\tthis._LastHandleClickHash = tmpConnectionHash;\n\t\t\t\t\tthis._LastHandleClickType = tmpHandleType;\n\t\t\t\t\tthis._startHandleDrag(pEvent, tmpConnectionHash, null, tmpHandleType, false);\n\t\t\t\t}\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'tether':\n\t\t\tcase 'tether-hitarea':\n\t\t\t{\n\t\t\t\tlet tmpPanelHash = this._getPanelHash(tmpTarget);\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on same tether to add a handle\n\t\t\t\tif (tmpPanelHash && tmpPanelHash === this._LastTetherClickHash\n\t\t\t\t\t&& (tmpNow - this._LastTetherClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\tthis._LastTetherClickTime = 0;\n\t\t\t\t\tthis._LastTetherClickHash = null;\n\t\t\t\t\tthis._addTetherBezierHandle(tmpTarget, pEvent);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._LastTetherClickTime = tmpNow;\n\t\t\t\t\tthis._LastTetherClickHash = tmpPanelHash;\n\t\t\t\t\tthis._selectTether(tmpTarget);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'tether-handle':\n\t\t\t{\n\t\t\t\tlet tmpPanelHash = this._getPanelHash(tmpTarget);\n\t\t\t\tlet tmpHandleType = tmpTarget.getAttribute('data-handle-type');\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on tether handle to toggle mode\n\t\t\t\tif (tmpPanelHash === this._LastHandleClickHash\n\t\t\t\t\t&& tmpHandleType === this._LastHandleClickType\n\t\t\t\t\t&& (tmpNow - this._LastHandleClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\tthis._toggleTetherLineMode(tmpPanelHash);\n\t\t\t\t\tthis._LastHandleClickTime = 0;\n\t\t\t\t\tthis._LastHandleClickHash = null;\n\t\t\t\t\tthis._LastHandleClickType = null;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._LastHandleClickTime = tmpNow;\n\t\t\t\t\tthis._LastHandleClickHash = tmpPanelHash;\n\t\t\t\t\tthis._LastHandleClickType = tmpHandleType;\n\t\t\t\t\tthis._startHandleDrag(pEvent, null, tmpPanelHash, tmpHandleType, true);\n\t\t\t\t}\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'connection':\n\t\t\tcase 'connection-hitarea':\n\t\t\t{\n\t\t\t\tlet tmpConnectionHash = this._getConnectionHash(tmpTarget);\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on same connection to add a handle\n\t\t\t\tif (tmpConnectionHash && tmpConnectionHash === this._LastConnectionClickHash\n\t\t\t\t\t&& (tmpNow - this._LastConnectionClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\tthis._LastConnectionClickTime = 0;\n\t\t\t\t\tthis._LastConnectionClickHash = null;\n\t\t\t\t\tthis._addBezierHandle(tmpTarget, pEvent);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._LastConnectionClickTime = tmpNow;\n\t\t\t\t\tthis._LastConnectionClickHash = tmpConnectionHash;\n\t\t\t\t\tthis._selectConnection(tmpTarget);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\t// Click on background - start panning or deselect\n\t\t\t\tif (pEvent.button === 0 && this._FlowView.options.EnablePanning)\n\t\t\t\t{\n\t\t\t\t\tthis._startPanning(pEvent);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Handle pointer move event\n\t * @param {PointerEvent} pEvent\n\t */\n\t_onPointerMove(pEvent)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tswitch (this._State)\n\t\t{\n\t\t\tcase INTERACTION_STATES.DRAGGING_NODE:\n\t\t\t\tthis._onNodeDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.DRAGGING_PANEL:\n\t\t\t\tthis._onPanelDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.DRAGGING_HANDLE:\n\t\t\t\tthis._onHandleDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.CONNECTING:\n\t\t\t\tthis._onConnectionDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.RESIZING_PANEL:\n\t\t\t\tthis._onPanelResize(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.PANNING:\n\t\t\t\tthis._onPan(pEvent);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Handle pointer up event\n\t * @param {PointerEvent} pEvent\n\t */\n\t_onPointerUp(pEvent)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\t// Release pointer capture\n\t\tif (this._SVGElement.hasPointerCapture && this._SVGElement.hasPointerCapture(pEvent.pointerId))\n\t\t{\n\t\t\tthis._SVGElement.releasePointerCapture(pEvent.pointerId);\n\t\t}\n\n\t\tswitch (this._State)\n\t\t{\n\t\t\tcase INTERACTION_STATES.DRAGGING_NODE:\n\t\t\t\tthis._endNodeDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.DRAGGING_PANEL:\n\t\t\t\tthis._endPanelDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.DRAGGING_HANDLE:\n\t\t\t\tthis._endHandleDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.RESIZING_PANEL:\n\t\t\t\tthis._endPanelResize(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.CONNECTING:\n\t\t\t\tthis._endConnection(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.PANNING:\n\t\t\t\tthis._endPanning(pEvent);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Handle mouse wheel for zoom\n\t * @param {WheelEvent} pEvent\n\t */\n\t_onWheel(pEvent)\n\t{\n\t\tif (!this._FlowView || !this._FlowView.options.EnableZooming) return;\n\n\t\tpEvent.preventDefault();\n\n\t\tlet tmpDelta = pEvent.deltaY > 0 ? -this._FlowView.options.ZoomStep : this._FlowView.options.ZoomStep;\n\t\tlet tmpNewZoom = this._FlowView.viewState.Zoom + tmpDelta;\n\n\t\t// Zoom toward mouse position\n\t\tlet tmpRect = this._SVGElement.getBoundingClientRect();\n\t\tlet tmpMouseX = pEvent.clientX - tmpRect.left;\n\t\tlet tmpMouseY = pEvent.clientY - tmpRect.top;\n\n\t\tthis._FlowView.setZoom(tmpNewZoom, tmpMouseX, tmpMouseY);\n\t}\n\n\t/**\n\t * Handle keyboard events\n\t * @param {KeyboardEvent} pEvent\n\t */\n\t_onKeyDown(pEvent)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\t// Only handle events when the flow is focused/visible\n\t\tif (pEvent.key === 'Delete' || pEvent.key === 'Backspace')\n\t\t{\n\t\t\t// Don't delete if user is typing in an input or inside a panel\n\t\t\tif (pEvent.target && (pEvent.target.tagName === 'INPUT' || pEvent.target.tagName === 'TEXTAREA' || pEvent.target.tagName === 'SELECT'))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pEvent.target && pEvent.target.closest && pEvent.target.closest('.pict-flow-panel'))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis._FlowView.deleteSelected();\n\t\t\tpEvent.preventDefault();\n\t\t}\n\t\telse if (pEvent.key === 'Escape')\n\t\t{\n\t\t\tif (this._State === INTERACTION_STATES.CONNECTING)\n\t\t\t{\n\t\t\t\tthis._cancelConnection();\n\t\t\t}\n\n\t\t\t// Exit fullscreen if currently in fullscreen mode\n\t\t\tif (this._FlowView._IsFullscreen)\n\t\t\t{\n\t\t\t\tthis._FlowView.exitFullscreen();\n\t\t\t\t// Update the toolbar button text\n\t\t\t\tif (this._FlowView._ToolbarView)\n\t\t\t\t{\n\t\t\t\t\tlet tmpFlowViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\t\t\t\tlet tmpBtnElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-${tmpFlowViewIdentifier}`);\n\t\t\t\t\tif (tmpBtnElements.length > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBtnElements[0].innerHTML = '&#x26F6; Fullscreen';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpEvent.preventDefault();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis._FlowView.deselectAll();\n\t\t}\n\t}\n\n\t// ---- Node Dragging ----\n\n\t_startNodeDrag(pEvent, pTarget)\n\t{\n\t\tif (!this._FlowView.options.EnableNodeDragging) return;\n\n\t\tlet tmpNodeHash = this._getNodeHash(pTarget);\n\t\tif (!tmpNodeHash) return;\n\n\t\tthis._FlowView.selectNode(tmpNodeHash);\n\n\t\tlet tmpNode = this._FlowView.getNode(tmpNodeHash);\n\t\tif (!tmpNode) return;\n\n\t\tthis._State = INTERACTION_STATES.DRAGGING_NODE;\n\t\tthis._DragNodeHash = tmpNodeHash;\n\t\tthis._DragStartX = pEvent.clientX;\n\t\tthis._DragStartY = pEvent.clientY;\n\t\tthis._DragNodeStartX = tmpNode.X;\n\t\tthis._DragNodeStartY = tmpNode.Y;\n\n\t\tthis._SVGElement.classList.add('panning');\n\n\t\tlet tmpNodeGroup = this._FlowView._NodesLayer.querySelector(`[data-node-hash=\"${tmpNodeHash}\"]`);\n\t\tif (tmpNodeGroup)\n\t\t{\n\t\t\ttmpNodeGroup.classList.add('dragging');\n\t\t}\n\t}\n\n\t_onNodeDrag(pEvent)\n\t{\n\t\tif (!this._DragNodeHash) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\t\tlet tmpDX = (pEvent.clientX - this._DragStartX) / tmpVS.Zoom;\n\t\tlet tmpDY = (pEvent.clientY - this._DragStartY) / tmpVS.Zoom;\n\n\t\tlet tmpNewX = this._DragNodeStartX + tmpDX;\n\t\tlet tmpNewY = this._DragNodeStartY + tmpDY;\n\n\t\tthis._FlowView.updateNodePosition(this._DragNodeHash, tmpNewX, tmpNewY);\n\t}\n\n\t_endNodeDrag(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\n\t\tlet tmpNodeGroup = this._FlowView._NodesLayer.querySelector(`[data-node-hash=\"${this._DragNodeHash}\"]`);\n\t\tif (tmpNodeGroup)\n\t\t{\n\t\t\ttmpNodeGroup.classList.remove('dragging');\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tlet tmpNode = this._FlowView.getNode(this._DragNodeHash);\n\t\tif (tmpNode && this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onNodeMoved', tmpNode);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._DragNodeHash = null;\n\t}\n\n\t// ---- Panel Dragging ----\n\n\t_startPanelDrag(pEvent, pTarget)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (!tmpPanelHash) return;\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === tmpPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tthis._State = INTERACTION_STATES.DRAGGING_PANEL;\n\t\tthis._DragPanelHash = tmpPanelHash;\n\t\tthis._DragPanelStartX = pEvent.clientX;\n\t\tthis._DragPanelStartY = pEvent.clientY;\n\t\tthis._DragPanelDataStartX = tmpPanel.X;\n\t\tthis._DragPanelDataStartY = tmpPanel.Y;\n\n\t\tthis._SVGElement.classList.add('panning');\n\t}\n\n\t_onPanelDrag(pEvent)\n\t{\n\t\tif (!this._DragPanelHash) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\t\tlet tmpDX = (pEvent.clientX - this._DragPanelStartX) / tmpVS.Zoom;\n\t\tlet tmpDY = (pEvent.clientY - this._DragPanelStartY) / tmpVS.Zoom;\n\n\t\tlet tmpNewX = this._DragPanelDataStartX + tmpDX;\n\t\tlet tmpNewY = this._DragPanelDataStartY + tmpDY;\n\n\t\tthis._FlowView.updatePanelPosition(this._DragPanelHash, tmpNewX, tmpNewY);\n\t}\n\n\t_endPanelDrag(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\n\t\tthis._FlowView.marshalFromView();\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === this._DragPanelHash);\n\t\tif (tmpPanel && this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onPanelMoved', tmpPanel);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._DragPanelHash = null;\n\t}\n\n\t// ---- Panel Resizing ----\n\n\t_startPanelResize(pEvent, pTarget)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (!tmpPanelHash) return;\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === tmpPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tthis._State = INTERACTION_STATES.RESIZING_PANEL;\n\t\tthis._ResizePanelHash = tmpPanelHash;\n\t\tthis._ResizeStartY = pEvent.clientY;\n\t\tthis._ResizePanelStartHeight = tmpPanel.Height;\n\n\t\tthis._SVGElement.classList.add('panning');\n\t}\n\n\t_onPanelResize(pEvent)\n\t{\n\t\tif (!this._ResizePanelHash) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\t\tlet tmpDY = (pEvent.clientY - this._ResizeStartY) / tmpVS.Zoom;\n\t\tlet tmpNewHeight = Math.max(120, this._ResizePanelStartHeight + tmpDY);\n\n\t\t// Update the panel data\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === this._ResizePanelHash);\n\t\tif (tmpPanel)\n\t\t{\n\t\t\ttmpPanel.Height = tmpNewHeight;\n\t\t}\n\n\t\t// Update the foreignObject height directly for smooth resizing\n\t\tif (this._FlowView._PanelsLayer)\n\t\t{\n\t\t\tlet tmpFO = this._FlowView._PanelsLayer.querySelector('[data-panel-hash=\"' + this._ResizePanelHash + '\"]');\n\t\t\tif (tmpFO)\n\t\t\t{\n\t\t\t\ttmpFO.setAttribute('height', String(tmpNewHeight));\n\t\t\t}\n\t\t}\n\t}\n\n\t_endPanelResize(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\n\t\t// Re-render to sync tethers\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._ResizePanelHash = null;\n\t}\n\n\t// ---- Handle Dragging ----\n\n\t_startHandleDrag(pEvent, pConnectionHash, pPanelHash, pHandleType, pIsTether)\n\t{\n\t\tthis._State = INTERACTION_STATES.DRAGGING_HANDLE;\n\t\tthis._DragHandleConnectionHash = pConnectionHash;\n\t\tthis._DragHandlePanelHash = pPanelHash;\n\t\tthis._DragHandleType = pHandleType;\n\t\tthis._DragHandleIsTether = pIsTether;\n\t\tthis._DragStartX = pEvent.clientX;\n\t\tthis._DragStartY = pEvent.clientY;\n\t\tthis._SVGElement.classList.add('panning');\n\t}\n\n\t_onHandleDrag(pEvent)\n\t{\n\t\tlet tmpCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);\n\n\t\tif (this._DragHandleIsTether)\n\t\t{\n\t\t\tthis._FlowView.updateTetherHandle(\n\t\t\t\tthis._DragHandlePanelHash,\n\t\t\t\tthis._DragHandleType,\n\t\t\t\ttmpCoords.x,\n\t\t\t\ttmpCoords.y\n\t\t\t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._FlowView.updateConnectionHandle(\n\t\t\t\tthis._DragHandleConnectionHash,\n\t\t\t\tthis._DragHandleType,\n\t\t\t\ttmpCoords.x,\n\t\t\t\ttmpCoords.y\n\t\t\t);\n\t\t}\n\t}\n\n\t_endHandleDrag(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\n\t\t\tif (this._DragHandleIsTether)\n\t\t\t{\n\t\t\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === this._DragHandlePanelHash);\n\t\t\t\tif (tmpPanel)\n\t\t\t\t{\n\t\t\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onTetherHandleMoved', tmpPanel);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlet tmpConnection = this._FlowView.getConnection(this._DragHandleConnectionHash);\n\t\t\t\tif (tmpConnection)\n\t\t\t\t{\n\t\t\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionHandleMoved', tmpConnection);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._DragHandleConnectionHash = null;\n\t\tthis._DragHandlePanelHash = null;\n\t\tthis._DragHandleType = null;\n\t\tthis._DragHandleIsTether = false;\n\t}\n\n\t// ---- Right-Click Handle Add/Remove ----\n\n\t/**\n\t * Add a bezier handle to a connection at the right-click position.\n\t * @param {Element} pTarget - The SVG element that was right-clicked\n\t * @param {MouseEvent} pEvent - The contextmenu event\n\t */\n\t_addBezierHandle(pTarget, pEvent)\n\t{\n\t\tlet tmpConnectionHash = this._getConnectionHash(pTarget);\n\t\tif (!tmpConnectionHash) return;\n\n\t\t// Select the connection so handle circles are rendered after the re-render\n\t\tthis._FlowView.selectConnection(tmpConnectionHash);\n\n\t\tlet tmpCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);\n\t\tthis._FlowView.addConnectionHandle(tmpConnectionHash, tmpCoords.x, tmpCoords.y);\n\t}\n\n\t/**\n\t * Remove a bezier handle from a connection.\n\t * @param {Element} pTarget - The handle SVG element that was right-clicked\n\t */\n\t_removeBezierHandle(pTarget)\n\t{\n\t\tlet tmpConnectionHash = this._getConnectionHash(pTarget);\n\t\tif (!tmpConnectionHash) return;\n\n\t\tlet tmpHandleType = pTarget.getAttribute('data-handle-type');\n\t\tif (!tmpHandleType || !tmpHandleType.startsWith('bezier-handle-')) return;\n\n\t\tlet tmpIndex = parseInt(tmpHandleType.replace('bezier-handle-', ''), 10);\n\t\tif (isNaN(tmpIndex)) return;\n\n\t\tthis._FlowView.removeConnectionHandle(tmpConnectionHash, tmpIndex);\n\t}\n\n\t/**\n\t * Add a bezier handle to a tether.\n\t * @param {Element} pTarget - The tether SVG element that was right-clicked or double-clicked\n\t * @param {Event} pEvent - The mouse event (for coordinate extraction)\n\t */\n\t_addTetherBezierHandle(pTarget, pEvent)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (!tmpPanelHash) return;\n\n\t\t// Select the tether so handles render after re-render\n\t\tthis._FlowView.selectTether(tmpPanelHash);\n\n\t\tlet tmpCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);\n\t\tthis._FlowView.addTetherHandle(tmpPanelHash, tmpCoords.x, tmpCoords.y);\n\t}\n\n\t/**\n\t * Remove a bezier handle from a tether.\n\t * @param {Element} pTarget - The tether handle SVG element that was right-clicked\n\t */\n\t_removeTetherBezierHandle(pTarget)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (!tmpPanelHash) return;\n\n\t\tlet tmpHandleType = pTarget.getAttribute('data-handle-type');\n\t\tif (!tmpHandleType || !tmpHandleType.startsWith('bezier-handle-')) return;\n\n\t\tlet tmpIndex = parseInt(tmpHandleType.replace('bezier-handle-', ''), 10);\n\t\tif (isNaN(tmpIndex)) return;\n\n\t\tthis._FlowView.removeTetherHandle(tmpPanelHash, tmpIndex);\n\t}\n\n\t// ---- Line Mode Toggling ----\n\n\t_toggleConnectionLineMode(pConnectionHash)\n\t{\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection) return;\n\n\t\tif (!tmpConnection.Data) tmpConnection.Data = {};\n\n\t\tlet tmpCurrentMode = tmpConnection.Data.LineMode || 'bezier';\n\t\ttmpConnection.Data.LineMode = (tmpCurrentMode === 'bezier') ? 'orthogonal' : 'bezier';\n\n\t\t// Reset handle positions when switching modes\n\t\ttmpConnection.Data.HandleCustomized = false;\n\t\ttmpConnection.Data.BezierHandles = [];\n\t\ttmpConnection.Data.BezierHandleX = null;\n\t\ttmpConnection.Data.BezierHandleY = null;\n\t\ttmpConnection.Data.OrthoCorner1X = null;\n\t\ttmpConnection.Data.OrthoCorner1Y = null;\n\t\ttmpConnection.Data.OrthoCorner2X = null;\n\t\ttmpConnection.Data.OrthoCorner2Y = null;\n\t\ttmpConnection.Data.OrthoMidOffset = 0;\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionModeChanged', tmpConnection);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\t}\n\n\t_toggleTetherLineMode(pPanelHash)\n\t{\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tif (this._FlowView._TetherService)\n\t\t{\n\t\t\tthis._FlowView._TetherService.toggleLineMode(tmpPanel);\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onTetherModeChanged', tmpPanel);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\t}\n\n\t// ---- Tether Selection ----\n\n\t_selectTether(pTarget)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (tmpPanelHash)\n\t\t{\n\t\t\tthis._FlowView.selectTether(tmpPanelHash);\n\t\t}\n\t}\n\n\t// ---- Connection Creation ----\n\n\t_startConnection(pEvent, pTarget)\n\t{\n\t\tif (!this._FlowView.options.EnableConnectionCreation) return;\n\n\t\tlet tmpNodeHash = pTarget.getAttribute('data-node-hash');\n\t\tlet tmpPortHash = pTarget.getAttribute('data-port-hash');\n\t\tlet tmpPortDirection = pTarget.getAttribute('data-port-direction');\n\n\t\tif (!tmpNodeHash || !tmpPortHash) return;\n\n\t\tif (tmpPortDirection !== 'output')\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.CONNECTING;\n\t\tthis._ConnectSourceNodeHash = tmpNodeHash;\n\t\tthis._ConnectSourcePortHash = tmpPortHash;\n\n\t\tthis._SVGElement.classList.add('connecting');\n\n\t\tlet tmpPortPos = this._FlowView.getPortPosition(tmpNodeHash, tmpPortHash);\n\t\tif (tmpPortPos)\n\t\t{\n\t\t\tthis._ConnectDragLine = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n\t\t\tthis._ConnectDragLine.setAttribute('class', 'pict-flow-drag-connection');\n\t\t\tthis._ConnectDragLine.setAttribute('d', `M ${tmpPortPos.x} ${tmpPortPos.y} L ${tmpPortPos.x} ${tmpPortPos.y}`);\n\t\t\tthis._FlowView._ViewportElement.appendChild(this._ConnectDragLine);\n\t\t}\n\n\t\tpEvent.stopPropagation();\n\t}\n\n\t_onConnectionDrag(pEvent)\n\t{\n\t\tif (!this._ConnectDragLine) return;\n\n\t\tlet tmpSourcePos = this._FlowView.getPortPosition(this._ConnectSourceNodeHash, this._ConnectSourcePortHash);\n\t\tif (!tmpSourcePos) return;\n\n\t\tlet tmpEndCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);\n\n\t\tlet tmpDX = Math.abs(tmpEndCoords.x - tmpSourcePos.x) * 0.5;\n\t\tlet tmpPath = `M ${tmpSourcePos.x} ${tmpSourcePos.y} C ${tmpSourcePos.x + tmpDX} ${tmpSourcePos.y}, ${tmpEndCoords.x - tmpDX} ${tmpEndCoords.y}, ${tmpEndCoords.x} ${tmpEndCoords.y}`;\n\t\tthis._ConnectDragLine.setAttribute('d', tmpPath);\n\t}\n\n\t_endConnection(pEvent)\n\t{\n\t\tif (this._ConnectDragLine && this._ConnectDragLine.parentNode)\n\t\t{\n\t\t\tthis._ConnectDragLine.parentNode.removeChild(this._ConnectDragLine);\n\t\t}\n\t\tthis._ConnectDragLine = null;\n\n\t\tthis._SVGElement.classList.remove('connecting');\n\n\t\tlet tmpTarget = document.elementFromPoint(pEvent.clientX, pEvent.clientY);\n\t\tif (tmpTarget)\n\t\t{\n\t\t\tlet tmpTargetPortHash = tmpTarget.getAttribute('data-port-hash');\n\t\t\tlet tmpTargetNodeHash = tmpTarget.getAttribute('data-node-hash');\n\t\t\tlet tmpTargetPortDirection = tmpTarget.getAttribute('data-port-direction');\n\n\t\t\tif (tmpTargetPortHash && tmpTargetNodeHash && tmpTargetPortDirection === 'input')\n\t\t\t{\n\t\t\t\tthis._FlowView.addConnection(\n\t\t\t\t\tthis._ConnectSourceNodeHash,\n\t\t\t\t\tthis._ConnectSourcePortHash,\n\t\t\t\t\ttmpTargetNodeHash,\n\t\t\t\t\ttmpTargetPortHash\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._ConnectSourceNodeHash = null;\n\t\tthis._ConnectSourcePortHash = null;\n\t}\n\n\t_cancelConnection()\n\t{\n\t\tif (this._ConnectDragLine && this._ConnectDragLine.parentNode)\n\t\t{\n\t\t\tthis._ConnectDragLine.parentNode.removeChild(this._ConnectDragLine);\n\t\t}\n\t\tthis._ConnectDragLine = null;\n\n\t\tthis._SVGElement.classList.remove('connecting');\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._ConnectSourceNodeHash = null;\n\t\tthis._ConnectSourcePortHash = null;\n\t}\n\n\t// ---- Panning ----\n\n\t_startPanning(pEvent)\n\t{\n\t\tthis._FlowView.deselectAll();\n\n\t\tthis._State = INTERACTION_STATES.PANNING;\n\t\tthis._PanStartX = pEvent.clientX;\n\t\tthis._PanStartY = pEvent.clientY;\n\t\tthis._PanStartPanX = this._FlowView.viewState.PanX;\n\t\tthis._PanStartPanY = this._FlowView.viewState.PanY;\n\n\t\tthis._SVGElement.classList.add('panning');\n\t}\n\n\t_onPan(pEvent)\n\t{\n\t\tlet tmpDX = pEvent.clientX - this._PanStartX;\n\t\tlet tmpDY = pEvent.clientY - this._PanStartY;\n\n\t\tthis._FlowView.viewState.PanX = this._PanStartPanX + tmpDX;\n\t\tthis._FlowView.viewState.PanY = this._PanStartPanY + tmpDY;\n\n\t\tthis._FlowView.updateViewportTransform();\n\t}\n\n\t_endPanning(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t}\n\n\t// ---- Connection Selection ----\n\n\t_selectConnection(pTarget)\n\t{\n\t\tlet tmpConnectionHash = this._getConnectionHash(pTarget);\n\t\tif (tmpConnectionHash)\n\t\t{\n\t\t\tthis._FlowView.selectConnection(tmpConnectionHash);\n\t\t}\n\t}\n\n\t// ---- Utilities ----\n\n\t_getElementType(pTarget)\n\t{\n\t\tif (!pTarget) return 'background';\n\n\t\tlet tmpType = pTarget.getAttribute ? pTarget.getAttribute('data-element-type') : null;\n\t\tif (tmpType) return tmpType;\n\n\t\tlet tmpParent = pTarget.parentElement;\n\t\tlet tmpDepth = 0;\n\t\twhile (tmpParent && tmpDepth < 5)\n\t\t{\n\t\t\ttmpType = tmpParent.getAttribute ? tmpParent.getAttribute('data-element-type') : null;\n\t\t\tif (tmpType) return tmpType;\n\t\t\ttmpParent = tmpParent.parentElement;\n\t\t\ttmpDepth++;\n\t\t}\n\n\t\treturn 'background';\n\t}\n\n\t_getNodeHash(pTarget)\n\t{\n\t\tif (!pTarget) return null;\n\n\t\tlet tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-node-hash') : null;\n\t\tif (tmpHash) return tmpHash;\n\n\t\tlet tmpParent = pTarget.parentElement;\n\t\tlet tmpDepth = 0;\n\t\twhile (tmpParent && tmpDepth < 5)\n\t\t{\n\t\t\ttmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-node-hash') : null;\n\t\t\tif (tmpHash) return tmpHash;\n\t\t\ttmpParent = tmpParent.parentElement;\n\t\t\ttmpDepth++;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t_getPanelHash(pTarget)\n\t{\n\t\tif (!pTarget) return null;\n\n\t\tlet tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-panel-hash') : null;\n\t\tif (tmpHash) return tmpHash;\n\n\t\tlet tmpParent = pTarget.parentElement;\n\t\tlet tmpDepth = 0;\n\t\twhile (tmpParent && tmpDepth < 5)\n\t\t{\n\t\t\ttmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-panel-hash') : null;\n\t\t\tif (tmpHash) return tmpHash;\n\t\t\ttmpParent = tmpParent.parentElement;\n\t\t\ttmpDepth++;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t_getConnectionHash(pTarget)\n\t{\n\t\tif (!pTarget) return null;\n\n\t\tlet tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-connection-hash') : null;\n\t\tif (tmpHash) return tmpHash;\n\n\t\tlet tmpParent = pTarget.parentElement;\n\t\tlet tmpDepth = 0;\n\t\twhile (tmpParent && tmpDepth < 5)\n\t\t{\n\t\t\ttmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-connection-hash') : null;\n\t\t\tif (tmpHash) return tmpHash;\n\t\t\ttmpParent = tmpParent.parentElement;\n\t\t\ttmpDepth++;\n\t\t}\n\n\t\treturn null;\n\t}\n}\n\nmodule.exports = PictServiceFlowInteractionManager;\nmodule.exports.INTERACTION_STATES = INTERACTION_STATES;\n\n},{\"fable-serviceproviderbase\":4}],31:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nclass PictServiceFlowLayout extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowLayout';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Layout configuration\n\t\tthis._HorizontalSpacing = 250;\n\t\tthis._VerticalSpacing = 120;\n\t\tthis._StartX = 100;\n\t\tthis._StartY = 100;\n\t}\n\n\t/**\n\t * Snap a coordinate to the nearest grid point\n\t * @param {number} pValue - The coordinate value\n\t * @param {number} pGridSize - The grid size\n\t * @returns {number}\n\t */\n\tsnapToGrid(pValue, pGridSize)\n\t{\n\t\tif (!pGridSize || pGridSize <= 0) return pValue;\n\t\treturn Math.round(pValue / pGridSize) * pGridSize;\n\t}\n\n\t/**\n\t * Auto-layout nodes using a simple left-to-right topological approach\n\t * @param {Array} pNodes - Array of node data objects\n\t * @param {Array} pConnections - Array of connection data objects\n\t */\n\tautoLayout(pNodes, pConnections)\n\t{\n\t\tif (!pNodes || pNodes.length === 0) return;\n\n\t\t// Build adjacency information\n\t\tlet tmpNodeMap = {};\n\t\tlet tmpInDegree = {};\n\t\tlet tmpOutEdges = {};\n\n\t\tfor (let i = 0; i < pNodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = pNodes[i];\n\t\t\ttmpNodeMap[tmpNode.Hash] = tmpNode;\n\t\t\ttmpInDegree[tmpNode.Hash] = 0;\n\t\t\ttmpOutEdges[tmpNode.Hash] = [];\n\t\t}\n\n\t\tfor (let i = 0; i < pConnections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = pConnections[i];\n\t\t\tif (tmpInDegree.hasOwnProperty(tmpConn.TargetNodeHash))\n\t\t\t{\n\t\t\t\ttmpInDegree[tmpConn.TargetNodeHash]++;\n\t\t\t}\n\t\t\tif (tmpOutEdges.hasOwnProperty(tmpConn.SourceNodeHash))\n\t\t\t{\n\t\t\t\ttmpOutEdges[tmpConn.SourceNodeHash].push(tmpConn.TargetNodeHash);\n\t\t\t}\n\t\t}\n\n\t\t// Topological sort (Kahn's algorithm)\n\t\tlet tmpLayers = [];\n\t\tlet tmpQueue = [];\n\t\tlet tmpAssigned = {};\n\n\t\t// Start with nodes that have no incoming edges\n\t\tfor (let tmpHash in tmpInDegree)\n\t\t{\n\t\t\tif (tmpInDegree[tmpHash] === 0)\n\t\t\t{\n\t\t\t\ttmpQueue.push(tmpHash);\n\t\t\t}\n\t\t}\n\n\t\twhile (tmpQueue.length > 0)\n\t\t{\n\t\t\tlet tmpCurrentLayer = [];\n\n\t\t\tlet tmpNextQueue = [];\n\t\t\tfor (let i = 0; i < tmpQueue.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNodeHash = tmpQueue[i];\n\t\t\t\tif (tmpAssigned[tmpNodeHash]) continue;\n\n\t\t\t\ttmpAssigned[tmpNodeHash] = true;\n\t\t\t\ttmpCurrentLayer.push(tmpNodeHash);\n\n\t\t\t\t// Process outgoing edges\n\t\t\t\tlet tmpEdges = tmpOutEdges[tmpNodeHash] || [];\n\t\t\t\tfor (let j = 0; j < tmpEdges.length; j++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpTargetHash = tmpEdges[j];\n\t\t\t\t\ttmpInDegree[tmpTargetHash]--;\n\t\t\t\t\tif (tmpInDegree[tmpTargetHash] <= 0 && !tmpAssigned[tmpTargetHash])\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpNextQueue.push(tmpTargetHash);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tmpCurrentLayer.length > 0)\n\t\t\t{\n\t\t\t\ttmpLayers.push(tmpCurrentLayer);\n\t\t\t}\n\n\t\t\ttmpQueue = tmpNextQueue;\n\t\t}\n\n\t\t// Handle any remaining unassigned nodes (cycles or disconnected)\n\t\tlet tmpRemainingNodes = [];\n\t\tfor (let i = 0; i < pNodes.length; i++)\n\t\t{\n\t\t\tif (!tmpAssigned[pNodes[i].Hash])\n\t\t\t{\n\t\t\t\ttmpRemainingNodes.push(pNodes[i].Hash);\n\t\t\t}\n\t\t}\n\t\tif (tmpRemainingNodes.length > 0)\n\t\t{\n\t\t\ttmpLayers.push(tmpRemainingNodes);\n\t\t}\n\n\t\t// Assign positions based on layers\n\t\tlet tmpCurrentX = this._StartX;\n\n\t\tfor (let tmpLayerIndex = 0; tmpLayerIndex < tmpLayers.length; tmpLayerIndex++)\n\t\t{\n\t\t\tlet tmpLayer = tmpLayers[tmpLayerIndex];\n\t\t\tlet tmpMaxWidth = 0;\n\n\t\t\t// Calculate the total height for this layer to center vertically\n\t\t\tlet tmpTotalHeight = 0;\n\t\t\tfor (let i = 0; i < tmpLayer.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNode = tmpNodeMap[tmpLayer[i]];\n\t\t\t\tif (tmpNode)\n\t\t\t\t{\n\t\t\t\t\ttmpTotalHeight += tmpNode.Height || 80;\n\t\t\t\t\tif (i < tmpLayer.length - 1)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpTotalHeight += this._VerticalSpacing;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet tmpCurrentY = this._StartY;\n\n\t\t\tfor (let i = 0; i < tmpLayer.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNode = tmpNodeMap[tmpLayer[i]];\n\t\t\t\tif (!tmpNode) continue;\n\n\t\t\t\ttmpNode.X = tmpCurrentX;\n\t\t\t\ttmpNode.Y = tmpCurrentY;\n\n\t\t\t\tlet tmpWidth = tmpNode.Width || 180;\n\t\t\t\tlet tmpHeight = tmpNode.Height || 80;\n\n\t\t\t\ttmpMaxWidth = Math.max(tmpMaxWidth, tmpWidth);\n\t\t\t\ttmpCurrentY += tmpHeight + this._VerticalSpacing;\n\t\t\t}\n\n\t\t\ttmpCurrentX += tmpMaxWidth + this._HorizontalSpacing;\n\t\t}\n\t}\n\n\t/**\n\t * Auto-layout a subset of nodes, positioning them to the right of\n\t * any fixed (already-positioned) nodes.\n\t *\n\t * Uses the same topological sort approach as autoLayout, but only\n\t * repositions the nodes in pNodesToLayout. The pFixedNodes are used\n\t * to compute a bounding box that the new layout avoids.\n\t *\n\t * @param {Array} pNodesToLayout - Nodes that need new positions\n\t * @param {Array} pFixedNodes - Nodes that already have positions (read-only)\n\t * @param {Array} pConnections - All connections in the flow\n\t */\n\tautoLayoutSubset(pNodesToLayout, pFixedNodes, pConnections)\n\t{\n\t\tif (!pNodesToLayout || pNodesToLayout.length === 0) return;\n\n\t\t// Compute the starting X position to the right of all fixed nodes\n\t\tlet tmpStartX = this._StartX;\n\t\tlet tmpStartY = this._StartY;\n\n\t\tif (pFixedNodes && pFixedNodes.length > 0)\n\t\t{\n\t\t\tlet tmpMaxX = -Infinity;\n\n\t\t\tfor (let i = 0; i < pFixedNodes.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpRight = pFixedNodes[i].X + (pFixedNodes[i].Width || 180);\n\t\t\t\tif (tmpRight > tmpMaxX)\n\t\t\t\t{\n\t\t\t\t\ttmpMaxX = tmpRight;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Place unmatched nodes to the right of all fixed nodes\n\t\t\ttmpStartX = tmpMaxX + this._HorizontalSpacing;\n\t\t}\n\n\t\t// Build a set of nodes we are laying out for quick lookup\n\t\tlet tmpNodeSet = {};\n\t\tfor (let i = 0; i < pNodesToLayout.length; i++)\n\t\t{\n\t\t\ttmpNodeSet[pNodesToLayout[i].Hash] = true;\n\t\t}\n\n\t\t// Build adjacency information only for nodes in the subset\n\t\tlet tmpNodeMap = {};\n\t\tlet tmpInDegree = {};\n\t\tlet tmpOutEdges = {};\n\n\t\tfor (let i = 0; i < pNodesToLayout.length; i++)\n\t\t{\n\t\t\tlet tmpNode = pNodesToLayout[i];\n\t\t\ttmpNodeMap[tmpNode.Hash] = tmpNode;\n\t\t\ttmpInDegree[tmpNode.Hash] = 0;\n\t\t\ttmpOutEdges[tmpNode.Hash] = [];\n\t\t}\n\n\t\t// Only count edges between nodes in the subset\n\t\tfor (let i = 0; i < pConnections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = pConnections[i];\n\t\t\tlet tmpSourceInSubset = tmpNodeSet[tmpConn.SourceNodeHash];\n\t\t\tlet tmpTargetInSubset = tmpNodeSet[tmpConn.TargetNodeHash];\n\n\t\t\tif (tmpSourceInSubset && tmpTargetInSubset)\n\t\t\t{\n\t\t\t\ttmpInDegree[tmpConn.TargetNodeHash]++;\n\t\t\t\ttmpOutEdges[tmpConn.SourceNodeHash].push(tmpConn.TargetNodeHash);\n\t\t\t}\n\t\t}\n\n\t\t// Topological sort (Kahn's algorithm)\n\t\tlet tmpLayers = [];\n\t\tlet tmpQueue = [];\n\t\tlet tmpAssigned = {};\n\n\t\tfor (let tmpHash in tmpInDegree)\n\t\t{\n\t\t\tif (tmpInDegree[tmpHash] === 0)\n\t\t\t{\n\t\t\t\ttmpQueue.push(tmpHash);\n\t\t\t}\n\t\t}\n\n\t\twhile (tmpQueue.length > 0)\n\t\t{\n\t\t\tlet tmpCurrentLayer = [];\n\t\t\tlet tmpNextQueue = [];\n\n\t\t\tfor (let i = 0; i < tmpQueue.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNodeHash = tmpQueue[i];\n\t\t\t\tif (tmpAssigned[tmpNodeHash]) continue;\n\n\t\t\t\ttmpAssigned[tmpNodeHash] = true;\n\t\t\t\ttmpCurrentLayer.push(tmpNodeHash);\n\n\t\t\t\tlet tmpEdges = tmpOutEdges[tmpNodeHash] || [];\n\t\t\t\tfor (let j = 0; j < tmpEdges.length; j++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpTargetHash = tmpEdges[j];\n\t\t\t\t\ttmpInDegree[tmpTargetHash]--;\n\t\t\t\t\tif (tmpInDegree[tmpTargetHash] <= 0 && !tmpAssigned[tmpTargetHash])\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpNextQueue.push(tmpTargetHash);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tmpCurrentLayer.length > 0)\n\t\t\t{\n\t\t\t\ttmpLayers.push(tmpCurrentLayer);\n\t\t\t}\n\n\t\t\ttmpQueue = tmpNextQueue;\n\t\t}\n\n\t\t// Handle remaining unassigned nodes (cycles or disconnected)\n\t\tlet tmpRemainingNodes = [];\n\t\tfor (let i = 0; i < pNodesToLayout.length; i++)\n\t\t{\n\t\t\tif (!tmpAssigned[pNodesToLayout[i].Hash])\n\t\t\t{\n\t\t\t\ttmpRemainingNodes.push(pNodesToLayout[i].Hash);\n\t\t\t}\n\t\t}\n\t\tif (tmpRemainingNodes.length > 0)\n\t\t{\n\t\t\ttmpLayers.push(tmpRemainingNodes);\n\t\t}\n\n\t\t// Assign positions based on layers, starting from tmpStartX\n\t\tlet tmpCurrentX = tmpStartX;\n\n\t\tfor (let tmpLayerIndex = 0; tmpLayerIndex < tmpLayers.length; tmpLayerIndex++)\n\t\t{\n\t\t\tlet tmpLayer = tmpLayers[tmpLayerIndex];\n\t\t\tlet tmpMaxWidth = 0;\n\t\t\tlet tmpCurrentY = tmpStartY;\n\n\t\t\tfor (let i = 0; i < tmpLayer.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNode = tmpNodeMap[tmpLayer[i]];\n\t\t\t\tif (!tmpNode) continue;\n\n\t\t\t\ttmpNode.X = tmpCurrentX;\n\t\t\t\ttmpNode.Y = tmpCurrentY;\n\n\t\t\t\tlet tmpWidth = tmpNode.Width || 180;\n\t\t\t\tlet tmpHeight = tmpNode.Height || 80;\n\n\t\t\t\ttmpMaxWidth = Math.max(tmpMaxWidth, tmpWidth);\n\t\t\t\ttmpCurrentY += tmpHeight + this._VerticalSpacing;\n\t\t\t}\n\n\t\t\ttmpCurrentX += tmpMaxWidth + this._HorizontalSpacing;\n\t\t}\n\t}\n\n\t/**\n\t * Center all nodes around a given point\n\t * @param {Array} pNodes\n\t * @param {number} pCenterX\n\t * @param {number} pCenterY\n\t */\n\tcenterNodes(pNodes, pCenterX, pCenterY)\n\t{\n\t\tif (!pNodes || pNodes.length === 0) return;\n\n\t\tlet tmpMinX = Infinity, tmpMinY = Infinity;\n\t\tlet tmpMaxX = -Infinity, tmpMaxY = -Infinity;\n\n\t\tfor (let i = 0; i < pNodes.length; i++)\n\t\t{\n\t\t\ttmpMinX = Math.min(tmpMinX, pNodes[i].X);\n\t\t\ttmpMinY = Math.min(tmpMinY, pNodes[i].Y);\n\t\t\ttmpMaxX = Math.max(tmpMaxX, pNodes[i].X + (pNodes[i].Width || 180));\n\t\t\ttmpMaxY = Math.max(tmpMaxY, pNodes[i].Y + (pNodes[i].Height || 80));\n\t\t}\n\n\t\tlet tmpCurrentCenterX = (tmpMinX + tmpMaxX) / 2;\n\t\tlet tmpCurrentCenterY = (tmpMinY + tmpMaxY) / 2;\n\t\tlet tmpOffsetX = pCenterX - tmpCurrentCenterX;\n\t\tlet tmpOffsetY = pCenterY - tmpCurrentCenterY;\n\n\t\tfor (let i = 0; i < pNodes.length; i++)\n\t\t{\n\t\t\tpNodes[i].X += tmpOffsetX;\n\t\t\tpNodes[i].Y += tmpOffsetY;\n\t\t}\n\t}\n}\n\nmodule.exports = PictServiceFlowLayout;\n\n},{\"fable-serviceproviderbase\":4}],32:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-PanelManager\n *\n * Manages the lifecycle of properties panels in the flow diagram:\n * opening, closing, toggling, and position updates for panels\n * associated with flow nodes.\n */\nclass PictServiceFlowPanelManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowPanelManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Open a properties panel for a node.\n\t * @param {string} pNodeHash - The hash of the node to open a panel for\n\t * @returns {Object|false} The panel data, or false if the node was not found\n\t */\n\topenPanel(pNodeHash)\n\t{\n\t\tlet tmpNode = this._FlowView.getNode(pNodeHash);\n\t\tif (!tmpNode) return false;\n\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNode.Type);\n\t\tif (!tmpNodeTypeConfig) return false;\n\n\t\t// Check if a panel is already open for this node\n\t\tlet tmpExisting = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.NodeHash === pNodeHash);\n\t\tif (tmpExisting) return tmpExisting;\n\n\t\tlet tmpPanelConfig = tmpNodeTypeConfig.PropertiesPanel;\n\t\tlet tmpPanelHash = `panel-${this.fable.getUUID()}`;\n\t\tlet tmpWidth, tmpHeight, tmpPanelType, tmpTitle;\n\n\t\tif (tmpPanelConfig)\n\t\t{\n\t\t\ttmpWidth = tmpPanelConfig.DefaultWidth || 300;\n\t\t\ttmpHeight = tmpPanelConfig.DefaultHeight || 200;\n\t\t\ttmpPanelType = tmpPanelConfig.PanelType || 'Base';\n\t\t\ttmpTitle = tmpPanelConfig.Title || tmpNodeTypeConfig.Label || 'Properties';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// No PropertiesPanel configured — open an auto-generated info panel\n\t\t\ttmpWidth = 240;\n\t\t\ttmpHeight = 180;\n\t\t\ttmpPanelType = 'Info';\n\t\t\ttmpTitle = tmpNodeTypeConfig.Label || tmpNode.Title || 'Node Info';\n\t\t}\n\n\t\tlet tmpPanelData =\n\t\t{\n\t\t\tHash: tmpPanelHash,\n\t\t\tNodeHash: pNodeHash,\n\t\t\tPanelType: tmpPanelType,\n\t\t\tTitle: tmpTitle,\n\t\t\tX: tmpNode.X + tmpNode.Width + 30,\n\t\t\tY: tmpNode.Y,\n\t\t\tWidth: tmpWidth,\n\t\t\tHeight: tmpHeight\n\t\t};\n\n\t\tthis._FlowView._FlowData.OpenPanels.push(tmpPanelData);\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onPanelOpened', tmpPanelData);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn tmpPanelData;\n\t}\n\n\t/**\n\t * Close a properties panel by panel hash.\n\t * @param {string} pPanelHash\n\t * @returns {boolean}\n\t */\n\tclosePanel(pPanelHash)\n\t{\n\t\tlet tmpIndex = this._FlowView._FlowData.OpenPanels.findIndex((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (tmpIndex < 0) return false;\n\n\t\tlet tmpRemovedPanel = this._FlowView._FlowData.OpenPanels.splice(tmpIndex, 1)[0];\n\n\t\t// Clean up the panel instance\n\t\tif (this._FlowView._PropertiesPanelView)\n\t\t{\n\t\t\tthis._FlowView._PropertiesPanelView.destroyPanel(pPanelHash);\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onPanelClosed', tmpRemovedPanel);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Close all panels for a given node.\n\t * @param {string} pNodeHash\n\t * @returns {boolean}\n\t */\n\tclosePanelForNode(pNodeHash)\n\t{\n\t\tlet tmpPanelsToClose = this._FlowView._FlowData.OpenPanels.filter((pPanel) => pPanel.NodeHash === pNodeHash);\n\t\tif (tmpPanelsToClose.length === 0) return false;\n\n\t\tfor (let i = 0; i < tmpPanelsToClose.length; i++)\n\t\t{\n\t\t\tlet tmpIndex = this._FlowView._FlowData.OpenPanels.indexOf(tmpPanelsToClose[i]);\n\t\t\tif (tmpIndex >= 0)\n\t\t\t{\n\t\t\t\tthis._FlowView._FlowData.OpenPanels.splice(tmpIndex, 1);\n\t\t\t}\n\t\t\tif (this._FlowView._PropertiesPanelView)\n\t\t\t{\n\t\t\t\tthis._FlowView._PropertiesPanelView.destroyPanel(tmpPanelsToClose[i].Hash);\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Toggle a properties panel for a node (open if closed, close if open).\n\t * @param {string} pNodeHash\n\t * @returns {Object|false}\n\t */\n\ttogglePanel(pNodeHash)\n\t{\n\t\tlet tmpExisting = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.NodeHash === pNodeHash);\n\t\tif (tmpExisting)\n\t\t{\n\t\t\tthis.closePanel(tmpExisting.Hash);\n\t\t\treturn false;\n\t\t}\n\t\treturn this.openPanel(pNodeHash);\n\t}\n\n\t/**\n\t * Update a panel's position (for drag).\n\t * @param {string} pPanelHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdatePanelPosition(pPanelHash, pX, pY)\n\t{\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\ttmpPanel.X = pX;\n\t\ttmpPanel.Y = pY;\n\n\t\t// Reset tether handle positions when panel moves\n\t\tthis._FlowView._resetHandlesForPanel(pPanelHash);\n\n\t\t// Update the foreignObject position directly for smooth dragging\n\t\tif (this._FlowView._PanelsLayer)\n\t\t{\n\t\t\tlet tmpFO = this._FlowView._PanelsLayer.querySelector(`[data-panel-hash=\"${pPanelHash}\"]`);\n\t\t\tif (tmpFO)\n\t\t\t{\n\t\t\t\ttmpFO.setAttribute('x', String(pX));\n\t\t\t\ttmpFO.setAttribute('y', String(pY));\n\t\t\t}\n\t\t}\n\n\t\t// Update the tether for this panel\n\t\tthis._FlowView._renderTethersForNode(tmpPanel.NodeHash);\n\t}\n}\n\nmodule.exports = PictServiceFlowPanelManager;\n\n},{\"fable-serviceproviderbase\":4}],33:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-PathGenerator\n *\n * Centralizes SVG path generation for the flow diagram.\n * Provides shared building blocks used by both the ConnectionRenderer\n * (port-to-port connections) and the TetherService (panel-to-node tethers).\n *\n * Responsibilities:\n * - Departure/approach point calculation from anchors\n * - Auto orthogonal corner computation for right-angle paths\n * - Cubic bezier evaluation at arbitrary parameter t\n * - SVG path string assembly (bezier, split-bezier, orthogonal)\n */\nclass PictServiceFlowPathGenerator extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowPathGenerator';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t// ---- Departure / Approach Calculation ----\n\n\t/**\n\t * Compute departure and approach points from start/end anchors.\n\t * The departure point extends outward from the start in its side direction,\n\t * and the approach point extends outward from the end in its side direction.\n\t *\n\t * @param {{x: number, y: number, side: string}} pFrom - Start anchor with side\n\t * @param {{x: number, y: number, side: string}} pTo - End anchor with side\n\t * @param {number} pDepartDist - Distance for departure/approach straight segments\n\t * @returns {{departX: number, departY: number, approachX: number, approachY: number, fromDir: {dx: number, dy: number}, toDir: {dx: number, dy: number}}}\n\t */\n\tcomputeDepartApproach(pFrom, pTo, pDepartDist)\n\t{\n\t\tlet tmpGeometry = this._FlowView._GeometryProvider;\n\n\t\tlet tmpFromDir = tmpGeometry.sideDirection(pFrom.side || 'right');\n\t\tlet tmpToDir = tmpGeometry.sideDirection(pTo.side || 'left');\n\n\t\treturn {\n\t\t\tdepartX: pFrom.x + tmpFromDir.dx * pDepartDist,\n\t\t\tdepartY: pFrom.y + tmpFromDir.dy * pDepartDist,\n\t\t\tapproachX: pTo.x + tmpToDir.dx * pDepartDist,\n\t\t\tapproachY: pTo.y + tmpToDir.dy * pDepartDist,\n\t\t\tfromDir: tmpFromDir,\n\t\t\ttoDir: tmpToDir\n\t\t};\n\t}\n\n\t// ---- Orthogonal Corner Calculation ----\n\n\t/**\n\t * Compute auto orthogonal corners for an L-shaped or Z-shaped path.\n\t * Determines corner placement based on departure/approach directions.\n\t *\n\t * Used by both connection and tether renderers for right-angle paths.\n\t *\n\t * @param {number} pDepartX\n\t * @param {number} pDepartY\n\t * @param {number} pApproachX\n\t * @param {number} pApproachY\n\t * @param {{dx: number, dy: number}} pFromDir - Departure direction vector\n\t * @param {{dx: number, dy: number}} pToDir - Approach direction vector\n\t * @param {number} pMidOffset - Offset for the corridor midpoint\n\t * @returns {{corner1: {x: number, y: number}, corner2: {x: number, y: number}, midpoint: {x: number, y: number}}}\n\t */\n\tcomputeAutoOrthogonalCorners(pDepartX, pDepartY, pApproachX, pApproachY, pFromDir, pToDir, pMidOffset)\n\t{\n\t\tlet tmpOffset = pMidOffset || 0;\n\t\tlet tmpFromHoriz = Math.abs(pFromDir.dx) > 0;\n\t\tlet tmpToHoriz = Math.abs(pToDir.dx) > 0;\n\n\t\tlet tmpCorner1, tmpCorner2, tmpMidpoint;\n\n\t\tif (tmpFromHoriz && tmpToHoriz)\n\t\t{\n\t\t\t// Both horizontal departure/approach: corridor is vertical\n\t\t\tlet tmpMidX = (pDepartX + pApproachX) / 2 + tmpOffset;\n\t\t\ttmpCorner1 = { x: tmpMidX, y: pDepartY };\n\t\t\ttmpCorner2 = { x: tmpMidX, y: pApproachY };\n\t\t\ttmpMidpoint = { x: tmpMidX, y: (pDepartY + pApproachY) / 2 };\n\t\t}\n\t\telse if (!tmpFromHoriz && !tmpToHoriz)\n\t\t{\n\t\t\t// Both vertical: corridor is horizontal\n\t\t\tlet tmpMidY = (pDepartY + pApproachY) / 2 + tmpOffset;\n\t\t\ttmpCorner1 = { x: pDepartX, y: tmpMidY };\n\t\t\ttmpCorner2 = { x: pApproachX, y: tmpMidY };\n\t\t\ttmpMidpoint = { x: (pDepartX + pApproachX) / 2, y: tmpMidY };\n\t\t}\n\t\telse if (tmpFromHoriz && !tmpToHoriz)\n\t\t{\n\t\t\t// Horizontal→Vertical: single L-bend\n\t\t\ttmpCorner1 = { x: pApproachX + tmpOffset, y: pDepartY };\n\t\t\ttmpCorner2 = { x: pApproachX + tmpOffset, y: pApproachY };\n\t\t\ttmpMidpoint = { x: pApproachX + tmpOffset, y: (pDepartY + pApproachY) / 2 };\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Vertical→Horizontal: single L-bend\n\t\t\ttmpCorner1 = { x: pDepartX, y: pApproachY + tmpOffset };\n\t\t\ttmpCorner2 = { x: pApproachX, y: pApproachY + tmpOffset };\n\t\t\ttmpMidpoint = { x: (pDepartX + pApproachX) / 2, y: pApproachY + tmpOffset };\n\t\t}\n\n\t\treturn { corner1: tmpCorner1, corner2: tmpCorner2, midpoint: tmpMidpoint };\n\t}\n\n\t// ---- Bezier Evaluation ----\n\n\t/**\n\t * Evaluate a cubic bezier curve at parameter t.\n\t * B(t) = (1-t)³P0 + 3(1-t)²tP1 + 3(1-t)t²P2 + t³P3\n\t *\n\t * @param {{x: number, y: number}} pP0 - Start point\n\t * @param {{x: number, y: number}} pP1 - First control point\n\t * @param {{x: number, y: number}} pP2 - Second control point\n\t * @param {{x: number, y: number}} pP3 - End point\n\t * @param {number} pT - Parameter in range [0, 1]\n\t * @returns {{x: number, y: number}}\n\t */\n\tevaluateCubicBezier(pP0, pP1, pP2, pP3, pT)\n\t{\n\t\tlet tmpOMT = 1 - pT;\n\t\tlet tmpOMT2 = tmpOMT * tmpOMT;\n\t\tlet tmpOMT3 = tmpOMT2 * tmpOMT;\n\t\tlet tmpT2 = pT * pT;\n\t\tlet tmpT3 = tmpT2 * pT;\n\n\t\treturn {\n\t\t\tx: tmpOMT3 * pP0.x + 3 * tmpOMT2 * pT * pP1.x + 3 * tmpOMT * tmpT2 * pP2.x + tmpT3 * pP3.x,\n\t\t\ty: tmpOMT3 * pP0.y + 3 * tmpOMT2 * pT * pP1.y + 3 * tmpOMT * tmpT2 * pP2.y + tmpT3 * pP3.y\n\t\t};\n\t}\n\n\t// ---- SVG Path String Assembly ----\n\n\t/**\n\t * Build an SVG bezier path string.\n\t * Pattern: M start L depart C cp1, cp2, approach L end\n\t *\n\t * @param {{x: number, y: number}} pStart - Start point\n\t * @param {{x: number, y: number}} pDepart - Departure point after straight segment\n\t * @param {{x: number, y: number}} pCP1 - First control point\n\t * @param {{x: number, y: number}} pCP2 - Second control point\n\t * @param {{x: number, y: number}} pApproach - Approach point before final straight segment\n\t * @param {{x: number, y: number}} pEnd - End point\n\t * @returns {string} SVG path d attribute\n\t */\n\tbuildBezierPathString(pStart, pDepart, pCP1, pCP2, pApproach, pEnd)\n\t{\n\t\treturn `M ${pStart.x} ${pStart.y} L ${pDepart.x} ${pDepart.y} C ${pCP1.x} ${pCP1.y}, ${pCP2.x} ${pCP2.y}, ${pApproach.x} ${pApproach.y} L ${pEnd.x} ${pEnd.y}`;\n\t}\n\n\t/**\n\t * Build an SVG split bezier path string (two cubic segments through a handle point).\n\t * Pattern: M start L depart C cp1a, cp1b, handle C cp2a, cp2b, approach L end\n\t *\n\t * @param {{x: number, y: number}} pStart\n\t * @param {{x: number, y: number}} pDepart\n\t * @param {{x: number, y: number}} pCP1a - First segment's first control point\n\t * @param {{x: number, y: number}} pCP1b - First segment's second control point\n\t * @param {{x: number, y: number}} pHandle - Handle point where the two segments meet\n\t * @param {{x: number, y: number}} pCP2a - Second segment's first control point\n\t * @param {{x: number, y: number}} pCP2b - Second segment's second control point\n\t * @param {{x: number, y: number}} pApproach\n\t * @param {{x: number, y: number}} pEnd\n\t * @returns {string} SVG path d attribute\n\t */\n\tbuildSplitBezierPathString(pStart, pDepart, pCP1a, pCP1b, pHandle, pCP2a, pCP2b, pApproach, pEnd)\n\t{\n\t\treturn `M ${pStart.x} ${pStart.y} L ${pDepart.x} ${pDepart.y} C ${pCP1a.x} ${pCP1a.y}, ${pCP1b.x} ${pCP1b.y}, ${pHandle.x} ${pHandle.y} C ${pCP2a.x} ${pCP2a.y}, ${pCP2b.x} ${pCP2b.y}, ${pApproach.x} ${pApproach.y} L ${pEnd.x} ${pEnd.y}`;\n\t}\n\n\t/**\n\t * Build an SVG multi-segment bezier path string.\n\t * Generates N+1 cubic bezier segments through N handle points.\n\t *\n\t * Pattern: M start L depart C cp,cp,handle[0] C cp,cp,handle[1] ... C cp,cp,approach L end\n\t *\n\t * Control points are computed using Catmull-Rom-to-Bezier conversion\n\t * for C1 (smooth tangent) continuity at every handle.\n\t *\n\t * @param {{x: number, y: number}} pStart - Port anchor start\n\t * @param {{x: number, y: number}} pDepart - Departure point after straight segment\n\t * @param {Array<{x: number, y: number}>} pHandles - Ordered handle waypoints\n\t * @param {{x: number, y: number}} pApproach - Approach point before final straight segment\n\t * @param {{x: number, y: number}} pEnd - Port anchor end\n\t * @param {{dx: number, dy: number}} pStartDir - Departure direction unit vector\n\t * @param {{dx: number, dy: number}} pEndDir - Approach direction unit vector\n\t * @returns {string} SVG path d attribute\n\t */\n\tbuildMultiBezierPathString(pStart, pDepart, pHandles, pApproach, pEnd, pStartDir, pEndDir)\n\t{\n\t\t// Build the full list of waypoints: depart, handle[0..N-1], approach\n\t\tlet tmpWaypoints = [pDepart];\n\t\tfor (let i = 0; i < pHandles.length; i++)\n\t\t{\n\t\t\ttmpWaypoints.push(pHandles[i]);\n\t\t}\n\t\ttmpWaypoints.push(pApproach);\n\n\t\tlet tmpPath = `M ${pStart.x} ${pStart.y} L ${pDepart.x} ${pDepart.y}`;\n\n\t\tfor (let i = 0; i < tmpWaypoints.length - 1; i++)\n\t\t{\n\t\t\tlet tmpFrom = tmpWaypoints[i];\n\t\t\tlet tmpTo = tmpWaypoints[i + 1];\n\n\t\t\tlet tmpSegDX = tmpTo.x - tmpFrom.x;\n\t\t\tlet tmpSegDY = tmpTo.y - tmpFrom.y;\n\t\t\tlet tmpSegLen = Math.sqrt(tmpSegDX * tmpSegDX + tmpSegDY * tmpSegDY);\n\t\t\tif (tmpSegLen < 1)\n\t\t\t{\n\t\t\t\ttmpSegLen = 1;\n\t\t\t}\n\t\t\tlet tmpScale = tmpSegLen * 0.35;\n\n\t\t\t// Tangent at tmpFrom\n\t\t\tlet tmpTanFromX, tmpTanFromY;\n\t\t\tif (i === 0)\n\t\t\t{\n\t\t\t\t// First segment: use the port departure direction\n\t\t\t\ttmpTanFromX = pStartDir.dx;\n\t\t\t\ttmpTanFromY = pStartDir.dy;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Interior handle: tangent points from previous toward next waypoint\n\t\t\t\tlet tmpPrev = tmpWaypoints[i - 1];\n\t\t\t\tlet tmpNext = tmpWaypoints[i + 1];\n\t\t\t\ttmpTanFromX = tmpNext.x - tmpPrev.x;\n\t\t\t\ttmpTanFromY = tmpNext.y - tmpPrev.y;\n\t\t\t\tlet tmpTanLen = Math.sqrt(tmpTanFromX * tmpTanFromX + tmpTanFromY * tmpTanFromY);\n\t\t\t\tif (tmpTanLen < 1) tmpTanLen = 1;\n\t\t\t\ttmpTanFromX /= tmpTanLen;\n\t\t\t\ttmpTanFromY /= tmpTanLen;\n\t\t\t}\n\n\t\t\t// Tangent at tmpTo\n\t\t\tlet tmpTanToX, tmpTanToY;\n\t\t\tif (i === tmpWaypoints.length - 2)\n\t\t\t{\n\t\t\t\t// Last segment: use the port approach direction (reversed for incoming)\n\t\t\t\ttmpTanToX = -pEndDir.dx;\n\t\t\t\ttmpTanToY = -pEndDir.dy;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Interior handle: tangent points from previous toward next waypoint\n\t\t\t\tlet tmpPrev = tmpWaypoints[i];\n\t\t\t\tlet tmpNext = tmpWaypoints[i + 2];\n\t\t\t\ttmpTanToX = tmpNext.x - tmpPrev.x;\n\t\t\t\ttmpTanToY = tmpNext.y - tmpPrev.y;\n\t\t\t\tlet tmpTanLen = Math.sqrt(tmpTanToX * tmpTanToX + tmpTanToY * tmpTanToY);\n\t\t\t\tif (tmpTanLen < 1) tmpTanLen = 1;\n\t\t\t\ttmpTanToX /= tmpTanLen;\n\t\t\t\ttmpTanToY /= tmpTanLen;\n\t\t\t}\n\n\t\t\tlet tmpCP1X = tmpFrom.x + tmpTanFromX * tmpScale;\n\t\t\tlet tmpCP1Y = tmpFrom.y + tmpTanFromY * tmpScale;\n\t\t\tlet tmpCP2X = tmpTo.x - tmpTanToX * tmpScale;\n\t\t\tlet tmpCP2Y = tmpTo.y - tmpTanToY * tmpScale;\n\n\t\t\ttmpPath += ` C ${tmpCP1X} ${tmpCP1Y}, ${tmpCP2X} ${tmpCP2Y}, ${tmpTo.x} ${tmpTo.y}`;\n\t\t}\n\n\t\ttmpPath += ` L ${pEnd.x} ${pEnd.y}`;\n\n\t\treturn tmpPath;\n\t}\n\n\t/**\n\t * Build an SVG orthogonal (right-angle) path string.\n\t * Pattern: M start L depart L corner1 L corner2 L approach L end\n\t *\n\t * @param {{x: number, y: number}} pStart\n\t * @param {{x: number, y: number}} pDepart\n\t * @param {{x: number, y: number}} pCorner1\n\t * @param {{x: number, y: number}} pCorner2\n\t * @param {{x: number, y: number}} pApproach\n\t * @param {{x: number, y: number}} pEnd\n\t * @returns {string} SVG path d attribute\n\t */\n\tbuildOrthogonalPathString(pStart, pDepart, pCorner1, pCorner2, pApproach, pEnd)\n\t{\n\t\treturn `M ${pStart.x} ${pStart.y} L ${pDepart.x} ${pDepart.y} L ${pCorner1.x} ${pCorner1.y} L ${pCorner2.x} ${pCorner2.y} L ${pApproach.x} ${pApproach.y} L ${pEnd.x} ${pEnd.y}`;\n\t}\n\n\t// ---- Directional Geometry ----\n\n\t/**\n\t * Compute full directional geometry between two port anchors, including\n\t * departure/approach points and bezier control points.\n\t *\n\t * Uses sophisticated facing detection: when ports face each other the\n\t * curve offset scales with inline distance; when ports are on the same\n\t * axis but not facing, a wider offset prevents the path from collapsing;\n\t * perpendicular exits use a moderate offset.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @returns {{departX: number, departY: number, approachX: number, approachY: number, cp1X: number, cp1Y: number, cp2X: number, cp2Y: number, startDir: {dx: number, dy: number}, endDir: {dx: number, dy: number}}}\n\t */\n\tcomputeDirectionalGeometry(pStart, pEnd)\n\t{\n\t\tlet tmpStartDir = this._FlowView._GeometryProvider.sideDirection(pStart.side || 'right');\n\t\tlet tmpEndDir = this._FlowView._GeometryProvider.sideDirection(pEnd.side || 'left');\n\n\t\tlet tmpStraightLen = 20;\n\n\t\tlet tmpDepartX = pStart.x + tmpStartDir.dx * tmpStraightLen;\n\t\tlet tmpDepartY = pStart.y + tmpStartDir.dy * tmpStraightLen;\n\n\t\tlet tmpApproachX = pEnd.x + tmpEndDir.dx * tmpStraightLen;\n\t\tlet tmpApproachY = pEnd.y + tmpEndDir.dy * tmpStraightLen;\n\n\t\tlet tmpDX = Math.abs(tmpApproachX - tmpDepartX);\n\t\tlet tmpDY = Math.abs(tmpApproachY - tmpDepartY);\n\t\tlet tmpDist = Math.sqrt(tmpDX * tmpDX + tmpDY * tmpDY);\n\n\t\tlet tmpBaseOffset = Math.max(Math.min(tmpDist * 0.4, 180), 30);\n\n\t\tlet tmpSameAxis = (tmpStartDir.dx !== 0 && tmpEndDir.dx !== 0) ||\n\t\t (tmpStartDir.dy !== 0 && tmpEndDir.dy !== 0);\n\n\t\tlet tmpFacingEachOther = false;\n\t\tif (tmpSameAxis)\n\t\t{\n\t\t\tif (tmpStartDir.dx === 1 && tmpEndDir.dx === -1 && pEnd.x >= pStart.x)\n\t\t\t{\n\t\t\t\ttmpFacingEachOther = true;\n\t\t\t}\n\t\t\telse if (tmpStartDir.dx === -1 && tmpEndDir.dx === 1 && pEnd.x <= pStart.x)\n\t\t\t{\n\t\t\t\ttmpFacingEachOther = true;\n\t\t\t}\n\t\t\telse if (tmpStartDir.dy === 1 && tmpEndDir.dy === -1 && pEnd.y >= pStart.y)\n\t\t\t{\n\t\t\t\ttmpFacingEachOther = true;\n\t\t\t}\n\t\t\telse if (tmpStartDir.dy === -1 && tmpEndDir.dy === 1 && pEnd.y <= pStart.y)\n\t\t\t{\n\t\t\t\ttmpFacingEachOther = true;\n\t\t\t}\n\t\t}\n\n\t\tlet tmpCurveOffset;\n\n\t\tif (tmpFacingEachOther)\n\t\t{\n\t\t\tlet tmpInlineDist = (tmpStartDir.dx !== 0) ? tmpDX : tmpDY;\n\t\t\ttmpCurveOffset = Math.max(tmpInlineDist * 0.35, 30);\n\t\t}\n\t\telse if (tmpSameAxis)\n\t\t{\n\t\t\ttmpCurveOffset = Math.max(tmpBaseOffset, 60);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpCurveOffset = Math.max(tmpBaseOffset * 0.8, 40);\n\t\t}\n\n\t\tlet tmpCP1X = tmpDepartX + tmpStartDir.dx * tmpCurveOffset;\n\t\tlet tmpCP1Y = tmpDepartY + tmpStartDir.dy * tmpCurveOffset;\n\t\tlet tmpCP2X = tmpApproachX + tmpEndDir.dx * tmpCurveOffset;\n\t\tlet tmpCP2Y = tmpApproachY + tmpEndDir.dy * tmpCurveOffset;\n\n\t\treturn {\n\t\t\tdepartX: tmpDepartX, departY: tmpDepartY,\n\t\t\tapproachX: tmpApproachX, approachY: tmpApproachY,\n\t\t\tcp1X: tmpCP1X, cp1Y: tmpCP1Y,\n\t\t\tcp2X: tmpCP2X, cp2Y: tmpCP2Y,\n\t\t\tstartDir: tmpStartDir, endDir: tmpEndDir\n\t\t};\n\t}\n\n\t// ---- Distance Utilities ----\n\n\t/**\n\t * Distance from point (pPX, pPY) to line segment (pAX, pAY)-(pBX, pBY).\n\t * Pure math utility, no state.\n\t *\n\t * @param {number} pPX\n\t * @param {number} pPY\n\t * @param {number} pAX\n\t * @param {number} pAY\n\t * @param {number} pBX\n\t * @param {number} pBY\n\t * @returns {number}\n\t */\n\tdistanceToSegment(pPX, pPY, pAX, pAY, pBX, pBY)\n\t{\n\t\tlet tmpDX = pBX - pAX;\n\t\tlet tmpDY = pBY - pAY;\n\t\tlet tmpLenSq = tmpDX * tmpDX + tmpDY * tmpDY;\n\n\t\tif (tmpLenSq < 0.001)\n\t\t{\n\t\t\t// Degenerate segment\n\t\t\tlet tmpDPX = pPX - pAX;\n\t\t\tlet tmpDPY = pPY - pAY;\n\t\t\treturn Math.sqrt(tmpDPX * tmpDPX + tmpDPY * tmpDPY);\n\t\t}\n\n\t\t// Project point onto segment, clamped to [0, 1]\n\t\tlet tmpT = ((pPX - pAX) * tmpDX + (pPY - pAY) * tmpDY) / tmpLenSq;\n\t\tif (tmpT < 0) tmpT = 0;\n\t\tif (tmpT > 1) tmpT = 1;\n\n\t\tlet tmpClosestX = pAX + tmpT * tmpDX;\n\t\tlet tmpClosestY = pAY + tmpT * tmpDY;\n\t\tlet tmpDistX = pPX - tmpClosestX;\n\t\tlet tmpDistY = pPY - tmpClosestY;\n\t\treturn Math.sqrt(tmpDistX * tmpDistX + tmpDistY * tmpDistY);\n\t}\n\n\t// ---- Auto Midpoint Calculation ----\n\n\t/**\n\t * Get the auto-calculated midpoint of the default bezier curve between\n\t * two port anchors, using the full directional geometry (facing detection,\n\t * adaptive curve offsets). Evaluates the cubic bezier at t=0.5.\n\t *\n\t * Used by ConnectionRenderer for connection midpoints.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetAutoMidpoint(pStart, pEnd)\n\t{\n\t\tlet tmpGeo = this.computeDirectionalGeometry(pStart, pEnd);\n\n\t\treturn this.evaluateCubicBezier(\n\t\t\t{ x: tmpGeo.departX, y: tmpGeo.departY },\n\t\t\t{ x: tmpGeo.cp1X, y: tmpGeo.cp1Y },\n\t\t\t{ x: tmpGeo.cp2X, y: tmpGeo.cp2Y },\n\t\t\t{ x: tmpGeo.approachX, y: tmpGeo.approachY },\n\t\t\t0.5\n\t\t);\n\t}\n\n\t/**\n\t * Get the auto-calculated midpoint using simple span-based control points.\n\t * Uses computeDepartApproach for basic geometry, then span * 0.4 for\n\t * control point distance. Evaluates the cubic bezier at t=0.5.\n\t *\n\t * Used by TetherService for tether midpoints.\n\t *\n\t * @param {{x: number, y: number, side: string}} pFrom\n\t * @param {{x: number, y: number, side: string}} pTo\n\t * @param {number} pDepartDist - Departure/approach distance\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetAutoMidpointSimple(pFrom, pTo, pDepartDist)\n\t{\n\t\tlet tmpDA = this.computeDepartApproach(pFrom, pTo, pDepartDist);\n\n\t\tlet tmpSpanX = Math.abs(tmpDA.approachX - tmpDA.departX);\n\t\tlet tmpSpanY = Math.abs(tmpDA.approachY - tmpDA.departY);\n\t\tlet tmpSpan = Math.max(tmpSpanX, tmpSpanY, 40);\n\t\tlet tmpCPDist = tmpSpan * 0.4;\n\n\t\tlet tmpP0 = { x: tmpDA.departX, y: tmpDA.departY };\n\t\tlet tmpP1 = { x: tmpDA.departX + tmpDA.fromDir.dx * tmpCPDist, y: tmpDA.departY + tmpDA.fromDir.dy * tmpCPDist };\n\t\tlet tmpP2 = { x: tmpDA.approachX + tmpDA.toDir.dx * tmpCPDist, y: tmpDA.approachY + tmpDA.toDir.dy * tmpCPDist };\n\t\tlet tmpP3 = { x: tmpDA.approachX, y: tmpDA.approachY };\n\n\t\treturn this.evaluateCubicBezier(tmpP0, tmpP1, tmpP2, tmpP3, 0.5);\n\t}\n}\n\nmodule.exports = PictServiceFlowPathGenerator;\n\n},{\"fable-serviceproviderbase\":4}],34:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-PortRenderer\n *\n * Renders port circles, labels, and badges for flow diagram nodes.\n *\n * Extracted from PictView-Flow-Node.js to isolate port rendering logic\n * from node body rendering and layout.\n *\n * Dependencies (all accessed via this._FlowView):\n * - _GeometryProvider — for getEdgeFromSide, getPortLocalPosition\n * - _ConnectorShapesProvider — for createPortElement\n * - _SVGHelperProvider — for createSVGElement\n */\nclass PictServiceFlowPortRenderer extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowPortRenderer';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Render ports for a node.\n\t * @param {Object} pNodeData\n\t * @param {SVGGElement} pGroup - The node's SVG group\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @param {Object} [pNodeTypeConfig] - Node type configuration (for label display options)\n\t * @param {number} pNodeTitleBarHeight - Title bar height (for port position offset)\n\t */\n\trenderPorts(pNodeData, pGroup, pWidth, pHeight, pNodeTypeConfig, pNodeTitleBarHeight)\n\t{\n\t\tif (!this._FlowView) return;\n\t\tif (!pNodeData.Ports || !Array.isArray(pNodeData.Ports)) return;\n\n\t\tlet tmpPortLabelsVertical = (pNodeTypeConfig && pNodeTypeConfig.PortLabelsVertical);\n\t\tlet tmpPortLabelPadding = (pNodeTypeConfig && pNodeTypeConfig.PortLabelPadding);\n\t\tlet tmpPortLabelsOutside = (pNodeTypeConfig && pNodeTypeConfig.PortLabelsOutside);\n\t\tlet tmpGeometryProvider = this._FlowView._GeometryProvider;\n\n\t\t// Group ports by their Side value (supports all 12 positions)\n\t\tlet tmpPortsBySide = {};\n\t\tfor (let i = 0; i < pNodeData.Ports.length; i++)\n\t\t{\n\t\t\tlet tmpPort = pNodeData.Ports[i];\n\t\t\tlet tmpSide = tmpPort.Side || (tmpPort.Direction === 'input' ? 'left' : 'right');\n\t\t\tif (!tmpPortsBySide[tmpSide])\n\t\t\t{\n\t\t\t\ttmpPortsBySide[tmpSide] = [];\n\t\t\t}\n\t\t\ttmpPortsBySide[tmpSide].push(tmpPort);\n\t\t}\n\n\t\t// Build port counts map for adaptive zone sizing\n\t\tlet tmpPortCountsBySide = {};\n\t\tfor (let tmpKey in tmpPortsBySide)\n\t\t{\n\t\t\ttmpPortCountsBySide[tmpKey] = tmpPortsBySide[tmpKey].length;\n\t\t}\n\n\t\tfor (let tmpSide in tmpPortsBySide)\n\t\t{\n\t\t\tlet tmpPorts = tmpPortsBySide[tmpSide];\n\t\t\t// Determine the edge for label positioning\n\t\t\tlet tmpEdge = tmpGeometryProvider ? tmpGeometryProvider.getEdgeFromSide(tmpSide) : tmpSide;\n\n\t\t\tfor (let i = 0; i < tmpPorts.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpPorts[i];\n\t\t\t\tlet tmpPosition = this.getPortLocalPosition(tmpSide, i, tmpPorts.length, pWidth, pHeight, pNodeTitleBarHeight, tmpPortCountsBySide);\n\n\t\t\t\t// Port label badge — flush against the node edge with no\n\t\t\t\t// border on the edge side; rendered before the port circle\n\t\t\t\t// so the circle visually sits on top of the badge\n\t\t\t\tlet tmpLabelElement = null;\n\t\t\t\tif (tmpPort.Label)\n\t\t\t\t{\n\t\t\t\t\tlet tmpPortTypeColorMap =\n\t\t\t\t\t{\n\t\t\t\t\t\t'event-in': '#3498db',\n\t\t\t\t\t\t'event-out': '#2ecc71',\n\t\t\t\t\t\t'setting': '#e67e22',\n\t\t\t\t\t\t'value': '#f1c40f',\n\t\t\t\t\t\t'error': '#e74c3c'\n\t\t\t\t\t};\n\t\t\t\t\tlet tmpBorderColor = tmpPort.PortType ? (tmpPortTypeColorMap[tmpPort.PortType] || '#95a5a6') : '#95a5a6';\n\n\t\t\t\t\tlet tmpBadgeHeight = 12;\n\t\t\t\t\tlet tmpBadgePadH = 5;\n\t\t\t\t\tlet tmpBadgeBorderW = 2;\n\t\t\t\t\tlet tmpEdgePad = 1;\n\t\t\t\t\tlet tmpPortRadius = 5;\n\n\t\t\t\t\tlet tmpTextLen = tmpPort.Label.length * 5;\n\t\t\t\t\tlet tmpBadgeX, tmpBadgeY, tmpBadgeWidth;\n\t\t\t\t\tlet tmpTextX, tmpTextAnchor;\n\t\t\t\t\tlet tmpStripeX, tmpStripeY, tmpStripeW, tmpStripeH;\n\t\t\t\t\tlet tmpBorderPath;\n\n\t\t\t\t\tif (tmpEdge === 'left')\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBadgeWidth = tmpPortRadius + tmpBadgePadH + tmpTextLen + tmpBadgePadH + tmpBadgeBorderW;\n\t\t\t\t\t\ttmpBadgeX = tmpEdgePad;\n\t\t\t\t\t\ttmpBadgeY = tmpPosition.y - tmpBadgeHeight / 2;\n\t\t\t\t\t\ttmpTextX = tmpBadgeX + tmpPortRadius + tmpBadgePadH;\n\t\t\t\t\t\ttmpTextAnchor = 'start';\n\t\t\t\t\t\ttmpStripeX = tmpBadgeX + tmpBadgeWidth - tmpBadgeBorderW;\n\t\t\t\t\t\ttmpStripeY = tmpBadgeY;\n\t\t\t\t\t\ttmpStripeW = tmpBadgeBorderW;\n\t\t\t\t\t\ttmpStripeH = tmpBadgeHeight;\n\t\t\t\t\t\ttmpBorderPath = 'M ' + tmpBadgeX + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + (tmpBadgeY + tmpBadgeHeight);\n\t\t\t\t\t}\n\t\t\t\t\telse if (tmpEdge === 'right')\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBadgeWidth = tmpBadgeBorderW + tmpBadgePadH + tmpTextLen + tmpBadgePadH + tmpPortRadius;\n\t\t\t\t\t\ttmpBadgeX = pWidth - tmpBadgeWidth - tmpEdgePad;\n\t\t\t\t\t\ttmpBadgeY = tmpPosition.y - tmpBadgeHeight / 2;\n\t\t\t\t\t\ttmpTextX = tmpBadgeX + tmpBadgeBorderW + tmpBadgePadH;\n\t\t\t\t\t\ttmpTextAnchor = 'start';\n\t\t\t\t\t\ttmpStripeX = tmpBadgeX;\n\t\t\t\t\t\ttmpStripeY = tmpBadgeY;\n\t\t\t\t\t\ttmpStripeW = tmpBadgeBorderW;\n\t\t\t\t\t\ttmpStripeH = tmpBadgeHeight;\n\t\t\t\t\t\ttmpBorderPath = 'M ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + (tmpBadgeY + tmpBadgeHeight);\n\t\t\t\t\t}\n\t\t\t\t\telse if (tmpEdge === 'top')\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBadgeWidth = tmpTextLen + tmpBadgePadH * 2;\n\t\t\t\t\t\ttmpBadgeX = tmpPosition.x - tmpBadgeWidth / 2;\n\t\t\t\t\t\ttmpBadgeY = tmpEdgePad;\n\t\t\t\t\t\ttmpTextX = tmpPosition.x;\n\t\t\t\t\t\ttmpTextAnchor = 'middle';\n\t\t\t\t\t\ttmpStripeX = tmpBadgeX;\n\t\t\t\t\t\ttmpStripeY = tmpBadgeY + tmpBadgeHeight - tmpBadgeBorderW;\n\t\t\t\t\t\ttmpStripeW = tmpBadgeWidth;\n\t\t\t\t\t\ttmpStripeH = tmpBadgeBorderW;\n\t\t\t\t\t\ttmpBorderPath = 'M ' + tmpBadgeX + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + tmpBadgeY;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBadgeWidth = tmpTextLen + tmpBadgePadH * 2;\n\t\t\t\t\t\ttmpBadgeX = tmpPosition.x - tmpBadgeWidth / 2;\n\t\t\t\t\t\ttmpBadgeY = pHeight - tmpBadgeHeight - tmpEdgePad;\n\t\t\t\t\t\ttmpTextX = tmpPosition.x;\n\t\t\t\t\t\ttmpTextAnchor = 'middle';\n\t\t\t\t\t\ttmpStripeX = tmpBadgeX;\n\t\t\t\t\t\ttmpStripeY = tmpBadgeY;\n\t\t\t\t\t\ttmpStripeW = tmpBadgeWidth;\n\t\t\t\t\t\ttmpStripeH = tmpBadgeBorderW;\n\t\t\t\t\t\ttmpBorderPath = 'M ' + tmpBadgeX + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + (tmpBadgeY + tmpBadgeHeight);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Background rect (cream, no stroke — border drawn separately)\n\t\t\t\t\tlet tmpBgRect = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\t\t\t\ttmpBgRect.setAttribute('class', 'pict-flow-port-label-bg');\n\t\t\t\t\ttmpBgRect.setAttribute('x', String(tmpBadgeX));\n\t\t\t\t\ttmpBgRect.setAttribute('y', String(tmpBadgeY));\n\t\t\t\t\ttmpBgRect.setAttribute('width', String(tmpBadgeWidth));\n\t\t\t\t\ttmpBgRect.setAttribute('height', String(tmpBadgeHeight));\n\t\t\t\t\ttmpBgRect.setAttribute('fill', 'var(--pf-port-label-bg, rgba(255, 253, 240, 0.5))');\n\t\t\t\t\tpGroup.appendChild(tmpBgRect);\n\n\t\t\t\t\t// 3-sided border path (open on the edge-facing side)\n\t\t\t\t\tlet tmpBorderPathEl = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\t\t\ttmpBorderPathEl.setAttribute('class', 'pict-flow-port-label-bg');\n\t\t\t\t\ttmpBorderPathEl.setAttribute('d', tmpBorderPath);\n\t\t\t\t\ttmpBorderPathEl.setAttribute('fill', 'none');\n\t\t\t\t\ttmpBorderPathEl.setAttribute('stroke', tmpBorderColor);\n\t\t\t\t\ttmpBorderPathEl.setAttribute('stroke-width', '0.75');\n\t\t\t\t\tpGroup.appendChild(tmpBorderPathEl);\n\n\t\t\t\t\t// Colored stripe on the inner side\n\t\t\t\t\tlet tmpStripe = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\t\t\t\ttmpStripe.setAttribute('class', 'pict-flow-port-label-bg');\n\t\t\t\t\ttmpStripe.setAttribute('x', String(tmpStripeX));\n\t\t\t\t\ttmpStripe.setAttribute('y', String(tmpStripeY));\n\t\t\t\t\ttmpStripe.setAttribute('width', String(tmpStripeW));\n\t\t\t\t\ttmpStripe.setAttribute('height', String(tmpStripeH));\n\t\t\t\t\ttmpStripe.setAttribute('fill', tmpBorderColor);\n\t\t\t\t\tpGroup.appendChild(tmpStripe);\n\n\t\t\t\t\t// Text label — appended after circle for z-order\n\t\t\t\t\ttmpLabelElement = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\t\ttmpLabelElement.setAttribute('class', 'pict-flow-port-label');\n\t\t\t\t\ttmpLabelElement.setAttribute('fill', 'var(--pf-port-label-text, #2c3e50)');\n\t\t\t\t\ttmpLabelElement.textContent = tmpPort.Label;\n\t\t\t\t\ttmpLabelElement.setAttribute('x', String(tmpTextX));\n\t\t\t\t\ttmpLabelElement.setAttribute('y', String(tmpBadgeY + tmpBadgeHeight / 2));\n\t\t\t\t\ttmpLabelElement.setAttribute('text-anchor', tmpTextAnchor);\n\t\t\t\t\ttmpLabelElement.setAttribute('dominant-baseline', 'central');\n\t\t\t\t}\n\n\t\t\t\t// Port circle (rendered on top of badge background)\n\t\t\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\t\t\tlet tmpCircle;\n\t\t\t\tif (tmpShapeProvider)\n\t\t\t\t{\n\t\t\t\t\ttmpCircle = tmpShapeProvider.createPortElement(tmpPort, tmpPosition, pNodeData.Hash);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttmpCircle = this._FlowView._SVGHelperProvider.createSVGElement('circle');\n\t\t\t\t\tlet tmpPortClass = `pict-flow-port ${tmpPort.Direction}`;\n\t\t\t\t\tif (tmpPort.PortType)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpPortClass += ` port-type-${tmpPort.PortType}`;\n\t\t\t\t\t}\n\t\t\t\t\ttmpCircle.setAttribute('class', tmpPortClass);\n\t\t\t\t\ttmpCircle.setAttribute('cx', String(tmpPosition.x));\n\t\t\t\t\ttmpCircle.setAttribute('cy', String(tmpPosition.y));\n\t\t\t\t\ttmpCircle.setAttribute('r', '5');\n\t\t\t\t\ttmpCircle.setAttribute('data-port-hash', tmpPort.Hash);\n\t\t\t\t\ttmpCircle.setAttribute('data-node-hash', pNodeData.Hash);\n\t\t\t\t\ttmpCircle.setAttribute('data-port-direction', tmpPort.Direction);\n\t\t\t\t\tif (tmpPort.PortType)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpCircle.setAttribute('data-port-type', tmpPort.PortType);\n\t\t\t\t\t}\n\t\t\t\t\ttmpCircle.setAttribute('data-element-type', 'port');\n\t\t\t\t}\n\t\t\t\tpGroup.appendChild(tmpCircle);\n\n\t\t\t\t// Port label text (on top of everything)\n\t\t\t\tif (tmpLabelElement)\n\t\t\t\t{\n\t\t\t\t\tpGroup.appendChild(tmpLabelElement);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Calculate port position relative to node origin.\n\t *\n\t * Delegates to the geometry provider's getPortLocalPosition method.\n\t *\n\t * @param {string} pSide - 'left', 'right', 'top', 'bottom' (or compound sides)\n\t * @param {number} pIndex - Index of this port on its side\n\t * @param {number} pTotal - Total ports on this side\n\t * @param {number} pWidth - Node width\n\t * @param {number} pHeight - Node height\n\t * @param {number} pNodeTitleBarHeight - Title bar height\n\t * @param {Object} [pPortCountsBySide] - Optional map of Side → count for adaptive zones\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetPortLocalPosition(pSide, pIndex, pTotal, pWidth, pHeight, pNodeTitleBarHeight, pPortCountsBySide)\n\t{\n\t\treturn this._FlowView._GeometryProvider.getPortLocalPosition(pSide, pIndex, pTotal, pWidth, pHeight, pNodeTitleBarHeight, pPortCountsBySide);\n\t}\n}\n\nmodule.exports = PictServiceFlowPortRenderer;\n\n},{\"fable-serviceproviderbase\":4}],35:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-RenderManager\n *\n * Orchestrates rendering of the flow diagram: nodes, connections,\n * tethers, panels, SVG marker definitions, and node position updates.\n *\n * Extracted from PictView-Flow.js to isolate rendering orchestration\n * from data management and interaction handling.\n */\nclass PictServiceFlowRenderManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowRenderManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Render the complete flow diagram\n\t */\n\trenderFlow()\n\t{\n\t\tif (!this._FlowView) return;\n\t\tif (!this._FlowView._NodesLayer || !this._FlowView._ConnectionsLayer) return;\n\n\t\t// Clear existing SVG content\n\t\twhile (this._FlowView._NodesLayer.firstChild)\n\t\t{\n\t\t\tthis._FlowView._NodesLayer.removeChild(this._FlowView._NodesLayer.firstChild);\n\t\t}\n\t\twhile (this._FlowView._ConnectionsLayer.firstChild)\n\t\t{\n\t\t\tthis._FlowView._ConnectionsLayer.removeChild(this._FlowView._ConnectionsLayer.firstChild);\n\t\t}\n\n\t\t// Render connections first (behind nodes)\n\t\tfor (let i = 0; i < this._FlowView._FlowData.Connections.length; i++)\n\t\t{\n\t\t\tlet tmpConnection = this._FlowView._FlowData.Connections[i];\n\t\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedConnectionHash === tmpConnection.Hash);\n\n\t\t\tthis._FlowView._ConnectionRenderer.renderConnection(\n\t\t\t\ttmpConnection,\n\t\t\t\tthis._FlowView._ConnectionsLayer,\n\t\t\t\ttmpIsSelected\n\t\t\t);\n\t\t}\n\n\t\t// Render nodes\n\t\tfor (let i = 0; i < this._FlowView._FlowData.Nodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = this._FlowView._FlowData.Nodes[i];\n\t\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedNodeHash === tmpNode.Hash);\n\t\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNode.Type);\n\n\t\t\t// Enrich saved port data with metadata from the node type's DefaultPorts.\n\t\t\t// Saved flow data may not include PortType or may have stale Side values,\n\t\t\t// so we match each port to its DefaultPort counterpart by Label and Direction,\n\t\t\t// then copy over PortType and Side from the authoritative node type definition.\n\t\t\tif (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultPorts && tmpNode.Ports)\n\t\t\t{\n\t\t\t\tfor (let p = 0; p < tmpNode.Ports.length; p++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpPort = tmpNode.Ports[p];\n\t\t\t\t\tfor (let d = 0; d < tmpNodeTypeConfig.DefaultPorts.length; d++)\n\t\t\t\t\t{\n\t\t\t\t\t\tlet tmpDefault = tmpNodeTypeConfig.DefaultPorts[d];\n\t\t\t\t\t\tif (tmpDefault.Label === tmpPort.Label && tmpDefault.Direction === tmpPort.Direction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (tmpDefault.PortType)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttmpPort.PortType = tmpDefault.PortType;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (tmpDefault.Side)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttmpPort.Side = tmpDefault.Side;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._FlowView._NodeView.renderNode(tmpNode, this._FlowView._NodesLayer, tmpIsSelected, tmpNodeTypeConfig);\n\t\t}\n\n\t\t// Render properties panels and tethers\n\t\tif (this._FlowView._PropertiesPanelView && this._FlowView._PanelsLayer && this._FlowView._TethersLayer)\n\t\t{\n\t\t\tthis._FlowView._PropertiesPanelView.renderPanels(\n\t\t\t\tthis._FlowView._FlowData.OpenPanels,\n\t\t\t\tthis._FlowView._PanelsLayer,\n\t\t\t\tthis._FlowView._TethersLayer,\n\t\t\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash\n\t\t\t);\n\t\t}\n\n\t\t// Update viewport transform\n\t\tthis._FlowView.updateViewportTransform();\n\t}\n\n\t/**\n\t * Re-render a single connection (remove and re-add) for smooth drag performance.\n\t * @param {string} pConnectionHash\n\t */\n\trenderSingleConnection(pConnectionHash)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._ConnectionsLayer) return;\n\n\t\t// Remove existing elements for this connection\n\t\tlet tmpExisting = this._FlowView._ConnectionsLayer.querySelectorAll(`[data-connection-hash=\"${pConnectionHash}\"]`);\n\t\tfor (let i = 0; i < tmpExisting.length; i++)\n\t\t{\n\t\t\ttmpExisting[i].remove();\n\t\t}\n\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection) return;\n\n\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedConnectionHash === pConnectionHash);\n\t\tthis._FlowView._ConnectionRenderer.renderConnection(tmpConnection, this._FlowView._ConnectionsLayer, tmpIsSelected);\n\t}\n\n\t/**\n\t * Re-render a single tether (remove and re-add) for smooth drag performance.\n\t * @param {string} pPanelHash\n\t */\n\trenderSingleTether(pPanelHash)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._TethersLayer || !this._FlowView._TetherService) return;\n\n\t\t// Remove existing tether elements for this panel\n\t\tlet tmpExisting = this._FlowView._TethersLayer.querySelectorAll(`[data-panel-hash=\"${pPanelHash}\"]`);\n\t\tfor (let i = 0; i < tmpExisting.length; i++)\n\t\t{\n\t\t\ttmpExisting[i].remove();\n\t\t}\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tlet tmpNodeData = this._FlowView.getNode(tmpPanel.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedTetherHash === pPanelHash);\n\t\tthis._FlowView._TetherService.renderTether(tmpPanel, tmpNodeData, this._FlowView._TethersLayer, tmpIsSelected, this._FlowView.options.ViewIdentifier);\n\t}\n\n\t/**\n\t * Update a single node's position in the SVG without full re-render (for drag performance)\n\t * @param {string} pNodeHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdateNodePosition(pNodeHash, pX, pY)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpNode = this._FlowView.getNode(pNodeHash);\n\t\tif (!tmpNode) return;\n\n\t\tif (this._FlowView.options.EnableGridSnap)\n\t\t{\n\t\t\tpX = this._FlowView._LayoutService.snapToGrid(pX, this._FlowView.options.GridSnapSize);\n\t\t\tpY = this._FlowView._LayoutService.snapToGrid(pY, this._FlowView.options.GridSnapSize);\n\t\t}\n\n\t\ttmpNode.X = pX;\n\t\ttmpNode.Y = pY;\n\n\t\t// Reset customized handle positions for connections/tethers involving this node\n\t\tthis._FlowView._resetHandlesForNode(pNodeHash);\n\n\t\t// Update the node's SVG group transform for smooth dragging\n\t\tlet tmpNodeGroup = this._FlowView._NodesLayer.querySelector(`[data-node-hash=\"${pNodeHash}\"]`);\n\t\tif (tmpNodeGroup)\n\t\t{\n\t\t\ttmpNodeGroup.setAttribute('transform', `translate(${pX}, ${pY})`);\n\t\t}\n\n\t\t// Re-render connections that involve this node\n\t\tthis.renderConnectionsForNode(pNodeHash);\n\n\t\t// Update tethers for any panels attached to this node\n\t\tthis.renderTethersForNode(pNodeHash);\n\t}\n\n\t/**\n\t * Re-render only connections that involve a specific node (for drag performance)\n\t * @param {string} pNodeHash\n\t */\n\trenderConnectionsForNode(pNodeHash)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._ConnectionsLayer) return;\n\n\t\tlet tmpAffectedConnections = this._FlowView._FlowData.Connections.filter((pConn) =>\n\t\t{\n\t\t\treturn pConn.SourceNodeHash === pNodeHash || pConn.TargetNodeHash === pNodeHash;\n\t\t});\n\n\t\tfor (let i = 0; i < tmpAffectedConnections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = tmpAffectedConnections[i];\n\t\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedConnectionHash === tmpConn.Hash);\n\n\t\t\t// Remove existing connection SVG elements\n\t\t\tlet tmpExisting = this._FlowView._ConnectionsLayer.querySelectorAll(`[data-connection-hash=\"${tmpConn.Hash}\"]`);\n\t\t\tfor (let j = 0; j < tmpExisting.length; j++)\n\t\t\t{\n\t\t\t\ttmpExisting[j].remove();\n\t\t\t}\n\n\t\t\t// Re-render this connection\n\t\t\tthis._FlowView._ConnectionRenderer.renderConnection(tmpConn, this._FlowView._ConnectionsLayer, tmpIsSelected);\n\t\t}\n\t}\n\n\t/**\n\t * Re-render tethers for panels attached to a specific node (for drag performance).\n\t * @param {string} pNodeHash\n\t */\n\trenderTethersForNode(pNodeHash)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._TethersLayer || !this._FlowView._TetherService) return;\n\n\t\tlet tmpAffectedPanels = this._FlowView._FlowData.OpenPanels.filter((pPanel) => pPanel.NodeHash === pNodeHash);\n\t\tif (tmpAffectedPanels.length === 0) return;\n\n\t\t// Remove existing tethers for these panels and re-render via TetherService\n\t\tfor (let i = 0; i < tmpAffectedPanels.length; i++)\n\t\t{\n\t\t\tlet tmpExisting = this._FlowView._TethersLayer.querySelectorAll(`[data-panel-hash=\"${tmpAffectedPanels[i].Hash}\"]`);\n\t\t\tfor (let j = 0; j < tmpExisting.length; j++)\n\t\t\t{\n\t\t\t\ttmpExisting[j].remove();\n\t\t\t}\n\n\t\t\tlet tmpNodeData = this._FlowView.getNode(tmpAffectedPanels[i].NodeHash);\n\t\t\tif (!tmpNodeData) continue;\n\n\t\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedTetherHash === tmpAffectedPanels[i].Hash);\n\t\t\tthis._FlowView._TetherService.renderTether(tmpAffectedPanels[i], tmpNodeData, this._FlowView._TethersLayer, tmpIsSelected, this._FlowView.options.ViewIdentifier);\n\t\t}\n\t}\n\n\t/**\n\t * Re-inject SVG marker definitions (arrowheads).\n\t * Called after a theme switch to update arrowhead colors.\n\t */\n\treinjectMarkerDefs()\n\t{\n\t\tif (!this._FlowView || !this._FlowView._ConnectorShapesProvider || !this._FlowView._SVGElement) return;\n\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\tlet tmpDefs = this._FlowView._SVGElement.querySelector('defs');\n\t\tif (!tmpDefs) return;\n\n\t\t// Remove existing marker elements\n\t\tlet tmpExistingMarkers = tmpDefs.querySelectorAll('marker');\n\t\tfor (let i = 0; i < tmpExistingMarkers.length; i++)\n\t\t{\n\t\t\ttmpExistingMarkers[i].remove();\n\t\t}\n\n\t\t// Re-generate and inject\n\t\tlet tmpMarkerMarkup = this._FlowView._ConnectorShapesProvider.generateMarkerDefs(tmpViewIdentifier);\n\t\tlet tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\t\ttmpTempSVG.innerHTML = tmpMarkerMarkup;\n\t\twhile (tmpTempSVG.firstChild)\n\t\t{\n\t\t\ttmpDefs.appendChild(tmpTempSVG.firstChild);\n\t\t}\n\t}\n}\n\nmodule.exports = PictServiceFlowRenderManager;\n\n},{\"fable-serviceproviderbase\":4}],36:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-SelectionManager\n *\n * Manages selection state for nodes, connections, and tethers in the flow diagram.\n * Handles selecting, deselecting, and deleting selected elements.\n */\nclass PictServiceFlowSelectionManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowSelectionManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Select a node\n\t * @param {string|null} pNodeHash - Hash of the node to select, or null to deselect\n\t */\n\tselectNode(pNodeHash)\n\t{\n\t\tlet tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedNodeHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = pNodeHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash = null;\n\n\t\tthis._FlowView.renderFlow();\n\n\t\tif (this._FlowView._EventHandlerProvider && pNodeHash !== tmpPreviousSelection)\n\t\t{\n\t\t\tlet tmpNode = pNodeHash ? this._FlowView._FlowData.Nodes.find((pNode) => pNode.Hash === pNodeHash) : null;\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onNodeSelected', tmpNode);\n\t\t}\n\t}\n\n\t/**\n\t * Select a connection\n\t * @param {string|null} pConnectionHash - Hash of the connection to select, or null to deselect\n\t */\n\tselectConnection(pConnectionHash)\n\t{\n\t\tlet tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedConnectionHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = pConnectionHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash = null;\n\n\t\tthis._FlowView.renderFlow();\n\n\t\tif (this._FlowView._EventHandlerProvider && pConnectionHash !== tmpPreviousSelection)\n\t\t{\n\t\t\tlet tmpConnection = pConnectionHash ? this._FlowView._FlowData.Connections.find((pConn) => pConn.Hash === pConnectionHash) : null;\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionSelected', tmpConnection);\n\t\t}\n\t}\n\n\t/**\n\t * Select a tether by its panel hash.\n\t * @param {string|null} pPanelHash - Hash of the panel whose tether to select, or null to deselect\n\t */\n\tselectTether(pPanelHash)\n\t{\n\t\tlet tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedTetherHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash = pPanelHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = null;\n\n\t\tthis._FlowView.renderFlow();\n\n\t\tif (this._FlowView._EventHandlerProvider && pPanelHash !== tmpPreviousSelection)\n\t\t{\n\t\t\tlet tmpPanel = pPanelHash ? this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash) : null;\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onTetherSelected', tmpPanel);\n\t\t}\n\t}\n\n\t/**\n\t * Deselect all nodes and connections\n\t */\n\tdeselectAll()\n\t{\n\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash = null;\n\t\tthis._FlowView.renderFlow();\n\t}\n\n\t/**\n\t * Delete the currently selected node or connection\n\t * @returns {boolean}\n\t */\n\tdeleteSelected()\n\t{\n\t\tif (this._FlowView._FlowData.ViewState.SelectedNodeHash)\n\t\t{\n\t\t\treturn this._FlowView.removeNode(this._FlowView._FlowData.ViewState.SelectedNodeHash);\n\t\t}\n\t\tif (this._FlowView._FlowData.ViewState.SelectedConnectionHash)\n\t\t{\n\t\t\treturn this._FlowView.removeConnection(this._FlowView._FlowData.ViewState.SelectedConnectionHash);\n\t\t}\n\t\treturn false;\n\t}\n}\n\nmodule.exports = PictServiceFlowSelectionManager;\n\n},{\"fable-serviceproviderbase\":4}],37:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-Tether\n *\n * Centralizes all tether geometry, path generation, handle state management,\n * and SVG rendering for the lines that connect properties panels to their nodes.\n *\n * Delegates to shared providers for:\n * - SVG element creation (_FlowView._SVGHelperProvider)\n * - Geometry calculations (_FlowView._GeometryProvider)\n * - Path string building (_FlowView._PathGenerator)\n *\n * Responsibilities:\n * - Smart 4-quadrant anchor detection (which edge of the node/panel to connect)\n * - Bezier and orthogonal path generation\n * - Auto-midpoint and auto-corner calculations\n * - Handle position updates during drag\n * - Handle reset when nodes or panels move\n * - Line mode toggling (bezier <-> orthogonal)\n * - SVG element creation for tether lines, hit areas, and drag handles\n */\nclass PictServiceFlowTether extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowTether';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t// ---- Anchor Calculation ----\n\n\t/**\n\t * Determine which node edge and panel edge to connect based on 4-quadrant detection.\n\t * Uses the relative position of the panel center to the node center.\n\t *\n\t * @param {Object} pPanelData - Panel data with X, Y, Width, Height\n\t * @param {Object} pNodeData - Node data with X, Y, Width, Height\n\t * @returns {{nodeAnchor: {x,y,side}, panelAnchor: {x,y,side}}}\n\t */\n\tgetSmartAnchors(pPanelData, pNodeData)\n\t{\n\t\tlet tmpNodeCX = pNodeData.X + pNodeData.Width / 2;\n\t\tlet tmpNodeCY = pNodeData.Y + pNodeData.Height / 2;\n\t\tlet tmpPanelCX = pPanelData.X + pPanelData.Width / 2;\n\t\tlet tmpPanelCY = pPanelData.Y + pPanelData.Height / 2;\n\n\t\tlet tmpDX = tmpPanelCX - tmpNodeCX;\n\t\tlet tmpDY = tmpPanelCY - tmpNodeCY;\n\n\t\tlet tmpNodeSide, tmpPanelSide;\n\n\t\tif (Math.abs(tmpDX) >= Math.abs(tmpDY))\n\t\t{\n\t\t\t// Panel is primarily to the left or right\n\t\t\tif (tmpDX >= 0)\n\t\t\t{\n\t\t\t\ttmpNodeSide = 'right';\n\t\t\t\ttmpPanelSide = 'left';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpNodeSide = 'left';\n\t\t\t\ttmpPanelSide = 'right';\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Panel is primarily above or below\n\t\t\tif (tmpDY >= 0)\n\t\t\t{\n\t\t\t\ttmpNodeSide = 'bottom';\n\t\t\t\ttmpPanelSide = 'top';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpNodeSide = 'top';\n\t\t\t\ttmpPanelSide = 'bottom';\n\t\t\t}\n\t\t}\n\n\t\tlet tmpNodeAnchor = this._FlowView._GeometryProvider.getEdgeCenter(pNodeData, tmpNodeSide);\n\t\tlet tmpPanelAnchor = this._FlowView._GeometryProvider.getEdgeCenter(pPanelData, tmpPanelSide);\n\n\t\treturn {\n\t\t\tnodeAnchor: Object.assign(tmpNodeAnchor, { side: tmpNodeSide }),\n\t\t\tpanelAnchor: Object.assign(tmpPanelAnchor, { side: tmpPanelSide })\n\t\t};\n\t}\n\n\t// ---- Path Generation ----\n\n\t/**\n\t * Generate a bezier path between two anchor points with directional departure/approach.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @param {number|null} pHandleX - User-set handle X or null for auto\n\t * @param {number|null} pHandleY - User-set handle Y or null for auto\n\t * @returns {string} SVG path d attribute\n\t */\n\tgenerateBezierPath(pFrom, pTo, pHandleX, pHandleY)\n\t{\n\t\tlet tmpDepartDist = 20;\n\t\tlet tmpFromDir = this._FlowView._GeometryProvider.sideDirection(pFrom.side);\n\t\tlet tmpToDir = this._FlowView._GeometryProvider.sideDirection(pTo.side);\n\n\t\tlet tmpDepartX = pFrom.x + tmpFromDir.dx * tmpDepartDist;\n\t\tlet tmpDepartY = pFrom.y + tmpFromDir.dy * tmpDepartDist;\n\t\tlet tmpApproachX = pTo.x + tmpToDir.dx * tmpDepartDist;\n\t\tlet tmpApproachY = pTo.y + tmpToDir.dy * tmpDepartDist;\n\n\t\tif (pHandleX == null || pHandleY == null)\n\t\t{\n\t\t\t// Auto bezier: simple cubic from depart to approach\n\t\t\tlet tmpSpanX = Math.abs(tmpApproachX - tmpDepartX);\n\t\t\tlet tmpSpanY = Math.abs(tmpApproachY - tmpDepartY);\n\t\t\tlet tmpSpan = Math.max(tmpSpanX, tmpSpanY, 40);\n\t\t\tlet tmpCPDist = tmpSpan * 0.4;\n\n\t\t\tlet tmpCP1X = tmpDepartX + tmpFromDir.dx * tmpCPDist;\n\t\t\tlet tmpCP1Y = tmpDepartY + tmpFromDir.dy * tmpCPDist;\n\t\t\tlet tmpCP2X = tmpApproachX + tmpToDir.dx * tmpCPDist;\n\t\t\tlet tmpCP2Y = tmpApproachY + tmpToDir.dy * tmpCPDist;\n\n\t\t\treturn this._FlowView._PathGenerator.buildBezierPathString(\n\t\t\t\t{ x: pFrom.x, y: pFrom.y },\n\t\t\t\t{ x: tmpDepartX, y: tmpDepartY },\n\t\t\t\t{ x: tmpCP1X, y: tmpCP1Y },\n\t\t\t\t{ x: tmpCP2X, y: tmpCP2Y },\n\t\t\t\t{ x: tmpApproachX, y: tmpApproachY },\n\t\t\t\t{ x: pTo.x, y: pTo.y }\n\t\t\t);\n\t\t}\n\n\t\t// User-set handle: split bezier into two segments through handle\n\t\tlet tmpCP1aDist = 30;\n\t\tlet tmpCP1aX = tmpDepartX + tmpFromDir.dx * tmpCP1aDist;\n\t\tlet tmpCP1aY = tmpDepartY + tmpFromDir.dy * tmpCP1aDist;\n\n\t\tlet tmpCP2aDist = 30;\n\t\tlet tmpCP2aX = tmpApproachX + tmpToDir.dx * tmpCP2aDist;\n\t\tlet tmpCP2aY = tmpApproachY + tmpToDir.dy * tmpCP2aDist;\n\n\t\t// Tangent at the handle — direction from first segment end to second segment start\n\t\tlet tmpTangentX = tmpApproachX - tmpDepartX;\n\t\tlet tmpTangentY = tmpApproachY - tmpDepartY;\n\t\tlet tmpTangentLen = Math.sqrt(tmpTangentX * tmpTangentX + tmpTangentY * tmpTangentY) || 1;\n\t\ttmpTangentX /= tmpTangentLen;\n\t\ttmpTangentY /= tmpTangentLen;\n\t\tlet tmpTangentDist = 25;\n\n\t\tlet tmpCP1bX = pHandleX - tmpTangentX * tmpTangentDist;\n\t\tlet tmpCP1bY = pHandleY - tmpTangentY * tmpTangentDist;\n\t\tlet tmpCP2bX = pHandleX + tmpTangentX * tmpTangentDist;\n\t\tlet tmpCP2bY = pHandleY + tmpTangentY * tmpTangentDist;\n\n\t\treturn this._FlowView._PathGenerator.buildSplitBezierPathString(\n\t\t\t{ x: pFrom.x, y: pFrom.y },\n\t\t\t{ x: tmpDepartX, y: tmpDepartY },\n\t\t\t{ x: tmpCP1aX, y: tmpCP1aY },\n\t\t\t{ x: tmpCP1bX, y: tmpCP1bY },\n\t\t\t{ x: pHandleX, y: pHandleY },\n\t\t\t{ x: tmpCP2bX, y: tmpCP2bY },\n\t\t\t{ x: tmpCP2aX, y: tmpCP2aY },\n\t\t\t{ x: tmpApproachX, y: tmpApproachY },\n\t\t\t{ x: pTo.x, y: pTo.y }\n\t\t);\n\t}\n\n\t/**\n\t * Generate an orthogonal (90-degree) path between two anchor points.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @param {Object|null} pCorners - {corner1: {x,y}, corner2: {x,y}} or null for auto\n\t * @param {number} pMidOffset - Offset for the corridor midpoint\n\t * @returns {string} SVG path d attribute\n\t */\n\tgenerateOrthogonalPath(pFrom, pTo, pCorners, pMidOffset)\n\t{\n\t\tlet tmpDepartDist = 20;\n\t\tlet tmpFromDir = this._FlowView._GeometryProvider.sideDirection(pFrom.side);\n\t\tlet tmpToDir = this._FlowView._GeometryProvider.sideDirection(pTo.side);\n\n\t\tlet tmpDepartX = pFrom.x + tmpFromDir.dx * tmpDepartDist;\n\t\tlet tmpDepartY = pFrom.y + tmpFromDir.dy * tmpDepartDist;\n\t\tlet tmpApproachX = pTo.x + tmpToDir.dx * tmpDepartDist;\n\t\tlet tmpApproachY = pTo.y + tmpToDir.dy * tmpDepartDist;\n\n\t\tlet tmpCorner1, tmpCorner2;\n\n\t\tif (pCorners && pCorners.corner1 && pCorners.corner2)\n\t\t{\n\t\t\ttmpCorner1 = pCorners.corner1;\n\t\t\ttmpCorner2 = pCorners.corner2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Auto-calculate corners based on direction\n\t\t\tlet tmpAutoCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(tmpDepartX, tmpDepartY, tmpApproachX, tmpApproachY, tmpFromDir, tmpToDir, pMidOffset);\n\t\t\ttmpCorner1 = tmpAutoCorners.corner1;\n\t\t\ttmpCorner2 = tmpAutoCorners.corner2;\n\t\t}\n\n\t\treturn this._FlowView._PathGenerator.buildOrthogonalPathString(\n\t\t\t{ x: pFrom.x, y: pFrom.y },\n\t\t\t{ x: tmpDepartX, y: tmpDepartY },\n\t\t\t{ x: tmpCorner1.x, y: tmpCorner1.y },\n\t\t\t{ x: tmpCorner2.x, y: tmpCorner2.y },\n\t\t\t{ x: tmpApproachX, y: tmpApproachY },\n\t\t\t{ x: pTo.x, y: pTo.y }\n\t\t);\n\t}\n\n\t// ---- Handle Position Computation ----\n\n\t/**\n\t * Get auto-calculated bezier midpoint for a tether at t=0.5 on the curve.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetAutoMidpoint(pFrom, pTo)\n\t{\n\t\treturn this._FlowView._PathGenerator.getAutoMidpointSimple(pFrom, pTo, 20);\n\t}\n\n\t/**\n\t * Get full orthogonal geometry including corners and midpoint for handle rendering.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @param {Object} pPanelData - Panel data with tether handle properties\n\t * @returns {{corner1: {x,y}, corner2: {x,y}, midpoint: {x,y}}}\n\t */\n\tgetOrthoGeometry(pFrom, pTo, pPanelData)\n\t{\n\t\tlet tmpDepartDist = 20;\n\t\tlet tmpFromDir = this._FlowView._GeometryProvider.sideDirection(pFrom.side);\n\t\tlet tmpToDir = this._FlowView._GeometryProvider.sideDirection(pTo.side);\n\n\t\tlet tmpDepartX = pFrom.x + tmpFromDir.dx * tmpDepartDist;\n\t\tlet tmpDepartY = pFrom.y + tmpFromDir.dy * tmpDepartDist;\n\t\tlet tmpApproachX = pTo.x + tmpToDir.dx * tmpDepartDist;\n\t\tlet tmpApproachY = pTo.y + tmpToDir.dy * tmpDepartDist;\n\n\t\tlet tmpCorners;\n\t\tif (pPanelData.TetherHandleCustomized && pPanelData.TetherOrthoCorner1X != null)\n\t\t{\n\t\t\ttmpCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(tmpDepartX, tmpDepartY, tmpApproachX, tmpApproachY, tmpFromDir, tmpToDir, pPanelData.TetherOrthoMidOffset || 0);\n\t\t\ttmpCorners.corner1 = { x: pPanelData.TetherOrthoCorner1X, y: pPanelData.TetherOrthoCorner1Y };\n\t\t\ttmpCorners.corner2 = { x: pPanelData.TetherOrthoCorner2X, y: pPanelData.TetherOrthoCorner2Y };\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(tmpDepartX, tmpDepartY, tmpApproachX, tmpApproachY, tmpFromDir, tmpToDir, pPanelData.TetherOrthoMidOffset || 0);\n\t\t}\n\n\t\tlet tmpMidpoint =\n\t\t{\n\t\t\tx: (tmpCorners.corner1.x + tmpCorners.corner2.x) / 2,\n\t\t\ty: (tmpCorners.corner1.y + tmpCorners.corner2.y) / 2\n\t\t};\n\n\t\treturn {\n\t\t\tcorner1: tmpCorners.corner1,\n\t\t\tcorner2: tmpCorners.corner2,\n\t\t\tmidpoint: tmpMidpoint\n\t\t};\n\t}\n\n\t// ---- Path Generation (high-level) ----\n\n\t/**\n\t * Generate the SVG path string for a tether based on its panel data and anchors.\n\t * @param {Object} pPanelData - Panel data with tether handle properties\n\t * @param {Object} pFrom - {x, y, side} panel anchor\n\t * @param {Object} pTo - {x, y, side} node anchor\n\t * @returns {string} SVG path d attribute\n\t */\n\tgeneratePath(pPanelData, pFrom, pTo)\n\t{\n\t\tlet tmpLineMode = pPanelData.TetherLineMode || 'bezier';\n\n\t\tif (tmpLineMode === 'orthogonal')\n\t\t{\n\t\t\tlet tmpCorners = null;\n\t\t\tif (pPanelData.TetherHandleCustomized && pPanelData.TetherOrthoCorner1X != null)\n\t\t\t{\n\t\t\t\ttmpCorners =\n\t\t\t\t{\n\t\t\t\t\tcorner1: { x: pPanelData.TetherOrthoCorner1X, y: pPanelData.TetherOrthoCorner1Y },\n\t\t\t\t\tcorner2: { x: pPanelData.TetherOrthoCorner2X, y: pPanelData.TetherOrthoCorner2Y }\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn this.generateOrthogonalPath(pFrom, pTo, tmpCorners, pPanelData.TetherOrthoMidOffset || 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Check for multi-handle array first\n\t\t\tlet tmpHandles = this._getTetherBezierHandles(pPanelData);\n\t\t\tif (tmpHandles.length > 0)\n\t\t\t{\n\t\t\t\treturn this.generateMultiBezierPath(pFrom, pTo, tmpHandles);\n\t\t\t}\n\n\t\t\t// Single-handle legacy path\n\t\t\tlet tmpHandleX = (pPanelData.TetherHandleCustomized && pPanelData.TetherBezierHandleX != null) ? pPanelData.TetherBezierHandleX : null;\n\t\t\tlet tmpHandleY = (pPanelData.TetherHandleCustomized && pPanelData.TetherBezierHandleY != null) ? pPanelData.TetherBezierHandleY : null;\n\t\t\treturn this.generateBezierPath(pFrom, pTo, tmpHandleX, tmpHandleY);\n\t\t}\n\t}\n\n\t/**\n\t * Get the bezier handles array for a tether, respecting the customized flag.\n\t * @param {Object} pPanelData\n\t * @returns {Array<{x: number, y: number}>}\n\t */\n\t_getTetherBezierHandles(pPanelData)\n\t{\n\t\tif (!pPanelData || !pPanelData.TetherHandleCustomized)\n\t\t{\n\t\t\treturn [];\n\t\t}\n\n\t\t// Multi-handle format\n\t\tif (Array.isArray(pPanelData.TetherBezierHandles) && pPanelData.TetherBezierHandles.length > 0)\n\t\t{\n\t\t\treturn pPanelData.TetherBezierHandles;\n\t\t}\n\n\t\t// Legacy single-handle format\n\t\tif (pPanelData.TetherBezierHandleX != null && pPanelData.TetherBezierHandleY != null)\n\t\t{\n\t\t\treturn [{ x: pPanelData.TetherBezierHandleX, y: pPanelData.TetherBezierHandleY }];\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Generate a multi-handle bezier path for a tether.\n\t * Delegates to PathGenerator.buildMultiBezierPathString.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @param {Array<{x: number, y: number}>} pHandles\n\t * @returns {string} SVG path d attribute\n\t */\n\tgenerateMultiBezierPath(pFrom, pTo, pHandles)\n\t{\n\t\tlet tmpDepartDist = 20;\n\t\tlet tmpFromDir = this._FlowView._GeometryProvider.sideDirection(pFrom.side);\n\t\tlet tmpToDir = this._FlowView._GeometryProvider.sideDirection(pTo.side);\n\n\t\tlet tmpDepart = {\n\t\t\tx: pFrom.x + tmpFromDir.dx * tmpDepartDist,\n\t\t\ty: pFrom.y + tmpFromDir.dy * tmpDepartDist\n\t\t};\n\t\tlet tmpApproach = {\n\t\t\tx: pTo.x + tmpToDir.dx * tmpDepartDist,\n\t\t\ty: pTo.y + tmpToDir.dy * tmpDepartDist\n\t\t};\n\n\t\treturn this._FlowView._PathGenerator.buildMultiBezierPathString(\n\t\t\tpFrom, tmpDepart, pFrom.side, tmpApproach, pTo.side, pTo, pHandles);\n\t}\n\n\t// ---- Handle State Management ----\n\n\t/**\n\t * Update a tether handle position during drag.\n\t * @param {Object} pPanelData - Panel data to update\n\t * @param {string} pHandleType - 'bezier-midpoint', 'ortho-corner1', 'ortho-corner2', 'ortho-midpoint'\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdateHandlePosition(pPanelData, pHandleType, pX, pY)\n\t{\n\t\tpPanelData.TetherHandleCustomized = true;\n\n\t\t// Multi-handle bezier: handle type is 'bezier-handle-N'\n\t\tif (pHandleType && pHandleType.startsWith('bezier-handle-'))\n\t\t{\n\t\t\tlet tmpIndex = parseInt(pHandleType.replace('bezier-handle-', ''), 10);\n\t\t\tif (!isNaN(tmpIndex) && Array.isArray(pPanelData.TetherBezierHandles)\n\t\t\t\t&& tmpIndex < pPanelData.TetherBezierHandles.length)\n\t\t\t{\n\t\t\t\tpPanelData.TetherBezierHandles[tmpIndex].x = pX;\n\t\t\t\tpPanelData.TetherBezierHandles[tmpIndex].y = pY;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (pHandleType)\n\t\t{\n\t\t\tcase 'bezier-midpoint':\n\t\t\t\t// Migrate to multi-handle array\n\t\t\t\tif (!Array.isArray(pPanelData.TetherBezierHandles)\n\t\t\t\t\t|| pPanelData.TetherBezierHandles.length === 0)\n\t\t\t\t{\n\t\t\t\t\tpPanelData.TetherBezierHandles = [{ x: pX, y: pY }];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpPanelData.TetherBezierHandles[0].x = pX;\n\t\t\t\t\tpPanelData.TetherBezierHandles[0].y = pY;\n\t\t\t\t}\n\t\t\t\t// Keep legacy fields in sync\n\t\t\t\tpPanelData.TetherBezierHandleX = pX;\n\t\t\t\tpPanelData.TetherBezierHandleY = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-corner1':\n\t\t\t\tpPanelData.TetherOrthoCorner1X = pX;\n\t\t\t\tpPanelData.TetherOrthoCorner1Y = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-corner2':\n\t\t\t\tpPanelData.TetherOrthoCorner2X = pX;\n\t\t\t\tpPanelData.TetherOrthoCorner2Y = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-midpoint':\n\t\t\t\t// Store the desired position for offset computation\n\t\t\t\tpPanelData.TetherOrthoMidOffset = (pPanelData.TetherOrthoMidOffset || 0);\n\t\t\t\tpPanelData._TetherMidDragX = pX;\n\t\t\t\tpPanelData._TetherMidDragY = pY;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Reset tether handle positions to auto for a single panel.\n\t * Preserves TetherLineMode but clears all handle coordinates.\n\t * @param {Object} pPanelData - Panel data to reset\n\t */\n\tresetHandlePositions(pPanelData)\n\t{\n\t\tif (pPanelData.TetherHandleCustomized)\n\t\t{\n\t\t\tpPanelData.TetherHandleCustomized = false;\n\t\t\tpPanelData.TetherBezierHandles = [];\n\t\t\tpPanelData.TetherBezierHandleX = null;\n\t\t\tpPanelData.TetherBezierHandleY = null;\n\t\t\tpPanelData.TetherOrthoCorner1X = null;\n\t\t\tpPanelData.TetherOrthoCorner1Y = null;\n\t\t\tpPanelData.TetherOrthoCorner2X = null;\n\t\t\tpPanelData.TetherOrthoCorner2Y = null;\n\t\t\tpPanelData.TetherOrthoMidOffset = 0;\n\t\t}\n\t}\n\n\t/**\n\t * Reset tether handle positions for all panels attached to a node.\n\t * Called when a node moves.\n\t * @param {Array} pOpenPanels - Array of all open panel data objects\n\t * @param {string} pNodeHash - The node hash whose panels should be reset\n\t */\n\tresetHandlesForNode(pOpenPanels, pNodeHash)\n\t{\n\t\tfor (let i = 0; i < pOpenPanels.length; i++)\n\t\t{\n\t\t\tlet tmpPanel = pOpenPanels[i];\n\t\t\tif (tmpPanel.NodeHash === pNodeHash)\n\t\t\t{\n\t\t\t\tthis.resetHandlePositions(tmpPanel);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Add a bezier handle to a tether at the specified position.\n\t * @param {Object} pPanelData - Panel data\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {Object} pFrom - Panel anchor {x, y, side}\n\t * @param {Object} pTo - Node anchor {x, y, side}\n\t */\n\taddHandle(pPanelData, pX, pY, pFrom, pTo)\n\t{\n\t\t// Ensure bezier mode and multi-handle array\n\t\tpPanelData.TetherLineMode = 'bezier';\n\n\t\tif (!Array.isArray(pPanelData.TetherBezierHandles))\n\t\t{\n\t\t\tpPanelData.TetherBezierHandles = [];\n\t\t\t// Migrate legacy single-handle if present\n\t\t\tif (pPanelData.TetherBezierHandleX != null && pPanelData.TetherBezierHandleY != null)\n\t\t\t{\n\t\t\t\tpPanelData.TetherBezierHandles.push({\n\t\t\t\t\tx: pPanelData.TetherBezierHandleX,\n\t\t\t\t\ty: pPanelData.TetherBezierHandleY\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Compute insertion index\n\t\tlet tmpInsertIndex = 0;\n\t\tif (this._FlowView._ConnectionRenderer && pFrom && pTo)\n\t\t{\n\t\t\ttmpInsertIndex = this._FlowView._ConnectionRenderer.computeInsertionIndex(\n\t\t\t\tpPanelData.TetherBezierHandles,\n\t\t\t\t{ x: pX, y: pY },\n\t\t\t\tpFrom,\n\t\t\t\tpTo\n\t\t\t);\n\t\t}\n\n\t\tpPanelData.TetherBezierHandles.splice(tmpInsertIndex, 0, { x: pX, y: pY });\n\t\tpPanelData.TetherHandleCustomized = true;\n\t}\n\n\t/**\n\t * Remove a bezier handle from a tether by index.\n\t * @param {Object} pPanelData - Panel data\n\t * @param {number} pIndex - Index in TetherBezierHandles array\n\t */\n\tremoveHandle(pPanelData, pIndex)\n\t{\n\t\tif (!Array.isArray(pPanelData.TetherBezierHandles)) return;\n\t\tif (pIndex < 0 || pIndex >= pPanelData.TetherBezierHandles.length) return;\n\n\t\tpPanelData.TetherBezierHandles.splice(pIndex, 1);\n\n\t\tif (pPanelData.TetherBezierHandles.length === 0)\n\t\t{\n\t\t\tpPanelData.TetherHandleCustomized = false;\n\t\t\tpPanelData.TetherBezierHandleX = null;\n\t\t\tpPanelData.TetherBezierHandleY = null;\n\t\t}\n\t}\n\n\t/**\n\t * Toggle tether line mode between bezier and orthogonal.\n\t * Resets handle positions on toggle.\n\t * @param {Object} pPanelData - Panel data to toggle\n\t * @returns {string} The new line mode ('bezier' or 'orthogonal')\n\t */\n\ttoggleLineMode(pPanelData)\n\t{\n\t\tlet tmpCurrentMode = pPanelData.TetherLineMode || 'bezier';\n\t\tpPanelData.TetherLineMode = (tmpCurrentMode === 'bezier') ? 'orthogonal' : 'bezier';\n\n\t\tpPanelData.TetherHandleCustomized = false;\n\t\tpPanelData.TetherBezierHandles = [];\n\t\tpPanelData.TetherBezierHandleX = null;\n\t\tpPanelData.TetherBezierHandleY = null;\n\t\tpPanelData.TetherOrthoCorner1X = null;\n\t\tpPanelData.TetherOrthoCorner1Y = null;\n\t\tpPanelData.TetherOrthoCorner2X = null;\n\t\tpPanelData.TetherOrthoCorner2Y = null;\n\t\tpPanelData.TetherOrthoMidOffset = 0;\n\n\t\treturn pPanelData.TetherLineMode;\n\t}\n\n\t// ---- SVG Rendering ----\n\n\t/**\n\t * Render a tether from a panel to its node.\n\t * Creates SVG path elements for the line and hit area, plus drag handles when selected.\n\t *\n\t * @param {Object} pPanelData - Panel data with position and tether properties\n\t * @param {Object} pNodeData - Node data with position\n\t * @param {SVGGElement} pTethersLayer - SVG group to append elements to\n\t * @param {boolean} pIsSelected - Whether this tether is currently selected\n\t * @param {string} pViewIdentifier - The flow view identifier (for marker URL)\n\t */\n\trenderTether(pPanelData, pNodeData, pTethersLayer, pIsSelected, pViewIdentifier)\n\t{\n\t\tif (!pNodeData) return;\n\n\t\tlet tmpAnchors = this.getSmartAnchors(pPanelData, pNodeData);\n\t\tlet tmpFrom = tmpAnchors.panelAnchor;\n\t\tlet tmpTo = tmpAnchors.nodeAnchor;\n\n\t\tlet tmpPath = this.generatePath(pPanelData, tmpFrom, tmpTo);\n\n\t\t// Hit area and visible tether path\n\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\tif (tmpShapeProvider)\n\t\t{\n\t\t\tlet tmpHitArea = tmpShapeProvider.createTetherHitAreaElement(tmpPath, pPanelData.Hash);\n\t\t\tpTethersLayer.appendChild(tmpHitArea);\n\n\t\t\tlet tmpPathElement = tmpShapeProvider.createTetherPathElement(\n\t\t\t\ttmpPath, pPanelData.Hash, pIsSelected, pViewIdentifier);\n\t\t\tpTethersLayer.appendChild(tmpPathElement);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpHitArea = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpHitArea.setAttribute('class', 'pict-flow-tether-hitarea');\n\t\t\ttmpHitArea.setAttribute('d', tmpPath);\n\t\t\ttmpHitArea.setAttribute('data-element-type', 'tether-hitarea');\n\t\t\ttmpHitArea.setAttribute('data-panel-hash', pPanelData.Hash);\n\t\t\tpTethersLayer.appendChild(tmpHitArea);\n\n\t\t\tlet tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpPathElement.setAttribute('class', `pict-flow-tether-line${pIsSelected ? ' selected' : ''}`);\n\t\t\ttmpPathElement.setAttribute('d', tmpPath);\n\t\t\ttmpPathElement.setAttribute('marker-end', `url(#flow-tether-arrowhead-${pViewIdentifier})`);\n\t\t\ttmpPathElement.setAttribute('data-element-type', 'tether');\n\t\t\ttmpPathElement.setAttribute('data-panel-hash', pPanelData.Hash);\n\t\t\tpTethersLayer.appendChild(tmpPathElement);\n\t\t}\n\n\t\t// Render drag handles when selected\n\t\tif (pIsSelected)\n\t\t{\n\t\t\tthis._renderHandles(pPanelData, pTethersLayer, tmpFrom, tmpTo);\n\t\t}\n\t}\n\n\t/**\n\t * Render drag handles for a selected tether.\n\t * @param {Object} pPanelData\n\t * @param {SVGGElement} pTethersLayer\n\t * @param {Object} pFrom - Panel anchor {x, y, side}\n\t * @param {Object} pTo - Node anchor {x, y, side}\n\t */\n\t_renderHandles(pPanelData, pTethersLayer, pFrom, pTo)\n\t{\n\t\tlet tmpLineMode = pPanelData.TetherLineMode || 'bezier';\n\n\t\tif (tmpLineMode === 'orthogonal')\n\t\t{\n\t\t\tlet tmpGeom = this.getOrthoGeometry(pFrom, pTo, pPanelData);\n\n\t\t\t// Corner 1 handle\n\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'ortho-corner1',\n\t\t\t\ttmpGeom.corner1.x, tmpGeom.corner1.y, 'pict-flow-tether-handle');\n\n\t\t\t// Midpoint handle\n\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'ortho-midpoint',\n\t\t\t\ttmpGeom.midpoint.x, tmpGeom.midpoint.y, 'pict-flow-tether-handle-midpoint');\n\n\t\t\t// Corner 2 handle\n\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'ortho-corner2',\n\t\t\t\ttmpGeom.corner2.x, tmpGeom.corner2.y, 'pict-flow-tether-handle');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Bezier handles\n\t\t\tlet tmpHandles = this._getTetherBezierHandles(pPanelData);\n\n\t\t\tif (tmpHandles.length > 0)\n\t\t\t{\n\t\t\t\t// Multi-handle: render each handle as a draggable circle\n\t\t\t\tfor (let i = 0; i < tmpHandles.length; i++)\n\t\t\t\t{\n\t\t\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'bezier-handle-' + i,\n\t\t\t\t\t\ttmpHandles[i].x, tmpHandles[i].y, 'pict-flow-tether-handle');\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// No custom handles: show auto-computed midpoint\n\t\t\t\tlet tmpMid = this.getAutoMidpoint(pFrom, pTo);\n\t\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'bezier-midpoint',\n\t\t\t\t\ttmpMid.x, tmpMid.y, 'pict-flow-tether-handle-midpoint');\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a single tether drag handle circle.\n\t * @param {SVGGElement} pLayer\n\t * @param {string} pPanelHash\n\t * @param {string} pHandleType\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {string} pClassName\n\t */\n\t_createHandle(pLayer, pPanelHash, pHandleType, pX, pY, pClassName)\n\t{\n\t\tif (!this._FlowView._ConnectorShapesProvider) return;\n\n\t\tlet tmpShapeKey = (pClassName === 'pict-flow-tether-handle-midpoint')\n\t\t\t? 'tether-handle-midpoint' : 'tether-handle';\n\n\t\tthis._FlowView._ConnectorShapesProvider.createFullHandle(\n\t\t\tpLayer, pPanelHash, pHandleType, pX, pY,\n\t\t\ttmpShapeKey, 'tether-handle', 'data-panel-hash');\n\t}\n}\n\nmodule.exports = PictServiceFlowTether;\n\n},{\"fable-serviceproviderbase\":4}],38:[function(require,module,exports){\nconst libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-ViewportManager\n *\n * Manages viewport transforms (pan/zoom), coordinate conversion between\n * screen and SVG space, zoom-to-fit calculations, and fullscreen toggling\n * for the flow diagram.\n */\nclass PictServiceFlowViewportManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowViewportManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\tthis._IsFullscreen = false;\n\t}\n\n\t/**\n\t * Update the viewport transform (pan and zoom)\n\t */\n\tupdateViewportTransform()\n\t{\n\t\tif (!this._FlowView._ViewportElement) return;\n\t\tlet tmpVS = this._FlowView._FlowData.ViewState;\n\t\tthis._FlowView._ViewportElement.setAttribute('transform',\n\t\t\t`translate(${tmpVS.PanX}, ${tmpVS.PanY}) scale(${tmpVS.Zoom})`\n\t\t);\n\t}\n\n\t/**\n\t * Set zoom level\n\t * @param {number} pZoom - The zoom level\n\t * @param {number} [pFocusX] - X coordinate to zoom toward (SVG space)\n\t * @param {number} [pFocusY] - Y coordinate to zoom toward (SVG space)\n\t */\n\tsetZoom(pZoom, pFocusX, pFocusY)\n\t{\n\t\tlet tmpNewZoom = Math.max(this._FlowView.options.MinZoom, Math.min(this._FlowView.options.MaxZoom, pZoom));\n\t\tlet tmpOldZoom = this._FlowView._FlowData.ViewState.Zoom;\n\n\t\tif (typeof pFocusX === 'number' && typeof pFocusY === 'number')\n\t\t{\n\t\t\t// Zoom toward focus point\n\t\t\tlet tmpVS = this._FlowView._FlowData.ViewState;\n\t\t\ttmpVS.PanX = pFocusX - (pFocusX - tmpVS.PanX) * (tmpNewZoom / tmpOldZoom);\n\t\t\ttmpVS.PanY = pFocusY - (pFocusY - tmpVS.PanY) * (tmpNewZoom / tmpOldZoom);\n\t\t}\n\n\t\tthis._FlowView._FlowData.ViewState.Zoom = tmpNewZoom;\n\t\tthis.updateViewportTransform();\n\t}\n\n\t/**\n\t * Zoom to fit all nodes in the viewport\n\t */\n\tzoomToFit()\n\t{\n\t\tif (this._FlowView._FlowData.Nodes.length === 0) return;\n\t\tif (!this._FlowView._SVGElement) return;\n\n\t\tlet tmpMinX = Infinity, tmpMinY = Infinity;\n\t\tlet tmpMaxX = -Infinity, tmpMaxY = -Infinity;\n\n\t\tfor (let i = 0; i < this._FlowView._FlowData.Nodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = this._FlowView._FlowData.Nodes[i];\n\t\t\ttmpMinX = Math.min(tmpMinX, tmpNode.X);\n\t\t\ttmpMinY = Math.min(tmpMinY, tmpNode.Y);\n\t\t\ttmpMaxX = Math.max(tmpMaxX, tmpNode.X + tmpNode.Width);\n\t\t\ttmpMaxY = Math.max(tmpMaxY, tmpNode.Y + tmpNode.Height);\n\t\t}\n\n\t\tlet tmpPadding = 50;\n\t\tlet tmpFlowWidth = tmpMaxX - tmpMinX + tmpPadding * 2;\n\t\tlet tmpFlowHeight = tmpMaxY - tmpMinY + tmpPadding * 2;\n\n\t\tlet tmpSVGRect = this._FlowView._SVGElement.getBoundingClientRect();\n\t\tlet tmpScaleX = tmpSVGRect.width / tmpFlowWidth;\n\t\tlet tmpScaleY = tmpSVGRect.height / tmpFlowHeight;\n\t\tlet tmpZoom = Math.min(tmpScaleX, tmpScaleY, 1.0); // Don't zoom in past 1.0\n\t\ttmpZoom = Math.max(this._FlowView.options.MinZoom, Math.min(this._FlowView.options.MaxZoom, tmpZoom));\n\n\t\tlet tmpCenterX = (tmpMinX + tmpMaxX) / 2;\n\t\tlet tmpCenterY = (tmpMinY + tmpMaxY) / 2;\n\n\t\tthis._FlowView._FlowData.ViewState.Zoom = tmpZoom;\n\t\tthis._FlowView._FlowData.ViewState.PanX = (tmpSVGRect.width / 2) - (tmpCenterX * tmpZoom);\n\t\tthis._FlowView._FlowData.ViewState.PanY = (tmpSVGRect.height / 2) - (tmpCenterY * tmpZoom);\n\n\t\tthis.updateViewportTransform();\n\t}\n\n\t/**\n\t * Convert screen coordinates to SVG viewport coordinates\n\t * @param {number} pScreenX\n\t * @param {number} pScreenY\n\t * @returns {{x: number, y: number}}\n\t */\n\tscreenToSVGCoords(pScreenX, pScreenY)\n\t{\n\t\tif (!this._FlowView._SVGElement)\n\t\t{\n\t\t\treturn { x: pScreenX, y: pScreenY };\n\t\t}\n\n\t\tlet tmpPoint = this._FlowView._SVGElement.createSVGPoint();\n\t\ttmpPoint.x = pScreenX;\n\t\ttmpPoint.y = pScreenY;\n\n\t\tlet tmpCTM = this._FlowView._SVGElement.getScreenCTM();\n\t\tif (tmpCTM)\n\t\t{\n\t\t\tlet tmpInverse = tmpCTM.inverse();\n\t\t\tlet tmpTransformed = tmpPoint.matrixTransform(tmpInverse);\n\t\t\t// Account for viewport pan/zoom\n\t\t\tlet tmpVS = this._FlowView._FlowData.ViewState;\n\t\t\treturn {\n\t\t\t\tx: (tmpTransformed.x - tmpVS.PanX) / tmpVS.Zoom,\n\t\t\t\ty: (tmpTransformed.y - tmpVS.PanY) / tmpVS.Zoom\n\t\t\t};\n\t\t}\n\n\t\treturn { x: pScreenX, y: pScreenY };\n\t}\n\n\t/**\n\t * Toggle fullscreen mode on the flow editor container.\n\t * Uses a CSS fixed-position overlay instead of the Fullscreen API.\n\t * @returns {boolean} The new fullscreen state\n\t */\n\ttoggleFullscreen()\n\t{\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\tlet tmpContainerElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Wrapper-${tmpViewIdentifier}`);\n\t\tif (tmpContainerElements.length < 1) return this._IsFullscreen;\n\n\t\tlet tmpContainer = tmpContainerElements[0];\n\n\t\tthis._IsFullscreen = !this._IsFullscreen;\n\n\t\tif (this._IsFullscreen)\n\t\t{\n\t\t\ttmpContainer.classList.add('pict-flow-fullscreen');\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpContainer.classList.remove('pict-flow-fullscreen');\n\t\t}\n\n\t\treturn this._IsFullscreen;\n\t}\n\n\t/**\n\t * Exit fullscreen mode if currently active.\n\t */\n\texitFullscreen()\n\t{\n\t\tif (!this._IsFullscreen) return;\n\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\tlet tmpContainerElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Wrapper-${tmpViewIdentifier}`);\n\t\tif (tmpContainerElements.length > 0)\n\t\t{\n\t\t\ttmpContainerElements[0].classList.remove('pict-flow-fullscreen');\n\t\t}\n\n\t\tthis._IsFullscreen = false;\n\t}\n}\n\nmodule.exports = PictServiceFlowViewportManager;\n\n},{\"fable-serviceproviderbase\":4}],39:[function(require,module,exports){\nconst libPictView = require('pict-view');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Flow-FloatingToolbar',\n\n\tDefaultRenderable: 'Flow-FloatingToolbar-Content',\n\tDefaultDestinationAddress: '#Flow-FloatingToolbar-Container',\n\n\tAutoRender: false,\n\n\tFlowViewIdentifier: 'Pict-Flow',\n\n\tCSS: false,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'Flow-FloatingToolbar-Template',\n\t\t\tTemplate: /*html*/`\n<div class=\"pict-flow-floating-toolbar\" id=\"Flow-FloatingToolbar-{~D:Record.FlowViewIdentifier~}\">\n\t<div class=\"pict-flow-floating-grip\" id=\"Flow-FloatingGrip-{~D:Record.FlowViewIdentifier~}\" title=\"Drag to move · Double-click to collapse\">\n\t\t<span id=\"Flow-FloatingIcon-grip-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</div>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"add-node\" title=\"Add Node\">\n\t\t<span id=\"Flow-FloatingIcon-plus-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"cards-popup\" title=\"Cards\">\n\t\t<span id=\"Flow-FloatingIcon-cards-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"delete-selected\" title=\"Delete Selected\">\n\t\t<span id=\"Flow-FloatingIcon-trash-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<div class=\"pict-flow-floating-separator\"></div>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"zoom-in\" title=\"Zoom In\">\n\t\t<span id=\"Flow-FloatingIcon-zoom-in-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"zoom-out\" title=\"Zoom Out\">\n\t\t<span id=\"Flow-FloatingIcon-zoom-out-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"zoom-fit\" title=\"Fit to View\">\n\t\t<span id=\"Flow-FloatingIcon-zoom-fit-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<div class=\"pict-flow-floating-separator\"></div>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"auto-layout\" title=\"Auto Layout\">\n\t\t<span id=\"Flow-FloatingIcon-auto-layout-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"layout-popup\" title=\"Layout\">\n\t\t<span id=\"Flow-FloatingIcon-layout-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"fullscreen\" title=\"Toggle Fullscreen\">\n\t\t<span id=\"Flow-FloatingIcon-fullscreen-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<div class=\"pict-flow-floating-separator\"></div>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"dock-toolbar\" title=\"Dock Toolbar\">\n\t\t<span id=\"Flow-FloatingIcon-dock-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: 'Flow-FloatingToolbar-Content',\n\t\t\tTemplateHash: 'Flow-FloatingToolbar-Template',\n\t\t\tDestinationAddress: '#Flow-FloatingToolbar-Container',\n\t\t\tRenderMethod: 'replace'\n\t\t}\n\t]\n};\n\nclass PictViewFlowFloatingToolbar extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictViewFlowFloatingToolbar';\n\n\t\tthis._ToolbarView = null;\n\t\tthis._FlowView = null;\n\n\t\tthis._IsCollapsed = false;\n\n\t\tthis._IsDragging = false;\n\t\tthis._DragStartX = 0;\n\t\tthis._DragStartY = 0;\n\t\tthis._DragStartLeft = 0;\n\t\tthis._DragStartTop = 0;\n\n\t\tthis._BoundMouseMove = null;\n\t\tthis._BoundMouseUp = null;\n\t}\n\n\trender(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\treturn super.render(pRenderableHash, pRenderDestinationAddress, this.options);\n\t}\n\n\tonAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\t// Bind click delegation for action buttons\n\t\tlet tmpFloatingToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpFloatingToolbar.length > 0)\n\t\t{\n\t\t\ttmpFloatingToolbar[0].addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\tlet tmpTarget = pEvent.target;\n\t\t\t\tif (!tmpTarget) return;\n\n\t\t\t\tlet tmpButton = tmpTarget.closest('[data-flow-action]');\n\t\t\t\tif (!tmpButton) return;\n\n\t\t\t\tlet tmpAction = tmpButton.getAttribute('data-flow-action');\n\t\t\t\tif (tmpAction === 'dock-toolbar')\n\t\t\t\t{\n\t\t\t\t\tif (this._ToolbarView)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._ToolbarView._setToolbarMode('docked');\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Delegate all other actions to the docked toolbar\n\t\t\t\tif (this._ToolbarView)\n\t\t\t\t{\n\t\t\t\t\tthis._ToolbarView._handleToolbarAction(tmpAction);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Bind drag behavior on the grip\n\t\tlet tmpGrip = this.pict.ContentAssignment.getElement(`#Flow-FloatingGrip-${tmpFlowViewIdentifier}`);\n\t\tif (tmpGrip.length > 0)\n\t\t{\n\t\t\ttmpGrip[0].addEventListener('mousedown', (pEvent) =>\n\t\t\t{\n\t\t\t\tthis._startDrag(pEvent);\n\t\t\t});\n\n\t\t\t// Double-click grip to toggle collapsed state\n\t\t\ttmpGrip[0].addEventListener('dblclick', (pEvent) =>\n\t\t\t{\n\t\t\t\tpEvent.preventDefault();\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tthis._toggleCollapse();\n\t\t\t});\n\t\t}\n\n\t\t// Populate icons\n\t\tthis._populateIcons();\n\n\t\t// Remove buttons from DOM based on options\n\t\tif (tmpFloatingToolbar.length > 0)\n\t\t{\n\t\t\tif (this.options.EnableAddNode === false)\n\t\t\t{\n\t\t\t\tlet tmpAddNodeBtn = tmpFloatingToolbar[0].querySelector('[data-flow-action=\"add-node\"]');\n\t\t\t\tif (tmpAddNodeBtn)\n\t\t\t\t{\n\t\t\t\t\ttmpAddNodeBtn.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.options.EnableCardPalette === false)\n\t\t\t{\n\t\t\t\tlet tmpCardsBtn = tmpFloatingToolbar[0].querySelector('[data-flow-action=\"cards-popup\"]');\n\t\t\t\tif (tmpCardsBtn)\n\t\t\t\t{\n\t\t\t\t\ttmpCardsBtn.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);\n\t}\n\n\t/**\n\t * Populate SVG icons into all floating toolbar button spans.\n\t */\n\t_populateIcons()\n\t{\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\t\tif (!tmpIconProvider) return;\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\tlet tmpIconMap =\n\t\t{\n\t\t\t'grip': 'grip',\n\t\t\t'plus': 'plus',\n\t\t\t'trash': 'trash',\n\t\t\t'zoom-in': 'zoom-in',\n\t\t\t'zoom-out': 'zoom-out',\n\t\t\t'zoom-fit': 'zoom-fit',\n\t\t\t'auto-layout': 'auto-layout',\n\t\t\t'cards': 'cards',\n\t\t\t'layout': 'layout',\n\t\t\t'fullscreen': 'fullscreen',\n\t\t\t'dock': 'dock'\n\t\t};\n\n\t\tlet tmpKeys = Object.keys(tmpIconMap);\n\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t{\n\t\t\tlet tmpElementId = `Flow-FloatingIcon-${tmpKeys[i]}-${tmpFlowViewIdentifier}`;\n\t\t\tlet tmpElements = this.pict.ContentAssignment.getElement(`#${tmpElementId}`);\n\t\t\tif (tmpElements.length > 0)\n\t\t\t{\n\t\t\t\ttmpElements[0].innerHTML = tmpIconProvider.getIconSVGMarkup(tmpIconMap[tmpKeys[i]], 16);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Toggle the floating toolbar between expanded and collapsed (grip-only square) states.\n\t */\n\t_toggleCollapse()\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbar.length < 1) return;\n\n\t\tthis._IsCollapsed = !this._IsCollapsed;\n\n\t\tif (this._IsCollapsed)\n\t\t{\n\t\t\ttmpToolbar[0].classList.add('collapsed');\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpToolbar[0].classList.remove('collapsed');\n\t\t}\n\t}\n\n\t/**\n\t * Start dragging the floating toolbar.\n\t * @param {MouseEvent} pEvent\n\t */\n\t_startDrag(pEvent)\n\t{\n\t\tpEvent.preventDefault();\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbar.length < 1) return;\n\n\t\tlet tmpEl = tmpToolbar[0];\n\t\tthis._IsDragging = true;\n\t\tthis._DragStartX = pEvent.clientX;\n\t\tthis._DragStartY = pEvent.clientY;\n\t\tthis._DragStartLeft = tmpEl.offsetLeft;\n\t\tthis._DragStartTop = tmpEl.offsetTop;\n\n\t\tthis._BoundMouseMove = (pMoveEvent) => { this._onDragMove(pMoveEvent); };\n\t\tthis._BoundMouseUp = () => { this._onDragEnd(); };\n\n\t\tdocument.addEventListener('mousemove', this._BoundMouseMove);\n\t\tdocument.addEventListener('mouseup', this._BoundMouseUp);\n\t}\n\n\t/**\n\t * Handle drag movement.\n\t * @param {MouseEvent} pEvent\n\t */\n\t_onDragMove(pEvent)\n\t{\n\t\tif (!this._IsDragging) return;\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbar.length < 1) return;\n\n\t\tlet tmpEl = tmpToolbar[0];\n\t\tlet tmpDeltaX = pEvent.clientX - this._DragStartX;\n\t\tlet tmpDeltaY = pEvent.clientY - this._DragStartY;\n\n\t\tlet tmpNewLeft = this._DragStartLeft + tmpDeltaX;\n\t\tlet tmpNewTop = this._DragStartTop + tmpDeltaY;\n\n\t\t// Clamp to parent bounds\n\t\tlet tmpParent = tmpEl.parentElement;\n\t\tif (tmpParent)\n\t\t{\n\t\t\tlet tmpMaxLeft = tmpParent.clientWidth - tmpEl.offsetWidth;\n\t\t\tlet tmpMaxTop = tmpParent.clientHeight - tmpEl.offsetHeight;\n\t\t\ttmpNewLeft = Math.max(0, Math.min(tmpNewLeft, tmpMaxLeft));\n\t\t\ttmpNewTop = Math.max(0, Math.min(tmpNewTop, tmpMaxTop));\n\t\t}\n\n\t\ttmpEl.style.left = tmpNewLeft + 'px';\n\t\ttmpEl.style.top = tmpNewTop + 'px';\n\t}\n\n\t/**\n\t * End dragging.\n\t */\n\t_onDragEnd()\n\t{\n\t\tthis._IsDragging = false;\n\n\t\tif (this._BoundMouseMove)\n\t\t{\n\t\t\tdocument.removeEventListener('mousemove', this._BoundMouseMove);\n\t\t\tthis._BoundMouseMove = null;\n\t\t}\n\t\tif (this._BoundMouseUp)\n\t\t{\n\t\t\tdocument.removeEventListener('mouseup', this._BoundMouseUp);\n\t\t\tthis._BoundMouseUp = null;\n\t\t}\n\n\t\t// Save position\n\t\tif (this._ToolbarView)\n\t\t{\n\t\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\t\tif (tmpToolbar.length > 0)\n\t\t\t{\n\t\t\t\tthis._ToolbarView._FloatingPosition.X = tmpToolbar[0].offsetLeft;\n\t\t\t\tthis._ToolbarView._FloatingPosition.Y = tmpToolbar[0].offsetTop;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Show the floating toolbar at the saved position.\n\t */\n\tshow()\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpContainer = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-Container-${tmpFlowViewIdentifier}`);\n\t\tif (tmpContainer.length > 0)\n\t\t{\n\t\t\ttmpContainer[0].style.display = 'block';\n\t\t}\n\n\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbar.length > 0 && this._ToolbarView)\n\t\t{\n\t\t\ttmpToolbar[0].style.left = this._ToolbarView._FloatingPosition.X + 'px';\n\t\t\ttmpToolbar[0].style.top = this._ToolbarView._FloatingPosition.Y + 'px';\n\t\t}\n\n\t\t// Restore expanded state when showing\n\t\tif (this._IsCollapsed && tmpToolbar.length > 0)\n\t\t{\n\t\t\tthis._IsCollapsed = false;\n\t\t\ttmpToolbar[0].classList.remove('collapsed');\n\t\t}\n\t}\n\n\t/**\n\t * Hide the floating toolbar.\n\t */\n\thide()\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpContainer = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-Container-${tmpFlowViewIdentifier}`);\n\t\tif (tmpContainer.length > 0)\n\t\t{\n\t\t\ttmpContainer[0].style.display = 'none';\n\t\t}\n\t}\n}\n\nmodule.exports = PictViewFlowFloatingToolbar;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n\n},{\"pict-view\":45}],40:[function(require,module,exports){\nconst libPictView = require('pict-view');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Flow-NodeRenderer',\n\n\tAutoRender: false,\n\n\t// Title bar height for nodes\n\tNodeTitleBarHeight: 22\n};\n\nclass PictViewFlowNode extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictViewFlowNode';\n\n\t\tthis._FlowView = null;\n\t}\n\n\t/**\n\t * Render a node into the nodes SVG layer\n\t * @param {Object} pNodeData - The node data object\n\t * @param {SVGGElement} pNodesLayer - The SVG <g> element to append to\n\t * @param {boolean} pIsSelected - Whether this node is selected\n\t * @param {Object} pNodeTypeConfig - The node type configuration\n\t */\n\trenderNode(pNodeData, pNodesLayer, pIsSelected, pNodeTypeConfig)\n\t{\n\t\tlet tmpGroup = this._FlowView._SVGHelperProvider.createSVGElement('g');\n\n\t\t// Build CSS class list with optional per-type modifier classes\n\t\tlet tmpClassList = `pict-flow-node ${pIsSelected ? 'selected' : ''} pict-flow-node-${pNodeData.Type || 'default'}`;\n\t\tif (pNodeTypeConfig)\n\t\t{\n\t\t\tif (pNodeTypeConfig.PortLabelsOnHover) tmpClassList += ' pict-flow-node-port-labels-hover';\n\t\t\tif (pNodeTypeConfig.PortLabelsVertical) tmpClassList += ' pict-flow-node-port-labels-vertical';\n\t\t}\n\t\ttmpGroup.setAttribute('class', tmpClassList);\n\t\ttmpGroup.setAttribute('transform', `translate(${pNodeData.X}, ${pNodeData.Y})`);\n\t\ttmpGroup.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpGroup.setAttribute('data-element-type', 'node');\n\n\t\tlet tmpWidth = pNodeData.Width || 180;\n\t\tlet tmpHeight = pNodeData.Height || 80;\n\t\tlet tmpTitleBarHeight = this.options.NodeTitleBarHeight;\n\n\t\t// Ensure node is tall enough for all ports in their zones\n\t\tlet tmpGeomProvider = this._FlowView._GeometryProvider;\n\t\tif (tmpGeomProvider && pNodeData.Ports && pNodeData.Ports.length > 0)\n\t\t{\n\t\t\tlet tmpMinHeight = tmpGeomProvider.computeMinimumNodeHeight(pNodeData.Ports, tmpTitleBarHeight);\n\t\t\tif (tmpMinHeight > tmpHeight)\n\t\t\t{\n\t\t\t\ttmpHeight = tmpMinHeight;\n\t\t\t}\n\t\t}\n\n\t\t// Write the adjusted dimensions back to the node data so that\n\t\t// connection rendering (which reads pNodeData.Width/Height to\n\t\t// compute port positions) uses the same values we render with.\n\t\tpNodeData.Width = tmpWidth;\n\t\tpNodeData.Height = tmpHeight;\n\n\t\t// Determine node body mode from theme (bracket vs rect)\n\t\tlet tmpNodeBodyMode = 'rect';\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\tlet tmpActiveTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\t\tif (tmpActiveTheme && tmpActiveTheme.NodeBodyMode)\n\t\t\t{\n\t\t\t\ttmpNodeBodyMode = tmpActiveTheme.NodeBodyMode;\n\t\t\t}\n\t\t}\n\n\t\tif (tmpNodeBodyMode === 'bracket')\n\t\t{\n\t\t\tthis._renderBracketNodeBody(tmpGroup, pNodeData, tmpWidth, tmpHeight, tmpTitleBarHeight, pNodeTypeConfig);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._renderRectNodeBody(tmpGroup, pNodeData, tmpWidth, tmpHeight, tmpTitleBarHeight, pNodeTypeConfig);\n\t\t}\n\n\t\t// Determine if this node has a title-bar icon (FlowCard with CardMetadata)\n\t\tlet tmpHasTitleIcon = false;\n\t\tlet tmpTitleIconSize = 12;\n\t\tlet tmpTitleIconMarginLeft = 8;\n\t\tlet tmpTitleIconGap = 4;\n\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.CardMetadata)\n\t\t{\n\t\t\tlet tmpMeta = pNodeTypeConfig.CardMetadata;\n\t\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\t\tif (tmpMeta.Icon || tmpIconProvider)\n\t\t\t{\n\t\t\t\ttmpHasTitleIcon = true;\n\t\t\t}\n\t\t}\n\n\t\t// Title text (position adjusts when a title-bar icon is present)\n\t\tlet tmpTitle = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\ttmpTitle.setAttribute('class', 'pict-flow-node-title');\n\t\tif (tmpHasTitleIcon)\n\t\t{\n\t\t\ttmpTitle.setAttribute('x', String(tmpTitleIconMarginLeft + tmpTitleIconSize + tmpTitleIconGap));\n\t\t\ttmpTitle.setAttribute('text-anchor', 'start');\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpTitle.setAttribute('x', String(tmpWidth / 2));\n\t\t\ttmpTitle.setAttribute('text-anchor', 'middle');\n\t\t}\n\t\ttmpTitle.setAttribute('y', String(tmpTitleBarHeight / 2 + 1));\n\t\ttmpTitle.setAttribute('dominant-baseline', 'central');\n\t\ttmpTitle.textContent = pNodeData.Title || 'Untitled';\n\t\ttmpGroup.appendChild(tmpTitle);\n\n\t\t// Determine whether labels should be rendered\n\t\tlet tmpShowTypeLabel = (!pNodeTypeConfig || pNodeTypeConfig.ShowTypeLabel !== false);\n\t\tlet tmpLabelsInFront = (!pNodeTypeConfig || pNodeTypeConfig.LabelsInFront !== false);\n\n\t\t// Helper: render type label + code badge + tooltip (the \"middle labels\")\n\t\tlet tmpRenderTypeLabels = () =>\n\t\t{\n\t\t\t// Type label (below title bar — hover-only for FlowCard nodes via CSS)\n\t\t\tif (tmpShowTypeLabel && pNodeTypeConfig && pNodeTypeConfig.Label && pNodeTypeConfig.Label !== pNodeData.Title)\n\t\t\t{\n\t\t\t\tlet tmpTypeLabel = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\ttmpTypeLabel.setAttribute('class', 'pict-flow-node-type-label');\n\t\t\t\ttmpTypeLabel.setAttribute('x', String(tmpWidth / 2));\n\t\t\t\ttmpTypeLabel.setAttribute('y', String(tmpTitleBarHeight + 16));\n\t\t\t\ttmpTypeLabel.setAttribute('text-anchor', 'middle');\n\t\t\t\ttmpTypeLabel.setAttribute('dominant-baseline', 'central');\n\t\t\t\ttmpTypeLabel.textContent = pNodeTypeConfig.Label;\n\t\t\t\ttmpGroup.appendChild(tmpTypeLabel);\n\t\t\t}\n\n\t\t\t// FlowCard metadata: icon in title bar, code badge in body (hover-only via CSS)\n\t\t\tif (pNodeTypeConfig && pNodeTypeConfig.CardMetadata)\n\t\t\t{\n\t\t\t\tlet tmpMeta = pNodeTypeConfig.CardMetadata;\n\t\t\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\t\t\tlet tmpTitleIconRendered = false;\n\n\t\t\t\t// Icon position in title bar (vertically centered)\n\t\t\t\tlet tmpIconX = tmpTitleIconMarginLeft;\n\t\t\t\tlet tmpIconY = (tmpTitleBarHeight - tmpTitleIconSize) / 2;\n\n\t\t\t\tif (tmpMeta.Icon && tmpIconProvider && !tmpIconProvider.isEmojiIcon(tmpMeta.Icon))\n\t\t\t\t{\n\t\t\t\t\t// SVG icon via the icon provider — rendered into title bar\n\t\t\t\t\tlet tmpResolvedKey = tmpIconProvider.resolveIconKey(tmpMeta);\n\t\t\t\t\tlet tmpIconGroup = tmpIconProvider.renderIconIntoSVGGroup(\n\t\t\t\t\t\ttmpResolvedKey, tmpGroup,\n\t\t\t\t\t\ttmpIconX, tmpIconY,\n\t\t\t\t\t\ttmpTitleIconSize);\n\t\t\t\t\tif (tmpIconGroup)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpIconGroup.setAttribute('class',\n\t\t\t\t\t\t\t(tmpIconGroup.getAttribute('class') || '') + ' pict-flow-node-title-icon');\n\t\t\t\t\t}\n\t\t\t\t\ttmpTitleIconRendered = true;\n\t\t\t\t}\n\t\t\t\telse if (tmpMeta.Icon && tmpIconProvider && tmpIconProvider.isEmojiIcon(tmpMeta.Icon))\n\t\t\t\t{\n\t\t\t\t\t// Emoji icon in title bar\n\t\t\t\t\tlet tmpIconText = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\t\ttmpIconText.setAttribute('class', 'pict-flow-node-card-icon pict-flow-node-title-icon-emoji');\n\t\t\t\t\ttmpIconText.setAttribute('font-size', String(tmpTitleIconSize));\n\t\t\t\t\ttmpIconText.setAttribute('text-anchor', 'middle');\n\t\t\t\t\ttmpIconText.setAttribute('dominant-baseline', 'central');\n\t\t\t\t\ttmpIconText.setAttribute('pointer-events', 'none');\n\t\t\t\t\ttmpIconText.setAttribute('x', String(tmpIconX + tmpTitleIconSize / 2));\n\t\t\t\t\ttmpIconText.setAttribute('y', String(tmpTitleBarHeight / 2));\n\t\t\t\t\ttmpIconText.textContent = tmpMeta.Icon;\n\t\t\t\t\ttmpGroup.appendChild(tmpIconText);\n\t\t\t\t\ttmpTitleIconRendered = true;\n\t\t\t\t}\n\t\t\t\telse if (tmpMeta.Icon)\n\t\t\t\t{\n\t\t\t\t\t// No icon provider — text fallback in title bar\n\t\t\t\t\tlet tmpIconText = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\t\ttmpIconText.setAttribute('class', 'pict-flow-node-card-icon pict-flow-node-title-icon-emoji');\n\t\t\t\t\ttmpIconText.setAttribute('font-size', String(tmpTitleIconSize));\n\t\t\t\t\ttmpIconText.setAttribute('text-anchor', 'middle');\n\t\t\t\t\ttmpIconText.setAttribute('dominant-baseline', 'central');\n\t\t\t\t\ttmpIconText.setAttribute('pointer-events', 'none');\n\t\t\t\t\ttmpIconText.setAttribute('x', String(tmpIconX + tmpTitleIconSize / 2));\n\t\t\t\t\ttmpIconText.setAttribute('y', String(tmpTitleBarHeight / 2));\n\t\t\t\t\ttmpIconText.textContent = tmpMeta.Icon;\n\t\t\t\t\ttmpGroup.appendChild(tmpIconText);\n\t\t\t\t\ttmpTitleIconRendered = true;\n\t\t\t\t}\n\n\t\t\t\t// Default fallback icon in title bar\n\t\t\t\tif (!tmpTitleIconRendered && tmpIconProvider)\n\t\t\t\t{\n\t\t\t\t\tlet tmpIconGroup = tmpIconProvider.renderIconIntoSVGGroup(\n\t\t\t\t\t\t'default', tmpGroup,\n\t\t\t\t\t\ttmpIconX, tmpIconY,\n\t\t\t\t\t\ttmpTitleIconSize);\n\t\t\t\t\tif (tmpIconGroup)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpIconGroup.setAttribute('class',\n\t\t\t\t\t\t\t(tmpIconGroup.getAttribute('class') || '') + ' pict-flow-node-title-icon');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Code badge in body (hover-only via CSS, skipped when ShowTypeLabel is false)\n\t\t\t\tlet tmpBodyCenterY = tmpTitleBarHeight + (tmpHeight - tmpTitleBarHeight) / 2;\n\t\t\t\tif (tmpShowTypeLabel && tmpMeta.Code)\n\t\t\t\t{\n\t\t\t\t\tlet tmpCodeText = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\t\ttmpCodeText.setAttribute('class', 'pict-flow-node-card-code');\n\t\t\t\t\ttmpCodeText.setAttribute('font-size', '10');\n\t\t\t\t\ttmpCodeText.setAttribute('font-family', 'monospace');\n\t\t\t\t\ttmpCodeText.setAttribute('fill', '#7f8c8d');\n\t\t\t\t\ttmpCodeText.setAttribute('text-anchor', 'middle');\n\t\t\t\t\ttmpCodeText.setAttribute('dominant-baseline', 'central');\n\t\t\t\t\ttmpCodeText.setAttribute('pointer-events', 'none');\n\t\t\t\t\ttmpCodeText.setAttribute('x', String(tmpWidth / 2));\n\t\t\t\t\ttmpCodeText.setAttribute('y', String(tmpBodyCenterY));\n\t\t\t\t\ttmpCodeText.textContent = tmpMeta.Code;\n\t\t\t\t\ttmpGroup.appendChild(tmpCodeText);\n\t\t\t\t}\n\n\t\t\t\t// Tooltip via SVG <title> element\n\t\t\t\tif (tmpMeta.Tooltip || tmpMeta.Description)\n\t\t\t\t{\n\t\t\t\t\tlet tmpSVGTitle = this._FlowView._SVGHelperProvider.createSVGElement('title');\n\t\t\t\t\ttmpSVGTitle.textContent = tmpMeta.Tooltip || tmpMeta.Description;\n\t\t\t\t\ttmpGroup.appendChild(tmpSVGTitle);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Render order depends on LabelsInFront:\n\t\t// true (default): body content first, then labels + ports (labels on top)\n\t\t// false: labels + ports first, then body content (content on top)\n\t\tif (tmpLabelsInFront)\n\t\t{\n\t\t\tthis._renderBodyContent(pNodeData, tmpGroup, tmpWidth, tmpHeight, pNodeTypeConfig);\n\t\t\ttmpRenderTypeLabels();\n\t\t\tthis._renderPorts(pNodeData, tmpGroup, tmpWidth, tmpHeight, pNodeTypeConfig);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRenderTypeLabels();\n\t\t\tthis._renderPorts(pNodeData, tmpGroup, tmpWidth, tmpHeight, pNodeTypeConfig);\n\t\t\tthis._renderBodyContent(pNodeData, tmpGroup, tmpWidth, tmpHeight, pNodeTypeConfig);\n\t\t}\n\n\t\t// Panel indicator icon (small rect in bottom-right corner)\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.PropertiesPanel)\n\t\t{\n\t\t\tlet tmpIndicatorSize = 10;\n\t\t\tlet tmpIndicatorMargin = 4;\n\t\t\tlet tmpIndicatorX = tmpWidth - tmpIndicatorSize - tmpIndicatorMargin;\n\t\t\tlet tmpIndicatorY = tmpHeight - tmpIndicatorSize - tmpIndicatorMargin;\n\t\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\t\tlet tmpIndicator;\n\n\t\t\tif (tmpShapeProvider)\n\t\t\t{\n\t\t\t\ttmpIndicator = tmpShapeProvider.createPanelIndicatorElement(\n\t\t\t\t\tpNodeData.Hash, tmpIndicatorX, tmpIndicatorY,\n\t\t\t\t\ttmpIndicatorSize, tmpIndicatorSize);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpIndicator = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\t\t\ttmpIndicator.setAttribute('class', 'pict-flow-node-panel-indicator');\n\t\t\t\ttmpIndicator.setAttribute('x', String(tmpIndicatorX));\n\t\t\t\ttmpIndicator.setAttribute('y', String(tmpIndicatorY));\n\t\t\t\ttmpIndicator.setAttribute('width', String(tmpIndicatorSize));\n\t\t\t\ttmpIndicator.setAttribute('height', String(tmpIndicatorSize));\n\t\t\t\ttmpIndicator.setAttribute('rx', '2');\n\t\t\t\ttmpIndicator.setAttribute('ry', '2');\n\t\t\t\ttmpIndicator.setAttribute('data-node-hash', pNodeData.Hash);\n\t\t\t\ttmpIndicator.setAttribute('data-element-type', 'panel-indicator');\n\t\t\t}\n\n\t\t\tlet tmpIndicatorTitle = this._FlowView._SVGHelperProvider.createSVGElement('title');\n\t\t\ttmpIndicatorTitle.textContent = 'Double-click to open properties';\n\t\t\ttmpIndicator.appendChild(tmpIndicatorTitle);\n\n\t\t\ttmpGroup.appendChild(tmpIndicator);\n\t\t}\n\n\t\tpNodesLayer.appendChild(tmpGroup);\n\t}\n\n\t/**\n\t * Render ports for a node — delegates to the PortRenderer service.\n\t * @param {Object} pNodeData\n\t * @param {SVGGElement} pGroup - The node's SVG group\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @param {Object} [pNodeTypeConfig] - Node type configuration (for label display options)\n\t */\n\t_renderPorts(pNodeData, pGroup, pWidth, pHeight, pNodeTypeConfig)\n\t{\n\t\tthis._FlowView._PortRenderer.renderPorts(pNodeData, pGroup, pWidth, pHeight, pNodeTypeConfig, this.options.NodeTitleBarHeight);\n\t}\n\n\t/**\n\t * Render custom body content for a node (svg, html, or canvas).\n\t *\n\t * Checks for a BodyContent configuration on the node type and renders\n\t * the appropriate content type into the node's SVG group.\n\t *\n\t * @param {Object} pNodeData - The node data object\n\t * @param {SVGGElement} pGroup - The node's SVG group\n\t * @param {number} pWidth - Node width\n\t * @param {number} pHeight - Node height\n\t * @param {Object} pNodeTypeConfig - The node type configuration\n\t */\n\t_renderBodyContent(pNodeData, pGroup, pWidth, pHeight, pNodeTypeConfig)\n\t{\n\t\tif (!pNodeTypeConfig || !pNodeTypeConfig.BodyContent) return;\n\n\t\tlet tmpBodyContent = pNodeTypeConfig.BodyContent;\n\t\tlet tmpContentType = tmpBodyContent.ContentType;\n\t\tif (!tmpContentType) return;\n\n\t\tlet tmpTitleBarHeight = this.options.NodeTitleBarHeight;\n\t\tlet tmpPadding = (typeof tmpBodyContent.Padding === 'number') ? tmpBodyContent.Padding : 2;\n\t\tlet tmpBodyBounds =\n\t\t{\n\t\t\tx: tmpPadding,\n\t\t\ty: tmpTitleBarHeight + tmpPadding,\n\t\t\twidth: pWidth - (tmpPadding * 2),\n\t\t\theight: pHeight - tmpTitleBarHeight - (tmpPadding * 2)\n\t\t};\n\n\t\tlet tmpPict = this._FlowView.pict || this.pict;\n\n\t\t// Register any templates defined in the BodyContent config (once)\n\t\tif (tmpBodyContent.Templates && Array.isArray(tmpBodyContent.Templates))\n\t\t{\n\t\t\tif (!this._registeredBodyTemplates)\n\t\t\t{\n\t\t\t\tthis._registeredBodyTemplates = new Set();\n\t\t\t}\n\t\t\tfor (let i = 0; i < tmpBodyContent.Templates.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpTpl = tmpBodyContent.Templates[i];\n\t\t\t\tif (tmpTpl.Hash && tmpTpl.Template && !this._registeredBodyTemplates.has(tmpTpl.Hash))\n\t\t\t\t{\n\t\t\t\t\ttmpPict.TemplateProvider.addTemplate(tmpTpl.Hash, tmpTpl.Template, 'PictViewFlowNode-BodyContent');\n\t\t\t\t\tthis._registeredBodyTemplates.add(tmpTpl.Hash);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch (tmpContentType)\n\t\t{\n\t\t\tcase 'svg':\n\t\t\t\tthis._renderBodyContentSVG(pNodeData, pGroup, tmpBodyContent, tmpBodyBounds, pNodeTypeConfig, tmpPict);\n\t\t\t\tbreak;\n\t\t\tcase 'html':\n\t\t\t\tthis._renderBodyContentHTML(pNodeData, pGroup, tmpBodyContent, tmpBodyBounds, pNodeTypeConfig, tmpPict);\n\t\t\t\tbreak;\n\t\t\tcase 'canvas':\n\t\t\t\tthis._renderBodyContentCanvas(pNodeData, pGroup, tmpBodyContent, tmpBodyBounds, pNodeTypeConfig);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthis.log.warn('PictViewFlowNode _renderBodyContent: unknown ContentType [' + tmpContentType + ']');\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Render SVG body content into a <g> group.\n\t */\n\t_renderBodyContentSVG(pNodeData, pGroup, pBodyContent, pBounds, pNodeTypeConfig, pPict)\n\t{\n\t\tlet tmpContentGroup = this._FlowView._SVGHelperProvider.createSVGElement('g');\n\t\ttmpContentGroup.setAttribute('class', 'pict-flow-node-body-content');\n\t\ttmpContentGroup.setAttribute('transform', `translate(${pBounds.x}, ${pBounds.y})`);\n\n\t\t// Render template content\n\t\tlet tmpRenderedContent = this._resolveBodyTemplate(pBodyContent, pNodeData, pPict);\n\t\tif (tmpRenderedContent)\n\t\t{\n\t\t\t// Parse SVG markup into the group via a temporary SVG element\n\t\t\tlet tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\t\t\ttmpTempSVG.innerHTML = tmpRenderedContent;\n\t\t\twhile (tmpTempSVG.firstChild)\n\t\t\t{\n\t\t\t\ttmpContentGroup.appendChild(tmpTempSVG.firstChild);\n\t\t\t}\n\t\t}\n\n\t\t// Invoke render callback if provided\n\t\tif (typeof pBodyContent.RenderCallback === 'function')\n\t\t{\n\t\t\tpBodyContent.RenderCallback(tmpContentGroup, pNodeData, pNodeTypeConfig, pBounds);\n\t\t}\n\n\t\tpGroup.appendChild(tmpContentGroup);\n\t}\n\n\t/**\n\t * Render HTML body content into a foreignObject.\n\t */\n\t_renderBodyContentHTML(pNodeData, pGroup, pBodyContent, pBounds, pNodeTypeConfig, pPict)\n\t{\n\t\tlet tmpFO = this._FlowView._SVGHelperProvider.createSVGElement('foreignObject');\n\t\ttmpFO.setAttribute('class', 'pict-flow-node-body-content-fo');\n\t\ttmpFO.setAttribute('x', String(pBounds.x));\n\t\ttmpFO.setAttribute('y', String(pBounds.y));\n\t\ttmpFO.setAttribute('width', String(pBounds.width));\n\t\ttmpFO.setAttribute('height', String(pBounds.height));\n\n\t\tlet tmpDiv = document.createElement('div');\n\t\ttmpDiv.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');\n\t\ttmpDiv.setAttribute('class', 'pict-flow-node-body-content-html');\n\n\t\t// Pointer event isolation — prevent node drag/canvas pan\n\t\ttmpDiv.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\ttmpDiv.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });\n\n\t\t// Render template content\n\t\tlet tmpRenderedContent = this._resolveBodyTemplate(pBodyContent, pNodeData, pPict);\n\t\tif (tmpRenderedContent)\n\t\t{\n\t\t\ttmpDiv.innerHTML = tmpRenderedContent;\n\t\t}\n\n\t\t// Invoke render callback if provided\n\t\tif (typeof pBodyContent.RenderCallback === 'function')\n\t\t{\n\t\t\tpBodyContent.RenderCallback(tmpDiv, pNodeData, pNodeTypeConfig, pBounds);\n\t\t}\n\n\t\ttmpFO.appendChild(tmpDiv);\n\t\tpGroup.appendChild(tmpFO);\n\t}\n\n\t/**\n\t * Render canvas body content into a foreignObject.\n\t */\n\t_renderBodyContentCanvas(pNodeData, pGroup, pBodyContent, pBounds, pNodeTypeConfig)\n\t{\n\t\tlet tmpFO = this._FlowView._SVGHelperProvider.createSVGElement('foreignObject');\n\t\ttmpFO.setAttribute('class', 'pict-flow-node-body-content-fo');\n\t\ttmpFO.setAttribute('x', String(pBounds.x));\n\t\ttmpFO.setAttribute('y', String(pBounds.y));\n\t\ttmpFO.setAttribute('width', String(pBounds.width));\n\t\ttmpFO.setAttribute('height', String(pBounds.height));\n\n\t\tlet tmpCanvas = document.createElement('canvas');\n\t\ttmpCanvas.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');\n\t\ttmpCanvas.setAttribute('class', 'pict-flow-node-body-content-canvas');\n\t\ttmpCanvas.width = Math.floor(pBounds.width);\n\t\ttmpCanvas.height = Math.floor(pBounds.height);\n\t\ttmpCanvas.style.width = '100%';\n\t\ttmpCanvas.style.height = '100%';\n\n\t\t// Pointer event isolation\n\t\ttmpCanvas.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\ttmpCanvas.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });\n\n\t\t// Invoke render callback (the primary rendering path for canvas)\n\t\tif (typeof pBodyContent.RenderCallback === 'function')\n\t\t{\n\t\t\tpBodyContent.RenderCallback(tmpCanvas, pNodeData, pNodeTypeConfig, pBounds);\n\t\t}\n\n\t\ttmpFO.appendChild(tmpCanvas);\n\t\tpGroup.appendChild(tmpFO);\n\t}\n\n\t/**\n\t * Resolve and render a body content template string.\n\t * @param {Object} pBodyContent - The BodyContent config\n\t * @param {Object} pNodeData - The node data (template record)\n\t * @param {Object} pPict - The Pict instance\n\t * @returns {string|null} Rendered template content, or null\n\t */\n\t_resolveBodyTemplate(pBodyContent, pNodeData, pPict)\n\t{\n\t\tif (pBodyContent.TemplateHash)\n\t\t{\n\t\t\treturn pPict.parseTemplateByHash(pBodyContent.TemplateHash, pNodeData);\n\t\t}\n\t\tif (pBodyContent.Template)\n\t\t{\n\t\t\treturn pPict.parseTemplate(pBodyContent.Template, pNodeData, null, [pNodeData]);\n\t\t}\n\t\treturn null;\n\t}\n\n\t// ── Node Body Renderers ──────────────────────────────────────────────\n\n\t/**\n\t * Render the standard rect-based node body (default mode).\n\t * @param {SVGGElement} pGroup\n\t * @param {Object} pNodeData\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @param {number} pTitleBarHeight\n\t * @param {Object} pNodeTypeConfig\n\t */\n\t_renderRectNodeBody(pGroup, pNodeData, pWidth, pHeight, pTitleBarHeight, pNodeTypeConfig)\n\t{\n\t\t// Node body (main rectangle)\n\t\tlet tmpBody = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpBody.setAttribute('class', 'pict-flow-node-body');\n\t\ttmpBody.setAttribute('x', '0');\n\t\ttmpBody.setAttribute('y', '0');\n\t\ttmpBody.setAttribute('width', String(pWidth));\n\t\ttmpBody.setAttribute('height', String(pHeight));\n\t\ttmpBody.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpBody.setAttribute('data-element-type', 'node-body');\n\n\t\t// Apply custom styles from node type\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.BodyStyle)\n\t\t{\n\t\t\tfor (let tmpStyleKey in pNodeTypeConfig.BodyStyle)\n\t\t\t{\n\t\t\t\ttmpBody.setAttribute(tmpStyleKey, pNodeTypeConfig.BodyStyle[tmpStyleKey]);\n\t\t\t}\n\t\t}\n\n\t\t// Apply per-instance style overrides (for node-specific editing)\n\t\t// These must be applied as inline styles so they override CSS rules\n\t\t// (CSS declarations take precedence over SVG presentation attributes).\n\t\tif (pNodeData.Style)\n\t\t{\n\t\t\tlet tmpInlineStyles = [];\n\t\t\tif (pNodeData.Style.BodyFill) tmpInlineStyles.push('fill:' + pNodeData.Style.BodyFill);\n\t\t\tif (pNodeData.Style.BodyStroke) tmpInlineStyles.push('stroke:' + pNodeData.Style.BodyStroke);\n\t\t\tif (pNodeData.Style.BodyStrokeWidth) tmpInlineStyles.push('stroke-width:' + pNodeData.Style.BodyStrokeWidth);\n\t\t\tif (tmpInlineStyles.length > 0)\n\t\t\t{\n\t\t\t\ttmpBody.setAttribute('style', tmpInlineStyles.join(';'));\n\t\t\t}\n\t\t}\n\n\t\tpGroup.appendChild(tmpBody);\n\n\t\t// Title bar background (top portion)\n\t\tlet tmpTitleBar = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpTitleBar.setAttribute('class', 'pict-flow-node-title-bar');\n\t\ttmpTitleBar.setAttribute('x', '0');\n\t\ttmpTitleBar.setAttribute('y', '0');\n\t\ttmpTitleBar.setAttribute('width', String(pWidth));\n\t\ttmpTitleBar.setAttribute('height', String(pTitleBarHeight));\n\t\ttmpTitleBar.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpTitleBar.setAttribute('data-element-type', 'node-body');\n\n\t\t// Apply custom title bar color\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleBar.setAttribute('fill', pNodeTypeConfig.TitleBarColor);\n\t\t}\n\n\t\tpGroup.appendChild(tmpTitleBar);\n\n\t\t// Title bar bottom fill (to square off the rounded corners at the bottom of the title bar)\n\t\tlet tmpTitleBarBottom = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpTitleBarBottom.setAttribute('class', 'pict-flow-node-title-bar-bottom');\n\t\ttmpTitleBarBottom.setAttribute('x', '0');\n\t\ttmpTitleBarBottom.setAttribute('y', String(pTitleBarHeight - 8));\n\t\ttmpTitleBarBottom.setAttribute('width', String(pWidth));\n\t\ttmpTitleBarBottom.setAttribute('height', '8');\n\t\ttmpTitleBarBottom.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpTitleBarBottom.setAttribute('data-element-type', 'node-body');\n\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleBarBottom.setAttribute('fill', pNodeTypeConfig.TitleBarColor);\n\t\t}\n\n\t\t// Per-instance title bar color override\n\t\t// Applied as inline style to override CSS rules.\n\t\tif (pNodeData.Style && pNodeData.Style.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleBar.setAttribute('style', 'fill:' + pNodeData.Style.TitleBarColor);\n\t\t\ttmpTitleBarBottom.setAttribute('style', 'fill:' + pNodeData.Style.TitleBarColor);\n\t\t}\n\n\t\tpGroup.appendChild(tmpTitleBarBottom);\n\t}\n\n\t/**\n\t * Render a bracket-style node body (used by sketch/blueprint themes).\n\t *\n\t * The bracket body consists of:\n\t * 1. A fill rect for the body background (no stroke)\n\t * 2. A fill rect for the title bar background (no stroke)\n\t * 3. A bracket path drawn via the noise provider (outline + title divider)\n\t *\n\t * @param {SVGGElement} pGroup\n\t * @param {Object} pNodeData\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @param {number} pTitleBarHeight\n\t * @param {Object} pNodeTypeConfig\n\t */\n\t_renderBracketNodeBody(pGroup, pNodeData, pWidth, pHeight, pTitleBarHeight, pNodeTypeConfig)\n\t{\n\t\t// 1. Body fill rect (background only, no stroke)\n\t\tlet tmpBodyFill = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpBodyFill.setAttribute('class', 'pict-flow-node-body pict-flow-node-bracket-fill');\n\t\ttmpBodyFill.setAttribute('x', '0');\n\t\ttmpBodyFill.setAttribute('y', '0');\n\t\ttmpBodyFill.setAttribute('width', String(pWidth));\n\t\ttmpBodyFill.setAttribute('height', String(pHeight));\n\t\ttmpBodyFill.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpBodyFill.setAttribute('data-element-type', 'node-body');\n\n\t\t// Per-instance style overrides\n\t\tif (pNodeData.Style)\n\t\t{\n\t\t\tlet tmpInlineStyles = [];\n\t\t\tif (pNodeData.Style.BodyFill) tmpInlineStyles.push('fill:' + pNodeData.Style.BodyFill);\n\t\t\tif (tmpInlineStyles.length > 0)\n\t\t\t{\n\t\t\t\ttmpBodyFill.setAttribute('style', tmpInlineStyles.join(';'));\n\t\t\t}\n\t\t}\n\n\t\tpGroup.appendChild(tmpBodyFill);\n\n\t\t// 2. Title bar fill rect (background only, no stroke)\n\t\tlet tmpTitleFill = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpTitleFill.setAttribute('class', 'pict-flow-node-title-bar pict-flow-node-bracket-title-fill');\n\t\ttmpTitleFill.setAttribute('x', '0');\n\t\ttmpTitleFill.setAttribute('y', '0');\n\t\ttmpTitleFill.setAttribute('width', String(pWidth));\n\t\ttmpTitleFill.setAttribute('height', String(pTitleBarHeight));\n\t\ttmpTitleFill.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpTitleFill.setAttribute('data-element-type', 'node-body');\n\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleFill.setAttribute('style', 'fill:' + pNodeTypeConfig.TitleBarColor);\n\t\t}\n\t\tif (pNodeData.Style && pNodeData.Style.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleFill.setAttribute('style', 'fill:' + pNodeData.Style.TitleBarColor);\n\t\t}\n\n\t\tpGroup.appendChild(tmpTitleFill);\n\n\t\t// 3. Bracket path (outline + title divider with optional noise)\n\t\tlet tmpBracketConfig = { SerifLength: 6, TitleSeparator: true };\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\tlet tmpActiveTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\t\tif (tmpActiveTheme && tmpActiveTheme.BracketConfig)\n\t\t\t{\n\t\t\t\ttmpBracketConfig = Object.assign(tmpBracketConfig, tmpActiveTheme.BracketConfig);\n\t\t\t}\n\t\t}\n\n\t\tlet tmpAmplitude = 0;\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\ttmpAmplitude = this._FlowView._ThemeProvider.getNodeNoiseAmplitude();\n\t\t}\n\n\t\tlet tmpBracketD = '';\n\t\tif (this._FlowView._NoiseProvider)\n\t\t{\n\t\t\ttmpBracketD = this._FlowView._NoiseProvider.generateBracketPath(\n\t\t\t\tpWidth, pHeight,\n\t\t\t\ttmpBracketConfig.SerifLength,\n\t\t\t\ttmpBracketConfig.TitleSeparator ? pTitleBarHeight : 0,\n\t\t\t\ttmpAmplitude,\n\t\t\t\tpNodeData.Hash\n\t\t\t);\n\t\t}\n\n\t\tlet tmpBracketPath = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\ttmpBracketPath.setAttribute('class', 'pict-flow-node-bracket');\n\t\ttmpBracketPath.setAttribute('d', tmpBracketD);\n\t\ttmpBracketPath.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpBracketPath.setAttribute('data-element-type', 'node-body');\n\n\t\t// Per-instance stroke overrides\n\t\tif (pNodeData.Style)\n\t\t{\n\t\t\tlet tmpInlineStyles = [];\n\t\t\tif (pNodeData.Style.BodyStroke) tmpInlineStyles.push('stroke:' + pNodeData.Style.BodyStroke);\n\t\t\tif (pNodeData.Style.BodyStrokeWidth) tmpInlineStyles.push('stroke-width:' + pNodeData.Style.BodyStrokeWidth);\n\t\t\tif (tmpInlineStyles.length > 0)\n\t\t\t{\n\t\t\t\ttmpBracketPath.setAttribute('style', tmpInlineStyles.join(';'));\n\t\t\t}\n\t\t}\n\n\t\tpGroup.appendChild(tmpBracketPath);\n\t}\n}\n\nmodule.exports = PictViewFlowNode;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n\n},{\"pict-view\":45}],41:[function(require,module,exports){\nconst libPictView = require('pict-view');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Flow-PropertiesPanel',\n\n\tAutoRender: false,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Wrapper',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel\">{~D:Record.PanelContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Header-Icon',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-header with-icon\">{~D:Record.Icon~} {~D:Record.Label~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Header',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-header\">{~D:Record.Label~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Description',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-description\">{~D:Record.Description~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Badges',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-badges\">{~D:Record.BadgesContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Badge-Category',\n\t\t\tTemplate: '<span class=\"pict-flow-info-panel-badge category\">{~D:Record.Category~}</span>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Badge-Code',\n\t\t\tTemplate: '<span class=\"pict-flow-info-panel-badge code\">{~D:Record.Code~}</span>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Section-Inputs',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-section\"><div class=\"pict-flow-info-panel-section-title\">Inputs</div>{~D:Record.PortsContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Section-Outputs',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-section\"><div class=\"pict-flow-info-panel-section-title\">Outputs</div>{~D:Record.PortsContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Input',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-port input\">{~D:Record.Label~}{~D:Record.Constraint~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Output',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-port output\">{~D:Record.Label~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Constraint',\n\t\t\tTemplate: ' <span class=\"pict-flow-info-panel-port-constraint\">{~D:Record.ConstraintText~}</span>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Section-Generic',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-section\"><div class=\"pict-flow-info-panel-section-title\">{~D:Record.SectionTitle~}</div>{~D:Record.PortsContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Event',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-port event\">{~D:Record.Label~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Value',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-port value\">{~D:Record.Label~}{~D:Record.DataType~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-DataType',\n\t\t\tTemplate: ' <span class=\"pict-flow-info-panel-port-constraint\">{~D:Record.DataTypeText~}</span>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-NodeProps-Editor',\n\t\t\tTemplate: '<div class=\"pict-flow-node-props-fields\"><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Title</label><input type=\"text\" class=\"pict-flow-node-props-input\" data-prop=\"Title\" value=\"{~D:Record.Title~}\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Width</label><input type=\"number\" class=\"pict-flow-node-props-input\" data-prop=\"Width\" value=\"{~D:Record.Width~}\" min=\"60\" step=\"10\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Height</label><input type=\"number\" class=\"pict-flow-node-props-input\" data-prop=\"Height\" value=\"{~D:Record.Height~}\" min=\"40\" step=\"10\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Body Fill</label><input type=\"color\" class=\"pict-flow-node-props-input pict-flow-node-props-color\" data-prop=\"Style.BodyFill\" value=\"{~D:Record.BodyFillValue~}\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Body Stroke</label><input type=\"color\" class=\"pict-flow-node-props-input pict-flow-node-props-color\" data-prop=\"Style.BodyStroke\" value=\"{~D:Record.BodyStrokeValue~}\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Stroke Width</label><input type=\"number\" class=\"pict-flow-node-props-input\" data-prop=\"Style.BodyStrokeWidth\" value=\"{~D:Record.BodyStrokeWidthValue~}\" min=\"0\" max=\"10\" step=\"0.5\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Title Bar</label><input type=\"color\" class=\"pict-flow-node-props-input pict-flow-node-props-color\" data-prop=\"Style.TitleBarColor\" value=\"{~D:Record.TitleBarColorValue~}\" /></div></div>'\n\t\t}\n\t]\n};\n\n/**\n * PictView-Flow-PropertiesPanel\n *\n * Renders and manages all open properties panels on the flow graph.\n * Panels are SVG foreignObject elements containing HTML, placed inside\n * the viewport group so they zoom/pan with the graph.\n *\n * Responsibilities:\n * - Reconcile DOM (add new panels, remove closed ones, update positions)\n * - Render tether lines from each panel to its node\n * - Manage panel instance cache (PictFlowCardPropertiesPanel subclasses)\n * - Isolate HTML events from SVG interactions\n */\nclass PictViewFlowPropertiesPanel extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictViewFlowPropertiesPanel';\n\n\t\tthis._FlowView = null;\n\n\t\t// Cache of active panel instances: Map<panelHash, PictFlowCardPropertiesPanel>\n\t\tthis._PanelInstances = {};\n\t}\n\n\t/**\n\t * Render all open panels and their tethers.\n\t *\n\t * Uses DOM reconciliation for panels (to preserve live HTML state)\n\t * and clear-and-rebuild for tethers (trivial SVG lines).\n\t *\n\t * @param {Array} pOpenPanels - Array of panel data objects from _FlowData.OpenPanels\n\t * @param {SVGGElement} pPanelsLayer - The SVG <g> for panel foreignObjects\n\t * @param {SVGGElement} pTethersLayer - The SVG <g> for tether lines\n\t * @param {string|null} pSelectedTetherHash - Hash of the selected tether's panel, or null\n\t */\n\trenderPanels(pOpenPanels, pPanelsLayer, pTethersLayer, pSelectedTetherHash)\n\t{\n\t\tif (!pPanelsLayer || !pTethersLayer) return;\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpOpenPanels = Array.isArray(pOpenPanels) ? pOpenPanels : [];\n\n\t\t// --- Reconcile panels layer (add new, remove closed, update positions) ---\n\t\tlet tmpExistingPanelHashes = new Set();\n\t\tlet tmpExistingForeignObjects = pPanelsLayer.querySelectorAll('.pict-flow-panel-foreign-object');\n\t\tfor (let i = 0; i < tmpExistingForeignObjects.length; i++)\n\t\t{\n\t\t\ttmpExistingPanelHashes.add(tmpExistingForeignObjects[i].getAttribute('data-panel-hash'));\n\t\t}\n\n\t\tlet tmpDesiredPanelHashes = new Set();\n\t\tfor (let i = 0; i < tmpOpenPanels.length; i++)\n\t\t{\n\t\t\ttmpDesiredPanelHashes.add(tmpOpenPanels[i].Hash);\n\t\t}\n\n\t\t// Remove panels that are no longer open\n\t\tfor (let i = 0; i < tmpExistingForeignObjects.length; i++)\n\t\t{\n\t\t\tlet tmpHash = tmpExistingForeignObjects[i].getAttribute('data-panel-hash');\n\t\t\tif (!tmpDesiredPanelHashes.has(tmpHash))\n\t\t\t{\n\t\t\t\ttmpExistingForeignObjects[i].remove();\n\t\t\t\t// Destroy cached instance\n\t\t\t\tif (this._PanelInstances[tmpHash])\n\t\t\t\t{\n\t\t\t\t\tthis._PanelInstances[tmpHash].destroy();\n\t\t\t\t\tdelete this._PanelInstances[tmpHash];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add or update panels\n\t\tfor (let i = 0; i < tmpOpenPanels.length; i++)\n\t\t{\n\t\t\tlet tmpPanelData = tmpOpenPanels[i];\n\n\t\t\tif (tmpExistingPanelHashes.has(tmpPanelData.Hash))\n\t\t\t{\n\t\t\t\t// Update position of existing panel\n\t\t\t\tlet tmpFO = pPanelsLayer.querySelector(`[data-panel-hash=\"${tmpPanelData.Hash}\"]`);\n\t\t\t\tif (tmpFO)\n\t\t\t\t{\n\t\t\t\t\ttmpFO.setAttribute('x', String(tmpPanelData.X));\n\t\t\t\t\ttmpFO.setAttribute('y', String(tmpPanelData.Y));\n\t\t\t\t\ttmpFO.setAttribute('width', String(tmpPanelData.Width));\n\t\t\t\t\ttmpFO.setAttribute('height', String(tmpPanelData.Height));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Create new panel\n\t\t\t\tthis._createPanelForeignObject(tmpPanelData, pPanelsLayer);\n\t\t\t}\n\t\t}\n\n\t\t// --- Clear and rebuild tethers ---\n\t\twhile (pTethersLayer.firstChild)\n\t\t{\n\t\t\tpTethersLayer.removeChild(pTethersLayer.firstChild);\n\t\t}\n\n\t\tfor (let i = 0; i < tmpOpenPanels.length; i++)\n\t\t{\n\t\t\tlet tmpIsSelected = (pSelectedTetherHash === tmpOpenPanels[i].Hash);\n\t\t\tthis._renderTether(tmpOpenPanels[i], pTethersLayer, tmpIsSelected);\n\t\t}\n\t}\n\n\t/**\n\t * Create a foreignObject containing the panel chrome and content.\n\t * Delegates to the PanelChrome provider for template-based chrome creation,\n\t * then renders panel content into the body container.\n\t *\n\t * @param {Object} pPanelData - Panel data from OpenPanels\n\t * @param {SVGGElement} pPanelsLayer\n\t */\n\t_createPanelForeignObject(pPanelData, pPanelsLayer)\n\t{\n\t\tlet tmpPanelChromeProvider = this._FlowView._PanelChromeProvider;\n\t\tif (!tmpPanelChromeProvider) return;\n\n\t\tlet tmpBody = tmpPanelChromeProvider.createPanelForeignObject(pPanelData, pPanelsLayer);\n\n\t\t// Render the panel content into the Properties tab pane\n\t\tif (tmpBody)\n\t\t{\n\t\t\tthis._renderPanelContent(pPanelData, tmpBody);\n\t\t}\n\n\t\tlet tmpFO = pPanelsLayer.querySelector(`[data-panel-hash=\"${pPanelData.Hash}\"]`);\n\t\tif (tmpFO)\n\t\t{\n\t\t\t// Render appearance and help tabs\n\t\t\tthis._renderAppearanceTab(pPanelData, tmpFO);\n\t\t\tthis._renderHelpTab(pPanelData, tmpFO);\n\n\t\t\t// Wire up tab switching\n\t\t\tthis._wireTabSwitching(tmpFO);\n\t\t}\n\t}\n\n\t/**\n\t * Instantiate (or reuse) the panel type implementation and render into the body container.\n\t *\n\t * @param {Object} pPanelData\n\t * @param {HTMLDivElement} pBodyContainer\n\t */\n\t_renderPanelContent(pPanelData, pBodyContainer)\n\t{\n\t\tlet tmpNodeData = this._FlowView.getNode(pPanelData.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNodeData.Type);\n\t\tif (!tmpNodeTypeConfig) return;\n\n\t\t// If no PropertiesPanel is configured, render the auto-generated info panel\n\t\tif (!tmpNodeTypeConfig.PropertiesPanel)\n\t\t{\n\t\t\tthis._renderInfoPanelContent(pBodyContainer, tmpNodeData, tmpNodeTypeConfig);\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpPanelConfig = tmpNodeTypeConfig.PropertiesPanel;\n\t\tlet tmpPanelType = tmpPanelConfig.PanelType || 'Base';\n\n\t\t// Try to get a registered panel type service\n\t\tlet tmpServiceName = `PictFlowCardPropertiesPanel-${tmpPanelType}`;\n\t\tlet tmpInstance = null;\n\n\t\tif (this._PanelInstances[pPanelData.Hash])\n\t\t{\n\t\t\t// Reuse existing instance\n\t\t\ttmpInstance = this._PanelInstances[pPanelData.Hash];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Create a new instance\n\t\t\tif (this.fable.servicesMap.hasOwnProperty(tmpServiceName))\n\t\t\t{\n\t\t\t\ttmpInstance = this.fable.instantiateServiceProviderWithoutRegistration(tmpServiceName, tmpPanelConfig);\n\t\t\t}\n\t\t\telse if (this.fable.servicesMap.hasOwnProperty('PictFlowCardPropertiesPanel'))\n\t\t\t{\n\t\t\t\t// Fall back to base class\n\t\t\t\ttmpInstance = this.fable.instantiateServiceProviderWithoutRegistration('PictFlowCardPropertiesPanel', tmpPanelConfig);\n\t\t\t}\n\n\t\t\tif (tmpInstance)\n\t\t\t{\n\t\t\t\ttmpInstance._FlowView = this._FlowView;\n\t\t\t\tthis._PanelInstances[pPanelData.Hash] = tmpInstance;\n\t\t\t}\n\t\t}\n\n\t\tif (tmpInstance)\n\t\t{\n\t\t\ttmpInstance.render(pBodyContainer, tmpNodeData);\n\t\t}\n\n\t\t// After the form renders, append port summary sections showing\n\t\t// event inputs, event outputs, and state outputs.\n\t\t// SettingsInputs are already visible as form fields above.\n\t\tthis._renderPortSummary(pBodyContainer, tmpNodeTypeConfig);\n\t}\n\n\t/**\n\t * Render an auto-generated info panel for nodes without a configured PropertiesPanel.\n\t * Shows the node type, description, and a summary of input/output ports with\n\t * their connection constraints.\n\t *\n\t * Uses configuration-based templates from _DefaultConfiguration.Templates\n\t * rendered via pict.parseTemplateByHash().\n\t *\n\t * @param {HTMLDivElement} pContainer\n\t * @param {Object} pNodeData\n\t * @param {Object} pNodeTypeConfig\n\t */\n\t_renderInfoPanelContent(pContainer, pNodeData, pNodeTypeConfig)\n\t{\n\t\tlet tmpMeta = pNodeTypeConfig.CardMetadata || {};\n\t\tlet tmpPorts = pNodeTypeConfig.DefaultPorts || [];\n\n\t\tlet tmpInputs = tmpPorts.filter((pPort) => pPort.Direction === 'input');\n\t\tlet tmpOutputs = tmpPorts.filter((pPort) => pPort.Direction === 'output');\n\n\t\tlet tmpLabel = pNodeTypeConfig.Label || pNodeData.Type;\n\n\t\t// Build content by rendering configuration-based templates\n\t\tlet tmpContentParts = [];\n\n\t\t// Header\n\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\tif (tmpMeta.Icon && tmpIconProvider && !tmpIconProvider.isEmojiIcon(tmpMeta.Icon))\n\t\t{\n\t\t\t// SVG icon markup for the header\n\t\t\tlet tmpResolvedKey = tmpIconProvider.resolveIconKey(tmpMeta);\n\t\t\tlet tmpIconMarkup = tmpIconProvider.getIconSVGMarkup(tmpResolvedKey, 18);\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Header-Icon', { Icon: tmpIconMarkup, Label: tmpLabel }));\n\t\t}\n\t\telse if (tmpMeta.Icon)\n\t\t{\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Header-Icon', { Icon: tmpMeta.Icon, Label: tmpLabel }));\n\t\t}\n\t\telse if (tmpIconProvider)\n\t\t{\n\t\t\t// No icon specified — render default fallback\n\t\t\tlet tmpDefaultMarkup = tmpIconProvider.getIconSVGMarkup('default', 18);\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Header-Icon', { Icon: tmpDefaultMarkup, Label: tmpLabel }));\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Header', { Label: tmpLabel }));\n\t\t}\n\n\t\t// Description\n\t\tif (tmpMeta.Description)\n\t\t{\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Description', { Description: tmpMeta.Description }));\n\t\t}\n\n\t\t// Category + Code badges\n\t\tif (tmpMeta.Category || tmpMeta.Code)\n\t\t{\n\t\t\tlet tmpBadgesContent = '';\n\t\t\tif (tmpMeta.Category)\n\t\t\t{\n\t\t\t\ttmpBadgesContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Badge-Category', { Category: tmpMeta.Category });\n\t\t\t}\n\t\t\tif (tmpMeta.Code)\n\t\t\t{\n\t\t\t\ttmpBadgesContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Badge-Code', { Code: tmpMeta.Code });\n\t\t\t}\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Badges', { BadgesContent: tmpBadgesContent }));\n\t\t}\n\n\t\t// Inputs\n\t\tif (tmpInputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpInputs.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpInputs[i];\n\t\t\t\tlet tmpConstraint = this._getPortConstraintHTML(tmpPort);\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Input', { Label: tmpPort.Label || 'In', Constraint: tmpConstraint });\n\t\t\t}\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Inputs', { PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\t// Outputs\n\t\tif (tmpOutputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpOutputs.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpOutputs[i];\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Output', { Label: tmpPort.Label || 'Out' });\n\t\t\t}\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Outputs', { PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\tpContainer.innerHTML = this.pict.parseTemplateByHash('Flow-InfoPanel-Wrapper', { PanelContent: tmpContentParts.join('') });\n\t}\n\n\t/**\n\t * Render port summary sections below the form panel content.\n\t * Shows event inputs, event outputs, and state outputs — the ports\n\t * that the form does not cover (since the form only shows SettingsInputs).\n\t *\n\t * @param {HTMLDivElement} pContainer\n\t * @param {Object} pNodeTypeConfig\n\t */\n\t_renderPortSummary(pContainer, pNodeTypeConfig)\n\t{\n\t\tlet tmpPorts = pNodeTypeConfig.DefaultPorts || [];\n\t\tif (tmpPorts.length === 0) return;\n\n\t\t// Categorize ports by type (settings are already shown as form fields)\n\t\tlet tmpEventInputs = [];\n\t\tlet tmpEventOutputs = [];\n\t\tlet tmpStateOutputs = [];\n\n\t\tfor (let i = 0; i < tmpPorts.length; i++)\n\t\t{\n\t\t\tlet tmpPort = tmpPorts[i];\n\t\t\tlet tmpPortType = tmpPort.PortType || '';\n\n\t\t\tif (tmpPortType === 'event-in')\n\t\t\t{\n\t\t\t\ttmpEventInputs.push(tmpPort);\n\t\t\t}\n\t\t\telse if (tmpPortType === 'event-out' || tmpPortType === 'error')\n\t\t\t{\n\t\t\t\ttmpEventOutputs.push(tmpPort);\n\t\t\t}\n\t\t\telse if (tmpPortType === 'value')\n\t\t\t{\n\t\t\t\ttmpStateOutputs.push(tmpPort);\n\t\t\t}\n\t\t}\n\n\t\t// Only render if there are non-settings ports to show\n\t\tif (tmpEventInputs.length === 0 && tmpEventOutputs.length === 0 && tmpStateOutputs.length === 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpSummaryParts = [];\n\n\t\t// Event Inputs\n\t\tif (tmpEventInputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpEventInputs.length; i++)\n\t\t\t{\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Event', { Label: tmpEventInputs[i].Label || tmpEventInputs[i].Name || 'Event In' });\n\t\t\t}\n\t\t\ttmpSummaryParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Generic', { SectionTitle: 'Event Inputs', PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\t// Event Outputs\n\t\tif (tmpEventOutputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpEventOutputs.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpEventOutputs[i];\n\t\t\t\tlet tmpLabel = tmpPort.Label || tmpPort.Name || 'Event Out';\n\t\t\t\tif (tmpPort.PortType === 'error')\n\t\t\t\t{\n\t\t\t\t\ttmpLabel += ' ⚠';\n\t\t\t\t}\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Event', { Label: tmpLabel });\n\t\t\t}\n\t\t\ttmpSummaryParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Generic', { SectionTitle: 'Event Outputs', PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\t// State Outputs\n\t\tif (tmpStateOutputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpStateOutputs.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpStateOutputs[i];\n\t\t\t\tlet tmpLabel = tmpPort.Label || tmpPort.Name || 'Output';\n\t\t\t\tlet tmpDataType = '';\n\t\t\t\tif (tmpPort.DataType)\n\t\t\t\t{\n\t\t\t\t\ttmpDataType = this.pict.parseTemplateByHash('Flow-InfoPanel-Port-DataType', { DataTypeText: tmpPort.DataType });\n\t\t\t\t}\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Value', { Label: tmpLabel, DataType: tmpDataType });\n\t\t\t}\n\t\t\ttmpSummaryParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Generic', { SectionTitle: 'State Outputs', PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\tif (tmpSummaryParts.length > 0)\n\t\t{\n\t\t\t// Create a wrapper div for the port summary and append it to the container\n\t\t\tlet tmpSummaryDiv = document.createElement('div');\n\t\t\ttmpSummaryDiv.className = 'pict-flow-info-panel pict-flow-port-summary';\n\t\t\ttmpSummaryDiv.innerHTML = tmpSummaryParts.join('');\n\t\t\tpContainer.appendChild(tmpSummaryDiv);\n\t\t}\n\t}\n\n\t/**\n\t * Build the constraint markup for a port using configuration templates.\n\t *\n\t * @param {Object} pPort\n\t * @returns {string} Rendered constraint HTML or empty string\n\t */\n\t_getPortConstraintHTML(pPort)\n\t{\n\t\tlet tmpMin = (typeof pPort.MinimumInputCount === 'number') ? pPort.MinimumInputCount : 0;\n\t\tlet tmpMax = (typeof pPort.MaximumInputCount === 'number') ? pPort.MaximumInputCount : -1;\n\n\t\tif (tmpMin > 0 || tmpMax > 0)\n\t\t{\n\t\t\tlet tmpConstraintText = '';\n\t\t\tif (tmpMax < 0)\n\t\t\t{\n\t\t\t\ttmpConstraintText = `(min ${tmpMin})`;\n\t\t\t}\n\t\t\telse if (tmpMin === tmpMax)\n\t\t\t{\n\t\t\t\ttmpConstraintText = `(exactly ${tmpMin})`;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpConstraintText = `(${tmpMin}\\u2013${tmpMax})`;\n\t\t\t}\n\t\t\treturn this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Constraint', { ConstraintText: tmpConstraintText });\n\t\t}\n\t\treturn '';\n\t}\n\n\t/**\n\t * Render the Appearance tab content with node property editor fields.\n\t *\n\t * @param {Object} pPanelData - Panel data from OpenPanels\n\t * @param {Element} pForeignObject - The panel's SVG foreignObject element\n\t */\n\t_renderAppearanceTab(pPanelData, pForeignObject)\n\t{\n\t\tlet tmpNodeData = this._FlowView.getNode(pPanelData.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpAppearancePane = pForeignObject.querySelector('.pict-flow-panel-tab-pane[data-tab=\"appearance\"]');\n\t\tif (!tmpAppearancePane) return;\n\n\t\t// Build the template record with safe defaults for Style values\n\t\tlet tmpStyle = tmpNodeData.Style || {};\n\n\t\t// Resolve default colors from the node type config or CSS token defaults\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNodeData.Type);\n\t\tlet tmpDefaultTitleBarColor = '#2c3e50';\n\t\tlet tmpDefaultBodyFill = '#ffffff';\n\t\tlet tmpDefaultBodyStroke = '#d0d4d8';\n\t\tif (tmpNodeTypeConfig)\n\t\t{\n\t\t\tif (tmpNodeTypeConfig.TitleBarColor) tmpDefaultTitleBarColor = tmpNodeTypeConfig.TitleBarColor;\n\t\t\tif (tmpNodeTypeConfig.BodyStyle)\n\t\t\t{\n\t\t\t\tif (tmpNodeTypeConfig.BodyStyle.fill) tmpDefaultBodyFill = tmpNodeTypeConfig.BodyStyle.fill;\n\t\t\t\tif (tmpNodeTypeConfig.BodyStyle.stroke) tmpDefaultBodyStroke = tmpNodeTypeConfig.BodyStyle.stroke;\n\t\t\t}\n\t\t}\n\n\t\tlet tmpRecord =\n\t\t{\n\t\t\tTitle: tmpNodeData.Title || '',\n\t\t\tWidth: tmpNodeData.Width || 180,\n\t\t\tHeight: tmpNodeData.Height || 80,\n\t\t\tBodyFillValue: tmpStyle.BodyFill || tmpDefaultBodyFill,\n\t\t\tBodyStrokeValue: tmpStyle.BodyStroke || tmpDefaultBodyStroke,\n\t\t\tBodyStrokeWidthValue: tmpStyle.BodyStrokeWidth || 1,\n\t\t\tTitleBarColorValue: tmpStyle.TitleBarColor || tmpDefaultTitleBarColor\n\t\t};\n\n\t\ttmpAppearancePane.innerHTML = this.pict.parseTemplateByHash('Flow-NodeProps-Editor', tmpRecord);\n\n\t\t// Wire up live change handlers on all input fields\n\t\tlet tmpInputs = tmpAppearancePane.querySelectorAll('.pict-flow-node-props-input');\n\t\tfor (let i = 0; i < tmpInputs.length; i++)\n\t\t{\n\t\t\tlet tmpInput = tmpInputs[i];\n\t\t\tlet tmpProp = tmpInput.getAttribute('data-prop');\n\n\t\t\ttmpInput.addEventListener('input', (pEvent) =>\n\t\t\t{\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tthis._applyNodePropChange(pPanelData.NodeHash, tmpProp, tmpInput.value, tmpInput.type);\n\t\t\t});\n\n\t\t\t// Prevent pointer events from propagating to SVG drag handler\n\t\t\ttmpInput.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\t}\n\t}\n\n\t/**\n\t * Render the Help tab content if help text is defined on the node type.\n\t * Shows the Help tab button only when help content is available.\n\t *\n\t * @param {Object} pPanelData - Panel data from OpenPanels\n\t * @param {Element} pForeignObject - The panel's SVG foreignObject element\n\t */\n\t_renderHelpTab(pPanelData, pForeignObject)\n\t{\n\t\tlet tmpNodeData = this._FlowView.getNode(pPanelData.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNodeData.Type);\n\t\tif (!tmpNodeTypeConfig) return;\n\n\t\tlet tmpHelpText = (tmpNodeTypeConfig.CardMetadata && tmpNodeTypeConfig.CardMetadata.Help)\n\t\t\t? tmpNodeTypeConfig.CardMetadata.Help\n\t\t\t: null;\n\n\t\tif (!tmpHelpText) return;\n\n\t\t// Show the Help tab button\n\t\tlet tmpHelpTabButton = pForeignObject.querySelector('.pict-flow-panel-tab[data-tab-target=\"help\"]');\n\t\tif (tmpHelpTabButton)\n\t\t{\n\t\t\ttmpHelpTabButton.style.display = '';\n\t\t}\n\n\t\t// Render help content into the help pane\n\t\tlet tmpHelpPane = pForeignObject.querySelector('.pict-flow-panel-tab-pane[data-tab=\"help\"]');\n\t\tif (tmpHelpPane)\n\t\t{\n\t\t\ttmpHelpPane.innerHTML = '<div class=\"pict-flow-panel-help-content\">' + tmpHelpText + '</div>';\n\t\t}\n\t}\n\n\t/**\n\t * Wire up tab switching on all tab buttons within a panel foreignObject.\n\t *\n\t * @param {Element} pForeignObject - The panel's SVG foreignObject element\n\t */\n\t_wireTabSwitching(pForeignObject)\n\t{\n\t\tlet tmpTabs = pForeignObject.querySelectorAll('.pict-flow-panel-tab');\n\t\tlet tmpPanes = pForeignObject.querySelectorAll('.pict-flow-panel-tab-pane');\n\n\t\tfor (let i = 0; i < tmpTabs.length; i++)\n\t\t{\n\t\t\ttmpTabs[i].addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tlet tmpTarget = pEvent.currentTarget.getAttribute('data-tab-target');\n\n\t\t\t\t// Deactivate all tabs and panes\n\t\t\t\tfor (let j = 0; j < tmpTabs.length; j++)\n\t\t\t\t{\n\t\t\t\t\ttmpTabs[j].classList.remove('active');\n\t\t\t\t}\n\t\t\t\tfor (let j = 0; j < tmpPanes.length; j++)\n\t\t\t\t{\n\t\t\t\t\ttmpPanes[j].classList.remove('active');\n\t\t\t\t\ttmpPanes[j].style.display = 'none';\n\t\t\t\t}\n\n\t\t\t\t// Activate the selected tab and pane\n\t\t\t\tpEvent.currentTarget.classList.add('active');\n\t\t\t\tlet tmpTargetPane = pForeignObject.querySelector('.pict-flow-panel-tab-pane[data-tab=\"' + tmpTarget + '\"]');\n\t\t\t\tif (tmpTargetPane)\n\t\t\t\t{\n\t\t\t\t\ttmpTargetPane.classList.add('active');\n\t\t\t\t\ttmpTargetPane.style.display = 'block';\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Apply a node property change from the properties editor and re-render.\n\t *\n\t * @param {string} pNodeHash - Hash of the node to update\n\t * @param {string} pPropPath - Property path (e.g. 'Title', 'Width', 'Style.BodyFill')\n\t * @param {string} pValue - The new value from the input\n\t * @param {string} pInputType - The input element type ('text', 'number', 'color')\n\t */\n\t_applyNodePropChange(pNodeHash, pPropPath, pValue, pInputType)\n\t{\n\t\tlet tmpNodeData = this._FlowView.getNode(pNodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\t// Parse numeric values\n\t\tlet tmpValue = pValue;\n\t\tif (pInputType === 'number')\n\t\t{\n\t\t\ttmpValue = parseFloat(pValue);\n\t\t\tif (isNaN(tmpValue)) return;\n\t\t}\n\n\t\t// Apply the value based on the property path\n\t\tif (pPropPath === 'Title')\n\t\t{\n\t\t\ttmpNodeData.Title = tmpValue;\n\t\t}\n\t\telse if (pPropPath === 'Width')\n\t\t{\n\t\t\ttmpNodeData.Width = tmpValue;\n\t\t}\n\t\telse if (pPropPath === 'Height')\n\t\t{\n\t\t\ttmpNodeData.Height = tmpValue;\n\t\t}\n\t\telse if (pPropPath.startsWith('Style.'))\n\t\t{\n\t\t\tif (!tmpNodeData.Style) tmpNodeData.Style = {};\n\t\t\tlet tmpStyleKey = pPropPath.substring(6); // Remove 'Style.' prefix\n\t\t\ttmpNodeData.Style[tmpStyleKey] = tmpValue;\n\t\t}\n\n\t\t// Re-render the flow to reflect changes\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\t// Fire change event\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Render a tether from a panel to its node.\n\t * Delegates to the TetherService for geometry, path generation, and SVG element creation.\n\t *\n\t * @param {Object} pPanelData\n\t * @param {SVGGElement} pTethersLayer\n\t * @param {boolean} pIsSelected\n\t */\n\t_renderTether(pPanelData, pTethersLayer, pIsSelected)\n\t{\n\t\tlet tmpTetherService = this._FlowView._TetherService;\n\t\tif (!tmpTetherService) return;\n\n\t\tlet tmpNodeData = this._FlowView.getNode(pPanelData.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\ttmpTetherService.renderTether(pPanelData, tmpNodeData, pTethersLayer, pIsSelected, tmpViewIdentifier);\n\t}\n\n\t/**\n\t * Marshal data from all open panels back into their node Data objects.\n\t */\n\tmarshalAllFromPanels()\n\t{\n\t\tfor (let tmpPanelHash in this._PanelInstances)\n\t\t{\n\t\t\tlet tmpInstance = this._PanelInstances[tmpPanelHash];\n\t\t\tif (tmpInstance && tmpInstance._NodeData)\n\t\t\t{\n\t\t\t\ttmpInstance.marshalFromPanel(tmpInstance._NodeData);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Destroy a specific panel instance and clean up.\n\t *\n\t * @param {string} pPanelHash\n\t */\n\tdestroyPanel(pPanelHash)\n\t{\n\t\tif (this._PanelInstances[pPanelHash])\n\t\t{\n\t\t\tthis._PanelInstances[pPanelHash].destroy();\n\t\t\tdelete this._PanelInstances[pPanelHash];\n\t\t}\n\t}\n\n\t/**\n\t * Destroy all panel instances.\n\t */\n\tdestroyAllPanels()\n\t{\n\t\tfor (let tmpPanelHash in this._PanelInstances)\n\t\t{\n\t\t\tthis._PanelInstances[tmpPanelHash].destroy();\n\t\t}\n\t\tthis._PanelInstances = {};\n\t}\n}\n\nmodule.exports = PictViewFlowPropertiesPanel;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n\n},{\"pict-view\":45}],42:[function(require,module,exports){\nconst libPictView = require('pict-view');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Flow-Toolbar',\n\n\tDefaultRenderable: 'Flow-Toolbar-Content',\n\tDefaultDestinationAddress: '#Flow-Toolbar-Container',\n\n\tAutoRender: false,\n\n\tFlowViewIdentifier: 'Pict-Flow',\n\n\tEnablePalette: true,\n\tEnableAddNode: true,\n\tEnableCardPalette: true,\n\n\tCSS: false,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'Flow-Toolbar-Template',\n\t\t\tTemplate: /*html*/`\n<div class=\"pict-flow-toolbar\" id=\"Flow-Toolbar-Bar-{~D:Record.FlowViewIdentifier~}\">\n\t<div class=\"pict-flow-toolbar-group\">\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"add-node\" id=\"Flow-Toolbar-AddNode-{~D:Record.FlowViewIdentifier~}\" title=\"Add Node\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-plus-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-text\">Node</span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"cards-popup\" id=\"Flow-Toolbar-Cards-{~D:Record.FlowViewIdentifier~}\" title=\"Card Palette\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-cards-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-text\">Cards</span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-chevron\" id=\"Flow-Toolbar-CardsChevron-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"delete-selected\" title=\"Delete Node\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-trash-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t</div>\n\t<div class=\"pict-flow-toolbar-group\">\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"layout-popup\" id=\"Flow-Toolbar-Layout-{~D:Record.FlowViewIdentifier~}\" title=\"Manage Layouts\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-layout-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-text\">Layout</span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-chevron\" id=\"Flow-Toolbar-LayoutChevron-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"auto-layout\" title=\"Auto Layout\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-auto-layout-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-text\">Auto Layout</span>\n\t\t</button>\n\t</div>\n\t<div class=\"pict-flow-toolbar-group\">\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"zoom-in\" title=\"Zoom In\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-zoom-in-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"zoom-out\" title=\"Zoom Out\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-zoom-out-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"zoom-fit\" title=\"Fit to View\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-zoom-fit-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t</div>\n\t<div class=\"pict-flow-toolbar-group pict-flow-toolbar-right\">\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"settings-popup\" id=\"Flow-Toolbar-Settings-{~D:Record.FlowViewIdentifier~}\" title=\"Theme Settings\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-settings-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"fullscreen\" id=\"Flow-Toolbar-Fullscreen-{~D:Record.FlowViewIdentifier~}\" title=\"Toggle Fullscreen\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Fullscreen-Icon-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"toggle-floating\" title=\"Float\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-grip-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"collapse-toolbar\" title=\"Collapse Toolbar\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-collapse-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t</div>\n</div>\n<div class=\"pict-flow-toolbar-collapsed\" id=\"Flow-Toolbar-Collapsed-{~D:Record.FlowViewIdentifier~}\">\n\t<button class=\"pict-flow-toolbar-expand-btn\" data-flow-action=\"expand-toolbar\" title=\"Expand Toolbar\" id=\"Flow-Toolbar-ExpandBtn-{~D:Record.FlowViewIdentifier~}\">\n\t\t<span id=\"Flow-Toolbar-Icon-expand-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n</div>\n<div class=\"pict-flow-toolbar-popup-anchor\" id=\"Flow-Toolbar-PopupAnchor-{~D:Record.FlowViewIdentifier~}\">\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: 'Flow-Toolbar-Content',\n\t\t\tTemplateHash: 'Flow-Toolbar-Template',\n\t\t\tDestinationAddress: '#Flow-Toolbar-Container',\n\t\t\tRenderMethod: 'replace'\n\t\t}\n\t]\n};\n\nclass PictViewFlowToolbar extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictViewFlowToolbar';\n\n\t\tthis._FlowView = null;\n\n\t\t// Toolbar mode state\n\t\tthis._ToolbarMode = 'docked'; // 'docked' | 'floating' | 'collapsed'\n\t\tthis._ActivePopup = null; // 'add-node' | 'cards' | 'layout' | null\n\t\tthis._FloatingPosition = { X: 80, Y: 80 };\n\t\tthis._DocumentClickHandler = null;\n\t\tthis._FloatingToolbarView = null;\n\t}\n\n\trender(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\t// Pass this.options as the template record so {~D:Record.FlowViewIdentifier~}\n\t\t// resolves correctly in the toolbar template.\n\t\treturn super.render(pRenderableHash, pRenderDestinationAddress, this.options);\n\t}\n\n\tonAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\t// Bind toolbar button events via event delegation\n\t\tlet tmpToolbarBar = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Bar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbarBar.length > 0)\n\t\t{\n\t\t\ttmpToolbarBar[0].addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\tlet tmpTarget = pEvent.target;\n\t\t\t\tif (!tmpTarget) return;\n\n\t\t\t\t// Walk up to find the button with the action\n\t\t\t\tlet tmpButton = tmpTarget.closest('[data-flow-action]');\n\t\t\t\tif (!tmpButton) return;\n\n\t\t\t\tlet tmpAction = tmpButton.getAttribute('data-flow-action');\n\t\t\t\tthis._handleToolbarAction(tmpAction);\n\t\t\t});\n\t\t}\n\n\t\t// Bind expand button click (it's outside the main toolbar bar)\n\t\tlet tmpExpandBtn = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-ExpandBtn-${tmpFlowViewIdentifier}`);\n\t\tif (tmpExpandBtn.length > 0)\n\t\t{\n\t\t\ttmpExpandBtn[0].addEventListener('click', () =>\n\t\t\t{\n\t\t\t\tthis._setToolbarMode('docked');\n\t\t\t});\n\t\t}\n\n\t\t// Populate SVG icons for toolbar buttons\n\t\tthis._populateToolbarIcons();\n\n\t\t// Remove buttons from DOM based on options\n\t\tif (this.options.EnableAddNode === false)\n\t\t{\n\t\t\tlet tmpAddNodeBtn = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-AddNode-${tmpFlowViewIdentifier}`);\n\t\t\tif (tmpAddNodeBtn.length > 0)\n\t\t\t{\n\t\t\t\ttmpAddNodeBtn[0].remove();\n\t\t\t}\n\t\t}\n\t\tif (this.options.EnableCardPalette === false)\n\t\t{\n\t\t\tlet tmpCardsBtn = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Cards-${tmpFlowViewIdentifier}`);\n\t\t\tif (tmpCardsBtn.length > 0)\n\t\t\t{\n\t\t\t\ttmpCardsBtn[0].remove();\n\t\t\t}\n\t\t}\n\n\t\treturn super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);\n\t}\n\n\t// ── Icon Population ───────────────────────────────────────────────────\n\n\t/**\n\t * Populate SVG icons for all toolbar buttons.\n\t */\n\t_populateToolbarIcons()\n\t{\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\t\tif (!tmpIconProvider) return;\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\t// Map of element ID suffix → icon key\n\t\tlet tmpIconMap =\n\t\t{\n\t\t\t'plus': 'plus',\n\t\t\t'trash': 'trash',\n\t\t\t'zoom-in': 'zoom-in',\n\t\t\t'zoom-out': 'zoom-out',\n\t\t\t'zoom-fit': 'zoom-fit',\n\t\t\t'auto-layout': 'auto-layout',\n\t\t\t'cards': 'cards',\n\t\t\t'layout': 'layout',\n\t\t\t'settings': 'settings',\n\t\t\t'grip': 'grip',\n\t\t\t'collapse': 'collapse',\n\t\t\t'expand': 'expand'\n\t\t};\n\n\t\tlet tmpKeys = Object.keys(tmpIconMap);\n\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t{\n\t\t\tlet tmpElementId = `Flow-Toolbar-Icon-${tmpKeys[i]}-${tmpFlowViewIdentifier}`;\n\t\t\tlet tmpElements = this.pict.ContentAssignment.getElement(`#${tmpElementId}`);\n\t\t\tif (tmpElements.length > 0)\n\t\t\t{\n\t\t\t\ttmpElements[0].innerHTML = tmpIconProvider.getIconSVGMarkup(tmpIconMap[tmpKeys[i]], 14);\n\t\t\t}\n\t\t}\n\n\t\t// Fullscreen icon\n\t\tlet tmpFullscreenIcon = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-Icon-${tmpFlowViewIdentifier}`);\n\t\tif (tmpFullscreenIcon.length > 0)\n\t\t{\n\t\t\ttmpFullscreenIcon[0].innerHTML = tmpIconProvider.getIconSVGMarkup('fullscreen', 14);\n\t\t}\n\n\t\t// Chevrons (smaller)\n\t\tlet tmpCardsChevron = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-CardsChevron-${tmpFlowViewIdentifier}`);\n\t\tif (tmpCardsChevron.length > 0)\n\t\t{\n\t\t\ttmpCardsChevron[0].innerHTML = tmpIconProvider.getIconSVGMarkup('chevron-down', 8);\n\t\t}\n\n\t\tlet tmpLayoutChevron = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-LayoutChevron-${tmpFlowViewIdentifier}`);\n\t\tif (tmpLayoutChevron.length > 0)\n\t\t{\n\t\t\ttmpLayoutChevron[0].innerHTML = tmpIconProvider.getIconSVGMarkup('chevron-down', 8);\n\t\t}\n\t}\n\n\t// ── Popup Management ──────────────────────────────────────────────────\n\n\t/**\n\t * Open a popup below a trigger button.\n\t * @param {string} pType - 'add-node' | 'cards' | 'layout'\n\t */\n\t_openPopup(pType)\n\t{\n\t\t// Toggle off if already open\n\t\tif (this._ActivePopup === pType)\n\t\t{\n\t\t\tthis._closePopup();\n\t\t\treturn;\n\t\t}\n\n\t\t// Close any existing popup first\n\t\tthis._closePopup();\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpAnchor = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-PopupAnchor-${tmpFlowViewIdentifier}`);\n\t\tif (tmpAnchor.length < 1) return;\n\n\t\t// Create popup div\n\t\tlet tmpPopup = document.createElement('div');\n\t\ttmpPopup.className = 'pict-flow-toolbar-popup';\n\t\ttmpPopup.setAttribute('id', `Flow-Toolbar-Popup-${tmpFlowViewIdentifier}`);\n\n\t\t// Build popup content\n\t\tswitch (pType)\n\t\t{\n\t\t\tcase 'add-node':\n\t\t\t\tthis._buildAddNodePopup(tmpPopup);\n\t\t\t\tbreak;\n\t\t\tcase 'cards':\n\t\t\t\tthis._buildCardsPopup(tmpPopup);\n\t\t\t\tbreak;\n\t\t\tcase 'layout':\n\t\t\t\tthis._buildLayoutPopup(tmpPopup);\n\t\t\t\tbreak;\n\t\t\tcase 'settings':\n\t\t\t\tthis._buildSettingsPopup(tmpPopup);\n\t\t\t\tbreak;\n\t\t}\n\n\t\ttmpAnchor[0].appendChild(tmpPopup);\n\t\tthis._ActivePopup = pType;\n\n\t\t// Position the popup below the trigger button\n\t\tthis._positionPopup(tmpPopup, pType);\n\n\t\t// Click-outside-to-close handler (delayed to avoid catching the opening click)\n\t\tsetTimeout(() =>\n\t\t{\n\t\t\tthis._DocumentClickHandler = (pEvent) =>\n\t\t\t{\n\t\t\t\tif (!tmpPopup.contains(pEvent.target))\n\t\t\t\t{\n\t\t\t\t\t// Check if click was on the trigger button itself (toggle behavior)\n\t\t\t\t\tlet tmpButton = pEvent.target.closest('[data-flow-action]');\n\t\t\t\t\tif (tmpButton)\n\t\t\t\t\t{\n\t\t\t\t\t\tlet tmpAction = tmpButton.getAttribute('data-flow-action');\n\t\t\t\t\t\tif (tmpAction === pType || tmpAction === pType.replace('-popup', '') + '-popup')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn; // Let the toggle handle it\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis._closePopup();\n\t\t\t\t}\n\t\t\t};\n\t\t\tdocument.addEventListener('click', this._DocumentClickHandler, true);\n\t\t}, 0);\n\n\t\t// Focus search input if Add Node popup\n\t\tif (pType === 'add-node')\n\t\t{\n\t\t\tlet tmpSearch = tmpPopup.querySelector('.pict-flow-popup-search');\n\t\t\tif (tmpSearch)\n\t\t\t{\n\t\t\t\tsetTimeout(() => { tmpSearch.focus(); }, 50);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Close the active popup and clean up.\n\t */\n\t_closePopup()\n\t{\n\t\tif (this._DocumentClickHandler)\n\t\t{\n\t\t\tdocument.removeEventListener('click', this._DocumentClickHandler, true);\n\t\t\tthis._DocumentClickHandler = null;\n\t\t}\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpPopup = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Popup-${tmpFlowViewIdentifier}`);\n\t\tif (tmpPopup.length > 0)\n\t\t{\n\t\t\ttmpPopup[0].parentNode.removeChild(tmpPopup[0]);\n\t\t}\n\n\t\tthis._ActivePopup = null;\n\t}\n\n\t/**\n\t * Position a popup below its trigger button.\n\t * @param {HTMLElement} pPopupDiv\n\t * @param {string} pType\n\t */\n\t_positionPopup(pPopupDiv, pType)\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\t// Determine which button triggered the popup\n\t\tlet tmpTriggerSelector;\n\t\tswitch (pType)\n\t\t{\n\t\t\tcase 'add-node':\n\t\t\t\ttmpTriggerSelector = `#Flow-Toolbar-AddNode-${tmpFlowViewIdentifier}`;\n\t\t\t\tbreak;\n\t\t\tcase 'cards':\n\t\t\t\ttmpTriggerSelector = `#Flow-Toolbar-Cards-${tmpFlowViewIdentifier}`;\n\t\t\t\tbreak;\n\t\t\tcase 'layout':\n\t\t\t\ttmpTriggerSelector = `#Flow-Toolbar-Layout-${tmpFlowViewIdentifier}`;\n\t\t\t\tbreak;\n\t\t\tcase 'settings':\n\t\t\t\ttmpTriggerSelector = `#Flow-Toolbar-Settings-${tmpFlowViewIdentifier}`;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\n\t\tlet tmpTriggerElements = this.pict.ContentAssignment.getElement(tmpTriggerSelector);\n\t\tif (tmpTriggerElements.length < 1) return;\n\n\t\tlet tmpAnchor = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-PopupAnchor-${tmpFlowViewIdentifier}`);\n\t\tif (tmpAnchor.length < 1) return;\n\n\t\tlet tmpTriggerRect = tmpTriggerElements[0].getBoundingClientRect();\n\t\tlet tmpAnchorRect = tmpAnchor[0].getBoundingClientRect();\n\n\t\tlet tmpLeft = tmpTriggerRect.left - tmpAnchorRect.left;\n\t\tpPopupDiv.style.left = tmpLeft + 'px';\n\t\tpPopupDiv.style.top = '0px';\n\t}\n\n\t// ── Add Node Popup ────────────────────────────────────────────────────\n\n\t/**\n\t * Build the searchable Add Node popup content.\n\t * @param {HTMLElement} pContainer\n\t */\n\t_buildAddNodePopup(pContainer)\n\t{\n\t\t// Search wrapper\n\t\tlet tmpSearchWrapper = document.createElement('div');\n\t\ttmpSearchWrapper.className = 'pict-flow-popup-search-wrapper';\n\n\t\tlet tmpSearchIcon = document.createElement('span');\n\t\ttmpSearchIcon.className = 'pict-flow-popup-search-icon';\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\t\tif (tmpIconProvider)\n\t\t{\n\t\t\ttmpSearchIcon.innerHTML = tmpIconProvider.getIconSVGMarkup('search', 12);\n\t\t}\n\t\ttmpSearchWrapper.appendChild(tmpSearchIcon);\n\n\t\tlet tmpSearchInput = document.createElement('input');\n\t\ttmpSearchInput.className = 'pict-flow-popup-search';\n\t\ttmpSearchInput.setAttribute('type', 'text');\n\t\ttmpSearchInput.setAttribute('placeholder', 'Search node types...');\n\t\ttmpSearchWrapper.appendChild(tmpSearchInput);\n\t\tpContainer.appendChild(tmpSearchWrapper);\n\n\t\t// Node list\n\t\tlet tmpListDiv = document.createElement('div');\n\t\ttmpListDiv.className = 'pict-flow-popup-node-list';\n\t\tpContainer.appendChild(tmpListDiv);\n\n\t\t// Initial population\n\t\tthis._populateNodeList(tmpListDiv, '');\n\n\t\t// Filter on input\n\t\ttmpSearchInput.addEventListener('input', () =>\n\t\t{\n\t\t\tthis._populateNodeList(tmpListDiv, tmpSearchInput.value);\n\t\t});\n\t}\n\n\t/**\n\t * Populate the node list in the Add Node popup, filtered by search text.\n\t * @param {HTMLElement} pListDiv\n\t * @param {string} pFilter\n\t */\n\t_populateNodeList(pListDiv, pFilter)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._NodeTypeProvider) return;\n\n\t\t// Clear\n\t\twhile (pListDiv.firstChild)\n\t\t{\n\t\t\tpListDiv.removeChild(pListDiv.firstChild);\n\t\t}\n\n\t\tlet tmpTypes = this._FlowView._NodeTypeProvider.getNodeTypes();\n\t\tlet tmpTypeKeys = Object.keys(tmpTypes);\n\t\tlet tmpFilter = (pFilter || '').toLowerCase().trim();\n\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\tlet tmpMatchCount = 0;\n\n\t\tfor (let i = 0; i < tmpTypeKeys.length; i++)\n\t\t{\n\t\t\tlet tmpTypeConfig = tmpTypes[tmpTypeKeys[i]];\n\t\t\tlet tmpMeta = tmpTypeConfig.CardMetadata || {};\n\n\t\t\t// Skip disabled cards\n\t\t\tif (tmpMeta.Enabled === false) continue;\n\n\t\t\t// Filter match: label, code, or category\n\t\t\tif (tmpFilter)\n\t\t\t{\n\t\t\t\tlet tmpLabel = (tmpTypeConfig.Label || '').toLowerCase();\n\t\t\t\tlet tmpCode = (tmpMeta.Code || '').toLowerCase();\n\t\t\t\tlet tmpCategory = (tmpMeta.Category || '').toLowerCase();\n\t\t\t\tif (tmpLabel.indexOf(tmpFilter) < 0 &&\n\t\t\t\t\ttmpCode.indexOf(tmpFilter) < 0 &&\n\t\t\t\t\ttmpCategory.indexOf(tmpFilter) < 0)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttmpMatchCount++;\n\n\t\t\tlet tmpRow = document.createElement('div');\n\t\t\ttmpRow.className = 'pict-flow-popup-list-item';\n\t\t\ttmpRow.setAttribute('data-node-type', tmpTypeKeys[i]);\n\n\t\t\t// Icon\n\t\t\tlet tmpIconSpan = document.createElement('span');\n\t\t\ttmpIconSpan.className = 'pict-flow-popup-list-item-icon';\n\t\t\tif (tmpIconProvider)\n\t\t\t{\n\t\t\t\tlet tmpResolvedKey = tmpIconProvider.resolveIconKey(tmpMeta);\n\t\t\t\ttmpIconSpan.innerHTML = tmpIconProvider.getIconSVGMarkup(tmpResolvedKey, 16);\n\t\t\t}\n\t\t\ttmpRow.appendChild(tmpIconSpan);\n\n\t\t\t// Label\n\t\t\tlet tmpLabelSpan = document.createElement('span');\n\t\t\ttmpLabelSpan.className = 'pict-flow-popup-list-item-label';\n\t\t\ttmpLabelSpan.textContent = tmpTypeConfig.Label;\n\t\t\ttmpRow.appendChild(tmpLabelSpan);\n\n\t\t\t// Code badge\n\t\t\tif (tmpMeta.Code)\n\t\t\t{\n\t\t\t\tlet tmpCodeSpan = document.createElement('span');\n\t\t\t\ttmpCodeSpan.className = 'pict-flow-popup-list-item-code';\n\t\t\t\ttmpCodeSpan.textContent = tmpMeta.Code;\n\t\t\t\ttmpRow.appendChild(tmpCodeSpan);\n\t\t\t}\n\n\t\t\t// Click handler\n\t\t\ttmpRow.addEventListener('click', () =>\n\t\t\t{\n\t\t\t\tthis._addNodeAtCenter(tmpTypeKeys[i]);\n\t\t\t\tthis._closePopup();\n\t\t\t});\n\n\t\t\tpListDiv.appendChild(tmpRow);\n\t\t}\n\n\t\tif (tmpMatchCount === 0)\n\t\t{\n\t\t\tlet tmpEmpty = document.createElement('div');\n\t\t\ttmpEmpty.className = 'pict-flow-popup-list-empty';\n\t\t\ttmpEmpty.textContent = 'No matching node types';\n\t\t\tpListDiv.appendChild(tmpEmpty);\n\t\t}\n\t}\n\n\t// ── Cards Popup ───────────────────────────────────────────────────────\n\n\t/**\n\t * Build the Cards popup content with search and categorized palette.\n\t * @param {HTMLElement} pContainer\n\t */\n\t_buildCardsPopup(pContainer)\n\t{\n\t\t// Search wrapper\n\t\tlet tmpSearchWrapper = document.createElement('div');\n\t\ttmpSearchWrapper.className = 'pict-flow-popup-search-wrapper';\n\n\t\tlet tmpSearchIcon = document.createElement('span');\n\t\ttmpSearchIcon.className = 'pict-flow-popup-search-icon';\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\t\tif (tmpIconProvider)\n\t\t{\n\t\t\ttmpSearchIcon.innerHTML = tmpIconProvider.getIconSVGMarkup('search', 12);\n\t\t}\n\t\ttmpSearchWrapper.appendChild(tmpSearchIcon);\n\n\t\tlet tmpSearchInput = document.createElement('input');\n\t\ttmpSearchInput.className = 'pict-flow-popup-search';\n\t\ttmpSearchInput.setAttribute('type', 'text');\n\t\ttmpSearchInput.setAttribute('placeholder', 'Search cards...');\n\t\ttmpSearchWrapper.appendChild(tmpSearchInput);\n\t\tpContainer.appendChild(tmpSearchWrapper);\n\n\t\t// Palette list container\n\t\tlet tmpListDiv = document.createElement('div');\n\t\ttmpListDiv.className = 'pict-flow-popup-node-list';\n\t\tpContainer.appendChild(tmpListDiv);\n\n\t\t// Initial population\n\t\tthis._renderPalette(tmpListDiv, '');\n\n\t\t// Filter on input\n\t\ttmpSearchInput.addEventListener('input', () =>\n\t\t{\n\t\t\tthis._renderPalette(tmpListDiv, tmpSearchInput.value);\n\t\t});\n\n\t\t// Focus search input\n\t\tsetTimeout(() => { tmpSearchInput.focus(); }, 50);\n\t}\n\n\t/**\n\t * Render the card palette with categories and card chips into a container.\n\t * @param {HTMLElement} pContainer - The target container element\n\t * @param {string} [pFilter] - Optional search filter text\n\t */\n\t_renderPalette(pContainer, pFilter)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._NodeTypeProvider) return;\n\n\t\t// Clear existing content\n\t\twhile (pContainer.firstChild)\n\t\t{\n\t\t\tpContainer.removeChild(pContainer.firstChild);\n\t\t}\n\n\t\tlet tmpCategories = this._FlowView._NodeTypeProvider.getCardsByCategory();\n\t\tlet tmpCategoryKeys = Object.keys(tmpCategories);\n\t\tlet tmpFilter = (pFilter || '').toLowerCase().trim();\n\t\tlet tmpTotalMatchCount = 0;\n\n\t\tfor (let i = 0; i < tmpCategoryKeys.length; i++)\n\t\t{\n\t\t\tlet tmpCategoryName = tmpCategoryKeys[i];\n\t\t\tlet tmpCards = tmpCategories[tmpCategoryName];\n\t\t\tlet tmpMatchingCards = [];\n\n\t\t\t// Filter cards within this category\n\t\t\tfor (let j = 0; j < tmpCards.length; j++)\n\t\t\t{\n\t\t\t\tlet tmpCardConfig = tmpCards[j];\n\t\t\t\tlet tmpMeta = tmpCardConfig.CardMetadata || {};\n\n\t\t\t\tif (tmpFilter)\n\t\t\t\t{\n\t\t\t\t\tlet tmpLabel = (tmpCardConfig.Label || '').toLowerCase();\n\t\t\t\t\tlet tmpCode = (tmpMeta.Code || '').toLowerCase();\n\t\t\t\t\tlet tmpCategory = tmpCategoryName.toLowerCase();\n\t\t\t\t\tif (tmpLabel.indexOf(tmpFilter) < 0 &&\n\t\t\t\t\t\ttmpCode.indexOf(tmpFilter) < 0 &&\n\t\t\t\t\t\ttmpCategory.indexOf(tmpFilter) < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttmpMatchingCards.push(tmpCardConfig);\n\t\t\t}\n\n\t\t\tif (tmpMatchingCards.length === 0) continue;\n\n\t\t\ttmpTotalMatchCount += tmpMatchingCards.length;\n\n\t\t\tlet tmpCategoryDiv = document.createElement('div');\n\t\t\ttmpCategoryDiv.className = 'pict-flow-palette-category';\n\t\t\ttmpCategoryDiv.style.padding = '0.35em 0.5em';\n\n\t\t\tlet tmpCategoryLabel = document.createElement('div');\n\t\t\ttmpCategoryLabel.className = 'pict-flow-palette-category-label';\n\t\t\ttmpCategoryLabel.textContent = tmpCategoryName;\n\t\t\ttmpCategoryDiv.appendChild(tmpCategoryLabel);\n\n\t\t\tlet tmpCardsDiv = document.createElement('div');\n\t\t\ttmpCardsDiv.className = 'pict-flow-palette-cards';\n\n\t\t\tfor (let j = 0; j < tmpMatchingCards.length; j++)\n\t\t\t{\n\t\t\t\tlet tmpCardConfig = tmpMatchingCards[j];\n\t\t\t\tlet tmpMeta = tmpCardConfig.CardMetadata || {};\n\n\t\t\t\tlet tmpCardEl = document.createElement('div');\n\t\t\t\ttmpCardEl.className = 'pict-flow-palette-card';\n\t\t\t\tif (tmpMeta.Enabled === false)\n\t\t\t\t{\n\t\t\t\t\ttmpCardEl.classList.add('disabled');\n\t\t\t\t}\n\t\t\t\ttmpCardEl.setAttribute('data-card-type', tmpCardConfig.Hash);\n\n\t\t\t\tif (tmpMeta.Tooltip)\n\t\t\t\t{\n\t\t\t\t\ttmpCardEl.setAttribute('title', tmpMeta.Tooltip);\n\t\t\t\t}\n\t\t\t\telse if (tmpMeta.Description)\n\t\t\t\t{\n\t\t\t\t\ttmpCardEl.setAttribute('title', tmpMeta.Description);\n\t\t\t\t}\n\n\t\t\t\t// Icon or color swatch\n\t\t\t\tif (tmpMeta.Icon)\n\t\t\t\t{\n\t\t\t\t\tlet tmpIconSpan = document.createElement('span');\n\t\t\t\t\ttmpIconSpan.className = 'pict-flow-palette-card-icon';\n\t\t\t\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\t\t\t\tif (tmpIconProvider && !tmpIconProvider.isEmojiIcon(tmpMeta.Icon))\n\t\t\t\t\t{\n\t\t\t\t\t\tlet tmpResolvedKey = tmpIconProvider.resolveIconKey(tmpMeta);\n\t\t\t\t\t\ttmpIconSpan.innerHTML = tmpIconProvider.getIconSVGMarkup(tmpResolvedKey, 14);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpIconSpan.textContent = tmpMeta.Icon;\n\t\t\t\t\t}\n\t\t\t\t\ttmpCardEl.appendChild(tmpIconSpan);\n\t\t\t\t}\n\t\t\t\telse if (this._FlowView._IconProvider)\n\t\t\t\t{\n\t\t\t\t\tlet tmpIconSpan = document.createElement('span');\n\t\t\t\t\ttmpIconSpan.className = 'pict-flow-palette-card-icon';\n\t\t\t\t\ttmpIconSpan.innerHTML = this._FlowView._IconProvider.getIconSVGMarkup('default', 14);\n\t\t\t\t\ttmpCardEl.appendChild(tmpIconSpan);\n\t\t\t\t}\n\t\t\t\telse if (tmpCardConfig.TitleBarColor)\n\t\t\t\t{\n\t\t\t\t\tlet tmpSwatch = document.createElement('span');\n\t\t\t\t\ttmpSwatch.className = 'pict-flow-palette-card-swatch';\n\t\t\t\t\ttmpSwatch.style.backgroundColor = tmpCardConfig.TitleBarColor;\n\t\t\t\t\ttmpCardEl.appendChild(tmpSwatch);\n\t\t\t\t}\n\n\t\t\t\t// Title\n\t\t\t\tlet tmpTitleSpan = document.createElement('span');\n\t\t\t\ttmpTitleSpan.className = 'pict-flow-palette-card-title';\n\t\t\t\ttmpTitleSpan.textContent = tmpCardConfig.Label;\n\t\t\t\ttmpCardEl.appendChild(tmpTitleSpan);\n\n\t\t\t\t// Code badge\n\t\t\t\tif (tmpMeta.Code)\n\t\t\t\t{\n\t\t\t\t\tlet tmpCodeSpan = document.createElement('span');\n\t\t\t\t\ttmpCodeSpan.className = 'pict-flow-palette-card-code';\n\t\t\t\t\ttmpCodeSpan.textContent = tmpMeta.Code;\n\t\t\t\t\ttmpCardEl.appendChild(tmpCodeSpan);\n\t\t\t\t}\n\n\t\t\t\t// Click handler\n\t\t\t\ttmpCardEl.addEventListener('click', () =>\n\t\t\t\t{\n\t\t\t\t\tthis._addCardFromPalette(tmpCardConfig.Hash);\n\t\t\t\t\tthis._closePopup();\n\t\t\t\t});\n\n\t\t\t\ttmpCardsDiv.appendChild(tmpCardEl);\n\t\t\t}\n\n\t\t\ttmpCategoryDiv.appendChild(tmpCardsDiv);\n\t\t\tpContainer.appendChild(tmpCategoryDiv);\n\t\t}\n\n\t\tif (tmpTotalMatchCount === 0)\n\t\t{\n\t\t\tlet tmpEmpty = document.createElement('div');\n\t\t\ttmpEmpty.className = 'pict-flow-popup-list-empty';\n\t\t\ttmpEmpty.textContent = tmpFilter ? 'No matching cards' : 'No card types available';\n\t\t\tpContainer.appendChild(tmpEmpty);\n\t\t}\n\t}\n\n\t// ── Layout Popup ──────────────────────────────────────────────────────\n\n\t/**\n\t * Build the Layout popup content.\n\t * @param {HTMLElement} pContainer\n\t */\n\t_buildLayoutPopup(pContainer)\n\t{\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\n\t\t// Save Layout section at top\n\t\tlet tmpSaveSection = document.createElement('div');\n\t\ttmpSaveSection.className = 'pict-flow-popup-layout-save-section';\n\n\t\t// Save input row (hidden initially)\n\t\tlet tmpSaveInputRow = document.createElement('div');\n\t\ttmpSaveInputRow.className = 'pict-flow-popup-layout-save-input-row';\n\t\ttmpSaveInputRow.style.display = 'none';\n\n\t\tlet tmpSaveInput = document.createElement('input');\n\t\ttmpSaveInput.className = 'pict-flow-popup-layout-save-input';\n\t\ttmpSaveInput.setAttribute('type', 'text');\n\t\ttmpSaveInput.setAttribute('placeholder', 'Layout name...');\n\t\ttmpSaveInputRow.appendChild(tmpSaveInput);\n\n\t\tlet tmpSaveConfirmBtn = document.createElement('button');\n\t\ttmpSaveConfirmBtn.className = 'pict-flow-popup-layout-save-confirm';\n\t\ttmpSaveConfirmBtn.title = 'Save';\n\t\tif (tmpIconProvider)\n\t\t{\n\t\t\ttmpSaveConfirmBtn.innerHTML = tmpIconProvider.getIconSVGMarkup('save', 14);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpSaveConfirmBtn.textContent = '✓';\n\t\t}\n\t\ttmpSaveInputRow.appendChild(tmpSaveConfirmBtn);\n\n\t\t// \"Save Current Layout\" clickable row\n\t\tlet tmpSaveRow = document.createElement('div');\n\t\ttmpSaveRow.className = 'pict-flow-popup-layout-save';\n\n\t\tlet tmpSaveIcon = document.createElement('span');\n\t\ttmpSaveIcon.className = 'pict-flow-popup-layout-save-icon';\n\t\tif (tmpIconProvider)\n\t\t{\n\t\t\ttmpSaveIcon.innerHTML = tmpIconProvider.getIconSVGMarkup('save', 14);\n\t\t}\n\t\ttmpSaveRow.appendChild(tmpSaveIcon);\n\n\t\tlet tmpSaveText = document.createElement('span');\n\t\ttmpSaveText.textContent = 'Save Current Layout';\n\t\ttmpSaveRow.appendChild(tmpSaveText);\n\n\t\t// Click \"Save Current Layout\" to reveal the input row\n\t\ttmpSaveRow.addEventListener('click', () =>\n\t\t{\n\t\t\ttmpSaveRow.style.display = 'none';\n\t\t\ttmpSaveInputRow.style.display = '';\n\t\t\ttmpSaveInput.value = '';\n\t\t\tsetTimeout(() => { tmpSaveInput.focus(); }, 50);\n\t\t});\n\n\t\t// Confirm save via button click\n\t\tlet tmpDoSave = () =>\n\t\t{\n\t\t\tlet tmpName = tmpSaveInput.value.trim();\n\t\t\tif (tmpName === '') return;\n\t\t\tthis._FlowView._LayoutProvider.saveLayout(tmpName);\n\t\t\t// Refresh the popup content\n\t\t\twhile (pContainer.firstChild)\n\t\t\t{\n\t\t\t\tpContainer.removeChild(pContainer.firstChild);\n\t\t\t}\n\t\t\tthis._buildLayoutPopup(pContainer);\n\t\t};\n\n\t\ttmpSaveConfirmBtn.addEventListener('click', tmpDoSave);\n\n\t\t// Confirm save via Enter key\n\t\ttmpSaveInput.addEventListener('keydown', (pEvent) =>\n\t\t{\n\t\t\tif (pEvent.key === 'Enter')\n\t\t\t{\n\t\t\t\tpEvent.preventDefault();\n\t\t\t\ttmpDoSave();\n\t\t\t}\n\t\t\telse if (pEvent.key === 'Escape')\n\t\t\t{\n\t\t\t\t// Cancel — hide input, show the save row again\n\t\t\t\ttmpSaveInputRow.style.display = 'none';\n\t\t\t\ttmpSaveRow.style.display = '';\n\t\t\t}\n\t\t});\n\n\t\t// Prevent clicks inside the input from closing the popup\n\t\ttmpSaveInput.addEventListener('click', (pEvent) =>\n\t\t{\n\t\t\tpEvent.stopPropagation();\n\t\t});\n\n\t\ttmpSaveSection.appendChild(tmpSaveRow);\n\t\ttmpSaveSection.appendChild(tmpSaveInputRow);\n\t\tpContainer.appendChild(tmpSaveSection);\n\n\t\t// Divider\n\t\tlet tmpDivider = document.createElement('div');\n\t\ttmpDivider.className = 'pict-flow-popup-divider';\n\t\tpContainer.appendChild(tmpDivider);\n\n\t\t// Layout rows\n\t\tif (!this._FlowView || !this._FlowView._LayoutProvider)\n\t\t{\n\t\t\tlet tmpEmpty = document.createElement('div');\n\t\t\ttmpEmpty.className = 'pict-flow-popup-list-empty';\n\t\t\ttmpEmpty.textContent = 'No saved layouts';\n\t\t\tpContainer.appendChild(tmpEmpty);\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpLayouts = this._FlowView._LayoutProvider.getLayouts();\n\n\t\tif (tmpLayouts.length === 0)\n\t\t{\n\t\t\tlet tmpEmpty = document.createElement('div');\n\t\t\ttmpEmpty.className = 'pict-flow-popup-list-empty';\n\t\t\ttmpEmpty.textContent = 'No saved layouts';\n\t\t\tpContainer.appendChild(tmpEmpty);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (let i = 0; i < tmpLayouts.length; i++)\n\t\t{\n\t\t\tlet tmpLayout = tmpLayouts[i];\n\n\t\t\tlet tmpRow = document.createElement('div');\n\t\t\ttmpRow.className = 'pict-flow-popup-layout-row';\n\n\t\t\tlet tmpNameSpan = document.createElement('span');\n\t\t\ttmpNameSpan.className = 'pict-flow-popup-layout-name';\n\t\t\ttmpNameSpan.textContent = tmpLayout.Name;\n\t\t\ttmpRow.appendChild(tmpNameSpan);\n\n\t\t\t// Delete button (visible on hover via CSS)\n\t\t\tlet tmpDeleteBtn = document.createElement('button');\n\t\t\ttmpDeleteBtn.className = 'pict-flow-popup-layout-delete';\n\t\t\ttmpDeleteBtn.title = 'Delete layout';\n\t\t\tif (tmpIconProvider)\n\t\t\t{\n\t\t\t\ttmpDeleteBtn.innerHTML = tmpIconProvider.getIconSVGMarkup('trash', 12);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpDeleteBtn.textContent = '×';\n\t\t\t}\n\t\t\ttmpRow.appendChild(tmpDeleteBtn);\n\n\t\t\t// Click row → restore layout\n\t\t\ttmpRow.addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\t// Don't restore if they clicked the delete button\n\t\t\t\tif (pEvent.target.closest('.pict-flow-popup-layout-delete'))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis._FlowView._LayoutProvider.restoreLayout(tmpLayout.Hash);\n\t\t\t\tthis._closePopup();\n\t\t\t});\n\n\t\t\t// Click delete → delete layout and refresh popup\n\t\t\ttmpDeleteBtn.addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tthis._FlowView._LayoutProvider.deleteLayout(tmpLayout.Hash);\n\t\t\t\t// Refresh the popup content\n\t\t\t\twhile (pContainer.firstChild)\n\t\t\t\t{\n\t\t\t\t\tpContainer.removeChild(pContainer.firstChild);\n\t\t\t\t}\n\t\t\t\tthis._buildLayoutPopup(pContainer);\n\t\t\t});\n\n\t\t\tpContainer.appendChild(tmpRow);\n\t\t}\n\t}\n\n\t// ── Settings Popup ───────────────────────────────────────────────────\n\n\t/**\n\t * Build the Settings popup content (theme dropdown + noise slider).\n\t * @param {HTMLElement} pContainer\n\t */\n\t_buildSettingsPopup(pContainer)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._ThemeProvider) return;\n\n\t\tlet tmpThemeProvider = this._FlowView._ThemeProvider;\n\n\t\t// Theme selector section\n\t\tlet tmpThemeSection = document.createElement('div');\n\t\ttmpThemeSection.className = 'pict-flow-popup-settings-section';\n\n\t\tlet tmpThemeLabel = document.createElement('label');\n\t\ttmpThemeLabel.className = 'pict-flow-popup-settings-label';\n\t\ttmpThemeLabel.textContent = 'Theme';\n\t\ttmpThemeSection.appendChild(tmpThemeLabel);\n\n\t\tlet tmpThemeSelect = document.createElement('select');\n\t\ttmpThemeSelect.className = 'pict-flow-popup-settings-select';\n\n\t\tlet tmpThemeKeys = tmpThemeProvider.getThemeKeys();\n\t\tlet tmpActiveKey = tmpThemeProvider.getActiveThemeKey();\n\n\t\tfor (let i = 0; i < tmpThemeKeys.length; i++)\n\t\t{\n\t\t\tlet tmpOption = document.createElement('option');\n\t\t\ttmpOption.value = tmpThemeKeys[i];\n\n\t\t\tlet tmpTheme = tmpThemeProvider._Themes[tmpThemeKeys[i]];\n\t\t\ttmpOption.textContent = tmpTheme.Label || tmpThemeKeys[i];\n\n\t\t\tif (tmpThemeKeys[i] === tmpActiveKey)\n\t\t\t{\n\t\t\t\ttmpOption.selected = true;\n\t\t\t}\n\t\t\ttmpThemeSelect.appendChild(tmpOption);\n\t\t}\n\n\t\ttmpThemeSelect.addEventListener('change', () =>\n\t\t{\n\t\t\tthis._FlowView.setTheme(tmpThemeSelect.value);\n\t\t\t// Refresh the noise slider visibility\n\t\t\tthis._refreshNoiseSlider(pContainer);\n\t\t});\n\n\t\t// Prevent popup close on select interaction\n\t\ttmpThemeSelect.addEventListener('click', (pEvent) => { pEvent.stopPropagation(); });\n\n\t\ttmpThemeSection.appendChild(tmpThemeSelect);\n\t\tpContainer.appendChild(tmpThemeSection);\n\n\t\t// Divider\n\t\tlet tmpDivider = document.createElement('div');\n\t\ttmpDivider.className = 'pict-flow-popup-divider';\n\t\tpContainer.appendChild(tmpDivider);\n\n\t\t// Noise level section\n\t\tlet tmpNoiseSection = document.createElement('div');\n\t\ttmpNoiseSection.className = 'pict-flow-popup-settings-section pict-flow-popup-settings-noise';\n\t\ttmpNoiseSection.setAttribute('data-settings-type', 'noise');\n\n\t\tlet tmpNoiseLabel = document.createElement('label');\n\t\ttmpNoiseLabel.className = 'pict-flow-popup-settings-label';\n\t\ttmpNoiseLabel.textContent = 'Noise';\n\t\ttmpNoiseSection.appendChild(tmpNoiseLabel);\n\n\t\tlet tmpNoiseRow = document.createElement('div');\n\t\ttmpNoiseRow.className = 'pict-flow-popup-settings-slider-row';\n\n\t\tlet tmpNoiseSlider = document.createElement('input');\n\t\ttmpNoiseSlider.type = 'range';\n\t\ttmpNoiseSlider.className = 'pict-flow-popup-settings-slider';\n\t\ttmpNoiseSlider.min = '0';\n\t\ttmpNoiseSlider.max = '100';\n\t\ttmpNoiseSlider.value = String(Math.round(tmpThemeProvider.getNoiseLevel() * 100));\n\n\t\tlet tmpNoiseValue = document.createElement('span');\n\t\ttmpNoiseValue.className = 'pict-flow-popup-settings-slider-value';\n\t\ttmpNoiseValue.textContent = tmpNoiseSlider.value + '%';\n\n\t\ttmpNoiseSlider.addEventListener('input', () =>\n\t\t{\n\t\t\tlet tmpLevel = parseInt(tmpNoiseSlider.value, 10) / 100;\n\t\t\ttmpNoiseValue.textContent = tmpNoiseSlider.value + '%';\n\t\t\tthis._FlowView.setNoiseLevel(tmpLevel);\n\t\t});\n\n\t\t// Prevent popup close on slider interaction\n\t\ttmpNoiseSlider.addEventListener('click', (pEvent) => { pEvent.stopPropagation(); });\n\t\ttmpNoiseSlider.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\n\t\ttmpNoiseRow.appendChild(tmpNoiseSlider);\n\t\ttmpNoiseRow.appendChild(tmpNoiseValue);\n\t\ttmpNoiseSection.appendChild(tmpNoiseRow);\n\t\tpContainer.appendChild(tmpNoiseSection);\n\n\t\t// Show/hide noise slider based on active theme\n\t\tthis._refreshNoiseSlider(pContainer);\n\t}\n\n\t/**\n\t * Show or hide the noise slider based on whether the active theme supports noise.\n\t * @param {HTMLElement} pContainer - The settings popup container\n\t */\n\t_refreshNoiseSlider(pContainer)\n\t{\n\t\tlet tmpNoiseSection = pContainer.querySelector('[data-settings-type=\"noise\"]');\n\t\tif (!tmpNoiseSection) return;\n\n\t\tlet tmpTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\tif (tmpTheme && tmpTheme.NoiseConfig && tmpTheme.NoiseConfig.Enabled)\n\t\t{\n\t\t\ttmpNoiseSection.style.display = '';\n\t\t\t// Update slider value to reflect theme default\n\t\t\tlet tmpSlider = tmpNoiseSection.querySelector('.pict-flow-popup-settings-slider');\n\t\t\tlet tmpValueLabel = tmpNoiseSection.querySelector('.pict-flow-popup-settings-slider-value');\n\t\t\tif (tmpSlider)\n\t\t\t{\n\t\t\t\tlet tmpLevel = Math.round(this._FlowView._ThemeProvider.getNoiseLevel() * 100);\n\t\t\t\ttmpSlider.value = String(tmpLevel);\n\t\t\t\tif (tmpValueLabel) tmpValueLabel.textContent = tmpLevel + '%';\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpNoiseSection.style.display = 'none';\n\t\t}\n\t}\n\n\t// ── Toolbar Mode Switching ────────────────────────────────────────────\n\n\t/**\n\t * Switch between docked, floating, and collapsed modes.\n\t * @param {string} pMode - 'docked' | 'floating' | 'collapsed'\n\t */\n\t_setToolbarMode(pMode)\n\t{\n\t\t// Close any active popup first\n\t\tthis._closePopup();\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpBar = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Bar-${tmpFlowViewIdentifier}`);\n\t\tlet tmpCollapsed = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Collapsed-${tmpFlowViewIdentifier}`);\n\n\t\tswitch (pMode)\n\t\t{\n\t\t\tcase 'docked':\n\t\t\t\t// Show toolbar bar\n\t\t\t\tif (tmpBar.length > 0) tmpBar[0].style.display = '';\n\t\t\t\t// Hide collapsed button\n\t\t\t\tif (tmpCollapsed.length > 0) tmpCollapsed[0].classList.remove('visible');\n\t\t\t\t// Hide floating toolbar\n\t\t\t\tif (this._FloatingToolbarView) this._FloatingToolbarView.hide();\n\t\t\t\tbreak;\n\n\t\t\tcase 'floating':\n\t\t\t\t// Hide toolbar bar\n\t\t\t\tif (tmpBar.length > 0) tmpBar[0].style.display = 'none';\n\t\t\t\t// Hide collapsed button\n\t\t\t\tif (tmpCollapsed.length > 0) tmpCollapsed[0].classList.remove('visible');\n\t\t\t\t// Show floating toolbar\n\t\t\t\tthis._showFloatingToolbar();\n\t\t\t\tbreak;\n\n\t\t\tcase 'collapsed':\n\t\t\t\t// Hide toolbar bar\n\t\t\t\tif (tmpBar.length > 0) tmpBar[0].style.display = 'none';\n\t\t\t\t// Show collapsed button\n\t\t\t\tif (tmpCollapsed.length > 0) tmpCollapsed[0].classList.add('visible');\n\t\t\t\t// Hide floating toolbar\n\t\t\t\tif (this._FloatingToolbarView) this._FloatingToolbarView.hide();\n\t\t\t\tbreak;\n\t\t}\n\n\t\tthis._ToolbarMode = pMode;\n\t}\n\n\t/**\n\t * Lazily create and show the floating toolbar.\n\t */\n\t_showFloatingToolbar()\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tif (!this._FloatingToolbarView)\n\t\t{\n\t\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\t\tthis._FloatingToolbarView = this.fable.instantiateServiceProviderWithoutRegistration(\n\t\t\t\t'PictViewFlowFloatingToolbar',\n\t\t\t\t{\n\t\t\t\t\tFlowViewIdentifier: tmpFlowViewIdentifier,\n\t\t\t\t\tDefaultDestinationAddress: `#Flow-FloatingToolbar-Container-${tmpFlowViewIdentifier}`,\n\t\t\t\t\tEnableAddNode: this.options.EnableAddNode,\n\t\t\t\t\tEnableCardPalette: this.options.EnableCardPalette\n\t\t\t\t}\n\t\t\t);\n\t\t\tthis._FloatingToolbarView._ToolbarView = this;\n\t\t\tthis._FloatingToolbarView._FlowView = this._FlowView;\n\t\t\tthis._FloatingToolbarView.render();\n\t\t}\n\n\t\tthis._FloatingToolbarView.show();\n\t}\n\n\t// ── Node Placement Helpers ────────────────────────────────────────────\n\n\t/**\n\t * Add a node at the center of the visible viewport.\n\t * @param {string} pNodeType - The node type hash\n\t */\n\t_addNodeAtCenter(pNodeType)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\n\t\t// Calculate the center of the visible SVG area\n\t\tlet tmpSVGContainer = this._FlowView._SVGElement;\n\t\tlet tmpWidth = tmpSVGContainer ? tmpSVGContainer.clientWidth : 600;\n\t\tlet tmpHeight = tmpSVGContainer ? tmpSVGContainer.clientHeight : 400;\n\n\t\tlet tmpCenterX = (-tmpVS.PanX + tmpWidth / 2) / tmpVS.Zoom;\n\t\tlet tmpCenterY = (-tmpVS.PanY + tmpHeight / 2) / tmpVS.Zoom;\n\n\t\t// Slight offset to avoid stacking\n\t\tlet tmpNodeCount = this._FlowView.flowData.Nodes.length;\n\t\ttmpCenterX += (tmpNodeCount % 5) * 30;\n\t\ttmpCenterY += (tmpNodeCount % 5) * 30;\n\n\t\tthis._FlowView.addNode(pNodeType, tmpCenterX, tmpCenterY);\n\t}\n\n\t/**\n\t * Add a node from a palette card click.\n\t * @param {string} pCardType - The card type hash\n\t */\n\t_addCardFromPalette(pCardType)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\t\tlet tmpX = (-tmpVS.PanX + 200) / tmpVS.Zoom;\n\t\tlet tmpY = (-tmpVS.PanY + 200) / tmpVS.Zoom;\n\n\t\t// Offset to avoid overlap\n\t\tlet tmpNodeCount = this._FlowView.flowData.Nodes.length;\n\t\ttmpX += (tmpNodeCount % 5) * 40;\n\t\ttmpY += (tmpNodeCount % 5) * 40;\n\n\t\tthis._FlowView.addNode(pCardType, tmpX, tmpY);\n\t}\n\n\t// ── Action Handler ────────────────────────────────────────────────────\n\n\t/**\n\t * Handle a toolbar action\n\t * @param {string} pAction\n\t */\n\t_handleToolbarAction(pAction)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\tswitch (pAction)\n\t\t{\n\t\t\tcase 'add-node':\n\t\t\t\tthis._openPopup('add-node');\n\t\t\t\tbreak;\n\n\t\t\tcase 'delete-selected':\n\t\t\t\tthis._FlowView.deleteSelected();\n\t\t\t\tbreak;\n\n\t\t\tcase 'zoom-in':\n\t\t\t\tthis._FlowView.setZoom(this._FlowView.viewState.Zoom + this._FlowView.options.ZoomStep);\n\t\t\t\tbreak;\n\n\t\t\tcase 'zoom-out':\n\t\t\t\tthis._FlowView.setZoom(this._FlowView.viewState.Zoom - this._FlowView.options.ZoomStep);\n\t\t\t\tbreak;\n\n\t\t\tcase 'zoom-fit':\n\t\t\t\tthis._FlowView.zoomToFit();\n\t\t\t\tbreak;\n\n\t\t\tcase 'auto-layout':\n\t\t\t\tthis._FlowView.autoLayout();\n\t\t\t\tbreak;\n\n\t\t\tcase 'cards-popup':\n\t\t\t\tthis._openPopup('cards');\n\t\t\t\tbreak;\n\n\t\t\tcase 'layout-popup':\n\t\t\t\tthis._openPopup('layout');\n\t\t\t\tbreak;\n\n\t\t\tcase 'settings-popup':\n\t\t\t\tthis._openPopup('settings');\n\t\t\t\tbreak;\n\n\t\t\tcase 'toggle-floating':\n\t\t\t\tif (this._ToolbarMode === 'floating')\n\t\t\t\t{\n\t\t\t\t\tthis._setToolbarMode('docked');\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._setToolbarMode('floating');\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'collapse-toolbar':\n\t\t\t\tthis._setToolbarMode('collapsed');\n\t\t\t\tbreak;\n\n\t\t\tcase 'expand-toolbar':\n\t\t\t\tthis._setToolbarMode('docked');\n\t\t\t\tbreak;\n\n\t\t\tcase 'fullscreen':\n\t\t\t\t{\n\t\t\t\t\tlet tmpIsFullscreen = this._FlowView.toggleFullscreen();\n\t\t\t\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\t\t\t\tlet tmpIconElements = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-Icon-${tmpFlowViewIdentifier}`);\n\t\t\t\t\tif (tmpIconElements.length > 0 && tmpIconProvider)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpIconElements[0].innerHTML = tmpIconProvider.getIconSVGMarkup(\n\t\t\t\t\t\t\ttmpIsFullscreen ? 'exit-fullscreen' : 'fullscreen', 14);\n\t\t\t\t\t}\n\t\t\t\t\tlet tmpFullscreenBtn = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-${tmpFlowViewIdentifier}`);\n\t\t\t\t\tif (tmpFullscreenBtn.length > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpFullscreenBtn[0].setAttribute('title', tmpIsFullscreen ? 'Exit Fullscreen' : 'Toggle Fullscreen');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthis.log.warn(`PictViewFlowToolbar: unknown action '${pAction}'`);\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nmodule.exports = PictViewFlowToolbar;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n\n},{\"pict-view\":45}],43:[function(require,module,exports){\nconst libPictView = require('pict-view');\n\nconst libPictServiceFlowInteractionManager = require('../services/PictService-Flow-InteractionManager.js');\nconst libPictServiceFlowConnectionRenderer = require('../services/PictService-Flow-ConnectionRenderer.js');\nconst libPictServiceFlowTether = require('../services/PictService-Flow-Tether.js');\nconst libPictServiceFlowLayout = require('../services/PictService-Flow-Layout.js');\nconst libPictServiceFlowPathGenerator = require('../services/PictService-Flow-PathGenerator.js');\nconst libPictServiceFlowViewportManager = require('../services/PictService-Flow-ViewportManager.js');\nconst libPictServiceFlowSelectionManager = require('../services/PictService-Flow-SelectionManager.js');\nconst libPictServiceFlowPanelManager = require('../services/PictService-Flow-PanelManager.js');\nconst libPictServiceFlowDataManager = require('../services/PictService-Flow-DataManager.js');\nconst libPictServiceFlowConnectionHandleManager = require('../services/PictService-Flow-ConnectionHandleManager.js');\nconst libPictServiceFlowRenderManager = require('../services/PictService-Flow-RenderManager.js');\nconst libPictServiceFlowPortRenderer = require('../services/PictService-Flow-PortRenderer.js');\n\nconst libPictProviderFlowNodeTypes = require('../providers/PictProvider-Flow-NodeTypes.js');\nconst libPictProviderFlowEventHandler = require('../providers/PictProvider-Flow-EventHandler.js');\nconst libPictProviderFlowLayouts = require('../providers/PictProvider-Flow-Layouts.js');\nconst libPictProviderFlowSVGHelpers = require('../providers/PictProvider-Flow-SVGHelpers.js');\nconst libPictProviderFlowGeometry = require('../providers/PictProvider-Flow-Geometry.js');\nconst libPictProviderFlowPanelChrome = require('../providers/PictProvider-Flow-PanelChrome.js');\nconst libPictProviderFlowCSS = require('../providers/PictProvider-Flow-CSS.js');\nconst libPictProviderFlowIcons = require('../providers/PictProvider-Flow-Icons.js');\nconst libPictProviderFlowConnectorShapes = require('../providers/PictProvider-Flow-ConnectorShapes.js');\nconst libPictProviderFlowTheme = require('../providers/PictProvider-Flow-Theme.js');\nconst libPictProviderFlowNoise = require('../providers/PictProvider-Flow-Noise.js');\n\nconst libPictViewFlowNode = require('./PictView-Flow-Node.js');\nconst libPictViewFlowToolbar = require('./PictView-Flow-Toolbar.js');\nconst libPictViewFlowFloatingToolbar = require('./PictView-Flow-FloatingToolbar.js');\nconst libPictViewFlowPropertiesPanel = require('./PictView-Flow-PropertiesPanel.js');\n\nconst libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\nconst libPanelTemplate = require('../panels/FlowCardPropertiesPanel-Template.js');\nconst libPanelMarkdown = require('../panels/FlowCardPropertiesPanel-Markdown.js');\nconst libPanelForm = require('../panels/FlowCardPropertiesPanel-Form.js');\nconst libPanelView = require('../panels/FlowCardPropertiesPanel-View.js');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Pict-Flow',\n\n\tDefaultRenderable: 'Flow-Container',\n\tDefaultDestinationAddress: '#Flow-Container',\n\n\tAutoRender: false,\n\n\tFlowDataAddress: false,\n\n\tTargetElementAddress: '#Flow-SVG-Container',\n\n\tEnableToolbar: true,\n\tEnableAddNode: true,\n\tEnableCardPalette: true,\n\tIncludeDefaultNodeTypes: true,\n\tEnablePanning: true,\n\tEnableZooming: true,\n\tEnableNodeDragging: true,\n\tEnableConnectionCreation: true,\n\tEnableGridSnap: false,\n\tGridSnapSize: 20,\n\n\tMinZoom: 0.1,\n\tMaxZoom: 5.0,\n\tZoomStep: 0.1,\n\n\tDefaultNodeType: 'default',\n\tDefaultNodeWidth: 180,\n\tDefaultNodeHeight: 80,\n\n\tCSS: false,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'Flow-PanelChrome-Template',\n\t\t\tTemplate: /*html*/`<div class=\"pict-flow-panel\" xmlns=\"http://www.w3.org/1999/xhtml\"><div class=\"pict-flow-panel-titlebar\" data-element-type=\"panel-titlebar\" data-panel-hash=\"{~D:Record.Hash~}\"><span class=\"pict-flow-panel-title-text\">{~D:Record.Title~}</span><span class=\"pict-flow-panel-close-btn\" data-element-type=\"panel-close\" data-panel-hash=\"{~D:Record.Hash~}\"><span class=\"pict-flow-panel-close-icon\"></span></span></div><div class=\"pict-flow-panel-content\" data-panel-hash=\"{~D:Record.Hash~}\"><div class=\"pict-flow-panel-tab-pane active\" data-tab=\"properties\" data-panel-hash=\"{~D:Record.Hash~}\"></div><div class=\"pict-flow-panel-tab-pane\" data-tab=\"help\" data-panel-hash=\"{~D:Record.Hash~}\" style=\"display:none;\"></div><div class=\"pict-flow-panel-tab-pane\" data-tab=\"appearance\" data-panel-hash=\"{~D:Record.Hash~}\" style=\"display:none;\"></div></div><div class=\"pict-flow-panel-resize-handle\" data-element-type=\"panel-resize\" data-panel-hash=\"{~D:Record.Hash~}\"></div><div class=\"pict-flow-panel-tabbar\" data-panel-hash=\"{~D:Record.Hash~}\"><div class=\"pict-flow-panel-tab active\" data-tab-target=\"properties\" data-panel-hash=\"{~D:Record.Hash~}\">Properties</div><div class=\"pict-flow-panel-tab\" data-tab-target=\"help\" data-panel-hash=\"{~D:Record.Hash~}\" style=\"display:none;\">Help</div><div class=\"pict-flow-panel-tab\" data-tab-target=\"appearance\" data-panel-hash=\"{~D:Record.Hash~}\">Appearance</div></div></div>`\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-Container-Template',\n\t\t\tTemplate: /*html*/`\n<div class=\"pict-flow-container\" id=\"Flow-Wrapper-{~D:Record.ViewIdentifier~}\">\n\t<div id=\"Flow-Toolbar-{~D:Record.ViewIdentifier~}\"></div>\n\t<div id=\"Flow-FloatingToolbar-Container-{~D:Record.ViewIdentifier~}\" style=\"display:none;position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:100;\"></div>\n\t<div class=\"pict-flow-svg-container\" id=\"Flow-SVG-Container-{~D:Record.ViewIdentifier~}\">\n\t\t<svg class=\"pict-flow-svg\"\n\t\t\tid=\"Flow-SVG-{~D:Record.ViewIdentifier~}\"\n\t\t\txmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t<defs>\n\t\t\t\t<pattern id=\"flow-grid-{~D:Record.ViewIdentifier~}\"\n\t\t\t\t\twidth=\"20\" height=\"20\" patternUnits=\"userSpaceOnUse\">\n\t\t\t\t\t<line x1=\"20\" y1=\"0\" x2=\"20\" y2=\"20\" class=\"pict-flow-grid-pattern\" />\n\t\t\t\t\t<line x1=\"0\" y1=\"20\" x2=\"20\" y2=\"20\" class=\"pict-flow-grid-pattern\" />\n\t\t\t\t</pattern>\n\t\t\t</defs>\n\t\t\t<rect width=\"10000\" height=\"10000\" x=\"-5000\" y=\"-5000\"\n\t\t\t\tfill=\"url(#flow-grid-{~D:Record.ViewIdentifier~})\"\n\t\t\t\tclass=\"pict-flow-grid-background\" />\n\t\t\t<g class=\"pict-flow-viewport\" id=\"Flow-Viewport-{~D:Record.ViewIdentifier~}\">\n\t\t\t\t<g class=\"pict-flow-connections-layer\" id=\"Flow-Connections-{~D:Record.ViewIdentifier~}\"></g>\n\t\t\t\t<g class=\"pict-flow-nodes-layer\" id=\"Flow-Nodes-{~D:Record.ViewIdentifier~}\"></g>\n\t\t\t\t<g class=\"pict-flow-tethers-layer\" id=\"Flow-Tethers-{~D:Record.ViewIdentifier~}\"></g>\n\t\t\t\t<g class=\"pict-flow-panels-layer\" id=\"Flow-Panels-{~D:Record.ViewIdentifier~}\"></g>\n\t\t\t</g>\n\t\t</svg>\n\t</div>\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: 'Flow-Container',\n\t\t\tTemplateHash: 'Flow-Container-Template',\n\t\t\tDestinationAddress: '#Flow-Container',\n\t\t\tRenderMethod: 'replace'\n\t\t}\n\t]\n};\n\nclass PictViewFlow extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictSectionFlow';\n\n\t\t// ---- Declarative service registry ----\n\t\t// Each entry defines a service to register, instantiate, and guard.\n\t\t// Optional flags:\n\t\t// NoFlowView — instantiate without { FlowView: this }\n\t\t// PostInit — method name to call on the instance after creation\n\t\t// RegisterOnly — only register the type; do not bulk-instantiate\n\t\tthis._ServiceRegistry =\n\t\t[\n\t\t\t// Providers (stateless or config-only — no FlowView needed)\n\t\t\t{ ServiceType: 'PictProviderFlowSVGHelpers', Library: libPictProviderFlowSVGHelpers, Property: '_SVGHelperProvider', NoFlowView: true },\n\t\t\t{ ServiceType: 'PictProviderFlowGeometry', Library: libPictProviderFlowGeometry, Property: '_GeometryProvider', NoFlowView: true },\n\t\t\t{ ServiceType: 'PictProviderFlowNoise', Library: libPictProviderFlowNoise, Property: '_NoiseProvider', NoFlowView: true },\n\n\t\t\t// Providers (need FlowView)\n\t\t\t{ ServiceType: 'PictProviderFlowTheme', Library: libPictProviderFlowTheme, Property: '_ThemeProvider' },\n\t\t\t{ ServiceType: 'PictProviderFlowCSS', Library: libPictProviderFlowCSS, Property: '_CSSProvider', PostInit: 'registerCSS' },\n\t\t\t{ ServiceType: 'PictProviderFlowIcons', Library: libPictProviderFlowIcons, Property: '_IconProvider', PostInit: 'registerIconTemplates' },\n\t\t\t{ ServiceType: 'PictProviderFlowConnectorShapes', Library: libPictProviderFlowConnectorShapes, Property: '_ConnectorShapesProvider' },\n\t\t\t{ ServiceType: 'PictProviderFlowPanelChrome', Library: libPictProviderFlowPanelChrome, Property: '_PanelChromeProvider' },\n\t\t\t{ ServiceType: 'PictProviderFlowNodeTypes', Library: libPictProviderFlowNodeTypes, Property: '_NodeTypeProvider', ExtraOptions: () => ({ AdditionalNodeTypes: this.options.NodeTypes, IncludeDefaultNodeTypes: this.options.IncludeDefaultNodeTypes }) },\n\t\t\t{ ServiceType: 'PictProviderFlowEventHandler', Library: libPictProviderFlowEventHandler, Property: '_EventHandlerProvider' },\n\t\t\t{ ServiceType: 'PictProviderFlowLayouts', Library: libPictProviderFlowLayouts, Property: '_LayoutProvider', PostInit: 'loadPersistedLayouts' },\n\n\t\t\t// Services\n\t\t\t{ ServiceType: 'PictServiceFlowPathGenerator', Library: libPictServiceFlowPathGenerator, Property: '_PathGenerator' },\n\t\t\t{ ServiceType: 'PictServiceFlowDataManager', Library: libPictServiceFlowDataManager, Property: '_DataManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowConnectionHandleManager', Library: libPictServiceFlowConnectionHandleManager, Property: '_ConnectionHandleManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowRenderManager', Library: libPictServiceFlowRenderManager, Property: '_RenderManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowPortRenderer', Library: libPictServiceFlowPortRenderer, Property: '_PortRenderer' },\n\t\t\t{ ServiceType: 'PictServiceFlowInteractionManager', Library: libPictServiceFlowInteractionManager, Property: '_InteractionManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowConnectionRenderer', Library: libPictServiceFlowConnectionRenderer, Property: '_ConnectionRenderer' },\n\t\t\t{ ServiceType: 'PictServiceFlowTether', Library: libPictServiceFlowTether, Property: '_TetherService' },\n\t\t\t{ ServiceType: 'PictServiceFlowLayout', Library: libPictServiceFlowLayout, Property: '_LayoutService' },\n\t\t\t{ ServiceType: 'PictServiceFlowViewportManager', Library: libPictServiceFlowViewportManager, Property: '_ViewportManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowSelectionManager', Library: libPictServiceFlowSelectionManager, Property: '_SelectionManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowPanelManager', Library: libPictServiceFlowPanelManager, Property: '_PanelManager' },\n\n\t\t\t// View types (register only — instantiated with custom config in onAfterInitialRender)\n\t\t\t{ ServiceType: 'PictViewFlowNode', Library: libPictViewFlowNode, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictViewFlowToolbar', Library: libPictViewFlowToolbar, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictViewFlowFloatingToolbar', Library: libPictViewFlowFloatingToolbar, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictViewFlowPropertiesPanel', Library: libPictViewFlowPropertiesPanel, RegisterOnly: true },\n\n\t\t\t// Panel types (register only)\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel', Library: libPictFlowCardPropertiesPanel, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel-Template', Library: libPanelTemplate, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel-Markdown', Library: libPanelMarkdown, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel-Form', Library: libPanelForm, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel-View', Library: libPanelView, RegisterOnly: true }\n\t\t];\n\n\t\tthis._registerServiceTypes();\n\n\t\t// Internal state\n\t\tthis._FlowData = {\n\t\t\tNodes: [],\n\t\t\tConnections: [],\n\t\t\tOpenPanels: [],\n\t\t\tSavedLayouts: [],\n\t\t\tViewState: {\n\t\t\t\tPanX: 0,\n\t\t\t\tPanY: 0,\n\t\t\t\tZoom: 1,\n\t\t\t\tSelectedNodeHash: null,\n\t\t\t\tSelectedConnectionHash: null,\n\t\t\t\tSelectedTetherHash: null\n\t\t\t}\n\t\t};\n\n\t\tthis._SVGElement = null;\n\t\tthis._ViewportElement = null;\n\t\tthis._NodesLayer = null;\n\t\tthis._ConnectionsLayer = null;\n\t\tthis._TethersLayer = null;\n\t\tthis._PanelsLayer = null;\n\n\t\tthis._DataManager = null;\n\t\tthis._ConnectionHandleManager = null;\n\t\tthis._RenderManager = null;\n\t\tthis._PortRenderer = null;\n\n\t\tthis._InteractionManager = null;\n\t\tthis._ConnectionRenderer = null;\n\t\tthis._TetherService = null;\n\t\tthis._LayoutService = null;\n\t\tthis._PathGenerator = null;\n\t\tthis._ViewportManager = null;\n\t\tthis._SelectionManager = null;\n\t\tthis._PanelManager = null;\n\t\tthis._CSSProvider = null;\n\t\tthis._IconProvider = null;\n\t\tthis._ConnectorShapesProvider = null;\n\t\tthis._ThemeProvider = null;\n\t\tthis._NoiseProvider = null;\n\t\tthis._SVGHelperProvider = null;\n\t\tthis._GeometryProvider = null;\n\t\tthis._PanelChromeProvider = null;\n\t\tthis._NodeTypeProvider = null;\n\t\tthis._LayoutProvider = null;\n\t\tthis._EventHandlerProvider = null;\n\t\tthis._NodeView = null;\n\t\tthis._ToolbarView = null;\n\t\tthis._PropertiesPanelView = null;\n\n\t\tthis.initialRenderComplete = false;\n\t}\n\n\t_registerServiceTypes()\n\t{\n\t\tfor (let i = 0; i < this._ServiceRegistry.length; i++)\n\t\t{\n\t\t\tlet tmpEntry = this._ServiceRegistry[i];\n\t\t\tif (!this.fable.servicesMap.hasOwnProperty(tmpEntry.ServiceType))\n\t\t\t{\n\t\t\t\tthis.fable.addServiceType(tmpEntry.ServiceType, tmpEntry.Library);\n\t\t\t}\n\t\t}\n\t}\n\n\t_instantiateServices()\n\t{\n\t\tfor (let i = 0; i < this._ServiceRegistry.length; i++)\n\t\t{\n\t\t\tlet tmpEntry = this._ServiceRegistry[i];\n\t\t\tif (tmpEntry.RegisterOnly) continue;\n\t\t\tif (this[tmpEntry.Property]) continue;\n\n\t\t\tlet tmpOptions = tmpEntry.NoFlowView ? {} : { FlowView: this };\n\t\t\tif (typeof tmpEntry.ExtraOptions === 'function')\n\t\t\t{\n\t\t\t\tObject.assign(tmpOptions, tmpEntry.ExtraOptions());\n\t\t\t}\n\n\t\t\tthis[tmpEntry.Property] = this.fable.instantiateServiceProviderWithoutRegistration(tmpEntry.ServiceType, tmpOptions);\n\n\t\t\tif (tmpEntry.PostInit && typeof this[tmpEntry.Property][tmpEntry.PostInit] === 'function')\n\t\t\t{\n\t\t\t\tthis[tmpEntry.Property][tmpEntry.PostInit]();\n\t\t\t}\n\t\t}\n\t}\n\n\tget flowData()\n\t{\n\t\treturn this._FlowData;\n\t}\n\n\tget viewState()\n\t{\n\t\treturn this._FlowData.ViewState;\n\t}\n\n\t// Backward-compatible getter for InteractionManager direct access\n\tget _IsFullscreen()\n\t{\n\t\treturn this._ViewportManager ? this._ViewportManager._IsFullscreen : false;\n\t}\n\n\t/**\n\t * Override render to pass view options as the template record,\n\t * so template expressions like {~D:Record.ViewIdentifier~} resolve correctly.\n\t */\n\trender(pRenderableHash, pRenderDestinationAddress)\n\t{\n\t\treturn super.render(pRenderableHash, pRenderDestinationAddress, this.options);\n\t}\n\n\trenderAsync(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback)\n\t{\n\t\t// If no record address is explicitly provided, use this.options as the record\n\t\tif (typeof pTemplateRecordAddress === 'function' || typeof pTemplateRecordAddress === 'undefined')\n\t\t{\n\t\t\treturn super.renderAsync(pRenderableHash, pRenderDestinationAddress, this.options, pRootRenderable, pTemplateRecordAddress || fCallback);\n\t\t}\n\t\treturn super.renderAsync(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback);\n\t}\n\n\tonBeforeInitialize()\n\t{\n\t\tsuper.onBeforeInitialize();\n\n\t\t// Theme + Noise must be created first (CSS PostInit depends on theme state)\n\t\tthis._ThemeProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowTheme', { FlowView: this });\n\t\tthis._NoiseProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowNoise');\n\n\t\t// Apply initial theme from options\n\t\tif (this.options.Theme)\n\t\t{\n\t\t\tthis._ThemeProvider.setTheme(this.options.Theme);\n\t\t}\n\t\tif (typeof this.options.NoiseLevel === 'number')\n\t\t{\n\t\t\tthis._ThemeProvider.setNoiseLevel(this.options.NoiseLevel);\n\t\t}\n\n\t\t// Instantiate all remaining services (skips Theme + Noise since already set)\n\t\tthis._instantiateServices();\n\n\t\treturn super.onBeforeInitialize();\n\t}\n\n\tonAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)\n\t{\n\t\tif (!this.initialRenderComplete)\n\t\t{\n\t\t\tthis.onAfterInitialRender();\n\t\t\tthis.initialRenderComplete = true;\n\t\t}\n\t\treturn super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);\n\t}\n\n\tonAfterInitialRender()\n\t{\n\t\tlet tmpViewIdentifier = this.options.ViewIdentifier;\n\n\t\t// Grab SVG DOM elements\n\t\tlet tmpSVGElements = this.pict.ContentAssignment.getElement(`#Flow-SVG-${tmpViewIdentifier}`);\n\t\tif (tmpSVGElements.length < 1)\n\t\t{\n\t\t\tthis.log.error(`PictSectionFlow could not find SVG element #Flow-SVG-${tmpViewIdentifier}`);\n\t\t\treturn false;\n\t\t}\n\t\tthis._SVGElement = tmpSVGElements[0];\n\n\t\tlet tmpViewportElements = this.pict.ContentAssignment.getElement(`#Flow-Viewport-${tmpViewIdentifier}`);\n\t\tif (tmpViewportElements.length > 0)\n\t\t{\n\t\t\tthis._ViewportElement = tmpViewportElements[0];\n\t\t}\n\n\t\tlet tmpNodesElements = this.pict.ContentAssignment.getElement(`#Flow-Nodes-${tmpViewIdentifier}`);\n\t\tif (tmpNodesElements.length > 0)\n\t\t{\n\t\t\tthis._NodesLayer = tmpNodesElements[0];\n\t\t}\n\n\t\tlet tmpConnectionsElements = this.pict.ContentAssignment.getElement(`#Flow-Connections-${tmpViewIdentifier}`);\n\t\tif (tmpConnectionsElements.length > 0)\n\t\t{\n\t\t\tthis._ConnectionsLayer = tmpConnectionsElements[0];\n\t\t}\n\n\t\tlet tmpTethersElements = this.pict.ContentAssignment.getElement(`#Flow-Tethers-${tmpViewIdentifier}`);\n\t\tif (tmpTethersElements.length > 0)\n\t\t{\n\t\t\tthis._TethersLayer = tmpTethersElements[0];\n\t\t}\n\n\t\tlet tmpPanelsElements = this.pict.ContentAssignment.getElement(`#Flow-Panels-${tmpViewIdentifier}`);\n\t\tif (tmpPanelsElements.length > 0)\n\t\t{\n\t\t\tthis._PanelsLayer = tmpPanelsElements[0];\n\t\t}\n\n\t\t// Ensure all services are initialized (fallback if onBeforeInitialize was skipped)\n\t\tthis._instantiateServices();\n\n\t\t// Inject marker defs via the connector shapes provider\n\t\t// Note: insertAdjacentHTML does not work on SVG elements (wrong namespace),\n\t\t// so we parse via a temporary <svg> element to ensure SVG namespace.\n\t\tif (this._ConnectorShapesProvider && this._SVGElement)\n\t\t{\n\t\t\tlet tmpDefs = this._SVGElement.querySelector('defs');\n\t\t\tif (tmpDefs)\n\t\t\t{\n\t\t\t\tlet tmpMarkerMarkup = this._ConnectorShapesProvider.generateMarkerDefs(tmpViewIdentifier);\n\t\t\t\tlet tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\t\t\t\ttmpTempSVG.innerHTML = tmpMarkerMarkup;\n\t\t\t\twhile (tmpTempSVG.firstChild)\n\t\t\t\t{\n\t\t\t\t\ttmpDefs.appendChild(tmpTempSVG.firstChild);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Setup the toolbar if enabled\n\t\tif (this.options.EnableToolbar)\n\t\t{\n\t\t\tthis._ToolbarView = this.fable.instantiateServiceProviderWithoutRegistration('PictViewFlowToolbar',\n\t\t\t\tObject.assign({},\n\t\t\t\t\tlibPictViewFlowToolbar.default_configuration,\n\t\t\t\t\t{\n\t\t\t\t\t\tViewIdentifier: `Flow-Toolbar-${tmpViewIdentifier}`,\n\t\t\t\t\t\tDefaultDestinationAddress: `#Flow-Toolbar-${tmpViewIdentifier}`,\n\t\t\t\t\t\tFlowViewIdentifier: tmpViewIdentifier,\n\t\t\t\t\t\tEnableAddNode: this.options.EnableAddNode,\n\t\t\t\t\t\tEnableCardPalette: this.options.EnableCardPalette\n\t\t\t\t\t}\n\t\t\t\t));\n\t\t\t// Use the toolbar's render method after it's set up\n\t\t\tif (this._ToolbarView && typeof this._ToolbarView.render === 'function')\n\t\t\t{\n\t\t\t\tthis._ToolbarView._FlowView = this;\n\t\t\t\tthis._ToolbarView.render();\n\t\t\t}\n\t\t}\n\n\t\t// Setup the node renderer\n\t\tthis._NodeView = this.fable.instantiateServiceProviderWithoutRegistration('PictViewFlowNode',\n\t\t\tObject.assign({},\n\t\t\t\tlibPictViewFlowNode.default_configuration,\n\t\t\t\t{\n\t\t\t\t\tViewIdentifier: `Flow-NodeRenderer-${tmpViewIdentifier}`,\n\t\t\t\t\tAutoRender: false\n\t\t\t\t}\n\t\t\t));\n\t\tthis._NodeView._FlowView = this;\n\n\t\t// Setup the properties panel renderer\n\t\tthis._PropertiesPanelView = this.fable.instantiateServiceProviderWithoutRegistration('PictViewFlowPropertiesPanel',\n\t\t\tObject.assign({},\n\t\t\t\tlibPictViewFlowPropertiesPanel.default_configuration,\n\t\t\t\t{\n\t\t\t\t\tViewIdentifier: `Flow-PropertiesPanel-${tmpViewIdentifier}`,\n\t\t\t\t\tAutoRender: false\n\t\t\t\t}\n\t\t\t));\n\t\tthis._PropertiesPanelView._FlowView = this;\n\n\t\t// Bind interaction events\n\t\tthis._InteractionManager.initialize(this._SVGElement, this._ViewportElement);\n\n\t\t// Load initial flow data if an address is configured\n\t\tif (this.options.FlowDataAddress)\n\t\t{\n\t\t\tthis.marshalToView();\n\t\t}\n\n\t\t// Render the initial flow\n\t\tthis.renderFlow();\n\t}\n\n\t// ---- Data Manager Delegations ----\n\n\tmarshalToView() { return this._DataManager.marshalToView(); }\n\tmarshalFromView() { return this._DataManager.marshalFromView(); }\n\tgetFlowData() { return this._DataManager.getFlowData(); }\n\tsetFlowData(pFlowData) { return this._DataManager.setFlowData(pFlowData); }\n\taddNode(pType, pX, pY, pTitle, pData) { return this._DataManager.addNode(pType, pX, pY, pTitle, pData); }\n\tremoveNode(pNodeHash) { return this._DataManager.removeNode(pNodeHash); }\n\taddConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData) { return this._DataManager.addConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData); }\n\tremoveConnection(pConnectionHash) { return this._DataManager.removeConnection(pConnectionHash); }\n\n\t/**\n\t * Select a node\n\t * @param {string|null} pNodeHash - Hash of the node to select, or null to deselect\n\t */\n\tselectNode(pNodeHash)\n\t{\n\t\treturn this._SelectionManager.selectNode(pNodeHash);\n\t}\n\n\t/**\n\t * Select a connection\n\t * @param {string|null} pConnectionHash - Hash of the connection to select, or null to deselect\n\t */\n\tselectConnection(pConnectionHash)\n\t{\n\t\treturn this._SelectionManager.selectConnection(pConnectionHash);\n\t}\n\n\t/**\n\t * Deselect all nodes and connections\n\t */\n\tdeselectAll()\n\t{\n\t\treturn this._SelectionManager.deselectAll();\n\t}\n\n\t/**\n\t * Delete the currently selected node or connection\n\t * @returns {boolean}\n\t */\n\tdeleteSelected()\n\t{\n\t\treturn this._SelectionManager.deleteSelected();\n\t}\n\n\t/**\n\t * Update the viewport transform (pan and zoom)\n\t */\n\tupdateViewportTransform()\n\t{\n\t\treturn this._ViewportManager.updateViewportTransform();\n\t}\n\n\t/**\n\t * Set zoom level\n\t * @param {number} pZoom - The zoom level\n\t * @param {number} [pFocusX] - X coordinate to zoom toward (SVG space)\n\t * @param {number} [pFocusY] - Y coordinate to zoom toward (SVG space)\n\t */\n\tsetZoom(pZoom, pFocusX, pFocusY)\n\t{\n\t\treturn this._ViewportManager.setZoom(pZoom, pFocusX, pFocusY);\n\t}\n\n\t/**\n\t * Zoom to fit all nodes in the viewport\n\t */\n\tzoomToFit()\n\t{\n\t\treturn this._ViewportManager.zoomToFit();\n\t}\n\n\t/**\n\t * Apply auto-layout to all nodes\n\t */\n\tautoLayout()\n\t{\n\t\tif (this._LayoutService)\n\t\t{\n\t\t\tthis._LayoutService.autoLayout(this._FlowData.Nodes, this._FlowData.Connections);\n\t\t\tthis.renderFlow();\n\t\t\tthis.marshalFromView();\n\n\t\t\tif (this._EventHandlerProvider)\n\t\t\t{\n\t\t\t\tthis._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Toggle fullscreen mode on the flow editor container.\n\t * Uses a CSS fixed-position overlay instead of the Fullscreen API.\n\t * @returns {boolean} The new fullscreen state\n\t */\n\ttoggleFullscreen()\n\t{\n\t\treturn this._ViewportManager.toggleFullscreen();\n\t}\n\n\t/**\n\t * Exit fullscreen mode if currently active.\n\t */\n\texitFullscreen()\n\t{\n\t\treturn this._ViewportManager.exitFullscreen();\n\t}\n\n\t// ── Theme API ────────────────────────────────────────────────────────\n\n\t/**\n\t * Switch the active theme and re-render.\n\t * @param {string} pThemeKey - Theme key (e.g. 'default', 'sketch', 'blueprint', 'mono', 'retro-80s', 'retro-90s')\n\t */\n\tsetTheme(pThemeKey)\n\t{\n\t\tif (!this._ThemeProvider)\n\t\t{\n\t\t\tthis.log.warn('PictSectionFlow setTheme: ThemeProvider not available');\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpApplied = this._ThemeProvider.setTheme(pThemeKey);\n\t\tif (!tmpApplied) return;\n\n\t\t// Re-register CSS with the new theme overrides\n\t\tif (this._CSSProvider)\n\t\t{\n\t\t\tthis._CSSProvider.registerCSS();\n\t\t}\n\n\t\t// Re-inject marker defs (arrowhead colors may have changed)\n\t\tthis._reinjectMarkerDefs();\n\n\t\t// Full re-render\n\t\tif (this.initialRenderComplete)\n\t\t{\n\t\t\tthis.renderFlow();\n\t\t}\n\n\t\tif (this._EventHandlerProvider)\n\t\t{\n\t\t\tthis._EventHandlerProvider.fireEvent('onThemeChanged', pThemeKey);\n\t\t}\n\t}\n\n\t/**\n\t * Set the noise level (0 to 1) and re-render.\n\t * @param {number} pLevel - 0 = precise, 1 = maximum wobble\n\t */\n\tsetNoiseLevel(pLevel)\n\t{\n\t\tif (!this._ThemeProvider)\n\t\t{\n\t\t\tthis.log.warn('PictSectionFlow setNoiseLevel: ThemeProvider not available');\n\t\t\treturn;\n\t\t}\n\n\t\tthis._ThemeProvider.setNoiseLevel(pLevel);\n\n\t\t// Full re-render to apply new noise\n\t\tif (this.initialRenderComplete)\n\t\t{\n\t\t\tthis.renderFlow();\n\t\t}\n\t}\n\n\t/**\n\t * Get the current noise level (0 to 1).\n\t * @returns {number}\n\t */\n\tgetNoiseLevel()\n\t{\n\t\tif (this._ThemeProvider)\n\t\t{\n\t\t\treturn this._ThemeProvider.getNoiseLevel();\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get the active theme key.\n\t * @returns {string}\n\t */\n\tgetThemeKey()\n\t{\n\t\tif (this._ThemeProvider)\n\t\t{\n\t\t\treturn this._ThemeProvider.getActiveThemeKey();\n\t\t}\n\t\treturn 'default';\n\t}\n\n\t_reinjectMarkerDefs() { return this._RenderManager.reinjectMarkerDefs(); }\n\n\t/**\n\t * Get a node by hash\n\t * @param {string} pNodeHash\n\t * @returns {Object|null}\n\t */\n\tgetNode(pNodeHash)\n\t{\n\t\treturn this._FlowData.Nodes.find((pNode) => pNode.Hash === pNodeHash) || null;\n\t}\n\n\t/**\n\t * Get a connection by hash\n\t * @param {string} pConnectionHash\n\t * @returns {Object|null}\n\t */\n\tgetConnection(pConnectionHash)\n\t{\n\t\treturn this._FlowData.Connections.find((pConn) => pConn.Hash === pConnectionHash) || null;\n\t}\n\n\t/**\n\t * Select a tether by its panel hash.\n\t * @param {string|null} pPanelHash - Hash of the panel whose tether to select, or null to deselect\n\t */\n\tselectTether(pPanelHash)\n\t{\n\t\treturn this._SelectionManager.selectTether(pPanelHash);\n\t}\n\n\t// ---- Connection Handle Manager Delegations ----\n\n\tupdateConnectionHandle(pConnectionHash, pHandleType, pX, pY) { return this._ConnectionHandleManager.updateConnectionHandle(pConnectionHash, pHandleType, pX, pY); }\n\taddConnectionHandle(pConnectionHash, pX, pY) { return this._ConnectionHandleManager.addConnectionHandle(pConnectionHash, pX, pY); }\n\tremoveConnectionHandle(pConnectionHash, pIndex) { return this._ConnectionHandleManager.removeConnectionHandle(pConnectionHash, pIndex); }\n\t_resetHandlesForNode(pNodeHash) { return this._ConnectionHandleManager.resetHandlesForNode(pNodeHash); }\n\t_resetHandlesForPanel(pPanelHash) { return this._ConnectionHandleManager.resetHandlesForPanel(pPanelHash); }\n\n\t/**\n\t * Add a bezier handle to a tether at the specified SVG position.\n\t * @param {string} pPanelHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\taddTetherHandle(pPanelHash, pX, pY)\n\t{\n\t\tlet tmpPanel = this._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel || !this._TetherService) return;\n\n\t\tlet tmpNode = this.getNode(tmpPanel.NodeHash);\n\t\tif (!tmpNode) return;\n\n\t\tlet tmpAnchors = this._TetherService.getSmartAnchors(tmpPanel, tmpNode);\n\n\t\tthis._TetherService.addHandle(tmpPanel, pX, pY, tmpAnchors.panelAnchor, tmpAnchors.nodeAnchor);\n\n\t\tthis.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._EventHandlerProvider)\n\t\t{\n\t\t\tthis._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Remove a bezier handle from a tether by index.\n\t * @param {string} pPanelHash\n\t * @param {number} pIndex\n\t */\n\tremoveTetherHandle(pPanelHash, pIndex)\n\t{\n\t\tlet tmpPanel = this._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel || !this._TetherService) return;\n\n\t\tthis._TetherService.removeHandle(tmpPanel, pIndex);\n\n\t\tthis.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._EventHandlerProvider)\n\t\t{\n\t\t\tthis._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Update a tether handle position during drag (for real-time feedback).\n\t * Delegates state update to the TetherService.\n\t * @param {string} pPanelHash\n\t * @param {string} pHandleType\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdateTetherHandle(pPanelHash, pHandleType, pX, pY)\n\t{\n\t\tlet tmpPanel = this._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tif (this._TetherService)\n\t\t{\n\t\t\tthis._TetherService.updateHandlePosition(tmpPanel, pHandleType, pX, pY);\n\t\t}\n\n\t\tthis._renderSingleTether(pPanelHash);\n\t}\n\n\t/**\n\t * Get a port's absolute position in SVG coordinates.\n\t *\n\t * For left and right side ports, positioning is offset below the title bar\n\t * so that connection endpoints match the rendered port circles.\n\t *\n\t * @param {string} pNodeHash\n\t * @param {string} pPortHash\n\t * @returns {{x: number, y: number, side: string}|null}\n\t */\n\tgetPortPosition(pNodeHash, pPortHash)\n\t{\n\t\tlet tmpNode = this.getNode(pNodeHash);\n\t\tif (!tmpNode) return null;\n\n\t\tlet tmpPort = tmpNode.Ports.find((p) => p.Hash === pPortHash);\n\t\tif (!tmpPort) return null;\n\n\t\t// Count ports on the same side (matching both direction and side)\n\t\tlet tmpSameSidePorts = tmpNode.Ports.filter((p) => p.Side === tmpPort.Side);\n\t\tlet tmpPortIndex = tmpSameSidePorts.indexOf(tmpPort);\n\t\tlet tmpPortCount = tmpSameSidePorts.length;\n\n\t\tlet tmpTitleBarHeight = (this._NodeView && this._NodeView.options.NodeTitleBarHeight) || 28;\n\n\t\t// Use the adjusted node height that accounts for minimum port\n\t\t// spacing. Connections render before nodes, so the node renderer\n\t\t// may not have written back its adjusted height yet.\n\t\tlet tmpHeight = tmpNode.Height || 80;\n\t\tif (this._GeometryProvider && tmpNode.Ports && tmpNode.Ports.length > 0)\n\t\t{\n\t\t\tlet tmpMinHeight = this._GeometryProvider.computeMinimumNodeHeight(tmpNode.Ports, tmpTitleBarHeight);\n\t\t\tif (tmpMinHeight > tmpHeight)\n\t\t\t{\n\t\t\t\ttmpHeight = tmpMinHeight;\n\t\t\t}\n\t\t}\n\n\t\t// Build port counts map for adaptive zone sizing\n\t\tlet tmpPortCountsBySide = this._GeometryProvider.buildPortCountsBySide(tmpNode.Ports);\n\n\t\tlet tmpLocal = this._GeometryProvider.getPortLocalPosition(tmpPort.Side, tmpPortIndex, tmpPortCount, tmpNode.Width, tmpHeight, tmpTitleBarHeight, tmpPortCountsBySide);\n\n\t\treturn { x: tmpNode.X + tmpLocal.x, y: tmpNode.Y + tmpLocal.y, side: tmpPort.Side || 'right' };\n\t}\n\n\t/**\n\t * Convert screen coordinates to SVG viewport coordinates\n\t * @param {number} pScreenX\n\t * @param {number} pScreenY\n\t * @returns {{x: number, y: number}}\n\t */\n\tscreenToSVGCoords(pScreenX, pScreenY)\n\t{\n\t\treturn this._ViewportManager.screenToSVGCoords(pScreenX, pScreenY);\n\t}\n\n\t// ---- Render Manager Delegations ----\n\n\trenderFlow() { return this._RenderManager.renderFlow(); }\n\t_renderSingleConnection(pConnectionHash) { return this._RenderManager.renderSingleConnection(pConnectionHash); }\n\t_renderSingleTether(pPanelHash) { return this._RenderManager.renderSingleTether(pPanelHash); }\n\tupdateNodePosition(pNodeHash, pX, pY) { return this._RenderManager.updateNodePosition(pNodeHash, pX, pY); }\n\t_renderConnectionsForNode(pNodeHash) { return this._RenderManager.renderConnectionsForNode(pNodeHash); }\n\t_renderTethersForNode(pNodeHash) { return this._RenderManager.renderTethersForNode(pNodeHash); }\n\n\t// ---- Properties Panel Management ----\n\n\t/**\n\t * Open a properties panel for a node.\n\t * @param {string} pNodeHash - The hash of the node to open a panel for\n\t * @returns {Object|false} The panel data, or false if the node has no PropertiesPanel config\n\t */\n\topenPanel(pNodeHash)\n\t{\n\t\treturn this._PanelManager.openPanel(pNodeHash);\n\t}\n\n\t/**\n\t * Close a properties panel by panel hash.\n\t * @param {string} pPanelHash\n\t * @returns {boolean}\n\t */\n\tclosePanel(pPanelHash)\n\t{\n\t\treturn this._PanelManager.closePanel(pPanelHash);\n\t}\n\n\t/**\n\t * Close all panels for a given node.\n\t * @param {string} pNodeHash\n\t * @returns {boolean}\n\t */\n\tclosePanelForNode(pNodeHash)\n\t{\n\t\treturn this._PanelManager.closePanelForNode(pNodeHash);\n\t}\n\n\t/**\n\t * Toggle a properties panel for a node (open if closed, close if open).\n\t * @param {string} pNodeHash\n\t * @returns {Object|false}\n\t */\n\ttogglePanel(pNodeHash)\n\t{\n\t\treturn this._PanelManager.togglePanel(pNodeHash);\n\t}\n\n\t/**\n\t * Update a panel's position (for drag).\n\t * @param {string} pPanelHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdatePanelPosition(pPanelHash, pX, pY)\n\t{\n\t\treturn this._PanelManager.updatePanelPosition(pPanelHash, pX, pY);\n\t}\n}\n\nmodule.exports = PictViewFlow;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n\n},{\"../PictFlowCardPropertiesPanel.js\":11,\"../panels/FlowCardPropertiesPanel-Form.js\":12,\"../panels/FlowCardPropertiesPanel-Markdown.js\":13,\"../panels/FlowCardPropertiesPanel-Template.js\":14,\"../panels/FlowCardPropertiesPanel-View.js\":15,\"../providers/PictProvider-Flow-CSS.js\":16,\"../providers/PictProvider-Flow-ConnectorShapes.js\":17,\"../providers/PictProvider-Flow-EventHandler.js\":18,\"../providers/PictProvider-Flow-Geometry.js\":19,\"../providers/PictProvider-Flow-Icons.js\":20,\"../providers/PictProvider-Flow-Layouts.js\":21,\"../providers/PictProvider-Flow-NodeTypes.js\":22,\"../providers/PictProvider-Flow-Noise.js\":23,\"../providers/PictProvider-Flow-PanelChrome.js\":24,\"../providers/PictProvider-Flow-SVGHelpers.js\":25,\"../providers/PictProvider-Flow-Theme.js\":26,\"../services/PictService-Flow-ConnectionHandleManager.js\":27,\"../services/PictService-Flow-ConnectionRenderer.js\":28,\"../services/PictService-Flow-DataManager.js\":29,\"../services/PictService-Flow-InteractionManager.js\":30,\"../services/PictService-Flow-Layout.js\":31,\"../services/PictService-Flow-PanelManager.js\":32,\"../services/PictService-Flow-PathGenerator.js\":33,\"../services/PictService-Flow-PortRenderer.js\":34,\"../services/PictService-Flow-RenderManager.js\":35,\"../services/PictService-Flow-SelectionManager.js\":36,\"../services/PictService-Flow-Tether.js\":37,\"../services/PictService-Flow-ViewportManager.js\":38,\"./PictView-Flow-FloatingToolbar.js\":39,\"./PictView-Flow-Node.js\":40,\"./PictView-Flow-PropertiesPanel.js\":41,\"./PictView-Flow-Toolbar.js\":42,\"pict-view\":45}],44:[function(require,module,exports){\nmodule.exports={\n \"name\": \"pict-view\",\n \"version\": \"1.0.67\",\n \"description\": \"Pict View Base Class\",\n \"main\": \"source/Pict-View.js\",\n \"scripts\": {\n \"test\": \"npx quack test\",\n \"tests\": \"npx quack test -g\",\n \"start\": \"node source/Pict-View.js\",\n \"coverage\": \"npx quack coverage\",\n \"build\": \"npx quack build\",\n \"docker-dev-build\": \"docker build ./ -f Dockerfile_LUXURYCode -t pict-view-image:local\",\n \"docker-dev-run\": \"docker run -it -d --name pict-view-dev -p 30001:8080 -p 38086:8086 -v \\\"$PWD/.config:/home/coder/.config\\\" -v \\\"$PWD:/home/coder/pict-view\\\" -u \\\"$(id -u):$(id -g)\\\" -e \\\"DOCKER_USER=$USER\\\" pict-view-image:local\",\n \"docker-dev-shell\": \"docker exec -it pict-view-dev /bin/bash\",\n \"types\": \"tsc -p .\",\n \"lint\": \"eslint source/**\"\n },\n \"types\": \"types/source/Pict-View.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/stevenvelozo/pict-view.git\"\n },\n \"author\": \"steven velozo <steven@velozo.com>\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/stevenvelozo/pict-view/issues\"\n },\n \"homepage\": \"https://github.com/stevenvelozo/pict-view#readme\",\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.1\",\n \"browser-env\": \"^3.3.0\",\n \"eslint\": \"^9.39.1\",\n \"pict\": \"^1.0.348\",\n \"quackage\": \"^1.0.58\",\n \"typescript\": \"^5.9.3\"\n },\n \"mocha\": {\n \"diff\": true,\n \"extension\": [\n \"js\"\n ],\n \"package\": \"./package.json\",\n \"reporter\": \"spec\",\n \"slow\": \"75\",\n \"timeout\": \"5000\",\n \"ui\": \"tdd\",\n \"watch-files\": [\n \"source/**/*.js\",\n \"test/**/*.js\"\n ],\n \"watch-ignore\": [\n \"lib/vendor\"\n ]\n },\n \"dependencies\": {\n \"fable\": \"^3.1.63\",\n \"fable-serviceproviderbase\": \"^3.0.19\"\n }\n}\n\n},{}],45:[function(require,module,exports){\n\nconst libFableServiceBase = require('fable-serviceproviderbase');\n\nconst libPackage = require('../package.json');\n\nconst defaultPictViewSettings = (\n\t{\n\t\tDefaultRenderable: false,\n\t\tDefaultDestinationAddress: false,\n\t\tDefaultTemplateRecordAddress: false,\n\n\t\tViewIdentifier: false,\n\n\t\t// If this is set to true, when the App initializes this will.\n\t\t// After the App initializes, initialize will be called as soon as it's added.\n\t\tAutoInitialize: true,\n\t\tAutoInitializeOrdinal: 0,\n\n\t\t// If this is set to true, when the App autorenders (on load) this will.\n\t\t// After the App initializes, render will be called as soon as it's added.\n\t\tAutoRender: true,\n\t\tAutoRenderOrdinal: 0,\n\n\t\tAutoSolveWithApp: true,\n\t\tAutoSolveOrdinal: 0,\n\n\t\tCSSHash: false,\n\t\tCSS: false,\n\t\tCSSProvider: false,\n\t\tCSSPriority: 500,\n\n\t\tTemplates: [],\n\n\t\tDefaultTemplates: [],\n\n\t\tRenderables: [],\n\n\t\tManifests: {}\n\t});\n\n/** @typedef {(error?: Error) => void} ErrorCallback */\n/** @typedef {number | boolean} PictTimestamp */\n\n/**\n * @typedef {'replace' | 'append' | 'prepend' | 'append_once' | 'virtual-assignment'} RenderMethod\n */\n/**\n * @typedef {Object} Renderable\n *\n * @property {string} RenderableHash - A unique hash for the renderable.\n * @property {string} TemplateHash - The hash of the template to use for rendering this renderable.\n * @property {string} [DefaultTemplateRecordAddress] - The default address for resolving the data record for this renderable.\n * @property {string} [ContentDestinationAddress] - The default address (DOM CSS selector) for rendering the content of this renderable.\n * @property {RenderMethod} [RenderMethod=replace] - The method to use when projecting the renderable to the DOM ('replace', 'append', 'prepend', 'append_once', 'virtual-assignment').\n * @property {string} [TestAddress] - The address to use for testing the renderable.\n * @property {string} [TransactionHash] - The transaction hash for the root renderable.\n * @property {string} [RootRenderableViewHash] - The hash of the root renderable.\n * @property {string} [Content] - The rendered content for this renderable, if applicable.\n */\n\n/**\n * Represents a view in the Pict ecosystem.\n */\nclass PictView extends libFableServiceBase\n{\n\t/**\n\t * @param {any} pFable - The Fable object that this service is attached to.\n\t * @param {any} [pOptions] - (optional) The options for this service.\n\t * @param {string} [pServiceHash] - (optional) The hash of the service.\n\t */\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\t// Intersect default options, parent constructor, service information\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(defaultPictViewSettings)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\t\t//FIXME: add types to fable and ancillaries\n\t\t/** @type {any} */\n\t\tthis.fable;\n\t\t/** @type {any} */\n\t\tthis.options;\n\t\t/** @type {String} */\n\t\tthis.UUID;\n\t\t/** @type {String} */\n\t\tthis.Hash;\n\t\t/** @type {any} */\n\t\tthis.log;\n\n\t\tconst tmpHashIsUUID = this.Hash === this.UUID;\n\t\t//NOTE: since many places are using the view UUID as the HTML element ID, we prefix it to avoid starting with a number\n\t\tthis.UUID = `V-${this.UUID}`;\n\t\tif (tmpHashIsUUID)\n\t\t{\n\t\t\tthis.Hash = this.UUID;\n\t\t}\n\n\t\tif (!this.options.ViewIdentifier)\n\t\t{\n\t\t\tthis.options.ViewIdentifier = `AutoViewID-${this.fable.getUUID()}`;\n\t\t}\n\t\tthis.serviceType = 'PictView';\n\t\t/** @type {Record<string, any>} */\n\t\tthis._Package = libPackage;\n\t\t// Convenience and consistency naming\n\t\t/** @type {import('pict') & { log: any, instantiateServiceProviderWithoutRegistration: (hash: String) => any, instantiateServiceProviderIfNotExists: (hash: string) => any, TransactionTracking: import('pict/types/source/services/Fable-Service-TransactionTracking') }} */\n\t\tthis.pict = this.fable;\n\t\t// Wire in the essential Pict application state\n\t\tthis.AppData = this.pict.AppData;\n\t\tthis.Bundle = this.pict.Bundle;\n\n\t\t/** @type {PictTimestamp} */\n\t\tthis.initializeTimestamp = false;\n\t\t/** @type {PictTimestamp} */\n\t\tthis.lastSolvedTimestamp = false;\n\t\t/** @type {PictTimestamp} */\n\t\tthis.lastRenderedTimestamp = false;\n\t\t/** @type {PictTimestamp} */\n\t\tthis.lastMarshalFromViewTimestamp = false;\n\t\t/** @type {PictTimestamp} */\n\t\tthis.lastMarshalToViewTimestamp = false;\n\n\t\tthis.pict.instantiateServiceProviderIfNotExists('TransactionTracking');\n\n\t\t// Load all templates from the array in the options\n\t\t// Templates are in the form of {Hash:'Some-Template-Hash',Template:'Template content',Source:'TemplateSource'}\n\t\tfor (let i = 0; i < this.options.Templates.length; i++)\n\t\t{\n\t\t\tlet tmpTemplate = this.options.Templates[i];\n\n\t\t\tif (!('Hash' in tmpTemplate) || !('Template' in tmpTemplate))\n\t\t\t{\n\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not load Template ${i} in the options array.`, tmpTemplate);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!tmpTemplate.Source)\n\t\t\t\t{\n\t\t\t\t\ttmpTemplate.Source = `PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} options object.`;\n\t\t\t\t}\n\t\t\t\tthis.pict.TemplateProvider.addTemplate(tmpTemplate.Hash, tmpTemplate.Template, tmpTemplate.Source);\n\t\t\t}\n\t\t}\n\n\t\t// Load all default templates from the array in the options\n\t\t// Templates are in the form of {Prefix:'',Postfix:'-List-Row',Template:'Template content',Source:'TemplateSourceString'}\n\t\tfor (let i = 0; i < this.options.DefaultTemplates.length; i++)\n\t\t{\n\t\t\tlet tmpDefaultTemplate = this.options.DefaultTemplates[i];\n\n\t\t\tif (!('Postfix' in tmpDefaultTemplate) || !('Template' in tmpDefaultTemplate))\n\t\t\t{\n\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not load Default Template ${i} in the options array.`, tmpDefaultTemplate);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!tmpDefaultTemplate.Source)\n\t\t\t\t{\n\t\t\t\t\ttmpDefaultTemplate.Source = `PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} options object.`;\n\t\t\t\t}\n\t\t\t\tthis.pict.TemplateProvider.addDefaultTemplate(tmpDefaultTemplate.Prefix, tmpDefaultTemplate.Postfix, tmpDefaultTemplate.Template, tmpDefaultTemplate.Source);\n\t\t\t}\n\t\t}\n\n\t\t// Load the CSS if it's available\n\t\tif (this.options.CSS)\n\t\t{\n\t\t\tlet tmpCSSHash = this.options.CSSHash ? this.options.CSSHash : `View-${this.options.ViewIdentifier}`;\n\t\t\tlet tmpCSSProvider = this.options.CSSProvider ? this.options.CSSProvider : tmpCSSHash;\n\t\t\tthis.pict.CSSMap.addCSS(tmpCSSHash, this.options.CSS, tmpCSSProvider, this.options.CSSPriority);\n\t\t}\n\n\t\t// Load all renderables\n\t\t// Renderables are launchable renderable instructions with templates\n\t\t// They look as such: {Identifier:'ContentEntry', TemplateHash:'Content-Entry-Section-Main', ContentDestinationAddress:'#ContentSection', RecordAddress:'AppData.Content.DefaultText', ManifestTransformation:'ManyfestHash', ManifestDestinationAddress:'AppData.Content.DataToTransformContent'}\n\t\t// The only parts that are necessary are Identifier and Template\n\t\t// A developer can then do render('ContentEntry') and it just kinda works. Or they can override the ContentDestinationAddress\n\t\t/** @type {Record<String, Renderable>} */\n\t\tthis.renderables = {};\n\t\tfor (let i = 0; i < this.options.Renderables.length; i++)\n\t\t{\n\t\t\t/** @type {Renderable} */\n\t\t\tlet tmpRenderable = this.options.Renderables[i];\n\t\t\tthis.addRenderable(tmpRenderable);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a renderable to the view.\n\t *\n\t * @param {string | Renderable} pRenderableHash - The hash of the renderable, or a renderable object.\n\t * @param {string} [pTemplateHash] - (optional) The hash of the template for the renderable.\n\t * @param {string} [pDefaultTemplateRecordAddress] - (optional) The default data address for the template.\n\t * @param {string} [pDefaultDestinationAddress] - (optional) The default destination address for the renderable.\n\t * @param {RenderMethod} [pRenderMethod=replace] - (optional) The method to use when rendering the renderable (ex. 'replace').\n\t */\n\taddRenderable(pRenderableHash, pTemplateHash, pDefaultTemplateRecordAddress, pDefaultDestinationAddress, pRenderMethod)\n\t{\n\t\t/** @type {Renderable} */\n\t\tlet tmpRenderable;\n\n\t\tif (typeof(pRenderableHash) == 'object')\n\t\t{\n\t\t\t// The developer passed in the renderable as an object.\n\t\t\t// Use theirs instead!\n\t\t\ttmpRenderable = pRenderableHash;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/** @type {RenderMethod} */\n\t\t\tlet tmpRenderMethod = (typeof(pRenderMethod) !== 'string') ? pRenderMethod : 'replace';\n\t\t\ttmpRenderable = (\n\t\t\t\t{\n\t\t\t\t\tRenderableHash: pRenderableHash,\n\t\t\t\t\tTemplateHash: pTemplateHash,\n\t\t\t\t\tDefaultTemplateRecordAddress: pDefaultTemplateRecordAddress,\n\t\t\t\t\tContentDestinationAddress: pDefaultDestinationAddress,\n\t\t\t\t\tRenderMethod: tmpRenderMethod\n\t\t\t\t});\n\t\t}\n\n\t\tif ((typeof(tmpRenderable.RenderableHash) != 'string') || (typeof(tmpRenderable.TemplateHash) != 'string'))\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not load Renderable; RenderableHash or TemplateHash are invalid.`, tmpRenderable);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t{\n\t\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} adding renderable [${tmpRenderable.RenderableHash}] pointed to template ${tmpRenderable.TemplateHash}.`);\n\t\t\t}\n\n\t\t\tthis.renderables[tmpRenderable.RenderableHash] = tmpRenderable;\n\t\t}\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Initialization */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before the view is initialized.\n\t */\n\tonBeforeInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is initialized (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonBeforeInitializeAsync(fCallback)\n\t{\n\t\tthis.onBeforeInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when the view is initialized.\n\t */\n\tonInitialize()\n\t{\n\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when the view is initialized (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonInitializeAsync(fCallback)\n\t{\n\t\tthis.onInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Performs view initialization.\n\t */\n\tinitialize()\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow VIEW [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initialize:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tthis.onBeforeInitialize();\n\t\t\tthis.onInitialize();\n\t\t\tthis.onAfterInitialize();\n\t\t\tthis.initializeTimestamp = this.pict.log.getTimeStamp();\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initialize called but initialization is already completed. Aborting.`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Performs view initialization (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tinitializeAsync(fCallback)\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow VIEW [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initializeAsync:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t{\n\t\t\t\tthis.log.info(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} beginning initialization...`);\n\t\t\t}\n\n\t\t\ttmpAnticipate.anticipate(this.onBeforeInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onAfterInitializeAsync.bind(this));\n\n\t\t\ttmpAnticipate.wait(\n\t\t\t\t/** @param {Error} pError */\n\t\t\t\t(pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initialization failed: ${pError.message || pError}`, { stack: pError.stack });\n\t\t\t\t\t}\n\t\t\t\t\tthis.initializeTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.info(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initialization complete.`);\n\t\t\t\t\t}\n\t\t\t\t\treturn fCallback();\n\t\t\t\t});\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} async initialize called but initialization is already completed. Aborting.`);\n\t\t\t// TODO: Should this be an error?\n\t\t\treturn fCallback();\n\t\t}\n\t}\n\n\tonAfterInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is initialized (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonAfterInitializeAsync(fCallback)\n\t{\n\t\tthis.onAfterInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Render */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before the view is rendered.\n\t *\n\t * @param {Renderable} pRenderable - The renderable that will be rendered.\n\t */\n\tonBeforeRender(pRenderable)\n\t{\n\t\t// Overload this to mess with stuff before the content gets generated from the template\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is rendered (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that will be rendered.\n\t */\n\tonBeforeRenderAsync(fCallback, pRenderable)\n\t{\n\t\tthis.onBeforeRender(pRenderable);\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is projected into the DOM.\n\t *\n\t * @param {Renderable} pRenderable - The renderable that will be projected.\n\t */\n\tonBeforeProject(pRenderable)\n\t{\n\t\t// Overload this to mess with stuff before the content gets generated from the template\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeProject:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is projected into the DOM (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that will be projected.\n\t */\n\tonBeforeProjectAsync(fCallback, pRenderable)\n\t{\n\t\tthis.onBeforeProject(pRenderable);\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Builds the render options for a renderable.\n\t *\n\t * For DRY purposes on the three flavors of render.\n\t *\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object|ErrorCallback} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t */\n\tbuildRenderOptions(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\tlet tmpRenderOptions = {Valid: true};\n\t\ttmpRenderOptions.RenderableHash = (typeof (pRenderableHash) === 'string') ? pRenderableHash :\n\t\t\t\t\t\t\t\t(typeof (this.options.DefaultRenderable) == 'string') ?\n\t\t\t\t\t\t\t\tthis.options.DefaultRenderable : false;\n\t\tif (!tmpRenderOptions.RenderableHash)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not find a suitable RenderableHash ${tmpRenderOptions.RenderableHash} (param ${pRenderableHash}because it is not a valid renderable.`);\n\t\t\ttmpRenderOptions.Valid = false;\n\t\t}\n\n\t\ttmpRenderOptions.Renderable = this.renderables[tmpRenderOptions.RenderableHash];\n\t\tif (!tmpRenderOptions.Renderable)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderOptions.RenderableHash} (param ${pRenderableHash}) because it does not exist.`);\n\t\t\ttmpRenderOptions.Valid = false;\n\t\t}\n\n\t\ttmpRenderOptions.DestinationAddress = (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress :\n\t\t\t(typeof (tmpRenderOptions.Renderable.ContentDestinationAddress) === 'string') ? tmpRenderOptions.Renderable.ContentDestinationAddress :\n\t\t\t\t(typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : false;\n\t\tif (!tmpRenderOptions.DestinationAddress)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderOptions.RenderableHash} (param ${pRenderableHash}) because it does not have a valid destination address (param ${pRenderDestinationAddress}).`);\n\t\t\ttmpRenderOptions.Valid = false;\n\t\t}\n\n\t\tif (typeof(pTemplateRecordAddress) === 'object')\n\t\t{\n\t\t\ttmpRenderOptions.RecordAddress = 'Passed in as object';\n\t\t\ttmpRenderOptions.Record = pTemplateRecordAddress;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRenderOptions.RecordAddress = (typeof (pTemplateRecordAddress) === 'string') ? pTemplateRecordAddress :\n\t\t\t\t(typeof (tmpRenderOptions.Renderable.DefaultTemplateRecordAddress) === 'string') ? tmpRenderOptions.Renderable.DefaultTemplateRecordAddress :\n\t\t\t\t(typeof (this.options.DefaultTemplateRecordAddress) === 'string') ? this.options.DefaultTemplateRecordAddress : false;\n\t\t\ttmpRenderOptions.Record = (typeof (tmpRenderOptions.RecordAddress) === 'string') ? this.pict.DataProvider.getDataByAddress(tmpRenderOptions.RecordAddress) : undefined;\n\t\t}\n\n\t\treturn tmpRenderOptions;\n\t}\n\n\t/**\n\t * Assigns the content to the destination address.\n\t *\n\t * For DRY purposes on the three flavors of render.\n\t *\n\t * @param {Renderable} pRenderable - The renderable to render.\n\t * @param {string} pRenderDestinationAddress - The address where the renderable will be rendered.\n\t * @param {string} pContent - The content to render.\n\t * @returns {boolean} - Returns true if the content was assigned successfully.\n\t * @memberof PictView\n\t */\n\tassignRenderContent(pRenderable, pRenderDestinationAddress, pContent)\n\t{\n\t\treturn this.pict.ContentAssignment.projectContent(pRenderable.RenderMethod, pRenderDestinationAddress, pContent, pRenderable.TestAddress);\n\t}\n\n\t/**\n\t * Render a renderable from this view.\n\t *\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object} [pTemplateRecordAddress] - The address where the data for the template is stored.\n\t * @param {Renderable} [pRootRenderable] - The root renderable for the render operation, if applicable.\n\t * @return {boolean}\n\t */\n\trender(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable)\n\t{\n\t\treturn this.renderWithScope(this, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable);\n\t}\n\n\t/**\n\t * Render a renderable from this view, providing a specifici scope for the template.\n\t *\n\t * @param {any} pScope - The scope to use for the template rendering.\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object} [pTemplateRecordAddress] - The address where the data for the template is stored.\n\t * @param {Renderable} [pRootRenderable] - The root renderable for the render operation, if applicable.\n\t * @return {boolean}\n\t */\n\trenderWithScope(pScope, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable)\n\t{\n\t\tlet tmpRenderableHash = (typeof (pRenderableHash) === 'string') ? pRenderableHash :\n\t\t\t(typeof (this.options.DefaultRenderable) == 'string') ? this.options.DefaultRenderable : false;\n\t\tif (!tmpRenderableHash)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it is not a valid renderable.`);\n\t\t\treturn false;\n\t\t}\n\n\t\t/** @type {Renderable} */\n\t\tlet tmpRenderable;\n\t\tif (tmpRenderableHash == '__Virtual')\n\t\t{\n\t\t\ttmpRenderable = {\n\t\t\t\tRenderableHash: '__Virtual',\n\t\t\t\tTemplateHash: this.renderables[this.options.DefaultRenderable].TemplateHash,\n\t\t\t\tContentDestinationAddress: (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress :\n\t\t\t\t\t(typeof (tmpRenderable.ContentDestinationAddress) === 'string') ? tmpRenderable.ContentDestinationAddress :\n\t\t\t\t\t(typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : null,\n\t\t\t\tRenderMethod: 'virtual-assignment',\n\t\t\t\tTransactionHash: pRootRenderable && pRootRenderable.TransactionHash,\n\t\t\t\tRootRenderableViewHash: pRootRenderable && pRootRenderable.RootRenderableViewHash,\n\t\t\t};\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRenderable = Object.assign({}, this.renderables[tmpRenderableHash]);\n\t\t\ttmpRenderable.ContentDestinationAddress = (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress :\n\t\t\t\t(typeof (tmpRenderable.ContentDestinationAddress) === 'string') ? tmpRenderable.ContentDestinationAddress :\n\t\t\t\t(typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : null;\n\t\t}\n\n\t\tif (!tmpRenderable.TransactionHash)\n\t\t{\n\t\t\ttmpRenderable.TransactionHash = `ViewRender-V-${this.options.ViewIdentifier}-R-${tmpRenderableHash}-U-${this.pict.getUUID()}`;\n\t\t\ttmpRenderable.RootRenderableViewHash = this.Hash;\n\t\t\tthis.pict.TransactionTracking.registerTransaction(tmpRenderable.TransactionHash);\n\t\t}\n\n\t\tif (!tmpRenderable)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not exist.`);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!tmpRenderable.ContentDestinationAddress)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not have a valid destination address.`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpRecordAddress;\n\t\tlet tmpRecord;\n\n\t\tif (typeof(pTemplateRecordAddress) === 'object')\n\t\t{\n\t\t\ttmpRecord = pTemplateRecordAddress;\n\t\t\ttmpRecordAddress = 'Passed in as object';\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRecordAddress = (typeof (pTemplateRecordAddress) === 'string') ? pTemplateRecordAddress :\n\t\t\t\t(typeof (tmpRenderable.DefaultTemplateRecordAddress) === 'string') ? tmpRenderable.DefaultTemplateRecordAddress :\n\t\t\t\t\t(typeof (this.options.DefaultTemplateRecordAddress) === 'string') ? this.options.DefaultTemplateRecordAddress : false;\n\n\t\t\ttmpRecord = (typeof (tmpRecordAddress) === 'string') ? this.pict.DataProvider.getDataByAddress(tmpRecordAddress) : undefined;\n\t\t}\n\n\t\t// Execute the developer-overridable pre-render behavior\n\t\tthis.onBeforeRender(tmpRenderable);\n\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow VIEW [${this.UUID}]::[${this.Hash}] Renderable[${tmpRenderableHash}] Destination[${tmpRenderable.ContentDestinationAddress}] TemplateRecordAddress[${tmpRecordAddress}] render:`);\n\t\t}\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} Beginning Render of Renderable[${tmpRenderableHash}] to Destination [${tmpRenderable.ContentDestinationAddress}]...`);\n\t\t}\n\t\t// Generate the content output from the template and data\n\t\ttmpRenderable.Content = this.pict.parseTemplateByHash(tmpRenderable.TemplateHash, tmpRecord, null, [this], pScope, { RootRenderable: typeof pRootRenderable === 'object' ? pRootRenderable : tmpRenderable });\n\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} Assigning Renderable[${tmpRenderableHash}] content length ${tmpRenderable.Content.length} to Destination [${tmpRenderable.ContentDestinationAddress}] using render method [${tmpRenderable.RenderMethod}].`);\n\t\t}\n\n\t\tthis.onBeforeProject(tmpRenderable);\n\t\tthis.onProject(tmpRenderable);\n\n\t\tif (tmpRenderable.RenderMethod !== 'virtual-assignment')\n\t\t{\n\t\t\tthis.onAfterProject(tmpRenderable);\n\n\t\t\t// Execute the developer-overridable post-render behavior\n\t\t\tthis.onAfterRender(tmpRenderable);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Render a renderable from this view.\n\t *\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object|ErrorCallback} [pTemplateRecordAddress] - The address where the data for the template is stored.\n\t * @param {Renderable|ErrorCallback} [pRootRenderable] - The root renderable for the render operation, if applicable.\n\t * @param {ErrorCallback} [fCallback] - The callback to call when the async operation is complete.\n\t *\n\t * @return {void}\n\t */\n\trenderAsync(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback)\n\t{\n\t\treturn this.renderWithScopeAsync(this, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback);\n\t}\n\n\t/**\n\t * Render a renderable from this view.\n\t *\n\t * @param {any} pScope - The scope to use for the template rendering.\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object|ErrorCallback} [pTemplateRecordAddress] - The address where the data for the template is stored.\n\t * @param {Renderable|ErrorCallback} [pRootRenderable] - The root renderable for the render operation, if applicable.\n\t * @param {ErrorCallback} [fCallback] - The callback to call when the async operation is complete.\n\t *\n\t * @return {void}\n\t */\n\trenderWithScopeAsync(pScope, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback)\n\t{\n\t\tlet tmpRenderableHash = (typeof (pRenderableHash) === 'string') ? pRenderableHash :\n\t\t\t(typeof (this.options.DefaultRenderable) == 'string') ? this.options.DefaultRenderable : false;\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback :\n\t\t\t\t\t\t\t(typeof(pTemplateRecordAddress) === 'function') ? pTemplateRecordAddress :\n\t\t\t\t\t\t\t(typeof(pRenderDestinationAddress) === 'function') ? pRenderDestinationAddress :\n\t\t\t\t\t\t\t(typeof(pRenderableHash) === 'function') ? pRenderableHash :\n\t\t\t\t\t\t\t(typeof(pRootRenderable) === 'function') ? pRootRenderable :\n\t\t\t\t\t\t\tnull;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tif (!tmpRenderableHash)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not asynchronously render ${tmpRenderableHash} (param ${pRenderableHash}because it is not a valid renderable.`);\n\t\t\treturn tmpCallback(new Error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not asynchronously render ${tmpRenderableHash} (param ${pRenderableHash}because it is not a valid renderable.`));\n\t\t}\n\n\t\t/** @type {Renderable} */\n\t\tlet tmpRenderable;\n\t\tif (tmpRenderableHash == '__Virtual')\n\t\t{\n\t\t\ttmpRenderable = {\n\t\t\t\tRenderableHash: '__Virtual',\n\t\t\t\tTemplateHash: this.renderables[this.options.DefaultRenderable].TemplateHash,\n\t\t\t\tContentDestinationAddress: (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress : (typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : null,\n\t\t\t\tRenderMethod: 'virtual-assignment',\n\t\t\t\tTransactionHash: pRootRenderable && typeof pRootRenderable !== 'function' && pRootRenderable.TransactionHash,\n\t\t\t\tRootRenderableViewHash: pRootRenderable && typeof pRootRenderable !== 'function' && pRootRenderable.RootRenderableViewHash,\n\t\t\t};\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRenderable = Object.assign({}, this.renderables[tmpRenderableHash]);\n\t\t\ttmpRenderable.ContentDestinationAddress = (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress :\n\t\t\t\t(typeof (tmpRenderable.ContentDestinationAddress) === 'string') ? tmpRenderable.ContentDestinationAddress :\n\t\t\t\t(typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : null;\n\t\t}\n\n\t\tif (!tmpRenderable.TransactionHash)\n\t\t{\n\t\t\ttmpRenderable.TransactionHash = `ViewRender-V-${this.options.ViewIdentifier}-R-${tmpRenderableHash}-U-${this.pict.getUUID()}`;\n\t\t\ttmpRenderable.RootRenderableViewHash = this.Hash;\n\t\t\tthis.pict.TransactionTracking.registerTransaction(tmpRenderable.TransactionHash);\n\t\t}\n\n\t\tif (!tmpRenderable)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not exist.`);\n\t\t\treturn tmpCallback(new Error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not exist.`));\n\t\t}\n\n\t\tif (!tmpRenderable.ContentDestinationAddress)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not have a valid destination address.`);\n\t\t\treturn tmpCallback(new Error(`Could not render ${tmpRenderableHash}`));\n\t\t}\n\n\t\tlet tmpRecordAddress;\n\t\tlet tmpRecord;\n\n\t\tif (typeof(pTemplateRecordAddress) === 'object')\n\t\t{\n\t\t\ttmpRecord = pTemplateRecordAddress;\n\t\t\ttmpRecordAddress = 'Passed in as object';\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRecordAddress = (typeof (pTemplateRecordAddress) === 'string') ? pTemplateRecordAddress :\n\t\t\t\t(typeof (tmpRenderable.DefaultTemplateRecordAddress) === 'string') ? tmpRenderable.DefaultTemplateRecordAddress :\n\t\t\t\t\t(typeof (this.options.DefaultTemplateRecordAddress) === 'string') ? this.options.DefaultTemplateRecordAddress : false;\n\n\t\t\ttmpRecord = (typeof (tmpRecordAddress) === 'string') ? this.pict.DataProvider.getDataByAddress(tmpRecordAddress) : undefined;\n\t\t}\n\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow VIEW [${this.UUID}]::[${this.Hash}] Renderable[${tmpRenderableHash}] Destination[${tmpRenderable.ContentDestinationAddress}] TemplateRecordAddress[${tmpRecordAddress}] renderAsync:`);\n\t\t}\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} Beginning Asynchronous Render (callback-style)...`);\n\t\t}\n\n\t\tlet tmpAnticipate = this.fable.newAnticipate();\n\n\t\ttmpAnticipate.anticipate(\n\t\t\t(fOnBeforeRenderCallback) =>\n\t\t\t{\n\t\t\t\tthis.onBeforeRenderAsync(fOnBeforeRenderCallback, tmpRenderable);\n\t\t\t});\n\n\t\ttmpAnticipate.anticipate(\n\t\t\t(fAsyncTemplateCallback) =>\n\t\t\t{\n\t\t\t\t// Render the template (asynchronously)\n\t\t\t\tthis.pict.parseTemplateByHash(tmpRenderable.TemplateHash, tmpRecord,\n\t\t\t\t\t(pError, pContent) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pError)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render (asynchronously) ${tmpRenderableHash} (param ${pRenderableHash}) because it did not parse the template.`, pError);\n\t\t\t\t\t\t\treturn fAsyncTemplateCallback(pError);\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttmpRenderable.Content = pContent;\n\n\t\t\t\t\t\treturn fAsyncTemplateCallback();\n\t\t\t\t\t}, [this], pScope, { RootRenderable: typeof pRootRenderable === 'object' ? pRootRenderable : tmpRenderable });\n\t\t\t});\n\n\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t{\n\t\t\tthis.onBeforeProjectAsync(fNext, tmpRenderable);\n\t\t});\n\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t{\n\t\t\tthis.onProjectAsync(fNext, tmpRenderable);\n\t\t});\n\n\t\tif (tmpRenderable.RenderMethod !== 'virtual-assignment')\n\t\t{\n\t\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t\t{\n\t\t\t\tthis.onAfterProjectAsync(fNext, tmpRenderable);\n\t\t\t});\n\n\t\t\t// Execute the developer-overridable post-render behavior\n\t\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t\t{\n\t\t\t\tthis.onAfterRenderAsync(fNext, tmpRenderable);\n\t\t\t});\n\t\t}\n\n\t\ttmpAnticipate.wait(tmpCallback);\n\t}\n\n\t/**\n\t * Renders the default renderable.\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\trenderDefaultAsync(fCallback)\n\t{\n\t\t// Render the default renderable\n\t\tthis.renderAsync(fCallback);\n\t}\n\n\t/**\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t */\n\tbasicRender(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\treturn this.basicRenderWithScope(this, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress);\n\t}\n\n\t/**\n\t * @param {any} pScope - The scope to use for the template rendering.\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t */\n\tbasicRenderWithScope(pScope, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\tlet tmpRenderOptions = this.buildRenderOptions(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress);\n\t\tif (tmpRenderOptions.Valid)\n\t\t{\n\t\t\tthis.assignRenderContent(tmpRenderOptions.Renderable, tmpRenderOptions.DestinationAddress, this.pict.parseTemplateByHash(tmpRenderOptions.Renderable.TemplateHash, tmpRenderOptions.Record, null, [this], pScope, { RootRenderable: tmpRenderOptions.Renderable }));\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not perform a basic render of ${tmpRenderOptions.RenderableHash} because it is not valid.`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|Object|ErrorCallback} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t * @param {ErrorCallback} [fCallback] - The callback to call when the async operation is complete.\n\t */\n\tbasicRenderAsync(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, fCallback)\n\t{\n\t\treturn this.basicRenderWithScopeAsync(this, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, fCallback);\n\t}\n\n\t/**\n\t * @param {any} pScope - The scope to use for the template rendering.\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|Object|ErrorCallback} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t * @param {ErrorCallback} [fCallback] - The callback to call when the async operation is complete.\n\t */\n\tbasicRenderWithScopeAsync(pScope, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, fCallback)\n\t{\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback :\n\t\t\t\t\t\t\t(typeof(pTemplateRecordAddress) === 'function') ? pTemplateRecordAddress :\n\t\t\t\t\t\t\t(typeof(pRenderDestinationAddress) === 'function') ? pRenderDestinationAddress :\n\t\t\t\t\t\t\t(typeof(pRenderableHash) === 'function') ? pRenderableHash :\n\t\t\t\t\t\t\tnull;\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} basicRenderAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} basicRenderAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tconst tmpRenderOptions = this.buildRenderOptions(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress);\n\t\tif (tmpRenderOptions.Valid)\n\t\t{\n\t\t\tthis.pict.parseTemplateByHash(tmpRenderOptions.Renderable.TemplateHash, tmpRenderOptions.Record,\n\t\t\t\t/**\n\t\t\t\t * @param {Error} [pError] - The error that occurred during template parsing.\n\t\t\t\t * @param {string} [pContent] - The content that was rendered from the template.\n\t\t\t\t */\n\t\t\t\t(pError, pContent) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render (asynchronously) ${tmpRenderOptions.RenderableHash} because it did not parse the template.`, pError);\n\t\t\t\t\t\treturn tmpCallback(pError);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.assignRenderContent(tmpRenderOptions.Renderable, tmpRenderOptions.DestinationAddress, pContent);\n\t\t\t\t\treturn tmpCallback();\n\t\t\t\t}, [this], pScope, { RootRenderable: tmpRenderOptions.Renderable });\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpErrorMessage = `PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not perform a basic render of ${tmpRenderOptions.RenderableHash} because it is not valid.`;\n\t\t\tthis.log.error(tmpErrorMessage);\n\t\t\treturn tmpCallback(new Error(tmpErrorMessage));\n\t\t}\n\t}\n\n\t/**\n\t * @param {Renderable} pRenderable - The renderable that was rendered.\n\t */\n\tonProject(pRenderable)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onProject:`);\n\t\t}\n\t\tif (pRenderable.RenderMethod === 'virtual-assignment')\n\t\t{\n\t\t\tthis.pict.TransactionTracking.pushToTransactionQueue(pRenderable.TransactionHash, { ViewHash: this.Hash, Renderable: pRenderable }, 'Deferred-Post-Content-Assignment');\n\t\t}\n\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} Assigning Renderable[${pRenderable.RenderableHash}] content length ${pRenderable.Content.length} to Destination [${pRenderable.ContentDestinationAddress}] using Async render method ${pRenderable.RenderMethod}.`);\n\t\t}\n\n\t\t// Assign the content to the destination address\n\t\tthis.pict.ContentAssignment.projectContent(pRenderable.RenderMethod, pRenderable.ContentDestinationAddress, pRenderable.Content, pRenderable.TestAddress);\n\n\t\tthis.lastRenderedTimestamp = this.pict.log.getTimeStamp();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is projected into the DOM (async flow).\n\t *\n\t * @param {(error?: Error, content?: string) => void} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that is being projected.\n\t */\n\tonProjectAsync(fCallback, pRenderable)\n\t{\n\t\tthis.onProject(pRenderable);\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is rendered.\n\t *\n\t * @param {Renderable} pRenderable - The renderable that was rendered.\n\t */\n\tonAfterRender(pRenderable)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterRender:`);\n\t\t}\n\t\tif (pRenderable && pRenderable.RootRenderableViewHash === this.Hash)\n\t\t{\n\t\t\tconst tmpTransactionQueue = this.pict.TransactionTracking.clearTransactionQueue(pRenderable.TransactionHash) || [];\n\t\t\tfor (const tmpEvent of tmpTransactionQueue)\n\t\t\t{\n\t\t\t\tconst tmpView = this.pict.views[tmpEvent.Data.ViewHash];\n\t\t\t\tif (!tmpView)\n\t\t\t\t{\n\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterRender: Could not find view for transaction hash ${pRenderable.TransactionHash} and ViewHash ${tmpEvent.Data.ViewHash}.`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttmpView.onAfterProject();\n\n\t\t\t\t// Execute the developer-overridable post-render behavior\n\t\t\t\ttmpView.onAfterRender(tmpEvent.Data.Renderable);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is rendered (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that was rendered.\n\t */\n\tonAfterRenderAsync(fCallback, pRenderable)\n\t{\n\t\tthis.onAfterRender(pRenderable);\n\t\tconst tmpAnticipate = this.fable.newAnticipate();\n\t\tif (pRenderable && pRenderable.RootRenderableViewHash === this.Hash)\n\t\t{\n\t\t\tconst queue = this.pict.TransactionTracking.clearTransactionQueue(pRenderable.TransactionHash) || [];\n\t\t\tfor (const event of queue)\n\t\t\t{\n\t\t\t\t/** @type {PictView} */\n\t\t\t\tconst tmpView = this.pict.views[event.Data.ViewHash];\n\t\t\t\tif (!tmpView)\n\t\t\t\t{\n\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterRenderAsync: Could not find view for transaction hash ${pRenderable.TransactionHash} and ViewHash ${event.Data.ViewHash}.`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttmpAnticipate.anticipate(tmpView.onAfterProjectAsync.bind(tmpView));\n\t\t\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t\t\t{\n\t\t\t\t\ttmpView.onAfterRenderAsync(fNext, event.Data.Renderable);\n\t\t\t\t});\n\n\t\t\t\t// Execute the developer-overridable post-render behavior\n\t\t\t}\n\t\t}\n\t\treturn tmpAnticipate.wait(fCallback);\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is projected into the DOM.\n\t *\n\t * @param {Renderable} pRenderable - The renderable that was projected.\n\t */\n\tonAfterProject(pRenderable)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterProject:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is projected into the DOM (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that was projected.\n\t */\n\tonAfterProjectAsync(fCallback, pRenderable)\n\t{\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Solver */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before the view is solved.\n\t */\n\tonBeforeSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is solved (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonBeforeSolveAsync(fCallback)\n\t{\n\t\tthis.onBeforeSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when the view is solved.\n\t */\n\tonSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when the view is solved (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonSolveAsync(fCallback)\n\t{\n\t\tthis.onSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Performs view solving and triggers lifecycle hooks.\n\t *\n\t * @return {boolean} - True if the view was solved successfully, false otherwise.\n\t */\n\tsolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} executing solve() function...`);\n\t\t}\n\t\tthis.onBeforeSolve();\n\t\tthis.onSolve();\n\t\tthis.onAfterSolve();\n\t\tthis.lastSolvedTimestamp = this.pict.log.getTimeStamp();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Performs view solving and triggers lifecycle hooks (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tsolveAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : null;\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeSolveAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onSolveAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterSolveAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} solveAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastSolvedTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is solved.\n\t */\n\tonAfterSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is solved (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonAfterSolveAsync(fCallback)\n\t{\n\t\tthis.onAfterSolve();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Marshal From View */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before data is marshaled from the view.\n\t *\n\t * @return {boolean} - True if the operation was successful, false otherwise.\n\t */\n\tonBeforeMarshalFromView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeMarshalFromView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before data is marshaled from the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonBeforeMarshalFromViewAsync(fCallback)\n\t{\n\t\tthis.onBeforeMarshalFromView();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when data is marshaled from the view.\n\t */\n\tonMarshalFromView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onMarshalFromView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when data is marshaled from the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonMarshalFromViewAsync(fCallback)\n\t{\n\n\t\tthis.onMarshalFromView();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Marshals data from the view.\n\t *\n\t * @return {boolean} - True if the operation was successful, false otherwise.\n\t */\n\tmarshalFromView()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} executing solve() function...`);\n\t\t}\n\t\tthis.onBeforeMarshalFromView();\n\t\tthis.onMarshalFromView();\n\t\tthis.onAfterMarshalFromView();\n\t\tthis.lastMarshalFromViewTimestamp = this.pict.log.getTimeStamp();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Marshals data from the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tmarshalFromViewAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : null;\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeMarshalFromViewAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onMarshalFromViewAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterMarshalFromViewAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} marshalFromViewAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastMarshalFromViewTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after data is marshaled from the view.\n\t */\n\tonAfterMarshalFromView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterMarshalFromView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after data is marshaled from the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonAfterMarshalFromViewAsync(fCallback)\n\t{\n\t\tthis.onAfterMarshalFromView();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Marshal To View */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before data is marshaled into the view.\n\t */\n\tonBeforeMarshalToView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeMarshalToView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before data is marshaled into the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonBeforeMarshalToViewAsync(fCallback)\n\t{\n\t\tthis.onBeforeMarshalToView();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when data is marshaled into the view.\n\t */\n\tonMarshalToView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onMarshalToView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when data is marshaled into the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonMarshalToViewAsync(fCallback)\n\t{\n\t\tthis.onMarshalToView();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Marshals data into the view.\n\t *\n\t * @return {boolean} - True if the operation was successful, false otherwise.\n\t */\n\tmarshalToView()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} executing solve() function...`);\n\t\t}\n\t\tthis.onBeforeMarshalToView();\n\t\tthis.onMarshalToView();\n\t\tthis.onAfterMarshalToView();\n\t\tthis.lastMarshalToViewTimestamp = this.pict.log.getTimeStamp();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Marshals data into the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tmarshalToViewAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : null;\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\t\ttmpAnticipate.anticipate(this.onBeforeMarshalToViewAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onMarshalToViewAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterMarshalToViewAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} marshalToViewAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastMarshalToViewTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after data is marshaled into the view.\n\t */\n\tonAfterMarshalToView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterMarshalToView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after data is marshaled into the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonAfterMarshalToViewAsync(fCallback)\n\t{\n\t\tthis.onAfterMarshalToView();\n\t\treturn fCallback();\n\t}\n\n\t/** @return {boolean} - True if the object is a PictView. */\n\tget isPictView()\n\t{\n\t\treturn true;\n\t}\n}\n\nmodule.exports = PictView;\n\n},{\"../package.json\":44,\"fable-serviceproviderbase\":4}],46:[function(require,module,exports){\nconst MICRODDL_TYPE_MAP =\n{\n\t'@': { DataType: 'AutoIdentity', Label: 'Auto ID', HasSize: false },\n\t'%': { DataType: 'GUID', Label: 'GUID', HasSize: false },\n\t'$': { DataType: 'String', Label: 'String', HasSize: true },\n\t'*': { DataType: 'Text', Label: 'Text', HasSize: false },\n\t'#': { DataType: 'Numeric', Label: 'Numeric', HasSize: false },\n\t'.': { DataType: 'Decimal', Label: 'Decimal', HasSize: true },\n\t'&': { DataType: 'DateTime', Label: 'Date/Time', HasSize: false },\n\t'^': { DataType: 'Boolean', Label: 'Boolean', HasSize: false }\n};\n\nconst DATATYPE_TO_SYMBOL = {};\nfor (let tmpSymbol in MICRODDL_TYPE_MAP)\n{\n\tDATATYPE_TO_SYMBOL[MICRODDL_TYPE_MAP[tmpSymbol].DataType] = tmpSymbol;\n}\n\nfunction columnsToMicroDDL(pColumns, pTableName)\n{\n\tlet tmpTableName = (pTableName || 'Untitled').replace(/[^a-zA-Z0-9_]/g, '');\n\tlet tmpLines = ['!' + tmpTableName];\n\n\tfor (let i = 0; i < pColumns.length; i++)\n\t{\n\t\tlet tmpCol = pColumns[i];\n\t\tlet tmpSymbol = DATATYPE_TO_SYMBOL[tmpCol.DataType] || '$';\n\t\tlet tmpLine = tmpSymbol + (tmpCol.Name || 'Column' + i);\n\n\t\tif (MICRODDL_TYPE_MAP[tmpSymbol].HasSize && tmpCol.Size)\n\t\t{\n\t\t\ttmpLine += ' ' + tmpCol.Size;\n\t\t}\n\n\t\ttmpLines.push(tmpLine);\n\t}\n\n\treturn tmpLines.join('\\n');\n}\n\nfunction microDDLToColumns(pDDL)\n{\n\tlet tmpLines = pDDL.split('\\n');\n\tlet tmpColumns = [];\n\n\tfor (let i = 0; i < tmpLines.length; i++)\n\t{\n\t\tlet tmpLine = tmpLines[i].trim();\n\t\tif (!tmpLine || tmpLine.startsWith('!') || tmpLine.startsWith('//') || tmpLine.startsWith('--') || tmpLine.startsWith('->'))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet tmpSymbol = tmpLine.charAt(0);\n\t\tif (MICRODDL_TYPE_MAP.hasOwnProperty(tmpSymbol))\n\t\t{\n\t\t\tlet tmpRest = tmpLine.substring(1).trim();\n\t\t\tlet tmpParts = tmpRest.split(/\\s+/);\n\t\t\ttmpColumns.push(\n\t\t\t{\n\t\t\t\tName: tmpParts[0] || '',\n\t\t\t\tDataType: MICRODDL_TYPE_MAP[tmpSymbol].DataType,\n\t\t\t\tSize: tmpParts[1] || ''\n\t\t\t});\n\t\t}\n\t}\n\n\treturn tmpColumns;\n}\n\nmodule.exports = { MICRODDL_TYPE_MAP, DATATYPE_TO_SYMBOL, columnsToMicroDDL, microDDLToColumns };\n\n},{}],47:[function(require,module,exports){\nconst libPictView = require('pict-view');\n\nconst libSchemaUtils = require('./MappingEditor-SchemaUtils.js');\n\nconst _ViewConfiguration =\n{\n\tViewIdentifier: \"MeadowMappingEditor\",\n\n\tDefaultRenderable: \"MeadowMappingEditor-Content\",\n\tDefaultDestinationAddress: \"#MeadowMap-Editor-Container\",\n\n\tAutoRender: false,\n\n\tCSS: /*css*/`\n\t\t/* Meadow Mapping Editor */\n\t\t.meadow-mapping-editor {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.meadow-mapping-editor.active {\n\t\t\tdisplay: block;\n\t\t}\n\t\t.meadow-mapping-header {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 1em;\n\t\t\tmargin-bottom: 1em;\n\t\t}\n\t\t.meadow-mapping-header h3 {\n\t\t\tmargin: 0;\n\t\t\tflex: 1;\n\t\t}\n\t\t.meadow-mapping-list-table {\n\t\t\twidth: 100%;\n\t\t\tborder-collapse: collapse;\n\t\t\tmargin-bottom: 1em;\n\t\t}\n\t\t.meadow-mapping-list-table th {\n\t\t\ttext-align: left;\n\t\t\tfont-size: 0.72em;\n\t\t\tfont-weight: 600;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.5px;\n\t\t\tcolor: var(--facto-text-tertiary, #a09070);\n\t\t\tpadding: 0.5em 0.4em;\n\t\t\tborder-bottom: 1px solid var(--facto-border, #d6c8ae);\n\t\t}\n\t\t.meadow-mapping-list-table td {\n\t\t\tpadding: 0.35em 0.4em;\n\t\t\tborder-bottom: 1px solid var(--facto-border-subtle, #e8ddc8);\n\t\t\tvertical-align: middle;\n\t\t}\n\t\t.meadow-flow-container {\n\t\t\twidth: 100%;\n\t\t\theight: 500px;\n\t\t\tborder: 1px solid var(--facto-border, #d6c8ae);\n\t\t\tborder-radius: 6px;\n\t\t\tbackground: var(--facto-bg-surface, #fcf8f0);\n\t\t\tmargin-bottom: 0.75em;\n\t\t}\n\t\t.meadow-mapping-json-editor {\n\t\t\twidth: 100%;\n\t\t\tmin-height: 300px;\n\t\t\tfont-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;\n\t\t\tfont-size: 0.85em;\n\t\t\tpadding: 0.75em;\n\t\t\tborder: 1px solid var(--facto-border, #d6c8ae);\n\t\t\tborder-radius: 6px;\n\t\t\tbackground: var(--facto-bg-input, #fcf8f0);\n\t\t\tcolor: var(--facto-text, #3a3020);\n\t\t\tresize: vertical;\n\t\t\ttab-size: 4;\n\t\t}\n\t\t.meadow-mapping-store-checklist {\n\t\t\tdisplay: flex;\n\t\t\tflex-wrap: wrap;\n\t\t\tgap: 0.5em;\n\t\t\tmargin-top: 0.25em;\n\t\t}\n\t\t.meadow-mapping-store-checklist label {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.35em;\n\t\t\tfont-size: 0.82em;\n\t\t\tcursor: pointer;\n\t\t\tpadding: 0.3em 0.5em;\n\t\t\tborder: 1px solid var(--facto-border-subtle, #e8ddc8);\n\t\t\tborder-radius: 4px;\n\t\t\tbackground: var(--facto-bg-input, #fcf8f0);\n\t\t}\n\t\t.meadow-mapping-store-checklist label:has(input:checked) {\n\t\t\tborder-color: var(--facto-brand, #18a5a0);\n\t\t\tbackground: var(--facto-brand-a12, rgba(24,165,160,0.12));\n\t\t}\n\t\t.meadow-mapping-btn {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tpadding: 0.35em 0.9em;\n\t\t\tfont-size: 0.82em;\n\t\t\tfont-weight: 500;\n\t\t\tborder-radius: 4px;\n\t\t\tborder: 1px solid transparent;\n\t\t\tcursor: pointer;\n\t\t\ttext-decoration: none;\n\t\t\tline-height: 1.4;\n\t\t}\n\t\t.meadow-mapping-btn-primary {\n\t\t\tbackground: var(--facto-brand, #18a5a0);\n\t\t\tcolor: #fff;\n\t\t\tborder-color: var(--facto-brand, #18a5a0);\n\t\t}\n\t\t.meadow-mapping-btn-primary:hover {\n\t\t\topacity: 0.88;\n\t\t}\n\t\t.meadow-mapping-btn-secondary {\n\t\t\tbackground: var(--facto-bg-input, #fcf8f0);\n\t\t\tcolor: var(--facto-text, #3a3020);\n\t\t\tborder-color: var(--facto-border, #d6c8ae);\n\t\t}\n\t\t.meadow-mapping-btn-secondary:hover {\n\t\t\tbackground: var(--facto-border-subtle, #e8ddc8);\n\t\t}\n\t\t.meadow-mapping-btn-danger {\n\t\t\tbackground: #e74c3c;\n\t\t\tcolor: #fff;\n\t\t\tborder-color: #e74c3c;\n\t\t}\n\t\t.meadow-mapping-btn-danger:hover {\n\t\t\topacity: 0.88;\n\t\t}\n\t\t.meadow-mapping-btn-small {\n\t\t\tpadding: 0.2em 0.6em;\n\t\t\tfont-size: 0.78em;\n\t\t}\n\t\t.meadow-schema-mode-tabs {\n\t\t\tdisplay: flex;\n\t\t\tgap: 0.25em;\n\t\t}\n\t\t.meadow-schema-mode-tab {\n\t\t\tpadding: 0.25em 0.75em;\n\t\t\tfont-size: 0.8em;\n\t\t\tborder: 1px solid var(--facto-border, #d6c8ae);\n\t\t\tborder-radius: 4px;\n\t\t\tcursor: pointer;\n\t\t\tbackground: var(--facto-bg-input, #fcf8f0);\n\t\t\tcolor: var(--facto-text, #3a3020);\n\t\t}\n\t\t.meadow-schema-mode-tab.active {\n\t\t\tbackground: var(--facto-brand, #18a5a0);\n\t\t\tcolor: #fff;\n\t\t\tborder-color: var(--facto-brand, #18a5a0);\n\t\t}\n\t\t.meadow-section-title {\n\t\t\tfont-size: 0.72em;\n\t\t\tfont-weight: 600;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.5px;\n\t\t\tcolor: var(--facto-text-tertiary, #a09070);\n\t\t}\n\t`,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: \"MeadowMappingEditor-Template\",\n\t\t\tTemplate: /*html*/`\n<div>\n\t<div id=\"MeadowMap-Editor\" class=\"meadow-mapping-editor\">\n\t\t<div class=\"meadow-mapping-header\">\n\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-secondary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MeadowMappingEditor'].closeMappingEditor()\">&larr; Back</button>\n\t\t\t<h3 id=\"MeadowMap-Title\">Mapping Editor</h3>\n\t\t\t<div class=\"meadow-schema-mode-tabs\">\n\t\t\t\t<button class=\"meadow-schema-mode-tab active\" id=\"MeadowMap-Mode-Flow\" onclick=\"{~P~}.views['MeadowMappingEditor'].switchMapMode('flow')\">Visual Mapper</button>\n\t\t\t\t<button class=\"meadow-schema-mode-tab\" id=\"MeadowMap-Mode-JSON\" onclick=\"{~P~}.views['MeadowMappingEditor'].switchMapMode('json')\">JSON Config</button>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div id=\"MeadowMap-List-Wrap\">\n\t\t\t<div style=\"display:flex; align-items:center; justify-content:space-between; margin-bottom:0.75em;\">\n\t\t\t\t<div class=\"meadow-section-title\" style=\"margin:0;\">Existing Mappings</div>\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-primary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MeadowMappingEditor'].newMapping()\">+ New Mapping</button>\n\t\t\t</div>\n\t\t\t<div id=\"MeadowMap-List\"></div>\n\t\t</div>\n\n\t\t<div id=\"MeadowMap-Detail\" style=\"display:none;\">\n\t\t\t<div style=\"display:flex; gap:0.5em; align-items:center; margin-bottom:0.75em;\">\n\t\t\t\t<label style=\"font-size:0.78em; font-weight:600;\">Mapping Name</label>\n\t\t\t\t<input type=\"text\" id=\"MeadowMap-Name\" placeholder=\"Mapping name\" style=\"flex:1; padding:0.3em 0.5em; font-size:0.85em; border:1px solid var(--facto-border); border-radius:4px; background:var(--facto-bg-input); color:var(--facto-text);\">\n\t\t\t</div>\n\n\t\t\t<div style=\"display:flex; gap:0.5em; align-items:center; margin-bottom:0.75em;\">\n\t\t\t\t<label style=\"font-size:0.78em; font-weight:600;\">Source</label>\n\t\t\t\t<select id=\"MeadowMap-Source\" style=\"flex:1; padding:0.3em 0.5em; font-size:0.85em; border:1px solid var(--facto-border); border-radius:4px;\"></select>\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-secondary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MeadowMappingEditor'].discoverSourceFields()\">Discover Fields</button>\n\t\t\t</div>\n\n\t\t\t<div id=\"MeadowMap-Flow-Wrap\">\n\t\t\t\t<div id=\"MeadowMap-Flow-Container\" class=\"meadow-flow-container\"></div>\n\t\t\t</div>\n\n\t\t\t<div id=\"MeadowMap-JSON-Wrap\" style=\"display:none;\">\n\t\t\t\t<textarea class=\"meadow-mapping-json-editor\" id=\"MeadowMap-JSON\" placeholder='{\"Entity\":\"MyTable\",\"GUIDTemplate\":\"{~D:Record.IDRecord~}\",\"Mappings\":{},\"Solvers\":[],\"ManyfestAddresses\":false}'></textarea>\n\t\t\t</div>\n\n\t\t\t<div style=\"margin-top:0.75em;\">\n\t\t\t\t<div style=\"font-size:0.72em; font-weight:600; text-transform:uppercase; letter-spacing:0.5px; color:var(--facto-text-tertiary); margin-bottom:0.35em;\">Target Stores</div>\n\t\t\t\t<div id=\"MeadowMap-Stores\" class=\"meadow-mapping-store-checklist\"></div>\n\t\t\t</div>\n\n\t\t\t<div style=\"margin-top:0.75em; display:flex; gap:0.5em; flex-wrap:wrap; align-items:center;\">\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-primary\" onclick=\"{~P~}.views['MeadowMappingEditor'].saveMapping()\">Save Mapping</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: \"MeadowMappingEditor-Content\",\n\t\t\tTemplateHash: \"MeadowMappingEditor-Template\",\n\t\t\tDestinationAddress: \"#MeadowMap-Editor-Container\",\n\t\t\tRenderMethod: \"replace\"\n\t\t}\n\t]\n};\n\nclass MeadowMappingEditorView extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis._EditingContextID = 0;\n\t\tthis._EditingName = '';\n\t\tthis._CurrentMappings = [];\n\t\tthis._SelectedMappingID = 0;\n\t\tthis._DiscoveredFields = {};\n\t\tthis._FlowView = null;\n\t\tthis._MapEditorMode = 'flow';\n\t\tthis._MappingSources = [];\n\t\tthis._MappingStores = [];\n\t\tthis._CurrentTargetSchema = null;\n\t}\n\n\t// ── Overridable data methods ─────────────────────────────────────────────\n\t// Embedding apps override these to wire up their own persistence layer.\n\n\t/** Load all mappings for a context (e.g. dataset). Must return a Promise\n\t * that resolves to { Mappings: [...] }. */\n\t_doLoadMappings(pContextID)\n\t{\n\t\treturn Promise.resolve({ Mappings: [] });\n\t}\n\n\t/** Load all available sources. Must return a Promise that resolves to an\n\t * array of source objects with at least { IDSource, Name }. */\n\t_doLoadSources()\n\t{\n\t\treturn Promise.resolve([]);\n\t}\n\n\t/** Load all available target stores for a context. Must return a Promise\n\t * that resolves to { Stores: [...] }. */\n\t_doLoadStores(pContextID)\n\t{\n\t\treturn Promise.resolve({ Stores: [] });\n\t}\n\n\t/** Load the target schema for a context. Must return a Promise that\n\t * resolves to { SchemaDefinition: \"<micro-DDL string>\" }. */\n\t_doLoadTargetSchema(pContextID)\n\t{\n\t\treturn Promise.resolve({ SchemaDefinition: '' });\n\t}\n\n\t/** Load a single mapping by ID. Must return a Promise that resolves to\n\t * { Mapping: { Name, IDSource, IDProjectionStore, MappingConfiguration,\n\t * FlowDiagramState, Active, ... } }. */\n\t_doLoadMapping(pMappingID)\n\t{\n\t\treturn Promise.resolve({ Mapping: null });\n\t}\n\n\t/** Delete a mapping by ID. Must return a Promise. */\n\t_doDeleteMapping(pMappingID)\n\t{\n\t\treturn Promise.resolve({});\n\t}\n\n\t/** Discover fields from a source dataset. Must return a Promise that\n\t * resolves to { Headers: [...], SampleSize: N }. */\n\t_doDiscoverSourceFields(pContextID, pSourceID, pRecordLimit)\n\t{\n\t\treturn Promise.resolve({ Headers: [], SampleSize: 0 });\n\t}\n\n\t/** Create a new mapping. Must return a Promise that resolves to\n\t * { Mapping: { IDProjectionMapping, ... } }. */\n\t_doCreateMapping(pContextID, pData)\n\t{\n\t\treturn Promise.resolve({ Mapping: {} });\n\t}\n\n\t/** Update an existing mapping. Must return a Promise that resolves to\n\t * { Mapping: { ... } }. */\n\t_doUpdateMapping(pMappingID, pData)\n\t{\n\t\treturn Promise.resolve({ Mapping: {} });\n\t}\n\n\t/** Called when the editor is closed. Override to notify the parent view. */\n\t_onClose()\n\t{\n\t\t// Default: no-op. Override in embedding app.\n\t}\n\n\t/** Show a toast notification. */\n\t_doToast(pMessage, pOptions)\n\t{\n\t\tlet tmpModal = this.pict.views && this.pict.views['Pict-Section-Modal'];\n\t\tif (tmpModal && typeof tmpModal.toast === 'function')\n\t\t{\n\t\t\ttmpModal.toast(pMessage, pOptions);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.info('[MeadowMappingEditor] ' + pMessage);\n\t\t}\n\t}\n\n\t/** Show a confirmation dialog. Returns a Promise<boolean>. */\n\t_doConfirm(pMessage, pOptions)\n\t{\n\t\tlet tmpModal = this.pict.views && this.pict.views['Pict-Section-Modal'];\n\t\tif (tmpModal && typeof tmpModal.confirm === 'function')\n\t\t{\n\t\t\treturn tmpModal.confirm(pMessage, pOptions);\n\t\t}\n\t\t// Fallback to native confirm\n\t\treturn Promise.resolve(typeof window !== 'undefined' ? window.confirm(pMessage) : false);\n\t}\n\n\t// ── Public API ───────────────────────────────────────────────────────────\n\n\teditMappings(pContextID, pName)\n\t{\n\t\tthis._EditingContextID = pContextID;\n\t\tthis._EditingName = pName || '';\n\n\t\t// Render the sub-view so its DOM exists\n\t\tthis.render();\n\n\t\tlet tmpEditor = document.getElementById('MeadowMap-Editor');\n\t\tlet tmpTitle = document.getElementById('MeadowMap-Title');\n\n\t\tif (tmpEditor)\n\t\t{\n\t\t\ttmpEditor.classList.add('active');\n\t\t\ttmpEditor.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n\t\t}\n\t\tif (tmpTitle) tmpTitle.textContent = 'Mappings: ' + (pName || 'Untitled');\n\n\t\t// Show the mapping list, hide detail\n\t\tlet tmpMappingListWrap = document.getElementById('MeadowMap-List-Wrap');\n\t\tlet tmpMappingDetail = document.getElementById('MeadowMap-Detail');\n\t\tif (tmpMappingListWrap) tmpMappingListWrap.style.display = '';\n\t\tif (tmpMappingDetail) tmpMappingDetail.style.display = 'none';\n\n\t\t// Load mappings, sources, stores, and fresh schema in parallel\n\t\tPromise.all(\n\t\t[\n\t\t\tthis._doLoadMappings(pContextID),\n\t\t\tthis._doLoadSources(),\n\t\t\tthis._doLoadStores(pContextID),\n\t\t\tthis._doLoadTargetSchema(pContextID)\n\t\t]).then(\n\t\t\t(pResults) =>\n\t\t\t{\n\t\t\t\tthis._CurrentMappings = (pResults[0] && pResults[0].Mappings) ? pResults[0].Mappings : [];\n\t\t\t\tthis._MappingSources = Array.isArray(pResults[1]) ? pResults[1] : [];\n\t\t\t\tthis._MappingStores = (pResults[2] && pResults[2].Stores) ? pResults[2].Stores : [];\n\n\t\t\t\t// Pre-populate _DiscoveredFields from source Columns (config-driven).\n\t\t\t\t// Sources that include a Columns array provide field names without\n\t\t\t\t// requiring a separate \"Discover Fields\" API call.\n\t\t\t\tfor (let i = 0; i < this._MappingSources.length; i++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpSrc = this._MappingSources[i];\n\t\t\t\t\tif (Array.isArray(tmpSrc.Columns) && tmpSrc.Columns.length > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._DiscoveredFields[tmpSrc.IDSource] = tmpSrc.Columns;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Store the fresh schema locally for use by flow nodes\n\t\t\t\tlet tmpSchema = pResults[3];\n\t\t\t\tif (tmpSchema && tmpSchema.SchemaDefinition)\n\t\t\t\t{\n\t\t\t\t\tthis._CurrentTargetSchema = tmpSchema.SchemaDefinition;\n\t\t\t\t}\n\n\t\t\t\tthis.refreshMappingList();\n\t\t\t});\n\t}\n\n\tcloseMappingEditor()\n\t{\n\t\t// Clean up flow view\n\t\tif (this._FlowView)\n\t\t{\n\t\t\tthis._FlowView = null;\n\t\t}\n\n\t\tthis._SelectedMappingID = 0;\n\n\t\tthis._onClose();\n\t}\n\n\trefreshMappingList()\n\t{\n\t\tlet tmpContainer = document.getElementById('MeadowMap-List');\n\t\tif (!tmpContainer) return;\n\n\t\tif (this._CurrentMappings.length === 0)\n\t\t{\n\t\t\ttmpContainer.innerHTML = '<div style=\"text-align:center; padding:1.5em; color:var(--facto-text-tertiary, #a09070);\">No mappings yet. Create one to map source fields to target columns.</div>';\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpViewID = this.options.ViewIdentifier;\n\n\t\tlet tmpHtml = '<table class=\"meadow-mapping-list-table\"><thead><tr>';\n\t\ttmpHtml += '<th>ID</th><th>Name</th><th>Source</th><th>Active</th><th>Actions</th>';\n\t\ttmpHtml += '</tr></thead><tbody>';\n\n\t\tfor (let i = 0; i < this._CurrentMappings.length; i++)\n\t\t{\n\t\t\tlet tmpMap = this._CurrentMappings[i];\n\t\t\tlet tmpSourceName = '\\u2014';\n\t\t\tfor (let j = 0; j < this._MappingSources.length; j++)\n\t\t\t{\n\t\t\t\tif (this._MappingSources[j].IDSource === tmpMap.IDSource)\n\t\t\t\t{\n\t\t\t\t\ttmpSourceName = this._MappingSources[j].Name || 'Source ' + tmpMap.IDSource;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttmpHtml += '<tr>';\n\t\t\ttmpHtml += '<td>' + tmpMap.IDProjectionMapping + '</td>';\n\t\t\ttmpHtml += '<td><strong>' + (tmpMap.Name || '\\u2014') + '</strong></td>';\n\t\t\ttmpHtml += '<td>' + tmpSourceName + '</td>';\n\t\t\ttmpHtml += '<td>' + (tmpMap.Active ? '\\u2713' : '\\u2717') + '</td>';\n\t\t\ttmpHtml += '<td>';\n\t\t\ttmpHtml += '<button class=\"meadow-mapping-btn meadow-mapping-btn-primary meadow-mapping-btn-small\" onclick=\"window._Pict.views[\\'' + tmpViewID + '\\'].openMappingDetail(' + tmpMap.IDProjectionMapping + ')\">Edit</button> ';\n\t\t\ttmpHtml += '<button class=\"meadow-mapping-btn meadow-mapping-btn-danger meadow-mapping-btn-small\" onclick=\"window._Pict.views[\\'' + tmpViewID + '\\'].deleteMapping(' + tmpMap.IDProjectionMapping + ')\">Delete</button>';\n\t\t\ttmpHtml += '</td>';\n\t\t\ttmpHtml += '</tr>';\n\t\t}\n\n\t\ttmpHtml += '</tbody></table>';\n\t\ttmpContainer.innerHTML = tmpHtml;\n\t}\n\n\tnewMapping()\n\t{\n\t\tthis._SelectedMappingID = 0;\n\n\t\tlet tmpMappingListWrap = document.getElementById('MeadowMap-List-Wrap');\n\t\tlet tmpMappingDetail = document.getElementById('MeadowMap-Detail');\n\t\tif (tmpMappingListWrap) tmpMappingListWrap.style.display = 'none';\n\t\tif (tmpMappingDetail) tmpMappingDetail.style.display = '';\n\n\t\t// Reset fields\n\t\tlet tmpNameInput = document.getElementById('MeadowMap-Name');\n\t\tif (tmpNameInput) tmpNameInput.value = '';\n\n\t\t// Populate source dropdown -- auto-select the first source if one exists.\n\t\t// _DiscoveredFields for that source is already populated from the\n\t\t// source Columns loaded in editMappings(), so _rebuildFlowNodes\n\t\t// will immediately show the source fields on the SRC node.\n\t\tlet tmpAutoSourceID = (this._MappingSources.length > 0) ? this._MappingSources[0].IDSource : undefined;\n\t\tthis._populateSourceDropdown(tmpAutoSourceID);\n\t\tthis._populateStoreChecklist();\n\n\t\t// Clear JSON editor\n\t\tlet tmpJSONTextarea = document.getElementById('MeadowMap-JSON');\n\t\tif (tmpJSONTextarea)\n\t\t{\n\t\t\tlet tmpNewEntityName = (this._EditingName || 'Record').replace(/[^a-zA-Z0-9_]/g, '');\n\t\t\tlet tmpNewGUIDCol = 'GUID' + tmpNewEntityName;\n\t\t\tlet tmpNewIDCol = 'ID' + tmpNewEntityName;\n\t\t\tlet tmpNewMappings = {};\n\t\t\ttmpNewMappings[tmpNewGUIDCol] = '{~D:Record.IDRecord~}';\n\t\t\ttmpNewMappings[tmpNewIDCol] = '{~D:Record.IDRecord~}';\n\n\t\t\ttmpJSONTextarea.value = JSON.stringify(\n\t\t\t{\n\t\t\t\tEntity: tmpNewEntityName,\n\t\t\t\tGUIDTemplate: '{~D:Record.IDRecord~}',\n\t\t\t\tGUIDName: tmpNewGUIDCol,\n\t\t\t\tMappings: tmpNewMappings,\n\t\t\t\tSolvers: [],\n\t\t\t\tManyfestAddresses: false\n\t\t\t}, null, '\\t');\n\t\t}\n\n\t\t// Clear flow container\n\t\tlet tmpFlowContainer = document.getElementById('MeadowMap-Flow-Container');\n\t\tif (tmpFlowContainer) tmpFlowContainer.innerHTML = '';\n\t\tthis._FlowView = null;\n\n\t\t// Switch to flow mode and initialize the flow editor\n\t\tthis.switchMapMode('flow');\n\t\tthis.initFlowView();\n\n\t\t// Fetch fresh schema then build TGT node ports from schema columns\n\t\tthis._doLoadTargetSchema(this._EditingContextID).then(\n\t\t\t(pSchema) =>\n\t\t\t{\n\t\t\t\tif (pSchema && pSchema.SchemaDefinition)\n\t\t\t\t{\n\t\t\t\t\tthis._CurrentTargetSchema = pSchema.SchemaDefinition;\n\t\t\t\t}\n\t\t\t\tthis._rebuildFlowNodes();\n\t\t\t});\n\t}\n\n\topenMappingDetail(pMappingID)\n\t{\n\t\tthis._SelectedMappingID = pMappingID;\n\n\t\tthis._doLoadMapping(pMappingID).then(\n\t\t\t(pResponse) =>\n\t\t\t{\n\t\t\t\tif (!pResponse || !pResponse.Mapping)\n\t\t\t\t{\n\t\t\t\t\tthis._doToast('Mapping not found.', 'error');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet tmpMapping = pResponse.Mapping;\n\n\t\t\t\tlet tmpMappingListWrap = document.getElementById('MeadowMap-List-Wrap');\n\t\t\t\tlet tmpMappingDetail = document.getElementById('MeadowMap-Detail');\n\t\t\t\tif (tmpMappingListWrap) tmpMappingListWrap.style.display = 'none';\n\t\t\t\tif (tmpMappingDetail) tmpMappingDetail.style.display = '';\n\n\t\t\t\t// Set name\n\t\t\t\tlet tmpNameInput = document.getElementById('MeadowMap-Name');\n\t\t\t\tif (tmpNameInput) tmpNameInput.value = tmpMapping.Name || '';\n\n\t\t\t\t// Populate dropdowns\n\t\t\t\tthis._populateSourceDropdown(tmpMapping.IDSource);\n\n\t\t\t\t// Parse TargetStores from config, fall back to legacy IDProjectionStore\n\t\t\t\tlet tmpTargetStores = null;\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tlet tmpParsedConfig = JSON.parse(tmpMapping.MappingConfiguration || '{}');\n\t\t\t\t\tif (Array.isArray(tmpParsedConfig.TargetStores) && tmpParsedConfig.TargetStores.length > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpTargetStores = tmpParsedConfig.TargetStores;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (e) { /* ignore */ }\n\t\t\t\tif (!tmpTargetStores && tmpMapping.IDProjectionStore)\n\t\t\t\t{\n\t\t\t\t\ttmpTargetStores = [tmpMapping.IDProjectionStore];\n\t\t\t\t}\n\t\t\t\tthis._populateStoreChecklist(tmpTargetStores);\n\n\t\t\t\t// Parse mapping config\n\t\t\t\tlet tmpConfig = {};\n\t\t\t\ttry { tmpConfig = JSON.parse(tmpMapping.MappingConfiguration || '{}'); }\n\t\t\t\tcatch (e) { /* ignore */ }\n\n\t\t\t\t// Restore discovered source fields from config (config-driven approach).\n\t\t\t\t// sourceColumns is written by saveMapping() so the SRC node shows\n\t\t\t\t// all fields immediately without an extra API call.\n\t\t\t\tif (Array.isArray(tmpConfig.sourceColumns) && tmpConfig.sourceColumns.length > 0)\n\t\t\t\t{\n\t\t\t\t\tthis._DiscoveredFields[tmpMapping.IDSource] = tmpConfig.sourceColumns;\n\t\t\t\t}\n\n\t\t\t\t// Set JSON editor\n\t\t\t\tlet tmpJSONTextarea = document.getElementById('MeadowMap-JSON');\n\t\t\t\tif (tmpJSONTextarea)\n\t\t\t\t{\n\t\t\t\t\ttmpJSONTextarea.value = JSON.stringify(tmpConfig, null, '\\t');\n\t\t\t\t}\n\n\t\t\t\t// Clear flow container and re-initialize\n\t\t\t\tlet tmpFlowContainer = document.getElementById('MeadowMap-Flow-Container');\n\t\t\t\tif (tmpFlowContainer) tmpFlowContainer.innerHTML = '';\n\t\t\t\tthis._FlowView = null;\n\n\t\t\t\t// Switch to flow mode and initialize the flow editor\n\t\t\t\tthis.switchMapMode('flow');\n\t\t\t\tthis.initFlowView();\n\n\t\t\t\t// Fetch fresh schema then build TGT node ports from schema columns\n\t\t\t\tthis._doLoadTargetSchema(this._EditingContextID).then(\n\t\t\t\t\t(pSchema) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pSchema && pSchema.SchemaDefinition)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthis._CurrentTargetSchema = pSchema.SchemaDefinition;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Restore saved flow diagram state if available,\n\t\t\t\t\t\t// then rebuild ports from current schema (schema is\n\t\t\t\t\t\t// the source of truth for ports, not saved state).\n\t\t\t\t\t\tif (this._FlowView)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlet tmpFlowState = null;\n\t\t\t\t\t\t\ttry { tmpFlowState = JSON.parse(tmpMapping.FlowDiagramState || 'null'); }\n\t\t\t\t\t\t\tcatch (pParseError) { /* ignore invalid JSON */ }\n\n\t\t\t\t\t\t\tif (tmpFlowState && tmpFlowState.Nodes && tmpFlowState.Nodes.length > 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (typeof this._FlowView.setFlowData === 'function')\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tthis._FlowView.setFlowData(tmpFlowState);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Always rebuild SRC/TGT ports from current schema\n\t\t\t\t\t\t// after restoring positions and connections\n\t\t\t\t\t\tthis._rebuildFlowNodes();\n\t\t\t\t\t});\n\t\t\t});\n\t}\n\n\tasync deleteMapping(pMappingID)\n\t{\n\t\tlet tmpConfirmed = await this._doConfirm('Delete this mapping?', { title: 'Delete Mapping', confirmLabel: 'Delete', dangerous: true });\n\t\tif (!tmpConfirmed) return;\n\n\t\tthis._doDeleteMapping(pMappingID).then(\n\t\t\t() =>\n\t\t\t{\n\t\t\t\tthis._doLoadMappings(this._EditingContextID).then(\n\t\t\t\t\t(pResult) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._CurrentMappings = (pResult && pResult.Mappings) ? pResult.Mappings : [];\n\t\t\t\t\t\tthis.refreshMappingList();\n\t\t\t\t\t});\n\t\t\t});\n\t}\n\n\tswitchMapMode(pMode)\n\t{\n\t\tthis._MapEditorMode = pMode;\n\n\t\tlet tmpFlowWrap = document.getElementById('MeadowMap-Flow-Wrap');\n\t\tlet tmpJSONWrap = document.getElementById('MeadowMap-JSON-Wrap');\n\t\tlet tmpFlowTab = document.getElementById('MeadowMap-Mode-Flow');\n\t\tlet tmpJSONTab = document.getElementById('MeadowMap-Mode-JSON');\n\n\t\tif (pMode === 'flow')\n\t\t{\n\t\t\tif (tmpFlowWrap) tmpFlowWrap.style.display = '';\n\t\t\tif (tmpJSONWrap) tmpJSONWrap.style.display = 'none';\n\t\t\tif (tmpFlowTab) tmpFlowTab.classList.add('active');\n\t\t\tif (tmpJSONTab) tmpJSONTab.classList.remove('active');\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (tmpFlowWrap) tmpFlowWrap.style.display = 'none';\n\t\t\tif (tmpJSONWrap) tmpJSONWrap.style.display = '';\n\t\t\tif (tmpFlowTab) tmpFlowTab.classList.remove('active');\n\t\t\tif (tmpJSONTab) tmpJSONTab.classList.add('active');\n\n\t\t\t// If there's a flow view, serialize flow -> JSON\n\t\t\tif (this._FlowView && typeof this._FlowView.getFlowData === 'function')\n\t\t\t{\n\t\t\t\tlet tmpConfig = this.flowToMappingConfig();\n\t\t\t\tlet tmpJSONTextarea = document.getElementById('MeadowMap-JSON');\n\t\t\t\tif (tmpJSONTextarea)\n\t\t\t\t{\n\t\t\t\t\ttmpJSONTextarea.value = JSON.stringify(tmpConfig, null, '\\t');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t_populateSourceDropdown(pSelectedIDSource)\n\t{\n\t\tlet tmpSelect = document.getElementById('MeadowMap-Source');\n\t\tif (!tmpSelect) return;\n\n\t\tlet tmpHtml = '<option value=\"0\">Select a source...</option>';\n\t\tfor (let i = 0; i < this._MappingSources.length; i++)\n\t\t{\n\t\t\tlet tmpSrc = this._MappingSources[i];\n\t\t\tlet tmpSelected = (tmpSrc.IDSource === pSelectedIDSource) ? ' selected' : '';\n\t\t\ttmpHtml += '<option value=\"' + tmpSrc.IDSource + '\"' + tmpSelected + '>' + (tmpSrc.Name || 'Source ' + tmpSrc.IDSource) + '</option>';\n\t\t}\n\t\ttmpSelect.innerHTML = tmpHtml;\n\t}\n\n\t_populateStoreChecklist(pSelectedStoreIDs)\n\t{\n\t\tlet tmpContainer = document.getElementById('MeadowMap-Stores');\n\t\tif (!tmpContainer) return;\n\n\t\tlet tmpSelectedSet = {};\n\t\tif (Array.isArray(pSelectedStoreIDs))\n\t\t{\n\t\t\tfor (let i = 0; i < pSelectedStoreIDs.length; i++)\n\t\t\t{\n\t\t\t\ttmpSelectedSet[pSelectedStoreIDs[i]] = true;\n\t\t\t}\n\t\t}\n\t\telse if (pSelectedStoreIDs)\n\t\t{\n\t\t\t// Backwards compat: single IDProjectionStore value\n\t\t\ttmpSelectedSet[pSelectedStoreIDs] = true;\n\t\t}\n\n\t\tif (this._MappingStores.length === 0)\n\t\t{\n\t\t\ttmpContainer.innerHTML = '<div style=\"font-size:0.82em; color:var(--facto-text-tertiary, #a09070);\">No stores configured yet.</div>';\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpHtml = '';\n\t\tfor (let i = 0; i < this._MappingStores.length; i++)\n\t\t{\n\t\t\tlet tmpStore = this._MappingStores[i];\n\t\t\tlet tmpChecked = tmpSelectedSet[tmpStore.IDProjectionStore] ? ' checked' : '';\n\t\t\tlet tmpLabel = (tmpStore.TargetTableName || 'Store ' + tmpStore.IDProjectionStore) + ' (' + (tmpStore.Status || 'Unknown') + ')';\n\t\t\ttmpHtml += '<label>';\n\t\t\ttmpHtml += '<input type=\"checkbox\" value=\"' + tmpStore.IDProjectionStore + '\"' + tmpChecked + '>';\n\t\t\ttmpHtml += ' ' + tmpLabel;\n\t\t\ttmpHtml += '</label>';\n\t\t}\n\t\ttmpContainer.innerHTML = tmpHtml;\n\t}\n\n\t_getCheckedStoreIDs()\n\t{\n\t\tlet tmpContainer = document.getElementById('MeadowMap-Stores');\n\t\tif (!tmpContainer) return [];\n\n\t\tlet tmpChecked = tmpContainer.querySelectorAll('input[type=\"checkbox\"]:checked');\n\t\tlet tmpIDs = [];\n\t\tfor (let i = 0; i < tmpChecked.length; i++)\n\t\t{\n\t\t\ttmpIDs.push(parseInt(tmpChecked[i].value, 10));\n\t\t}\n\t\treturn tmpIDs;\n\t}\n\n\tdiscoverSourceFields()\n\t{\n\t\tlet tmpSourceSelect = document.getElementById('MeadowMap-Source');\n\t\tlet tmpIDSource = tmpSourceSelect ? parseInt(tmpSourceSelect.value, 10) : 0;\n\n\t\tif (!tmpIDSource)\n\t\t{\n\t\t\tthis._doToast('Select a source first.', {type: 'warning'});\n\t\t\treturn;\n\t\t}\n\n\t\tthis._doDiscoverSourceFields(this._EditingContextID, tmpIDSource, 50).then(\n\t\t\t(pResponse) =>\n\t\t\t{\n\t\t\t\tif (pResponse && pResponse.Error)\n\t\t\t\t{\n\t\t\t\t\tthis._doToast('Error: ' + pResponse.Error, {type: 'error'});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet tmpHeaders = (pResponse && pResponse.Headers) ? pResponse.Headers : [];\n\t\t\t\tthis._DiscoveredFields[tmpIDSource] = tmpHeaders;\n\n\t\t\t\tthis._doToast('Discovered ' + tmpHeaders.length + ' fields from ' + (pResponse.SampleSize || 0) + ' records: ' + tmpHeaders.join(', '), {type: 'success', duration: 6000});\n\n\t\t\t\t// Rebuild the flow if it exists\n\t\t\t\tthis._rebuildFlowNodes();\n\t\t\t});\n\t}\n\n\t_rebuildFlowNodes()\n\t{\n\t\t// Get current source and schema columns\n\t\tlet tmpSourceSelect = document.getElementById('MeadowMap-Source');\n\t\tlet tmpIDSource = tmpSourceSelect ? parseInt(tmpSourceSelect.value, 10) : 0;\n\t\tlet tmpFields = this._DiscoveredFields[tmpIDSource] || [];\n\n\t\t// Get schema columns from the target\n\t\tlet tmpSchemaColumns = this._getSchemaColumns();\n\n\t\t// Initialize the flow view if needed\n\t\tthis.initFlowView();\n\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpSourceTitle = 'Source: ' + (tmpSourceSelect && tmpSourceSelect.selectedIndex >= 0 ? tmpSourceSelect.options[tmpSourceSelect.selectedIndex].text : 'Source');\n\t\tlet tmpTargetTitle = 'Target: ' + (this._EditingName || 'Target');\n\n\t\t// Build deterministic source ports (Whole Record + discovered fields)\n\t\tlet tmpSourcePorts =\n\t\t[\n\t\t\t{ Hash: 'src-whole-record', Direction: 'output', Side: 'right', Label: 'Whole Record' }\n\t\t];\n\t\tfor (let i = 0; i < tmpFields.length; i++)\n\t\t{\n\t\t\ttmpSourcePorts.push(\n\t\t\t{\n\t\t\t\tHash: 'src-field-' + tmpFields[i].replace(/[^a-zA-Z0-9_-]/g, '_'),\n\t\t\t\tDirection: 'output',\n\t\t\t\tSide: 'right',\n\t\t\t\tLabel: tmpFields[i]\n\t\t\t});\n\t\t}\n\n\t\t// Build deterministic target ports -- entity-specific GUID and ID are always present\n\t\tlet tmpEntityName = (this._EditingName || 'Record').replace(/[^a-zA-Z0-9_]/g, '');\n\t\tlet tmpGUIDColumnName = 'GUID' + tmpEntityName;\n\t\tlet tmpIDColumnName = 'ID' + tmpEntityName;\n\n\t\tlet tmpTargetPorts =\n\t\t[\n\t\t\t{ Hash: 'tgt-col-' + tmpGUIDColumnName, Direction: 'input', Side: 'left', Label: tmpGUIDColumnName },\n\t\t\t{ Hash: 'tgt-col-' + tmpIDColumnName, Direction: 'input', Side: 'left', Label: tmpIDColumnName }\n\t\t];\n\t\tfor (let i = 0; i < tmpSchemaColumns.length; i++)\n\t\t{\n\t\t\t// Skip entity GUID/ID if they appear in schema columns (already added above)\n\t\t\tif (tmpSchemaColumns[i] === tmpGUIDColumnName || tmpSchemaColumns[i] === tmpIDColumnName) continue;\n\n\t\t\ttmpTargetPorts.push(\n\t\t\t{\n\t\t\t\tHash: 'tgt-col-' + tmpSchemaColumns[i].replace(/[^a-zA-Z0-9_-]/g, '_'),\n\t\t\t\tDirection: 'input',\n\t\t\t\tSide: 'left',\n\t\t\t\tLabel: tmpSchemaColumns[i]\n\t\t\t});\n\t\t}\n\n\t\t// Find existing SRC and TGT nodes (preserve user-added TPL/SOL nodes)\n\t\tlet tmpFlowData = this._FlowView.getFlowData();\n\t\tlet tmpSrcNode = null;\n\t\tlet tmpTgtNode = null;\n\n\t\tfor (let i = 0; i < tmpFlowData.Nodes.length; i++)\n\t\t{\n\t\t\tif (tmpFlowData.Nodes[i].Type === 'SRC') tmpSrcNode = tmpFlowData.Nodes[i];\n\t\t\tif (tmpFlowData.Nodes[i].Type === 'TGT') tmpTgtNode = tmpFlowData.Nodes[i];\n\t\t}\n\n\t\tif (tmpSrcNode)\n\t\t{\n\t\t\t// Merge source ports: start with newly built ports, then preserve\n\t\t\t// any existing ports from the saved state (e.g. previously discovered\n\t\t\t// fields) that aren't already in the new set.\n\t\t\tlet tmpMergedSrcPorts = tmpSourcePorts.slice();\n\t\t\tlet tmpSrcPortHashes = {};\n\t\t\tfor (let p = 0; p < tmpMergedSrcPorts.length; p++)\n\t\t\t{\n\t\t\t\ttmpSrcPortHashes[tmpMergedSrcPorts[p].Hash] = true;\n\t\t\t}\n\t\t\tlet tmpExistingPorts = tmpSrcNode.Ports || [];\n\t\t\tfor (let p = 0; p < tmpExistingPorts.length; p++)\n\t\t\t{\n\t\t\t\tif (!tmpSrcPortHashes[tmpExistingPorts[p].Hash])\n\t\t\t\t{\n\t\t\t\t\ttmpMergedSrcPorts.push(tmpExistingPorts[p]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update existing source node in-place\n\t\t\tlet tmpInternalNodes = this._FlowView._FlowData.Nodes;\n\t\t\tfor (let i = 0; i < tmpInternalNodes.length; i++)\n\t\t\t{\n\t\t\t\tif (tmpInternalNodes[i].Hash === tmpSrcNode.Hash)\n\t\t\t\t{\n\t\t\t\t\ttmpInternalNodes[i].Ports = tmpMergedSrcPorts;\n\t\t\t\t\ttmpInternalNodes[i].Title = tmpSourceTitle;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Push directly into _FlowData.Nodes to avoid addNode() rendering with empty ports\n\t\t\tthis._FlowView._FlowData.Nodes.push(\n\t\t\t{\n\t\t\t\tHash: 'node-src-' + this.fable.getUUID(),\n\t\t\t\tType: 'SRC',\n\t\t\t\tX: 50,\n\t\t\t\tY: 50,\n\t\t\t\tWidth: 200,\n\t\t\t\tHeight: 100,\n\t\t\t\tTitle: tmpSourceTitle,\n\t\t\t\tPorts: tmpSourcePorts,\n\t\t\t\tData: {}\n\t\t\t});\n\t\t}\n\n\t\tif (tmpTgtNode)\n\t\t{\n\t\t\t// Target ports: schema is the source of truth. Start with schema-\n\t\t\t// derived ports, then preserve any extra existing ports (e.g. user-\n\t\t\t// added custom columns) that aren't already in the new set.\n\t\t\tlet tmpMergedTgtPorts = tmpTargetPorts.slice();\n\t\t\tlet tmpTgtPortHashes = {};\n\t\t\tfor (let p = 0; p < tmpMergedTgtPorts.length; p++)\n\t\t\t{\n\t\t\t\ttmpTgtPortHashes[tmpMergedTgtPorts[p].Hash] = true;\n\t\t\t}\n\t\t\tlet tmpExistingTgtPorts = tmpTgtNode.Ports || [];\n\t\t\tfor (let p = 0; p < tmpExistingTgtPorts.length; p++)\n\t\t\t{\n\t\t\t\tif (!tmpTgtPortHashes[tmpExistingTgtPorts[p].Hash])\n\t\t\t\t{\n\t\t\t\t\ttmpMergedTgtPorts.push(tmpExistingTgtPorts[p]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update existing target node in-place\n\t\t\tlet tmpInternalNodes = this._FlowView._FlowData.Nodes;\n\t\t\tfor (let i = 0; i < tmpInternalNodes.length; i++)\n\t\t\t{\n\t\t\t\tif (tmpInternalNodes[i].Hash === tmpTgtNode.Hash)\n\t\t\t\t{\n\t\t\t\t\ttmpInternalNodes[i].Ports = tmpMergedTgtPorts;\n\t\t\t\t\ttmpInternalNodes[i].Title = tmpTargetTitle;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Push directly into _FlowData.Nodes to avoid addNode() rendering with empty ports\n\t\t\tthis._FlowView._FlowData.Nodes.push(\n\t\t\t{\n\t\t\t\tHash: 'node-tgt-' + this.fable.getUUID(),\n\t\t\t\tType: 'TGT',\n\t\t\t\tX: 550,\n\t\t\t\tY: 50,\n\t\t\t\tWidth: 200,\n\t\t\t\tHeight: 100,\n\t\t\t\tTitle: tmpTargetTitle,\n\t\t\t\tPorts: tmpTargetPorts,\n\t\t\t\tData: {}\n\t\t\t});\n\t\t}\n\n\t\t// Render the flow once with all ports correctly set\n\t\tif (typeof this._FlowView.renderFlow === 'function')\n\t\t{\n\t\t\tthis._FlowView.renderFlow();\n\t\t}\n\t\telse if (typeof this._FlowView.render === 'function')\n\t\t{\n\t\t\tthis._FlowView.render();\n\t\t}\n\t}\n\n\t_getSchemaColumns()\n\t{\n\t\t// Use the locally cached schema definition\n\t\tlet tmpColumns = [];\n\t\tlet tmpDDL = this._CurrentTargetSchema || '';\n\t\tif (tmpDDL)\n\t\t{\n\t\t\tlet tmpParsedColumns = libSchemaUtils.microDDLToColumns(tmpDDL);\n\t\t\tfor (let j = 0; j < tmpParsedColumns.length; j++)\n\t\t\t{\n\t\t\t\ttmpColumns.push(tmpParsedColumns[j].Name);\n\t\t\t}\n\t\t}\n\t\treturn tmpColumns;\n\t}\n\n\tinitFlowView()\n\t{\n\t\tif (this._FlowView) return;\n\n\t\tlet tmpFlowContainer = document.getElementById('MeadowMap-Flow-Container');\n\t\tif (!tmpFlowContainer) return;\n\n\t\ttry\n\t\t{\n\t\t\tlet libPictSectionFlow = require('pict-section-flow');\n\n\t\t\tthis._FlowView = this.pict.addView('MeadowMapping-Flow',\n\t\t\t{\n\t\t\t\tViewIdentifier: 'MeadowMapping-Flow',\n\t\t\t\tDefaultDestinationAddress: '#MeadowMap-Flow-Container',\n\t\t\t\tEnableToolbar: true,\n\t\t\t\tEnablePanning: true,\n\t\t\t\tEnableZooming: true,\n\t\t\t\tEnableNodeDragging: true,\n\t\t\t\tEnableConnectionCreation: true\n\t\t\t}, libPictSectionFlow);\n\n\t\t\t// Register card types\n\t\t\tlet libFlowCardSource = require('./flow-cards/FlowCard-MappingSource.js');\n\t\t\tlet libFlowCardTarget = require('./flow-cards/FlowCard-MappingTarget.js');\n\t\t\tlet libFlowCardTemplate = require('./flow-cards/FlowCard-TemplateExpression.js');\n\t\t\tlet libFlowCardSolver = require('./flow-cards/FlowCard-SolverExpression.js');\n\n\t\t\tthis.pict.addServiceType('FlowCardMappingSource', libFlowCardSource);\n\t\t\tthis.pict.addServiceType('FlowCardMappingTarget', libFlowCardTarget);\n\t\t\tthis.pict.addServiceType('FlowCardTemplateExpression', libFlowCardTemplate);\n\t\t\tthis.pict.addServiceType('FlowCardSolverExpression', libFlowCardSolver);\n\n\t\t\t// Render the flow view first so _NodeTypeProvider is initialized\n\t\t\tif (typeof this._FlowView.render === 'function')\n\t\t\t{\n\t\t\t\tthis._FlowView.render();\n\t\t\t}\n\n\t\t\t// Register card types with the flow view (must happen after render\n\t\t\t// so _NodeTypeProvider exists)\n\t\t\tlet tmpSourceCard = this.pict.instantiateServiceProviderWithoutRegistration('FlowCardMappingSource', {});\n\t\t\tlet tmpTargetCard = this.pict.instantiateServiceProviderWithoutRegistration('FlowCardMappingTarget', {});\n\t\t\tlet tmpTemplateCard = this.pict.instantiateServiceProviderWithoutRegistration('FlowCardTemplateExpression', {});\n\t\t\tlet tmpSolverCard = this.pict.instantiateServiceProviderWithoutRegistration('FlowCardSolverExpression', {});\n\n\t\t\ttmpSourceCard.registerWithFlowView(this._FlowView);\n\t\t\ttmpTargetCard.registerWithFlowView(this._FlowView);\n\t\t\ttmpTemplateCard.registerWithFlowView(this._FlowView);\n\t\t\ttmpSolverCard.registerWithFlowView(this._FlowView);\n\t\t}\n\t\tcatch (pFlowError)\n\t\t{\n\t\t\tthis.log.error('Failed to initialize flow view: ' + pFlowError.message);\n\t\t\ttmpFlowContainer.innerHTML = '<div style=\"padding:2em; text-align:center; color:var(--facto-text-tertiary, #a09070);\">Flow editor could not be loaded. Use JSON Config mode instead.</div>';\n\t\t}\n\t}\n\n\tflowToMappingConfig()\n\t{\n\t\tlet tmpEntityName = (this._EditingName || 'Record').replace(/[^a-zA-Z0-9_]/g, '');\n\t\tlet tmpGUIDColumnName = 'GUID' + tmpEntityName;\n\t\tlet tmpIDColumnName = 'ID' + tmpEntityName;\n\n\t\tlet tmpConfig =\n\t\t{\n\t\t\tEntity: tmpEntityName,\n\t\t\tGUIDTemplate: '{~D:Record.IDRecord~}',\n\t\t\tGUIDName: tmpGUIDColumnName,\n\t\t\tMappings: {},\n\t\t\tSolvers: [],\n\t\t\tManyfestAddresses: false\n\t\t};\n\n\t\tif (!this._FlowView || typeof this._FlowView.getFlowData !== 'function')\n\t\t{\n\t\t\treturn tmpConfig;\n\t\t}\n\n\t\tlet tmpFlowData = this._FlowView.getFlowData();\n\t\tif (!tmpFlowData || !tmpFlowData.Connections) return tmpConfig;\n\n\t\t// Build node hash->node map and port hash->{Label, NodeHash, NodeType} map\n\t\tlet tmpNodeMap = {};\n\t\tlet tmpPortMap = {};\n\n\t\tif (tmpFlowData.Nodes)\n\t\t{\n\t\t\tfor (let i = 0; i < tmpFlowData.Nodes.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNode = tmpFlowData.Nodes[i];\n\t\t\t\ttmpNodeMap[tmpNode.Hash] = tmpNode;\n\n\t\t\t\tif (tmpNode.Ports)\n\t\t\t\t{\n\t\t\t\t\tfor (let j = 0; j < tmpNode.Ports.length; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpPortMap[tmpNode.Ports[j].Hash] =\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLabel: tmpNode.Ports[j].Label,\n\t\t\t\t\t\t\tNodeHash: tmpNode.Hash,\n\t\t\t\t\t\t\tNodeType: tmpNode.Type\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Track solver nodes that connect to multiple target columns\n\t\tlet tmpSolverEntries = {};\n\n\t\t// Process each connection where the target is a TGT node\n\t\tfor (let i = 0; i < tmpFlowData.Connections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = tmpFlowData.Connections[i];\n\t\t\tlet tmpSourcePort = tmpPortMap[tmpConn.SourcePortHash];\n\t\t\tlet tmpTargetPort = tmpPortMap[tmpConn.TargetPortHash];\n\n\t\t\tif (!tmpSourcePort || !tmpTargetPort) continue;\n\n\t\t\t// Only process connections that end at a TGT node\n\t\t\tif (tmpTargetPort.NodeType !== 'TGT') continue;\n\n\t\t\tlet tmpTargetColumn = tmpTargetPort.Label;\n\t\t\tif (!tmpTargetColumn) continue;\n\n\t\t\tlet tmpSourceNode = tmpNodeMap[tmpSourcePort.NodeHash];\n\t\t\tif (!tmpSourceNode) continue;\n\n\t\t\tif (tmpSourceNode.Type === 'SRC')\n\t\t\t{\n\t\t\t\t// Direct mapping: SRC field -> TGT column\n\t\t\t\tlet tmpSourceField = tmpSourcePort.Label;\n\n\t\t\t\t// Skip \"Whole Record\" direct connections to TGT (need intermediate node)\n\t\t\t\tif (tmpSourceField === 'Whole Record') continue;\n\n\t\t\t\tlet tmpTemplate = (tmpConn.Data && tmpConn.Data.Template)\n\t\t\t\t\t? tmpConn.Data.Template\n\t\t\t\t\t: '{~D:Record.' + tmpSourceField + '~}';\n\n\t\t\t\t// Connection to the entity GUID port sets the GUIDTemplate for upsert uniqueness\n\t\t\t\tif (tmpTargetColumn === tmpGUIDColumnName)\n\t\t\t\t{\n\t\t\t\t\ttmpConfig.GUIDTemplate = tmpTemplate;\n\t\t\t\t}\n\n\t\t\t\ttmpConfig.Mappings[tmpTargetColumn] = tmpTemplate;\n\t\t\t}\n\t\t\telse if (tmpSourceNode.Type === 'TPL')\n\t\t\t{\n\t\t\t\t// Template expression: TPL result -> TGT column\n\t\t\t\tlet tmpExpression = (tmpSourceNode.Data && tmpSourceNode.Data.TemplateExpression)\n\t\t\t\t\t? tmpSourceNode.Data.TemplateExpression\n\t\t\t\t\t: '';\n\n\t\t\t\tif (tmpExpression)\n\t\t\t\t{\n\t\t\t\t\t// TPL connected to entity GUID sets the GUIDTemplate\n\t\t\t\t\tif (tmpTargetColumn === tmpGUIDColumnName)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpConfig.GUIDTemplate = tmpExpression;\n\t\t\t\t\t}\n\n\t\t\t\t\ttmpConfig.Mappings[tmpTargetColumn] = tmpExpression;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (tmpSourceNode.Type === 'SOL')\n\t\t\t{\n\t\t\t\t// Solver expression: SOL result -> TGT column\n\t\t\t\tlet tmpExpression = (tmpSourceNode.Data && tmpSourceNode.Data.SolverExpression)\n\t\t\t\t\t? tmpSourceNode.Data.SolverExpression\n\t\t\t\t\t: '';\n\n\t\t\t\tif (tmpExpression)\n\t\t\t\t{\n\t\t\t\t\t// Group outputs for the same solver node\n\t\t\t\t\tif (!tmpSolverEntries[tmpSourceNode.Hash])\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpSolverEntries[tmpSourceNode.Hash] =\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texpression: tmpExpression,\n\t\t\t\t\t\t\toutputs: {}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\ttmpSolverEntries[tmpSourceNode.Hash].outputs[tmpTargetColumn] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Ensure entity-specific GUID and ID are always present in Mappings\n\t\tif (!tmpConfig.Mappings.hasOwnProperty(tmpGUIDColumnName))\n\t\t{\n\t\t\ttmpConfig.Mappings[tmpGUIDColumnName] = tmpConfig.GUIDTemplate;\n\t\t}\n\t\tif (!tmpConfig.Mappings.hasOwnProperty(tmpIDColumnName))\n\t\t{\n\t\t\ttmpConfig.Mappings[tmpIDColumnName] = '{~D:Record.IDRecord~}';\n\t\t}\n\n\t\t// Add grouped solver entries\n\t\tlet tmpSolverKeys = Object.keys(tmpSolverEntries);\n\t\tfor (let i = 0; i < tmpSolverKeys.length; i++)\n\t\t{\n\t\t\ttmpConfig.Solvers.push(tmpSolverEntries[tmpSolverKeys[i]]);\n\t\t}\n\n\t\treturn tmpConfig;\n\t}\n\n\tsaveMapping()\n\t{\n\t\tlet tmpNameInput = document.getElementById('MeadowMap-Name');\n\t\tlet tmpSourceSelect = document.getElementById('MeadowMap-Source');\n\n\t\tlet tmpName = tmpNameInput ? tmpNameInput.value.trim() : '';\n\t\tlet tmpIDSource = tmpSourceSelect ? parseInt(tmpSourceSelect.value, 10) : 0;\n\t\tlet tmpCheckedStoreIDs = this._getCheckedStoreIDs();\n\t\tlet tmpIDProjectionStore = tmpCheckedStoreIDs.length > 0 ? tmpCheckedStoreIDs[0] : 0;\n\n\t\tif (!tmpName)\n\t\t{\n\t\t\tthis._doToast('Enter a mapping name.', {type: 'warning'});\n\t\t\treturn;\n\t\t}\n\n\t\t// Get mapping config\n\t\tlet tmpMappingConfig;\n\t\tif (this._MapEditorMode === 'json')\n\t\t{\n\t\t\tlet tmpJSONTextarea = document.getElementById('MeadowMap-JSON');\n\t\t\tlet tmpJSON = tmpJSONTextarea ? tmpJSONTextarea.value : '{}';\n\t\t\ttry\n\t\t\t{\n\t\t\t\ttmpMappingConfig = JSON.parse(tmpJSON);\n\t\t\t}\n\t\t\tcatch (e)\n\t\t\t{\n\t\t\t\tthis._doToast('Invalid JSON: ' + e.message, {type: 'error'});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpMappingConfig = this.flowToMappingConfig();\n\t\t}\n\n\t\t// Store target stores in the mapping config\n\t\ttmpMappingConfig.TargetStores = tmpCheckedStoreIDs;\n\n\t\t// Persist discovered source columns so the SRC node loads correctly\n\t\t// on next open without requiring a separate API call.\n\t\tlet tmpSavedColumns = this._DiscoveredFields[tmpIDSource];\n\t\tif (Array.isArray(tmpSavedColumns) && tmpSavedColumns.length > 0)\n\t\t{\n\t\t\ttmpMappingConfig.sourceColumns = tmpSavedColumns;\n\t\t}\n\n\t\t// Get flow diagram state\n\t\tlet tmpFlowState = {};\n\t\tif (this._FlowView && typeof this._FlowView.getFlowData === 'function')\n\t\t{\n\t\t\ttmpFlowState = this._FlowView.getFlowData();\n\t\t}\n\n\t\tlet tmpData =\n\t\t{\n\t\t\tName: tmpName,\n\t\t\tIDSource: tmpIDSource,\n\t\t\tIDProjectionStore: tmpIDProjectionStore,\n\t\t\tMappingConfiguration: JSON.stringify(tmpMappingConfig),\n\t\t\tFlowDiagramState: JSON.stringify(tmpFlowState),\n\t\t\tActive: 1\n\t\t};\n\n\t\tlet tmpPromise;\n\t\tif (this._SelectedMappingID)\n\t\t{\n\t\t\ttmpPromise = this._doUpdateMapping(this._SelectedMappingID, tmpData);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpPromise = this._doCreateMapping(this._EditingContextID, tmpData);\n\t\t}\n\n\t\ttmpPromise.then(\n\t\t\t(pResponse) =>\n\t\t\t{\n\t\t\t\tif (pResponse && pResponse.Error)\n\t\t\t\t{\n\t\t\t\t\tthis._doToast('Error: ' + pResponse.Error, {type: 'error'});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Update the selected mapping ID if it was a create\n\t\t\t\tif (pResponse && pResponse.Mapping && pResponse.Mapping.IDProjectionMapping)\n\t\t\t\t{\n\t\t\t\t\tthis._SelectedMappingID = pResponse.Mapping.IDProjectionMapping;\n\t\t\t\t}\n\n\t\t\t\tthis._doToast('Mapping saved.', {type: 'success'});\n\n\t\t\t\t// Refresh mapping list\n\t\t\t\tthis._doLoadMappings(this._EditingContextID).then(\n\t\t\t\t\t(pResult) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._CurrentMappings = (pResult && pResult.Mappings) ? pResult.Mappings : [];\n\t\t\t\t\t});\n\t\t\t});\n\t}\n\n}\n\nmodule.exports = MeadowMappingEditorView;\n\nmodule.exports.default_configuration = _ViewConfiguration;\n\n},{\"./MappingEditor-SchemaUtils.js\":46,\"./flow-cards/FlowCard-MappingSource.js\":48,\"./flow-cards/FlowCard-MappingTarget.js\":49,\"./flow-cards/FlowCard-SolverExpression.js\":50,\"./flow-cards/FlowCard-TemplateExpression.js\":51,\"pict-section-flow\":9,\"pict-view\":45}],48:[function(require,module,exports){\nconst libPictFlowCard = require('pict-section-flow').PictFlowCard;\n\n/**\n * FlowCard-MappingSource\n *\n * Represents a source dataset in the mapping flow.\n * Output ports are dynamically generated from discovered fields.\n */\nclass FlowCardMappingSource extends libPictFlowCard\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({},\n\t\t\t{\n\t\t\t\tTitle: 'Mapping Source',\n\t\t\t\tName: 'Mapping Source',\n\t\t\t\tCode: 'SRC',\n\t\t\t\tCategory: 'Data Source',\n\t\t\t\tDescription: 'Source dataset with discovered record fields',\n\t\t\t\tTitleBarColor: '#2980b9',\n\t\t\t\tWidth: 200,\n\t\t\t\tHeight: 100,\n\t\t\t\tInputs: [],\n\t\t\t\tOutputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Whole Record', Side: 'right' }\n\t\t\t\t],\n\t\t\t\tShowTypeLabel: true,\n\t\t\t\tPortLabelsOnHover: false,\n\t\t\t\tPortLabelsOutside: true\n\t\t\t},\n\t\t\tpOptions);\n\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'FlowCardMappingSource';\n\t}\n}\n\nmodule.exports = FlowCardMappingSource;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Mapping Source',\n\tCode: 'SRC',\n\tCategory: 'Data Source',\n\tTitleBarColor: '#2980b9',\n\tWidth: 200,\n\tHeight: 100\n};\n\n},{\"pict-section-flow\":9}],49:[function(require,module,exports){\nconst libPictFlowCard = require('pict-section-flow').PictFlowCard;\n\n/**\n * FlowCard-MappingTarget\n *\n * Represents the mapping target table in the mapping flow.\n * Input ports are dynamically generated from schema columns.\n */\nclass FlowCardMappingTarget extends libPictFlowCard\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({},\n\t\t\t{\n\t\t\t\tTitle: 'Mapping Target',\n\t\t\t\tName: 'Mapping Target',\n\t\t\t\tCode: 'TGT',\n\t\t\t\tCategory: 'Data Target',\n\t\t\t\tDescription: 'Mapping target table with schema columns',\n\t\t\t\tTitleBarColor: '#27ae60',\n\t\t\t\tWidth: 200,\n\t\t\t\tHeight: 100,\n\t\t\t\tInputs:\n\t\t\t\t[\n\t\t\t\t],\n\t\t\t\tOutputs: [],\n\t\t\t\tShowTypeLabel: true,\n\t\t\t\tPortLabelsOnHover: false,\n\t\t\t\tPortLabelsOutside: true\n\t\t\t},\n\t\t\tpOptions);\n\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'FlowCardMappingTarget';\n\t}\n}\n\nmodule.exports = FlowCardMappingTarget;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Mapping Target',\n\tCode: 'TGT',\n\tCategory: 'Data Target',\n\tTitleBarColor: '#27ae60',\n\tWidth: 200,\n\tHeight: 100\n};\n\n},{\"pict-section-flow\":9}],50:[function(require,module,exports){\nconst libPictFlowCard = require('pict-section-flow').PictFlowCard;\n\n/**\n * FlowCard-SolverExpression\n *\n * A transform card that applies a Fable ExpressionParser solver expression\n * to compute a derived value from the whole incoming record. Connect the\n * Source \"Whole Record\" output to this card's input, then connect this\n * card's output to a target column.\n *\n * Double-click the node to edit the solver expression in the properties panel.\n *\n * @class FlowCardSolverExpression\n * @extends PictFlowCard\n */\nclass FlowCardSolverExpression extends libPictFlowCard\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({},\n\t\t\t{\n\t\t\t\tTitle: 'Solver Expression',\n\t\t\t\tName: 'Solver Expression',\n\t\t\t\tCode: 'SOL',\n\t\t\t\tCategory: 'Transform',\n\t\t\t\tDescription: 'Apply a Fable solver expression for conditional logic and computed values',\n\t\t\t\tTitleBarColor: '#d35400',\n\t\t\t\tWidth: 220,\n\t\t\t\tHeight: 90,\n\t\t\t\tInputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Whole Record', Side: 'left' }\n\t\t\t\t],\n\t\t\t\tOutputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Result', Side: 'right' }\n\t\t\t\t],\n\t\t\t\tShowTypeLabel: true,\n\t\t\t\tPortLabelsOnHover: false,\n\t\t\t\tPortLabelsOutside: true,\n\t\t\t\tLabelsInFront: true,\n\t\t\t\tEnabled: true,\n\t\t\t\tBodyContent:\n\t\t\t\t{\n\t\t\t\t\tContentType: 'html',\n\t\t\t\t\tTemplate: '<div style=\"font-size:10px; padding:2px 4px; color:#ccc; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; max-width:200px;\">{~D:Record.Data.SolverExpression~}</div>'\n\t\t\t\t},\n\t\t\t\tPropertiesPanel:\n\t\t\t\t{\n\t\t\t\t\tPanelType: 'Template',\n\t\t\t\t\tDefaultWidth: 420,\n\t\t\t\t\tDefaultHeight: 160,\n\t\t\t\t\tTitle: 'Solver Expression',\n\t\t\t\t\tConfiguration:\n\t\t\t\t\t{\n\t\t\t\t\t\tTemplate: '<div style=\"padding:8px;\"><label style=\"display:block; margin-bottom:4px; font-weight:bold; font-size:12px;\">Solver Expression</label><textarea id=\"FlowCard-SOL-{~D:Record.Hash~}\" style=\"width:100%; height:80px; font-family:monospace; font-size:12px; resize:vertical; background:#1a1a2e; color:#e0e0e0; border:1px solid #444; border-radius:4px; padding:6px;\" onchange=\"(function(el){var n=pict.views[\\'MeadowMapping-Flow\\'];if(n&&n._FlowData){for(var i=0;i<n._FlowData.Nodes.length;i++){if(n._FlowData.Nodes[i].Hash===\\'{~D:Record.Hash~}\\'){n._FlowData.Nodes[i].Data.SolverExpression=el.value;if(typeof n.renderFlow===\\'function\\')n.renderFlow();break;}}}})(this)\">{~D:Record.Data.SolverExpression~}</textarea><div style=\"font-size:10px; color:#888; margin-top:4px;\">Example: IF(IncomingRecord.Type == \\'Premium\\', \\'GOLD\\', \\'SILVER\\')</div></div>'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tpOptions);\n\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'FlowCardSolverExpression';\n\t}\n}\n\nmodule.exports = FlowCardSolverExpression;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Solver Expression',\n\tCode: 'SOL',\n\tCategory: 'Transform',\n\tTitleBarColor: '#d35400',\n\tWidth: 220,\n\tHeight: 90\n};\n\n},{\"pict-section-flow\":9}],51:[function(require,module,exports){\nconst libPictFlowCard = require('pict-section-flow').PictFlowCard;\n\n/**\n * FlowCard-TemplateExpression\n *\n * A transform card that applies a Manyfest template expression to the\n * whole incoming record. Connect the Source \"Whole Record\" output to this\n * card's input, then connect this card's output to a target column.\n *\n * Double-click the node to edit the template expression in the properties panel.\n *\n * @class FlowCardTemplateExpression\n * @extends PictFlowCard\n */\nclass FlowCardTemplateExpression extends libPictFlowCard\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({},\n\t\t\t{\n\t\t\t\tTitle: 'Template Expression',\n\t\t\t\tName: 'Template Expression',\n\t\t\t\tCode: 'TPL',\n\t\t\t\tCategory: 'Transform',\n\t\t\t\tDescription: 'Apply a Manyfest template expression to map source fields to a target column',\n\t\t\t\tTitleBarColor: '#8e44ad',\n\t\t\t\tWidth: 220,\n\t\t\t\tHeight: 90,\n\t\t\t\tInputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Whole Record', Side: 'left' }\n\t\t\t\t],\n\t\t\t\tOutputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Result', Side: 'right' }\n\t\t\t\t],\n\t\t\t\tShowTypeLabel: true,\n\t\t\t\tPortLabelsOnHover: false,\n\t\t\t\tPortLabelsOutside: true,\n\t\t\t\tLabelsInFront: true,\n\t\t\t\tEnabled: true,\n\t\t\t\tBodyContent:\n\t\t\t\t{\n\t\t\t\t\tContentType: 'html',\n\t\t\t\t\tTemplate: '<div style=\"font-size:10px; padding:2px 4px; color:#ccc; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; max-width:200px;\">{~D:Record.Data.TemplateExpression~}</div>'\n\t\t\t\t},\n\t\t\t\tPropertiesPanel:\n\t\t\t\t{\n\t\t\t\t\tPanelType: 'Template',\n\t\t\t\t\tDefaultWidth: 420,\n\t\t\t\t\tDefaultHeight: 160,\n\t\t\t\t\tTitle: 'Template Expression',\n\t\t\t\t\tConfiguration:\n\t\t\t\t\t{\n\t\t\t\t\t\tTemplate: '<div style=\"padding:8px;\"><label style=\"display:block; margin-bottom:4px; font-weight:bold; font-size:12px;\">Template Expression</label><textarea id=\"FlowCard-TPL-{~D:Record.Hash~}\" style=\"width:100%; height:80px; font-family:monospace; font-size:12px; resize:vertical; background:#1a1a2e; color:#e0e0e0; border:1px solid #444; border-radius:4px; padding:6px;\" onchange=\"(function(el){var n=pict.views[\\'MeadowMapping-Flow\\'];if(n&&n._FlowData){for(var i=0;i<n._FlowData.Nodes.length;i++){if(n._FlowData.Nodes[i].Hash===\\'{~D:Record.Hash~}\\'){n._FlowData.Nodes[i].Data.TemplateExpression=el.value;if(typeof n.renderFlow===\\'function\\')n.renderFlow();break;}}}})(this)\">{~D:Record.Data.TemplateExpression~}</textarea><div style=\"font-size:10px; color:#888; margin-top:4px;\">Use {~D:Record.FieldName~} syntax. Example: {~D:Record.Name~} in {~D:Record.City~}</div></div>'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tpOptions);\n\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'FlowCardTemplateExpression';\n\t}\n}\n\nmodule.exports = FlowCardTemplateExpression;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Template Expression',\n\tCode: 'TPL',\n\tCategory: 'Transform',\n\tTitleBarColor: '#8e44ad',\n\tWidth: 220,\n\tHeight: 90\n};\n\n},{\"pict-section-flow\":9}]},{},[1])(1)\n});\n\n","'use strict';\n\nconst libPictApplication = require('pict-application');\nconst libMappingDemoEditorView = require('./views/MappingDemoEditorView.js');\n\n/**\n * MappingDemoApplication\n *\n * Minimal pict application that hosts the MappingDemoEditorView.\n * Loaded client-side by Pict.safeLoadPictApplication(MappingDemoApplication, 2).\n *\n * After initialization it exposes window.openMappingEditor() so the static\n * HTML pipeline UI can activate the visual editor from a plain onclick handler.\n */\nclass MappingDemoApplication extends libPictApplication\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.pict.addView(\n\t\t\t'MappingDemoEditor',\n\t\t\tlibMappingDemoEditorView.default_configuration,\n\t\t\tlibMappingDemoEditorView);\n\t}\n\n\tonAfterInitializeAsync(fCallback)\n\t{\n\t\t// Expose a global hook so the outer HTML can open the editor\n\t\twindow.openMappingEditor = () =>\n\t\t{\n\t\t\tlet tmpPlaceholder = document.getElementById('mapping-editor-placeholder');\n\t\t\tif (tmpPlaceholder)\n\t\t\t{\n\t\t\t\ttmpPlaceholder.style.display = 'none';\n\t\t\t}\n\n\t\t\twindow._Pict.views['MappingDemoEditor'].editMappings(1, 'Book');\n\t\t};\n\n\t\treturn super.onAfterInitializeAsync(fCallback);\n\t}\n}\n\nmodule.exports = MappingDemoApplication;\n\nmodule.exports.default_configuration =\n{\n\tName: 'MappingDemoApp',\n\tHash: 'MappingDemo',\n\tAutoSolveAfterInitialize: true\n};\n","(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()","'use strict';\n\n// Path: mapping-demo/source/views/ → ../../../../source/views/PictView-MeadowMappingEditor.js\nconst libMeadowMappingEditorView = require('../../../../source/views/PictView-MeadowMappingEditor.js');\n\n// ── View configuration ────────────────────────────────────────────────────────\n// Same template HTML as the generic base editor, but with 'MappingDemoEditor'\n// in all onclick handlers so pict resolves this registered view name.\n// DOM IDs stay as MeadowMap-* so inherited JS methods work without changes.\n\nconst _ViewConfiguration =\n{\n\tViewIdentifier: 'MappingDemoEditor',\n\n\tDefaultRenderable: 'MappingDemoEditor-Content',\n\tDefaultDestinationAddress: '#MeadowMap-Editor-Container',\n\n\tAutoRender: false,\n\n\tCSS: libMeadowMappingEditorView.default_configuration.CSS,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'MappingDemoEditor-Template',\n\t\t\tTemplate: /*html*/`\n<div>\n\t<div id=\"MeadowMap-Editor\" class=\"meadow-mapping-editor\">\n\t\t<div class=\"meadow-mapping-header\">\n\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-secondary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MappingDemoEditor'].closeMappingEditor()\">&larr; Back</button>\n\t\t\t<h3 id=\"MeadowMap-Title\">Mapping Editor</h3>\n\t\t\t<div class=\"meadow-schema-mode-tabs\">\n\t\t\t\t<button class=\"meadow-schema-mode-tab active\" id=\"MeadowMap-Mode-Flow\" onclick=\"{~P~}.views['MappingDemoEditor'].switchMapMode('flow')\">Visual Mapper</button>\n\t\t\t\t<button class=\"meadow-schema-mode-tab\" id=\"MeadowMap-Mode-JSON\" onclick=\"{~P~}.views['MappingDemoEditor'].switchMapMode('json')\">JSON Config</button>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div id=\"MeadowMap-List-Wrap\">\n\t\t\t<div style=\"display:flex; align-items:center; justify-content:space-between; margin-bottom:0.75em;\">\n\t\t\t\t<div class=\"meadow-section-title\" style=\"margin:0;\">Existing Mappings</div>\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-primary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MappingDemoEditor'].newMapping()\">+ New Mapping</button>\n\t\t\t</div>\n\t\t\t<div id=\"MeadowMap-List\"></div>\n\t\t</div>\n\n\t\t<div id=\"MeadowMap-Detail\" style=\"display:none;\">\n\t\t\t<div style=\"display:flex; gap:0.5em; align-items:center; margin-bottom:0.75em;\">\n\t\t\t\t<label style=\"font-size:0.78em; font-weight:600;\">Mapping Name</label>\n\t\t\t\t<input type=\"text\" id=\"MeadowMap-Name\" placeholder=\"Mapping name\" style=\"flex:1; padding:0.3em 0.5em; font-size:0.85em; border:1px solid var(--border); border-radius:4px; background:var(--bg-card); color:var(--text);\">\n\t\t\t</div>\n\n\t\t\t<div style=\"display:flex; gap:0.5em; align-items:center; margin-bottom:0.75em;\">\n\t\t\t\t<label style=\"font-size:0.78em; font-weight:600;\">Source</label>\n\t\t\t\t<select id=\"MeadowMap-Source\" style=\"flex:1; padding:0.3em 0.5em; font-size:0.85em; border:1px solid var(--border); border-radius:4px;\"></select>\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-secondary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MappingDemoEditor'].discoverSourceFields()\">Discover Fields</button>\n\t\t\t</div>\n\n\t\t\t<div id=\"MeadowMap-Flow-Wrap\">\n\t\t\t\t<div id=\"MeadowMap-Flow-Container\" class=\"meadow-flow-container\"></div>\n\t\t\t</div>\n\n\t\t\t<div id=\"MeadowMap-JSON-Wrap\" style=\"display:none;\">\n\t\t\t\t<textarea class=\"meadow-mapping-json-editor\" id=\"MeadowMap-JSON\" placeholder='{\"Entity\":\"Book\",\"GUIDTemplate\":\"{~D:Record.id~}\",\"Mappings\":{},\"Solvers\":[],\"ManyfestAddresses\":false}'></textarea>\n\t\t\t</div>\n\n\t\t\t<div style=\"margin-top:0.75em;\">\n\t\t\t\t<div style=\"font-size:0.72em; font-weight:600; text-transform:uppercase; letter-spacing:0.5px; color:var(--text-dim); margin-bottom:0.35em;\">Target Stores</div>\n\t\t\t\t<div id=\"MeadowMap-Stores\" class=\"meadow-mapping-store-checklist\"></div>\n\t\t\t</div>\n\n\t\t\t<div style=\"margin-top:0.75em; display:flex; gap:0.5em; flex-wrap:wrap; align-items:center;\">\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-primary\" onclick=\"{~P~}.views['MappingDemoEditor'].saveMapping()\">Save Mapping</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: 'MappingDemoEditor-Content',\n\t\t\tTemplateHash: 'MappingDemoEditor-Template',\n\t\t\tContentDestinationAddress: '#MeadowMap-Editor-Container',\n\t\t\tRenderMethod: 'replace'\n\t\t}\n\t]\n};\n\n/**\n * MappingDemoEditorView\n *\n * Extends MeadowMappingEditorView and wires the _do* data methods to the\n * mapping-demo server's REST API endpoints. All visual/canvas/serialization\n * logic lives in the base class; this subclass only supplies data access.\n */\nclass MappingDemoEditorView extends libMeadowMappingEditorView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\t}\n\n\t// ── Data methods ─────────────────────────────────────────────────────────\n\n\t_doLoadMappings(pContextID)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping').then(function(r) { return r.json(); });\n\t}\n\n\t_doLoadSources()\n\t{\n\t\treturn fetch('/1.0/Demo/Sources').then(function(r) { return r.json(); });\n\t}\n\n\t_doLoadStores(pContextID)\n\t{\n\t\treturn Promise.resolve({ Stores: [] });\n\t}\n\n\t_doLoadTargetSchema(pContextID)\n\t{\n\t\treturn fetch('/1.0/Demo/TargetSchema').then(function(r) { return r.json(); });\n\t}\n\n\t_doLoadMapping(pMappingID)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping/' + pMappingID).then(function(r) { return r.json(); });\n\t}\n\n\t_doDeleteMapping(pMappingID)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping/' + pMappingID,\n\t\t\t{ method: 'DELETE' }).then(function(r) { return r.json(); });\n\t}\n\n\t_doDiscoverSourceFields(pContextID, pSourceID, pRecordLimit)\n\t{\n\t\treturn fetch('/1.0/Demo/SourceSchema').then(function(r) { return r.json(); });\n\t}\n\n\t_doCreateMapping(pContextID, pData)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping',\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify(pData)\n\t\t\t}).then(function(r) { return r.json(); });\n\t}\n\n\t_doUpdateMapping(pMappingID, pData)\n\t{\n\t\treturn fetch('/1.0/Demo/VisualMapping/' + pMappingID,\n\t\t\t{\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify(pData)\n\t\t\t}).then(function(r) { return r.json(); });\n\t}\n\n\t_onClose()\n\t{\n\t\t// Hide the editor and restore the step placeholder\n\t\tlet tmpEditor = document.getElementById('MeadowMap-Editor');\n\t\tif (tmpEditor)\n\t\t{\n\t\t\ttmpEditor.classList.remove('active');\n\t\t}\n\n\t\tlet tmpPlaceholder = document.getElementById('mapping-editor-placeholder');\n\t\tif (tmpPlaceholder)\n\t\t{\n\t\t\ttmpPlaceholder.style.display = '';\n\t\t}\n\n\t\t// Dispatch an event so the outer UI can mark this step done\n\t\tdocument.dispatchEvent(new CustomEvent('mapping-editor-closed'));\n\t}\n}\n\nmodule.exports = MappingDemoEditorView;\n\nmodule.exports.default_configuration = _ViewConfiguration;\n","module.exports={\n \"name\": \"fable-serviceproviderbase\",\n \"version\": \"3.0.19\",\n \"description\": \"Simple base classes for fable services.\",\n \"main\": \"source/Fable-ServiceProviderBase.js\",\n \"scripts\": {\n \"start\": \"node source/Fable-ServiceProviderBase.js\",\n \"test\": \"npx quack test\",\n \"tests\": \"npx quack test -g\",\n \"coverage\": \"npx quack coverage\",\n \"build\": \"npx quack build\",\n \"types\": \"tsc -p ./tsconfig.build.json\",\n \"check\": \"tsc -p . --noEmit\"\n },\n \"types\": \"types/source/Fable-ServiceProviderBase.d.ts\",\n \"mocha\": {\n \"diff\": true,\n \"extension\": [\n \"js\"\n ],\n \"package\": \"./package.json\",\n \"reporter\": \"spec\",\n \"slow\": \"75\",\n \"timeout\": \"5000\",\n \"ui\": \"tdd\",\n \"watch-files\": [\n \"source/**/*.js\",\n \"test/**/*.js\"\n ],\n \"watch-ignore\": [\n \"lib/vendor\"\n ]\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/stevenvelozo/fable-serviceproviderbase.git\"\n },\n \"keywords\": [\n \"entity\",\n \"behavior\"\n ],\n \"author\": \"Steven Velozo <steven@velozo.com> (http://velozo.com/)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/stevenvelozo/fable-serviceproviderbase/issues\"\n },\n \"homepage\": \"https://github.com/stevenvelozo/fable-serviceproviderbase\",\n \"devDependencies\": {\n \"@types/mocha\": \"^10.0.10\",\n \"fable\": \"^3.1.62\",\n \"quackage\": \"^1.0.58\",\n \"typescript\": \"^5.9.3\"\n }\n}\n","/**\n* Fable Service Base\n* @author <steven@velozo.com>\n*/\n\nconst libPackage = require('../package.json');\n\nclass FableServiceProviderBase\n{\n\t/**\n\t * The constructor can be used in two ways:\n\t * 1) With a fable, options object and service hash (the options object and service hash are optional)a\n\t * 2) With an object or nothing as the first parameter, where it will be treated as the options object\n\t *\n\t * @param {import('fable')|Record<string, any>} [pFable] - (optional) The fable instance, or the options object if there is no fable\n\t * @param {Record<string, any>|string} [pOptions] - (optional) The options object, or the service hash if there is no fable\n\t * @param {string} [pServiceHash] - (optional) The service hash to identify this service instance\n\t */\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\t/** @type {import('fable')} */\n\t\tthis.fable;\n\t\t/** @type {string} */\n\t\tthis.UUID;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.options;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.services;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.servicesMap;\n\n\t\t// Check if a fable was passed in; connect it if so\n\t\tif ((typeof(pFable) === 'object') && pFable.isFable)\n\t\t{\n\t\t\tthis.connectFable(pFable);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.fable = false;\n\t\t}\n\n\t\t// Initialize the services map if it wasn't passed in\n\t\t/** @type {Record<string, any>} */\n\t\tthis._PackageFableServiceProvider = libPackage;\n\n\t\t// initialize options and UUID based on whether the fable was passed in or not.\n\t\tif (this.fable)\n\t\t{\n\t\t\tthis.UUID = pFable.getUUID();\n\t\t\tthis.options = (typeof(pOptions) === 'object') ? pOptions\n\t\t\t\t\t\t\t: {};\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// With no fable, check to see if there was an object passed into either of the first two\n\t\t\t// Parameters, and if so, treat it as the options object\n\t\t\tthis.options = ((typeof(pFable) === 'object') && !pFable.isFable) ? pFable\n\t\t\t\t\t\t\t: (typeof(pOptions) === 'object') ? pOptions\n\t\t\t\t\t\t\t: {};\n\t\t\tthis.UUID = `CORE-SVC-${Math.floor((Math.random() * (99999 - 10000)) + 10000)}`\n\t\t}\n\n\t\t// It's expected that the deriving class will set this\n\t\tthis.serviceType = `Unknown-${this.UUID}`;\n\n\t\t// The service hash is used to identify the specific instantiation of the service in the services map\n\t\tthis.Hash = (typeof(pServiceHash) === 'string') ? pServiceHash \n\t\t\t\t\t: (!this.fable && (typeof(pOptions) === 'string')) ? pOptions\n\t\t\t\t\t: `${this.UUID}`;\n\t}\n\n\t/**\n\t * @param {import('fable')} pFable\n\t */\n\tconnectFable(pFable)\n\t{\n\t\tif ((typeof(pFable) !== 'object') || (!pFable.isFable))\n\t\t{\n\t\t\tlet tmpErrorMessage = `Fable Service Provider Base: Cannot connect to Fable, invalid Fable object passed in. The pFable parameter was a [${typeof(pFable)}].}`;\n\t\t\tconsole.log(tmpErrorMessage);\n\t\t\treturn new Error(tmpErrorMessage);\n\t\t}\n\n\t\tif (!this.fable)\n\t\t{\n\t\t\tthis.fable = pFable;\n\t\t}\n\n\t\tif (!this.log)\n\t\t{\n\t\t\tthis.log = this.fable.Logging;\n\t\t}\n\t\tif (!this.services)\n\t\t{\n\t\t\tthis.services = this.fable.services;\n\t\t}\n\n\t\tif (!this.servicesMap)\n\t\t{\n\t\t\tthis.servicesMap = this.fable.servicesMap;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tstatic isFableService = true;\n}\n\nmodule.exports = FableServiceProviderBase;\n\n// This is left here in case we want to go back to having different code/base class for \"core\" services\nmodule.exports.CoreServiceProviderBase = FableServiceProviderBase;\n","module.exports={\n \"name\": \"pict-application\",\n \"version\": \"1.0.33\",\n \"description\": \"Application base class for a pict view-based application\",\n \"main\": \"source/Pict-Application.js\",\n \"scripts\": {\n \"test\": \"npx quack test\",\n \"start\": \"node source/Pict-Application.js\",\n \"coverage\": \"npx quack coverage\",\n \"build\": \"npx quack build\",\n \"docker-dev-build\": \"docker build ./ -f Dockerfile_LUXURYCode -t pict-application-image:local\",\n \"docker-dev-run\": \"docker run -it -d --name pict-application-dev -p 30001:8080 -p 38086:8086 -v \\\"$PWD/.config:/home/coder/.config\\\" -v \\\"$PWD:/home/coder/pict-application\\\" -u \\\"$(id -u):$(id -g)\\\" -e \\\"DOCKER_USER=$USER\\\" pict-application-image:local\",\n \"docker-dev-shell\": \"docker exec -it pict-application-dev /bin/bash\",\n \"tests\": \"npx quack test -g\",\n \"lint\": \"eslint source/**\",\n \"types\": \"tsc -p .\"\n },\n \"types\": \"types/source/Pict-Application.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/stevenvelozo/pict-application.git\"\n },\n \"author\": \"steven velozo <steven@velozo.com>\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/stevenvelozo/pict-application/issues\"\n },\n \"homepage\": \"https://github.com/stevenvelozo/pict-application#readme\",\n \"devDependencies\": {\n \"@eslint/js\": \"^9.28.0\",\n \"browser-env\": \"^3.3.0\",\n \"eslint\": \"^9.28.0\",\n \"pict\": \"^1.0.348\",\n \"pict-provider\": \"^1.0.10\",\n \"pict-view\": \"^1.0.66\",\n \"quackage\": \"^1.0.58\",\n \"typescript\": \"^5.9.3\"\n },\n \"mocha\": {\n \"diff\": true,\n \"extension\": [\n \"js\"\n ],\n \"package\": \"./package.json\",\n \"reporter\": \"spec\",\n \"slow\": \"75\",\n \"timeout\": \"5000\",\n \"ui\": \"tdd\",\n \"watch-files\": [\n \"source/**/*.js\",\n \"test/**/*.js\"\n ],\n \"watch-ignore\": [\n \"lib/vendor\"\n ]\n },\n \"dependencies\": {\n \"fable-serviceproviderbase\": \"^3.0.19\"\n }\n}\n","const libFableServiceBase = require('fable-serviceproviderbase')\n\nconst libPackage = require('../package.json');\n\nconst defaultPictSettings = (\n\t{\n\t\tName: 'DefaultPictApplication',\n\n\t\t// The main \"viewport\" is the view that is used to host our application\n\t\tMainViewportViewIdentifier: 'Default-View',\n\t\tMainViewportRenderableHash: false,\n\t\tMainViewportDestinationAddress: false,\n\t\tMainViewportDefaultDataAddress: false,\n\n\t\t// Whether or not we should automatically render the main viewport and other autorender views after we initialize the pict application\n\t\tAutoSolveAfterInitialize: true,\n\t\tAutoRenderMainViewportViewAfterInitialize: true,\n\t\tAutoRenderViewsAfterInitialize: false,\n\t\tAutoLoginAfterInitialize: false,\n\t\tAutoLoadDataAfterLogin: false,\n\n\t\tConfigurationOnlyViews: [],\n\n\t\tManifests: {},\n\t\t// The prefix to prepend on all template destination hashes\n\t\tIdentifierAddressPrefix: 'PICT-'\n\t});\n\n/**\n * Base class for pict applications.\n */\nclass PictApplication extends libFableServiceBase\n{\n\t/**\n\t * @param {import('fable')} pFable\n\t * @param {Record<string, any>} [pOptions]\n\t * @param {string} [pServiceHash]\n\t */\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpCarryOverConfiguration = (typeof(pFable.settings.PictApplicationConfiguration) === 'object') ? pFable.settings.PictApplicationConfiguration : {};\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(defaultPictSettings)), tmpCarryOverConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\t/** @type {any} */\n\t\tthis.options;\n\t\t/** @type {any} */\n\t\tthis.log;\n\t\t/** @type {import('pict') & import('fable')} */\n\t\tthis.fable;\n\t\t/** @type {string} */\n\t\tthis.UUID;\n\t\t/** @type {string} */\n\t\tthis.Hash;\n\t\t/**\n\t\t * @type {{ [key: string]: any }}\n\t\t */\n\t\tthis.servicesMap;\n\n\t\tthis.serviceType = 'PictApplication';\n\t\t/** @type {Record<string, any>} */\n\t\tthis._Package = libPackage;\n\n\t\t// Convenience and consistency naming\n\t\tthis.pict = this.fable;\n\t\t// Wire in the essential Pict state\n\t\t/** @type {Record<string, any>} */\n\t\tthis.AppData = this.fable.AppData;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.Bundle = this.fable.Bundle;\n\n\t\t/** @type {number} */\n\t\tthis.initializeTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastSolvedTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastLoginTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastMarshalFromViewsTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastMarshalToViewsTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastAutoRenderTimestamp;\n\t\t/** @type {number} */\n\t\tthis.lastLoadDataTimestamp;\n\n\t\t// Load all the manifests for the application\n\t\tlet tmpManifestKeys = Object.keys(this.options.Manifests);\n\t\tif (tmpManifestKeys.length > 0)\n\t\t{\n\t\t\tfor (let i = 0; i < tmpManifestKeys.length; i++)\n\t\t\t{\n\t\t\t\t// Load each manifest\n\t\t\t\tlet tmpManifestKey = tmpManifestKeys[i];\n\t\t\t\tthis.fable.instantiateServiceProvider('Manifest', this.options.Manifests[tmpManifestKey], tmpManifestKey);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Solve All Views */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonPreSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onPreSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonPreSolveAsync(fCallback)\n\t{\n\t\tthis.onPreSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeSolveAsync(fCallback)\n\t{\n\t\tthis.onBeforeSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonSolveAsync(fCallback)\n\t{\n\t\tthis.onSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tsolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} executing solve() function...`)\n\t\t}\n\n\t\t// Walk through any loaded providers and solve them as well.\n\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\tlet tmpProvidersToSolve = [];\n\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t{\n\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\tif (tmpProvider.options.AutoSolveWithApp)\n\t\t\t{\n\t\t\t\ttmpProvidersToSolve.push(tmpProvider);\n\t\t\t}\n\t\t}\n\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpProvidersToSolve.sort((a, b) => { return a.options.AutoSolveOrdinal - b.options.AutoSolveOrdinal; });\n\t\tfor (let i = 0; i < tmpProvidersToSolve.length; i++)\n\t\t{\n\t\t\ttmpProvidersToSolve[i].solve(tmpProvidersToSolve[i]);\n\t\t}\n\n\t\tthis.onBeforeSolve();\n\t\t// Now walk through any loaded views and initialize them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToSolve = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\tif (tmpView.options.AutoInitialize)\n\t\t\t{\n\t\t\t\ttmpViewsToSolve.push(tmpView);\n\t\t\t}\n\t\t}\n\t\t// Sort the views by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpViewsToSolve.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\tfor (let i = 0; i < tmpViewsToSolve.length; i++)\n\t\t{\n\t\t\ttmpViewsToSolve[i].solve();\n\t\t}\n\t\tthis.onSolve();\n\t\tthis.onAfterSolve();\n\t\tthis.lastSolvedTimestamp = this.fable.log.getTimeStamp();\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tsolveAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\ttmpAnticipate.anticipate(this.onBeforeSolveAsync.bind(this));\n\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : false;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\t\t// Walk through any loaded providers and solve them as well.\n\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\tlet tmpProvidersToSolve = [];\n\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t{\n\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\tif (tmpProvider.options.AutoSolveWithApp)\n\t\t\t{\n\t\t\t\ttmpProvidersToSolve.push(tmpProvider);\n\t\t\t}\n\t\t}\n\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpProvidersToSolve.sort((a, b) => { return a.options.AutoSolveOrdinal - b.options.AutoSolveOrdinal; });\n\t\tfor (let i = 0; i < tmpProvidersToSolve.length; i++)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvidersToSolve[i].solveAsync.bind(tmpProvidersToSolve[i]));\n\t\t}\n\n\t\t// Walk through any loaded views and solve them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToSolve = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\tif (tmpView.options.AutoSolveWithApp)\n\t\t\t{\n\t\t\t\ttmpViewsToSolve.push(tmpView);\n\t\t\t}\n\t\t}\n\t\t// Sort the views by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpViewsToSolve.sort((a, b) => { return a.options.AutoSolveOrdinal - b.options.AutoSolveOrdinal; });\n\t\tfor (let i = 0; i < tmpViewsToSolve.length; i++)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpViewsToSolve[i].solveAsync.bind(tmpViewsToSolve[i]));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onSolveAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterSolveAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastSolvedTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterSolveAsync(fCallback)\n\t{\n\t\tthis.onAfterSolve();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Application Login */\n\t/* -------------------------------------------------------------------------- */\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeLoginAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeLoginAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonLoginAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onLoginAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tloginAsync(fCallback)\n\t{\n\t\tconst tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\tlet tmpCallback = fCallback;\n\n\t\tif (typeof(tmpCallback) !== 'function')\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loginAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loginAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeLoginAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onLoginAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterLoginAsync.bind(this));\n\n\t\t// check and see if we should automatically trigger a data load\n\t\tif (this.options.AutoLoadDataAfterLogin)\n\t\t{\n\t\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t\t{\n\t\t\t\tif (!this.isLoggedIn())\n\t\t\t\t{\n\t\t\t\t\treturn fNext();\n\t\t\t\t}\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto loading data after login...`);\n\t\t\t\t}\n\t\t\t\t//TODO: should data load errors funnel here? this creates a weird coupling between login and data load callbacks\n\t\t\t\tthis.loadDataAsync((pError) =>\n\t\t\t\t{\n\t\t\t\t\tfNext(pError);\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loginAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastLoginTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * Check if the application state is logged in. Defaults to true. Override this method in your application based on login requirements.\n\t *\n\t * @return {boolean}\n\t */\n\tisLoggedIn()\n\t{\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterLoginAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterLoginAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Application LoadData */\n\t/* -------------------------------------------------------------------------- */\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeLoadDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeLoadDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonLoadDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onLoadDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tloadDataAsync(fCallback)\n\t{\n\t\tconst tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\tlet tmpCallback = fCallback;\n\n\t\tif (typeof(tmpCallback) !== 'function')\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loadDataAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loadDataAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeLoadDataAsync.bind(this));\n\n\t\t// Walk through any loaded providers and load their data as well.\n\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\tlet tmpProvidersToLoadData = [];\n\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t{\n\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\tif (tmpProvider.options.AutoLoadDataWithApp)\n\t\t\t{\n\t\t\t\ttmpProvidersToLoadData.push(tmpProvider);\n\t\t\t}\n\t\t}\n\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpProvidersToLoadData.sort((a, b) => { return a.options.AutoLoadDataOrdinal - b.options.AutoLoadDataOrdinal; });\n\n\t\tfor (const tmpProvider of tmpProvidersToLoadData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onBeforeLoadDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onLoadDataAsync.bind(this));\n\n\t\t//TODO: think about ways to parallelize these\n\t\tfor (const tmpProvider of tmpProvidersToLoadData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onLoadDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onAfterLoadDataAsync.bind(this));\n\n\t\tfor (const tmpProvider of tmpProvidersToLoadData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onAfterLoadDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.wait(\n\t\t\t/** @param {Error} [pError] */\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} loadDataAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastLoadDataTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterLoadDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterLoadDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Application SaveData */\n\t/* -------------------------------------------------------------------------- */\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeSaveDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeSaveDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonSaveDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onSaveDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tsaveDataAsync(fCallback)\n\t{\n\t\tconst tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\tlet tmpCallback = fCallback;\n\n\t\tif (typeof(tmpCallback) !== 'function')\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} saveDataAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} saveDataAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeSaveDataAsync.bind(this));\n\n\t\t// Walk through any loaded providers and load their data as well.\n\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\tlet tmpProvidersToSaveData = [];\n\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t{\n\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\tif (tmpProvider.options.AutoSaveDataWithApp)\n\t\t\t{\n\t\t\t\ttmpProvidersToSaveData.push(tmpProvider);\n\t\t\t}\n\t\t}\n\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\ttmpProvidersToSaveData.sort((a, b) => { return a.options.AutoSaveDataOrdinal - b.options.AutoSaveDataOrdinal; });\n\n\t\tfor (const tmpProvider of tmpProvidersToSaveData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onBeforeSaveDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onSaveDataAsync.bind(this));\n\n\t\t//TODO: think about ways to parallelize these\n\t\tfor (const tmpProvider of tmpProvidersToSaveData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onSaveDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onAfterSaveDataAsync.bind(this));\n\n\t\tfor (const tmpProvider of tmpProvidersToSaveData)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpProvider.onAfterSaveDataAsync.bind(tmpProvider));\n\t\t}\n\n\t\ttmpAnticipate.wait(\n\t\t\t/** @param {Error} [pError] */\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} saveDataAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastSaveDataTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterSaveDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterSaveDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Initialize Application */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeInitializeAsync(fCallback)\n\t{\n\t\tthis.onBeforeInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonInitializeAsync(fCallback)\n\t{\n\t\tthis.onInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tinitialize()\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} initialize:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tthis.onBeforeInitialize();\n\n\t\t\tif ('ConfigurationOnlyViews' in this.options)\n\t\t\t{\n\t\t\t\t// Load all the configuration only views\n\t\t\t\tfor (let i = 0; i < this.options.ConfigurationOnlyViews.length; i++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpViewIdentifier = (typeof(this.options.ConfigurationOnlyViews[i].ViewIdentifier) === 'undefined') ? `AutoView-${this.fable.getUUID()}`\n\t\t\t\t\t\t\t\t\t\t\t: this.options.ConfigurationOnlyViews[i].ViewIdentifier;\n\t\t\t\t\tthis.log.info(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} adding configuration only view: ${tmpViewIdentifier}`);\n\t\t\t\t\tthis.pict.addView(tmpViewIdentifier, this.options.ConfigurationOnlyViews[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.onInitialize();\n\n\t\t\t// Walk through any loaded providers and initialize them as well.\n\t\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\t\tlet tmpProvidersToInitialize = [];\n\t\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\t\tif (tmpProvider.options.AutoInitialize)\n\t\t\t\t{\n\t\t\t\t\ttmpProvidersToInitialize.push(tmpProvider);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\t\ttmpProvidersToInitialize.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\t\tfor (let i = 0; i < tmpProvidersToInitialize.length; i++)\n\t\t\t{\n\t\t\t\ttmpProvidersToInitialize[i].initialize();\n\t\t\t}\n\n\t\t\t// Now walk through any loaded views and initialize them as well.\n\t\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\t\tlet tmpViewsToInitialize = [];\n\t\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\t\tif (tmpView.options.AutoInitialize)\n\t\t\t\t{\n\t\t\t\t\ttmpViewsToInitialize.push(tmpView);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the views by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\t\ttmpViewsToInitialize.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\t\tfor (let i = 0; i < tmpViewsToInitialize.length; i++)\n\t\t\t{\n\t\t\t\ttmpViewsToInitialize[i].initialize();\n\t\t\t}\n\n\t\t\tthis.onAfterInitialize();\n\t\t\tif (this.options.AutoSolveAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto solving after initialization...`);\n\t\t\t\t}\n\t\t\t\t// Solve the template synchronously\n\t\t\t\tthis.solve();\n\t\t\t}\n\t\t\t// Now check and see if we should automatically render as well\n\t\t\tif (this.options.AutoRenderMainViewportViewAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto rendering after initialization...`);\n\t\t\t\t}\n\t\t\t\t// Render the template synchronously\n\t\t\t\tthis.render();\n\t\t\t}\n\t\t\tthis.initializeTimestamp = this.fable.log.getTimeStamp();\n\t\t\tthis.onCompletionOfInitialize();\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initialize called but initialization is already completed. Aborting.`);\n\t\t\treturn false;\n\t\t}\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tinitializeAsync(fCallback)\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} initializeAsync:`);\n\t\t}\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : false;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initializeAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initializeAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t\tif (this.pict.LogNoisiness > 3)\n\t\t\t{\n\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} beginning initialization...`);\n\t\t\t}\n\n\t\t\tif ('ConfigurationOnlyViews' in this.options)\n\t\t\t{\n\t\t\t\t// Load all the configuration only views\n\t\t\t\tfor (let i = 0; i < this.options.ConfigurationOnlyViews.length; i++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpViewIdentifier = (typeof(this.options.ConfigurationOnlyViews[i].ViewIdentifier) === 'undefined') ? `AutoView-${this.fable.getUUID()}`\n\t\t\t\t\t\t\t\t\t\t\t: this.options.ConfigurationOnlyViews[i].ViewIdentifier;\n\t\t\t\t\tthis.log.info(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} adding configuration only view: ${tmpViewIdentifier}`);\n\t\t\t\t\tthis.pict.addView(tmpViewIdentifier, this.options.ConfigurationOnlyViews[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttmpAnticipate.anticipate(this.onBeforeInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onInitializeAsync.bind(this));\n\n\t\t\t// Walk through any loaded providers and solve them as well.\n\t\t\tlet tmpLoadedProviders = Object.keys(this.pict.providers);\n\t\t\tlet tmpProvidersToInitialize = [];\n\t\t\tfor (let i = 0; i < tmpLoadedProviders.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpProvider = this.pict.providers[tmpLoadedProviders[i]];\n\t\t\t\tif (tmpProvider.options.AutoInitialize)\n\t\t\t\t{\n\t\t\t\t\ttmpProvidersToInitialize.push(tmpProvider);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the providers by their priority (if they are all priority 0, it will end up being add order due to JSON Object Property Key order stuff)\n\t\t\ttmpProvidersToInitialize.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\t\tfor (let i = 0; i < tmpProvidersToInitialize.length; i++)\n\t\t\t{\n\t\t\t\ttmpAnticipate.anticipate(tmpProvidersToInitialize[i].initializeAsync.bind(tmpProvidersToInitialize[i]));\n\t\t\t}\n\n\t\t\t// Now walk through any loaded views and initialize them as well.\n\t\t\t// TODO: Some optimization cleverness could be gained by grouping them into a parallelized async operation, by ordinal.\n\t\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\t\tlet tmpViewsToInitialize = [];\n\t\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\t\tif (tmpView.options.AutoInitialize)\n\t\t\t\t{\n\t\t\t\t\ttmpViewsToInitialize.push(tmpView);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the views by their priority\n\t\t\t// If they are all the default priority 0, it will end up being add order due to JSON Object Property Key order stuff\n\t\t\ttmpViewsToInitialize.sort((a, b) => { return a.options.AutoInitializeOrdinal - b.options.AutoInitializeOrdinal; });\n\t\t\tfor (let i = 0; i < tmpViewsToInitialize.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpView = tmpViewsToInitialize[i];\n\t\t\t\ttmpAnticipate.anticipate(tmpView.initializeAsync.bind(tmpView));\n\t\t\t}\n\n\t\t\ttmpAnticipate.anticipate(this.onAfterInitializeAsync.bind(this));\n\n\t\t\tif (this.options.AutoLoginAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto login (asynchronously) after initialization...`);\n\t\t\t\t}\n\t\t\t\ttmpAnticipate.anticipate(this.loginAsync.bind(this));\n\t\t\t}\n\n\t\t\tif (this.options.AutoSolveAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto solving (asynchronously) after initialization...`);\n\t\t\t\t}\n\t\t\t\ttmpAnticipate.anticipate(this.solveAsync.bind(this));\n\t\t\t}\n\n\t\t\tif (this.options.AutoRenderMainViewportViewAfterInitialize)\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 1)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} auto rendering (asynchronously) after initialization...`);\n\t\t\t\t}\n\t\t\t\ttmpAnticipate.anticipate(this.renderMainViewportAsync.bind(this));\n\t\t\t}\n\n\t\t\ttmpAnticipate.wait(\n\t\t\t\t(pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initializeAsync Error: ${pError.message || pError}`, { stack: pError.stack });\n\t\t\t\t\t}\n\t\t\t\t\tthis.initializeTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} initialization complete.`);\n\t\t\t\t\t}\n\t\t\t\t\treturn tmpCallback();\n\t\t\t\t});\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} async initialize called but initialization is already completed. Aborting.`);\n\t\t\t// TODO: Should this be an error?\n\t\t\treturn this.onCompletionOfInitializeAsync(tmpCallback);\n\t\t}\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterInitializeAsync(fCallback)\n\t{\n\t\tthis.onAfterInitialize();\n\t\treturn fCallback();\n\t}\n\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonCompletionOfInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onCompletionOfInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonCompletionOfInitializeAsync(fCallback)\n\t{\n\t\tthis.onCompletionOfInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Marshal Data From All Views */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeMarshalFromViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeMarshalFromViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeMarshalFromViewsAsync(fCallback)\n\t{\n\t\tthis.onBeforeMarshalFromViews();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonMarshalFromViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onMarshalFromViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonMarshalFromViewsAsync(fCallback)\n\t{\n\t\tthis.onMarshalFromViews();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tmarshalFromViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} executing marshalFromViews() function...`)\n\t\t}\n\t\tthis.onBeforeMarshalFromViews();\n\t\t// Now walk through any loaded views and initialize them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToMarshalFromViews = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\ttmpViewsToMarshalFromViews.push(tmpView);\n\t\t}\n\t\tfor (let i = 0; i < tmpViewsToMarshalFromViews.length; i++)\n\t\t{\n\t\t\ttmpViewsToMarshalFromViews[i].marshalFromView();\n\t\t}\n\t\tthis.onMarshalFromViews();\n\t\tthis.onAfterMarshalFromViews();\n\t\tthis.lastMarshalFromViewsTimestamp = this.fable.log.getTimeStamp();\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tmarshalFromViewsAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : false;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewsAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewsAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeMarshalFromViewsAsync.bind(this));\n\t\t// Walk through any loaded views and marshalFromViews them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToMarshalFromViews = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\ttmpViewsToMarshalFromViews.push(tmpView);\n\t\t}\n\t\tfor (let i = 0; i < tmpViewsToMarshalFromViews.length; i++)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpViewsToMarshalFromViews[i].marshalFromViewAsync.bind(tmpViewsToMarshalFromViews[i]));\n\t\t}\n\t\ttmpAnticipate.anticipate(this.onMarshalFromViewsAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterMarshalFromViewsAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewsAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastMarshalFromViewsTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterMarshalFromViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterMarshalFromViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterMarshalFromViewsAsync(fCallback)\n\t{\n\t\tthis.onAfterMarshalFromViews();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Marshal Data To All Views */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeMarshalToViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeMarshalToViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeMarshalToViewsAsync(fCallback)\n\t{\n\t\tthis.onBeforeMarshalToViews();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonMarshalToViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onMarshalToViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonMarshalToViewsAsync(fCallback)\n\t{\n\t\tthis.onMarshalToViews();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tmarshalToViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} executing marshalToViews() function...`)\n\t\t}\n\t\tthis.onBeforeMarshalToViews();\n\t\t// Now walk through any loaded views and initialize them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToMarshalToViews = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\ttmpViewsToMarshalToViews.push(tmpView);\n\t\t}\n\t\tfor (let i = 0; i < tmpViewsToMarshalToViews.length; i++)\n\t\t{\n\t\t\ttmpViewsToMarshalToViews[i].marshalToView();\n\t\t}\n\t\tthis.onMarshalToViews();\n\t\tthis.onAfterMarshalToViews();\n\t\tthis.lastMarshalToViewsTimestamp = this.fable.log.getTimeStamp();\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tmarshalToViewsAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : false;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewsAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewsAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeMarshalToViewsAsync.bind(this));\n\t\t// Walk through any loaded views and marshalToViews them as well.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\tlet tmpViewsToMarshalToViews = [];\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\ttmpViewsToMarshalToViews.push(tmpView);\n\t\t}\n\t\tfor (let i = 0; i < tmpViewsToMarshalToViews.length; i++)\n\t\t{\n\t\t\ttmpAnticipate.anticipate(tmpViewsToMarshalToViews[i].marshalToViewAsync.bind(tmpViewsToMarshalToViews[i]));\n\t\t}\n\t\ttmpAnticipate.anticipate(this.onMarshalToViewsAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterMarshalToViewsAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewsAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastMarshalToViewsTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterMarshalToViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterMarshalToViews:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterMarshalToViewsAsync(fCallback)\n\t{\n\t\tthis.onAfterMarshalToViews();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Render View */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * @return {boolean}\n\t */\n\tonBeforeRender()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onBeforeRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonBeforeRenderAsync(fCallback)\n\t{\n\t\tthis.onBeforeRender();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {string} [pViewIdentifier] - The hash of the view to render. By default, the main viewport view is rendered.\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string} [pTemplateDataAddress] - The address where the data for the template is stored.\n\t *\n\t * TODO: Should we support objects for pTemplateDataAddress for parity with pict-view?\n\t */\n\trender(pViewIdentifier, pRenderableHash, pRenderDestinationAddress, pTemplateDataAddress)\n\t{\n\t\tlet tmpViewIdentifier = (typeof(pViewIdentifier) !== 'string') ? this.options.MainViewportViewIdentifier : pViewIdentifier;\n\t\tlet tmpRenderableHash = (typeof(pRenderableHash) !== 'string') ? this.options.MainViewportRenderableHash : pRenderableHash;\n\t\tlet tmpRenderDestinationAddress = (typeof(pRenderDestinationAddress) !== 'string') ? this.options.MainViewportDestinationAddress : pRenderDestinationAddress;\n\t\tlet tmpTemplateDataAddress = (typeof(pTemplateDataAddress) !== 'string') ? this.options.MainViewportDefaultDataAddress : pTemplateDataAddress;\n\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} VIEW Renderable[${tmpRenderableHash}] Destination[${tmpRenderDestinationAddress}] TemplateDataAddress[${tmpTemplateDataAddress}] render:`);\n\t\t}\n\n\t\tthis.onBeforeRender();\n\n\t\t// Now get the view (by hash) from the loaded views\n\t\tlet tmpView = (typeof (tmpViewIdentifier) === 'string') ? this.servicesMap.PictView[tmpViewIdentifier] : false;\n\t\tif (!tmpView)\n\t\t{\n\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} could not render from View ${tmpViewIdentifier} because it is not a valid view.`);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.onRender();\n\n\t\ttmpView.render(tmpRenderableHash, tmpRenderDestinationAddress, tmpTemplateDataAddress);\n\n\t\tthis.onAfterRender();\n\n\t\treturn true;\n\t}\n\t/**\n\t * @return {boolean}\n\t */\n\tonRender()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonRenderAsync(fCallback)\n\t{\n\t\tthis.onRender();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {string|((error?: Error) => void)} pViewIdentifier - The hash of the view to render. By default, the main viewport view is rendered. (or the callback)\n\t * @param {string|((error?: Error) => void)} [pRenderableHash] - The hash of the renderable to render. (or the callback)\n\t * @param {string|((error?: Error) => void)} [pRenderDestinationAddress] - The address where the renderable will be rendered. (or the callback)\n\t * @param {string|((error?: Error) => void)} [pTemplateDataAddress] - The address where the data for the template is stored. (or the callback)\n\t * @param {(error?: Error) => void} [fCallback] - The callback, if all other parameters are provided.\n\t *\n\t * TODO: Should we support objects for pTemplateDataAddress for parity with pict-view?\n\t */\n\trenderAsync(pViewIdentifier, pRenderableHash, pRenderDestinationAddress, pTemplateDataAddress, fCallback)\n\t{\n\t\tlet tmpViewIdentifier = (typeof(pViewIdentifier) !== 'string') ? this.options.MainViewportViewIdentifier : pViewIdentifier;\n\t\tlet tmpRenderableHash = (typeof(pRenderableHash) !== 'string') ? this.options.MainViewportRenderableHash : pRenderableHash;\n\t\tlet tmpRenderDestinationAddress = (typeof(pRenderDestinationAddress) !== 'string') ? this.options.MainViewportDestinationAddress : pRenderDestinationAddress;\n\t\tlet tmpTemplateDataAddress = (typeof(pTemplateDataAddress) !== 'string') ? this.options.MainViewportDefaultDataAddress : pTemplateDataAddress;\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback :\n\t\t\t\t\t\t\t(typeof(pTemplateDataAddress) === 'function') ? pTemplateDataAddress :\n\t\t\t\t\t\t\t(typeof(pRenderDestinationAddress) === 'function') ? pRenderDestinationAddress :\n\t\t\t\t\t\t\t(typeof(pRenderableHash) === 'function') ? pRenderableHash :\n\t\t\t\t\t\t\t(typeof(pViewIdentifier) === 'function') ? pViewIdentifier :\n\t\t\t\t\t\t\tfalse;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} VIEW Renderable[${tmpRenderableHash}] Destination[${tmpRenderDestinationAddress}] TemplateDataAddress[${tmpTemplateDataAddress}] renderAsync:`);\n\t\t}\n\n\t\tlet tmpRenderAnticipate = this.fable.newAnticipate();\n\n\t\ttmpRenderAnticipate.anticipate(this.onBeforeRenderAsync.bind(this));\n\n\t\tlet tmpView = (typeof (tmpViewIdentifier) === 'string') ? this.servicesMap.PictView[tmpViewIdentifier] : false;\n\t\tif (!tmpView)\n\t\t{\n\t\t\tlet tmpErrorMessage = `PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} could not asynchronously render from View ${tmpViewIdentifier} because it is not a valid view.`;\n\t\t\tif (this.pict.LogNoisiness > 3)\n\t\t\t{\n\t\t\t\tthis.log.error(tmpErrorMessage);\n\t\t\t}\n\t\t\treturn tmpCallback(new Error(tmpErrorMessage));\n\t\t}\n\n\t\ttmpRenderAnticipate.anticipate(this.onRenderAsync.bind(this));\n\n\t\ttmpRenderAnticipate.anticipate(\n\t\t\t(fNext) =>\n\t\t\t{\n\t\t\t\ttmpView.renderAsync.call(tmpView, tmpRenderableHash, tmpRenderDestinationAddress, tmpTemplateDataAddress, fNext);\n\t\t\t});\n\n\t\ttmpRenderAnticipate.anticipate(this.onAfterRenderAsync.bind(this));\n\n\t\treturn tmpRenderAnticipate.wait(tmpCallback);\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tonAfterRender()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} onAfterRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\tonAfterRenderAsync(fCallback)\n\t{\n\t\tthis.onAfterRender();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\trenderMainViewport()\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderMainViewport:`);\n\t\t}\n\n\t\treturn this.render();\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\trenderMainViewportAsync(fCallback)\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow APPLICATION [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderMainViewportAsync:`);\n\t\t}\n\n\t\treturn this.renderAsync(fCallback);\n\t}\n\t/**\n\t * @return {void}\n\t */\n\trenderAutoViews()\n\t{\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} beginning renderAutoViews...`);\n\t\t}\n\t\t// Now walk through any loaded views and sort them by the AutoRender ordinal\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\t// Sort the views by their priority\n\t\t// If they are all the default priority 0, it will end up being add order due to JSON Object Property Key order stuff\n\t\ttmpLoadedViews.sort((a, b) =>\n\t\t{\n\t\t\treturn this.pict.views[a].options.AutoRenderOrdinal - this.pict.views[b].options.AutoRenderOrdinal;\n\t\t});\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\tif (tmpView.options.AutoRender)\n\t\t\t{\n\t\t\t\ttmpView.render();\n\t\t\t}\n\t\t}\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAutoViewsAsync complete.`);\n\t\t}\n\t}\n\t/**\n\t * @param {(error?: Error) => void} fCallback\n\t */\n\trenderAutoViewsAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback :\n\t\t\t\t\t\t\tfalse;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAutoViewsAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAutoViewsAsync Auto Callback Error: ${pError}`, pError)\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} beginning renderAutoViewsAsync...`);\n\t\t}\n\n\t\t// Now walk through any loaded views and sort them by the AutoRender ordinal\n\t\t// TODO: Some optimization cleverness could be gained by grouping them into a parallelized async operation, by ordinal.\n\t\tlet tmpLoadedViews = Object.keys(this.pict.views);\n\t\t// Sort the views by their priority\n\t\t// If they are all the default priority 0, it will end up being add order due to JSON Object Property Key order stuff\n\t\ttmpLoadedViews.sort((a, b) =>\n\t\t{\n\t\t\treturn this.pict.views[a].options.AutoRenderOrdinal - this.pict.views[b].options.AutoRenderOrdinal;\n\t\t});\n\t\tfor (let i = 0; i < tmpLoadedViews.length; i++)\n\t\t{\n\t\t\tlet tmpView = this.pict.views[tmpLoadedViews[i]];\n\t\t\tif (tmpView.options.AutoRender)\n\t\t\t{\n\t\t\t\ttmpAnticipate.anticipate(tmpView.renderAsync.bind(tmpView));\n\t\t\t}\n\t\t}\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tthis.lastAutoRenderTimestamp = this.fable.log.getTimeStamp();\n\t\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictApp [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAutoViewsAsync complete.`);\n\t\t\t\t}\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tget isPictApplication()\n\t{\n\t\treturn true;\n\t}\n}\n\nmodule.exports = PictApplication;\n","module.exports={\n \"name\": \"pict-provider\",\n \"version\": \"1.0.12\",\n \"description\": \"Pict Provider Base Class\",\n \"main\": \"source/Pict-Provider.js\",\n \"scripts\": {\n \"start\": \"node source/Pict-Provider.js\",\n \"test\": \"npx quack test\",\n \"tests\": \"npx quack test -g\",\n \"coverage\": \"npx quack coverage\",\n \"build\": \"npx quack build\",\n \"docker-dev-build\": \"docker build ./ -f Dockerfile_LUXURYCode -t pict-provider-image:local\",\n \"docker-dev-run\": \"docker run -it -d --name pict-provider-dev -p 24125:8080 -p 30027:8086 -v \\\"$PWD/.config:/home/coder/.config\\\" -v \\\"$PWD:/home/coder/pict-provider\\\" -u \\\"$(id -u):$(id -g)\\\" -e \\\"DOCKER_USER=$USER\\\" pict-provider-image:local\",\n \"docker-dev-shell\": \"docker exec -it pict-provider-dev /bin/bash\",\n \"lint\": \"eslint source/**\",\n \"types\": \"tsc -p .\"\n },\n \"types\": \"types/source/Pict-Provider.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/stevenvelozo/pict-provider.git\"\n },\n \"author\": \"steven velozo <steven@velozo.com>\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/stevenvelozo/pict-provider/issues\"\n },\n \"homepage\": \"https://github.com/stevenvelozo/pict-provider#readme\",\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.1\",\n \"eslint\": \"^9.39.1\",\n \"pict\": \"^1.0.351\",\n \"quackage\": \"^1.0.58\",\n \"typescript\": \"^5.9.3\"\n },\n \"dependencies\": {\n \"fable-serviceproviderbase\": \"^3.0.19\"\n },\n \"mocha\": {\n \"diff\": true,\n \"extension\": [\n \"js\"\n ],\n \"package\": \"./package.json\",\n \"reporter\": \"spec\",\n \"slow\": \"75\",\n \"timeout\": \"5000\",\n \"ui\": \"tdd\",\n \"watch-files\": [\n \"source/**/*.js\",\n \"test/**/*.js\"\n ],\n \"watch-ignore\": [\n \"lib/vendor\"\n ]\n }\n}\n","const libFableServiceBase = require('fable-serviceproviderbase');\n\nconst libPackage = require('../package.json');\n\nconst defaultPictProviderSettings = (\n\t{\n\t\tProviderIdentifier: false,\n\n\t\t// If this is set to true, when the App initializes this will.\n\t\t// After the App initializes, initialize will be called as soon as it's added.\n\t\tAutoInitialize: true,\n\t\tAutoInitializeOrdinal: 0,\n\n\t\tAutoLoadDataWithApp: true,\n\t\tAutoLoadDataOrdinal: 0,\n\n\t\tAutoSolveWithApp: true,\n\t\tAutoSolveOrdinal: 0,\n\n\t\tManifests: {},\n\n\t\tTemplates: []\n\t});\n\nclass PictProvider extends libFableServiceBase\n{\n\t/**\n\t * @param {import('fable')} pFable - The Fable instance.\n\t * @param {Record<string, any>} [pOptions] - The options for the provider.\n\t * @param {string} [pServiceHash] - The service hash for the provider.\n\t */\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\t// Intersect default options, parent constructor, service information\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(defaultPictProviderSettings)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\t/** @type {import('fable') & import('pict') & { instantiateServiceProviderWithoutRegistration(pServiceType: string, pOptions?: Record<string, any>, pCustomServiceHash?: string): any }} */\n\t\tthis.fable;\n\t\t/** @type {import('fable') & import('pict') & { instantiateServiceProviderWithoutRegistration(pServiceType: string, pOptions?: Record<string, any>, pCustomServiceHash?: string): any }} */\n\t\tthis.pict;\n\t\t/** @type {any} */\n\t\tthis.log;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.options;\n\t\t/** @type {string} */\n\t\tthis.UUID;\n\t\t/** @type {string} */\n\t\tthis.Hash;\n\n\t\tif (!this.options.ProviderIdentifier)\n\t\t{\n\t\t\tthis.options.ProviderIdentifier = `AutoProviderID-${this.fable.getUUID()}`;\n\t\t}\n\n\t\tthis.serviceType = 'PictProvider';\n\t\t/** @type {Record<string, any>} */\n\t\tthis._Package = libPackage;\n\n\t\t// Convenience and consistency naming\n\t\tthis.pict = this.fable;\n\n\t\t// Wire in the essential Pict application state\n\t\t/** @type {Record<string, any>} */\n\t\tthis.AppData = this.pict.AppData;\n\t\t/** @type {Record<string, any>} */\n\t\tthis.Bundle = this.pict.Bundle;\n\n\t\tthis.initializeTimestamp = false;\n\t\tthis.lastSolvedTimestamp = false;\n\n\t\tfor (let i = 0; i < this.options.Templates.length; i++)\n\t\t{\n\t\t\tlet tmpDefaultTemplate = this.options.Templates[i];\n\n\t\t\tif (!tmpDefaultTemplate.hasOwnProperty('Postfix') || !tmpDefaultTemplate.hasOwnProperty('Template'))\n\t\t\t{\n\t\t\t\tthis.log.error(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} could not load Default Template ${i} in the options array.`, tmpDefaultTemplate);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!tmpDefaultTemplate.Source)\n\t\t\t\t{\n\t\t\t\t\ttmpDefaultTemplate.Source = `PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} options object.`;\n\t\t\t\t}\n\t\t\t\tthis.pict.TemplateProvider.addDefaultTemplate(tmpDefaultTemplate.Prefix, tmpDefaultTemplate.Postfix, tmpDefaultTemplate.Template, tmpDefaultTemplate.Source);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Initialization */\n\t/* -------------------------------------------------------------------------- */\n\tonBeforeInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onBeforeInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after pre-pinitialization.\n\t *\n\t * @return {void}\n\t */\n\tonBeforeInitializeAsync(fCallback)\n\t{\n\t\tthis.onBeforeInitialize();\n\t\treturn fCallback();\n\t}\n\n\tonInitialize()\n\t{\n\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after initialization.\n\t *\n\t * @return {void}\n\t */\n\tonInitializeAsync(fCallback)\n\t{\n\t\tthis.onInitialize();\n\t\treturn fCallback();\n\t}\n\n\tinitialize()\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow PROVIDER [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initialize:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tthis.onBeforeInitialize();\n\t\t\tthis.onInitialize();\n\t\t\tthis.onAfterInitialize();\n\t\t\tthis.initializeTimestamp = this.pict.log.getTimeStamp();\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initialize called but initialization is already completed. Aborting.`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after initialization.\n\t *\n\t * @return {void}\n\t */\n\tinitializeAsync(fCallback)\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow PROVIDER [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initializeAsync:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t{\n\t\t\t\tthis.log.info(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} beginning initialization...`);\n\t\t\t}\n\n\t\t\ttmpAnticipate.anticipate(this.onBeforeInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onAfterInitializeAsync.bind(this));\n\n\t\t\ttmpAnticipate.wait(\n\t\t\t\t(pError) =>\n\t\t\t\t{\n\t\t\t\t\tthis.initializeTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initialization failed: ${pError.message || pError}`, { Stack: pError.stack });\n\t\t\t\t\t}\n\t\t\t\t\telse if (this.pict.LogNoisiness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.info(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} initialization complete.`);\n\t\t\t\t\t}\n\t\t\t\t\treturn fCallback();\n\t\t\t\t})\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} async initialize called but initialization is already completed. Aborting.`);\n\t\t\t// TODO: Should this be an error?\n\t\t\treturn fCallback();\n\t\t}\n\t}\n\n\tonAfterInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onAfterInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after initialization.\n\t *\n\t * @return {void}\n\t */\n\tonAfterInitializeAsync(fCallback)\n\t{\n\t\tthis.onAfterInitialize();\n\t\treturn fCallback();\n\t}\n\n\tonPreRender()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onPreRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after pre-render.\n\t *\n\t * @return {void}\n\t */\n\tonPreRenderAsync(fCallback)\n\t{\n\t\tthis.onPreRender();\n\t\treturn fCallback();\n\t}\n\n\trender()\n\t{\n\t\treturn this.onPreRender();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after render.\n\t *\n\t * @return {void}\n\t */\n\trenderAsync(fCallback)\n\t{\n\t\tthis.onPreRender();\n\t\treturn fCallback();\n\t}\n\n\tonPreSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onPreSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after pre-solve.\n\t *\n\t * @return {void}\n\t */\n\tonPreSolveAsync(fCallback)\n\t{\n\t\tthis.onPreSolve();\n\t\treturn fCallback();\n\t}\n\n\tsolve()\n\t{\n\t\treturn this.onPreSolve();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after solve.\n\t *\n\t * @return {void}\n\t */\n\tsolveAsync(fCallback)\n\t{\n\t\tthis.onPreSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data pre-load.\n\t */\n\tonBeforeLoadDataAsync(fCallback)\n\t{\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Hook to allow the provider to load data during application data load.\n\t *\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data load.\n\t */\n\tonLoadDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onLoadDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data post-load.\n\t */\n\tonAfterLoadDataAsync(fCallback)\n\t{\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data pre-load.\n\t *\n\t * @return {void}\n\t */\n\tonBeforeSaveDataAsync(fCallback)\n\t{\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Hook to allow the provider to load data during application data load.\n\t *\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data load.\n\t *\n\t * @return {void}\n\t */\n\tonSaveDataAsync(fCallback)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictProvider [${this.UUID}]::[${this.Hash}] ${this.options.ProviderIdentifier} onSaveDataAsync:`);\n\t\t}\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * @param {(pError?: Error) => void} fCallback - The callback to call after the data post-load.\n\t *\n\t * @return {void}\n\t */\n\tonAfterSaveDataAsync(fCallback)\n\t{\n\t\treturn fCallback();\n\t}\n}\n\nmodule.exports = PictProvider;\n","// The container for all the Pict-Section-Flow related code.\n\n// The main flow diagram view class\nmodule.exports = require('./views/PictView-Flow.js');\n\n// Node rendering view\nmodule.exports.PictViewFlowNode = require('./views/PictView-Flow-Node.js');\n\n// Toolbar views\nmodule.exports.PictViewFlowToolbar = require('./views/PictView-Flow-Toolbar.js');\nmodule.exports.PictViewFlowFloatingToolbar = require('./views/PictView-Flow-FloatingToolbar.js');\n\n// Services\nmodule.exports.PictServiceFlowInteractionManager = require('./services/PictService-Flow-InteractionManager.js');\nmodule.exports.PictServiceFlowConnectionRenderer = require('./services/PictService-Flow-ConnectionRenderer.js');\nmodule.exports.PictServiceFlowTether = require('./services/PictService-Flow-Tether.js');\nmodule.exports.PictServiceFlowLayout = require('./services/PictService-Flow-Layout.js');\nmodule.exports.PictServiceFlowPathGenerator = require('./services/PictService-Flow-PathGenerator.js');\nmodule.exports.PictServiceFlowViewportManager = require('./services/PictService-Flow-ViewportManager.js');\nmodule.exports.PictServiceFlowSelectionManager = require('./services/PictService-Flow-SelectionManager.js');\nmodule.exports.PictServiceFlowPanelManager = require('./services/PictService-Flow-PanelManager.js');\nmodule.exports.PictServiceFlowDataManager = require('./services/PictService-Flow-DataManager.js');\nmodule.exports.PictServiceFlowConnectionHandleManager = require('./services/PictService-Flow-ConnectionHandleManager.js');\nmodule.exports.PictServiceFlowRenderManager = require('./services/PictService-Flow-RenderManager.js');\nmodule.exports.PictServiceFlowPortRenderer = require('./services/PictService-Flow-PortRenderer.js');\n\n// Providers\nmodule.exports.PictProviderFlowNodeTypes = require('./providers/PictProvider-Flow-NodeTypes.js');\nmodule.exports.PictProviderFlowEventHandler = require('./providers/PictProvider-Flow-EventHandler.js');\nmodule.exports.PictProviderFlowLayouts = require('./providers/PictProvider-Flow-Layouts.js');\nmodule.exports.PictProviderFlowSVGHelpers = require('./providers/PictProvider-Flow-SVGHelpers.js');\nmodule.exports.PictProviderFlowGeometry = require('./providers/PictProvider-Flow-Geometry.js');\nmodule.exports.PictProviderFlowPanelChrome = require('./providers/PictProvider-Flow-PanelChrome.js');\nmodule.exports.PictProviderFlowCSS = require('./providers/PictProvider-Flow-CSS.js');\nmodule.exports.PictProviderFlowIcons = require('./providers/PictProvider-Flow-Icons.js');\nmodule.exports.PictProviderFlowConnectorShapes = require('./providers/PictProvider-Flow-ConnectorShapes.js');\n\n// FlowCard base class\nmodule.exports.PictFlowCard = require('./PictFlowCard.js');\n\n// FlowCardPropertiesPanel base class and panel types\nmodule.exports.PictFlowCardPropertiesPanel = require('./PictFlowCardPropertiesPanel.js');\nmodule.exports.FlowCardPropertiesPanelTemplate = require('./panels/FlowCardPropertiesPanel-Template.js');\nmodule.exports.FlowCardPropertiesPanelMarkdown = require('./panels/FlowCardPropertiesPanel-Markdown.js');\nmodule.exports.FlowCardPropertiesPanelForm = require('./panels/FlowCardPropertiesPanel-Form.js');\nmodule.exports.FlowCardPropertiesPanelView = require('./panels/FlowCardPropertiesPanel-View.js');\n\n// Properties panel renderer view\nmodule.exports.PictViewFlowPropertiesPanel = require('./views/PictView-Flow-PropertiesPanel.js');\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictFlowCard - Base class for flow diagram cards.\n *\n * Developers create subclasses of PictFlowCard to define reusable node types\n * that appear in the flow palette. Each card describes a discrete operation\n * (e.g. \"If-Then-Else\", \"File Read\") with configurable inputs, outputs, and\n * metadata. Pict-Section-Flow uses registered cards to build a palette for\n * the user to drag onto the graph.\n *\n * Configurable properties:\n * - Title (string, required) - Display name shown on the node\n * - Name (string, optional) - Longer descriptive name\n * - Code (string, required) - Short identifier (e.g. \"ITE\", \"SW\")\n * - Description (string, optional) - Brief explanation of what the card does\n * - Icon (string, optional) - Icon identifier or emoji\n * - PreviewImage (string, optional) - URL to a preview/thumbnail image\n * - Documentation (string, optional) - URL or inline documentation text\n * - Tooltip (string, optional) - Hover tooltip text\n * - Inputs (array) - Named input ports, each with:\n * - Name (string) - Port label\n * - Side (string) - Port side ('left', 'top', etc.)\n * - MinimumInputCount (number) - Minimum connections accepted (default 0)\n * - MaximumInputCount (number) - Maximum connections accepted (default -1, unlimited)\n * - Outputs (array) - Named output ports\n * - Enabled (boolean) - Whether this card is available in the palette\n * - PropertiesPanel (object, optional) - Configuration for the on-graph properties panel\n * - PanelType (string) - 'Template', 'Markdown', 'Form', or 'View'\n * - DefaultWidth (number) - Panel width (default 300)\n * - DefaultHeight (number) - Panel height (default 200)\n * - Title (string) - Panel title bar text\n * - Configuration (object) - Panel-type-specific configuration\n * - BodyContent (object, optional) - Custom content rendered in the node body area\n * - ContentType (string) - 'svg', 'html', or 'canvas'\n * - Template (string) - Pict template string (html/svg only)\n * - TemplateHash (string) - Registered template hash (takes precedence over Template)\n * - Templates (array) - Template definitions to register [{Hash, Template}]\n * - RenderCallback (function)- Imperative render callback (required for canvas)\n * - Padding (number) - Inner padding in pixels (default 2)\n * - ShowTypeLabel (boolean, default true) - Whether to show the type label/code badge on hover\n * - PortLabelsOnHover (boolean, default false) - When true, port labels only appear on hover\n * - PortLabelsVertical (boolean, default false) - When true, port labels render vertically\n * - PortLabelPadding (boolean, default false) - When true, port labels have extra padding to avoid overlapping body content\n * - LabelsInFront (boolean, default true) - When true, labels render in front of body content; when false, behind\n *\n * Usage:\n * class MyCard extends PictFlowCard {\n * constructor(pFable, pOptions, pServiceHash) {\n * super(pFable, pOptions, pServiceHash);\n * this.cardTitle = 'My Card';\n * this.cardCode = 'MC';\n * this.cardInputs = [{ Name: 'Data', Side: 'left', MinimumInputCount: 1, MaximumInputCount: 1 }];\n * this.cardOutputs = [{ Name: 'Result', Side: 'right' }];\n * }\n * }\n */\nclass PictFlowCard extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, PictFlowCard.default_configuration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCard';\n\n\t\t// --- Card metadata ---\n\t\tthis.cardTitle = (tmpOptions.Title) ? tmpOptions.Title : 'Card';\n\t\tthis.cardName = (tmpOptions.Name) ? tmpOptions.Name : false;\n\t\tthis.cardCode = (tmpOptions.Code) ? tmpOptions.Code : '';\n\t\tthis.cardDescription = (tmpOptions.Description) ? tmpOptions.Description : false;\n\t\tthis.cardIcon = (tmpOptions.Icon) ? tmpOptions.Icon : false;\n\t\tthis.cardPreviewImage = (tmpOptions.PreviewImage) ? tmpOptions.PreviewImage : false;\n\t\tthis.cardDocumentation = (tmpOptions.Documentation) ? tmpOptions.Documentation : false;\n\t\tthis.cardTooltip = (tmpOptions.Tooltip) ? tmpOptions.Tooltip : false;\n\t\tthis.cardHelp = (tmpOptions.Help) ? tmpOptions.Help : false;\n\n\t\t// --- Card enabled state ---\n\t\tthis.cardEnabled = (typeof tmpOptions.Enabled === 'boolean') ? tmpOptions.Enabled : true;\n\n\t\t// --- Card appearance ---\n\t\tthis.cardTitleBarColor = (tmpOptions.TitleBarColor) ? tmpOptions.TitleBarColor : '#2c3e50';\n\t\tthis.cardBodyStyle = (tmpOptions.BodyStyle) ? tmpOptions.BodyStyle : {};\n\t\tthis.cardWidth = (typeof tmpOptions.Width === 'number') ? tmpOptions.Width : 180;\n\t\tthis.cardHeight = (typeof tmpOptions.Height === 'number') ? tmpOptions.Height : 80;\n\t\tthis.cardCategory = (tmpOptions.Category) ? tmpOptions.Category : 'General';\n\n\t\t// --- Input and Output port definitions ---\n\t\t// Inputs: [{ Name: 'In', Side: 'left', MinimumInputCount: 0, MaximumInputCount: -1 }]\n\t\t// Outputs: [{ Name: 'Out', Side: 'right' }]\n\t\tthis.cardInputs = Array.isArray(tmpOptions.Inputs) ? tmpOptions.Inputs : [];\n\t\tthis.cardOutputs = Array.isArray(tmpOptions.Outputs) ? tmpOptions.Outputs : [];\n\n\t\t// --- Properties panel configuration ---\n\t\tthis.cardPropertiesPanel = (tmpOptions.PropertiesPanel && typeof tmpOptions.PropertiesPanel === 'object')\n\t\t\t? tmpOptions.PropertiesPanel\n\t\t\t: null;\n\n\t\t// --- Body content configuration ---\n\t\tthis.cardBodyContent = (tmpOptions.BodyContent && typeof tmpOptions.BodyContent === 'object')\n\t\t\t? tmpOptions.BodyContent\n\t\t\t: null;\n\n\t\t// --- Label display booleans ---\n\t\tthis.cardShowTypeLabel = (typeof tmpOptions.ShowTypeLabel === 'boolean') ? tmpOptions.ShowTypeLabel : true;\n\t\tthis.cardPortLabelsOnHover = (typeof tmpOptions.PortLabelsOnHover === 'boolean') ? tmpOptions.PortLabelsOnHover : false;\n\t\tthis.cardPortLabelsVertical = (typeof tmpOptions.PortLabelsVertical === 'boolean') ? tmpOptions.PortLabelsVertical : false;\n\t\tthis.cardPortLabelPadding = (typeof tmpOptions.PortLabelPadding === 'boolean') ? tmpOptions.PortLabelPadding : false;\n\t\tthis.cardPortLabelsOutside = (typeof tmpOptions.PortLabelsOutside === 'boolean') ? tmpOptions.PortLabelsOutside : false;\n\t\tthis.cardLabelsInFront = (typeof tmpOptions.LabelsInFront === 'boolean') ? tmpOptions.LabelsInFront : true;\n\t}\n\n\t/**\n\t * Generate the node type configuration object for the NodeTypes provider.\n\t * This converts the card's properties into the format expected by\n\t * PictProviderFlowNodeTypes.registerNodeType().\n\t *\n\t * @returns {Object} Node type configuration\n\t */\n\tgetNodeTypeConfiguration()\n\t{\n\t\tlet tmpPorts = [];\n\n\t\t// Build input ports\n\t\tfor (let i = 0; i < this.cardInputs.length; i++)\n\t\t{\n\t\t\tlet tmpInput = this.cardInputs[i];\n\t\t\tlet tmpPort =\n\t\t\t\t{\n\t\t\t\t\tHash: null,\n\t\t\t\t\tDirection: 'input',\n\t\t\t\t\tSide: tmpInput.Side || 'left',\n\t\t\t\t\tLabel: tmpInput.Name || `In ${i + 1}`,\n\t\t\t\t\tMinimumInputCount: (typeof tmpInput.MinimumInputCount === 'number') ? tmpInput.MinimumInputCount : 0,\n\t\t\t\t\tMaximumInputCount: (typeof tmpInput.MaximumInputCount === 'number') ? tmpInput.MaximumInputCount : -1\n\t\t\t\t};\n\t\t\tif (tmpInput.PortType)\n\t\t\t{\n\t\t\t\ttmpPort.PortType = tmpInput.PortType;\n\t\t\t}\n\t\t\tif (tmpInput.DataType)\n\t\t\t{\n\t\t\t\ttmpPort.DataType = tmpInput.DataType;\n\t\t\t}\n\t\t\ttmpPorts.push(tmpPort);\n\t\t}\n\n\t\t// Build output ports\n\t\tfor (let i = 0; i < this.cardOutputs.length; i++)\n\t\t{\n\t\t\tlet tmpOutput = this.cardOutputs[i];\n\t\t\tlet tmpOutPort =\n\t\t\t\t{\n\t\t\t\t\tHash: null,\n\t\t\t\t\tDirection: 'output',\n\t\t\t\t\tSide: tmpOutput.Side || 'right',\n\t\t\t\t\tLabel: tmpOutput.Name || `Out ${i + 1}`\n\t\t\t\t};\n\t\t\tif (tmpOutput.PortType)\n\t\t\t{\n\t\t\t\ttmpOutPort.PortType = tmpOutput.PortType;\n\t\t\t}\n\t\t\tif (tmpOutput.DataType)\n\t\t\t{\n\t\t\t\ttmpOutPort.DataType = tmpOutput.DataType;\n\t\t\t}\n\t\t\ttmpPorts.push(tmpOutPort);\n\t\t}\n\n\t\t// If no ports were defined, provide sensible defaults\n\t\tif (tmpPorts.length === 0)\n\t\t{\n\t\t\ttmpPorts.push({ Hash: null, Direction: 'input', Side: 'left', Label: 'In' });\n\t\t\ttmpPorts.push({ Hash: null, Direction: 'output', Side: 'right', Label: 'Out' });\n\t\t}\n\n\t\tlet tmpResult =\n\t\t{\n\t\t\tHash: this.cardCode,\n\t\t\tLabel: this.cardTitle,\n\t\t\tDefaultWidth: this.cardWidth,\n\t\t\tDefaultHeight: this.cardHeight,\n\t\t\tDefaultPorts: tmpPorts,\n\t\t\tTitleBarColor: this.cardTitleBarColor,\n\t\t\tBodyStyle: JSON.parse(JSON.stringify(this.cardBodyStyle)),\n\t\t\t// Extended FlowCard metadata stored alongside the type\n\t\t\tCardMetadata:\n\t\t\t{\n\t\t\t\tName: this.cardName,\n\t\t\t\tCode: this.cardCode,\n\t\t\t\tDescription: this.cardDescription,\n\t\t\t\tIcon: this.cardIcon,\n\t\t\t\tPreviewImage: this.cardPreviewImage,\n\t\t\t\tDocumentation: this.cardDocumentation,\n\t\t\t\tTooltip: this.cardTooltip,\n\t\t\t\tEnabled: this.cardEnabled,\n\t\t\t\tCategory: this.cardCategory,\n\t\t\t\tHelp: this.cardHelp\n\t\t\t}\n\t\t};\n\n\t\t// Include label display booleans\n\t\ttmpResult.ShowTypeLabel = this.cardShowTypeLabel;\n\t\ttmpResult.PortLabelsOnHover = this.cardPortLabelsOnHover;\n\t\ttmpResult.PortLabelsVertical = this.cardPortLabelsVertical;\n\t\ttmpResult.PortLabelPadding = this.cardPortLabelPadding;\n\t\ttmpResult.PortLabelsOutside = this.cardPortLabelsOutside;\n\t\ttmpResult.LabelsInFront = this.cardLabelsInFront;\n\n\t\t// Include properties panel config if defined\n\t\tif (this.cardPropertiesPanel)\n\t\t{\n\t\t\ttmpResult.PropertiesPanel = JSON.parse(JSON.stringify(this.cardPropertiesPanel));\n\t\t}\n\n\t\t// Include body content config if defined\n\t\tif (this.cardBodyContent)\n\t\t{\n\t\t\ttmpResult.BodyContent = JSON.parse(JSON.stringify(this.cardBodyContent));\n\t\t\t// RenderCallback cannot be serialized — preserve the original function reference\n\t\t\tif (typeof this.options.BodyContent.RenderCallback === 'function')\n\t\t\t{\n\t\t\t\ttmpResult.BodyContent.RenderCallback = this.options.BodyContent.RenderCallback;\n\t\t\t}\n\t\t}\n\n\t\treturn tmpResult;\n\t}\n\n\t/**\n\t * Register this card with a FlowView's node type provider.\n\t *\n\t * @param {Object} pFlowView - The PictViewFlow instance\n\t * @returns {boolean} Whether registration succeeded\n\t */\n\tregisterWithFlowView(pFlowView)\n\t{\n\t\tif (!pFlowView || !pFlowView._NodeTypeProvider)\n\t\t{\n\t\t\tthis.log.warn('PictFlowCard registerWithFlowView: no valid FlowView or NodeTypeProvider');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpConfig = this.getNodeTypeConfiguration();\n\t\treturn pFlowView._NodeTypeProvider.registerNodeType(tmpConfig);\n\t}\n}\n\nmodule.exports = PictFlowCard;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Card',\n\tName: false,\n\tCode: '',\n\tDescription: false,\n\tIcon: false,\n\tPreviewImage: false,\n\tDocumentation: false,\n\tTooltip: false,\n\tInputs: [],\n\tOutputs: [],\n\tEnabled: true,\n\tTitleBarColor: '#2c3e50',\n\tBodyStyle: {},\n\tWidth: 180,\n\tHeight: 80,\n\tCategory: 'General',\n\tPropertiesPanel: null,\n\tBodyContent: null,\n\tShowTypeLabel: true,\n\tPortLabelsOnHover: false,\n\tPortLabelsVertical: false,\n\tPortLabelPadding: false,\n\tPortLabelsOutside: false,\n\tLabelsInFront: true\n};\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictFlowCardPropertiesPanel - Base class for flow card property panels.\n *\n * Developers create subclasses to define what UI appears when a user opens\n * the properties panel for a node on the flow graph. Four built-in panel\n * types are provided:\n *\n * - Template - Renders pict templates\n * - Markdown - Renders markdown via pict-section-content\n * - Form - Creates an ephemeral pict-section-form section\n * - View - Renders an existing registered pict-view\n *\n * Configurable properties:\n * - PanelType (string) - Panel type identifier\n * - Title (string) - Panel title bar text\n * - Width (number) - Default panel width in pixels\n * - Height (number) - Default panel height in pixels\n * - Configuration (object) - Panel-type-specific configuration\n */\nclass PictFlowCardPropertiesPanel extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, PictFlowCardPropertiesPanel.default_configuration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel';\n\n\t\t// Panel metadata\n\t\tthis.panelType = tmpOptions.PanelType || 'Base';\n\t\tthis.panelTitle = tmpOptions.Title || 'Properties';\n\t\tthis.panelWidth = (typeof tmpOptions.Width === 'number') ? tmpOptions.Width : 300;\n\t\tthis.panelHeight = (typeof tmpOptions.Height === 'number') ? tmpOptions.Height : 200;\n\n\t\t// Reference to the flow view (set when panel is activated)\n\t\tthis._FlowView = null;\n\n\t\t// The node data this panel is operating on (set when panel is opened)\n\t\tthis._NodeData = null;\n\n\t\t// The DOM container element for panel content (set during render)\n\t\tthis._ContentContainer = null;\n\n\t\t// The panel configuration (panel-type-specific)\n\t\tthis._Configuration = tmpOptions.Configuration || {};\n\t}\n\n\t/**\n\t * Render the panel's content into a DOM container element.\n\t * Subclasses MUST override this.\n\t *\n\t * @param {HTMLElement} pContainer - The DOM element to render into\n\t * @param {Object} pNodeData - The node data object (has .Data property)\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tthis._ContentContainer = pContainer;\n\t\tthis._NodeData = pNodeData;\n\t}\n\n\t/**\n\t * Marshal data FROM the node's Data object INTO the panel UI.\n\t * Called when the panel opens or when data changes externally.\n\t *\n\t * @param {Object} pNodeData\n\t */\n\tmarshalToPanel(pNodeData)\n\t{\n\t\tthis._NodeData = pNodeData;\n\t}\n\n\t/**\n\t * Marshal data FROM the panel UI INTO the node's Data object.\n\t * Called before saving or when the panel is about to close.\n\t *\n\t * @param {Object} pNodeData\n\t */\n\tmarshalFromPanel(pNodeData)\n\t{\n\t\t// Subclasses override\n\t}\n\n\t/**\n\t * Called when the panel is being destroyed (closed).\n\t * Subclasses should clean up resources.\n\t */\n\tdestroy()\n\t{\n\t\tthis._ContentContainer = null;\n\t\tthis._NodeData = null;\n\t}\n}\n\nmodule.exports = PictFlowCardPropertiesPanel;\n\nmodule.exports.default_configuration =\n{\n\tPanelType: 'Base',\n\tTitle: 'Properties',\n\tWidth: 300,\n\tHeight: 200,\n\tConfiguration: {}\n};\n","const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\n\n/**\n * FlowCardPropertiesPanel-Form\n *\n * Creates an ephemeral pict-section-form section to edit the node's Data object.\n * Uses PictViewFormMetacontroller.injectManifest() to dynamically create form\n * sections at runtime.\n *\n * Note: pict-section-form must be available in the consuming application\n * (it is an optional/peer dependency, not bundled with pict-section-flow).\n *\n * Configuration:\n * {\n * PanelType: 'Form',\n * Configuration: {\n * Manifest: {\n * Sections: [...],\n * Descriptors: {...}\n * }\n * }\n * }\n */\nclass FlowCardPropertiesPanelForm extends libPictFlowCardPropertiesPanel\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel-Form';\n\n\t\tthis._Metacontroller = null;\n\t\tthis._InjectedSectionHash = null;\n\t}\n\n\t/**\n\t * Render the form into the panel body.\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tsuper.render(pContainer, pNodeData);\n\n\t\tif (!this._Configuration || !this._Configuration.Manifest)\n\t\t{\n\t\t\tpContainer.innerHTML = '<em>No form manifest configured</em>';\n\t\t\treturn;\n\t\t}\n\n\t\t// Create a unique container ID for the form section\n\t\tlet tmpContainerID = `pict-flow-panel-form-${pNodeData.Hash}`;\n\t\tpContainer.innerHTML = `<div id=\"${tmpContainerID}\"></div>`;\n\n\t\t// Bind the node data to AppData.Record so the form descriptors\n\t\t// (which use addresses like Record.Data.SearchString) resolve against\n\t\t// the actual node object.\n\t\tthis.fable.AppData.Record = pNodeData;\n\n\t\ttry\n\t\t{\n\t\t\t// Look for an existing metacontroller or create one\n\t\t\tif (!this._Metacontroller)\n\t\t\t{\n\t\t\t\t// Try to find PictFormMetacontroller service type (check both common names)\n\t\t\t\tlet tmpServiceType = null;\n\t\t\t\tif (this.fable.servicesMap.hasOwnProperty('PictFormMetacontroller'))\n\t\t\t\t{\n\t\t\t\t\ttmpServiceType = 'PictFormMetacontroller';\n\t\t\t\t}\n\t\t\t\telse if (this.fable.servicesMap.hasOwnProperty('PictViewFormMetacontroller'))\n\t\t\t\t{\n\t\t\t\t\ttmpServiceType = 'PictViewFormMetacontroller';\n\t\t\t\t}\n\n\t\t\t\tif (tmpServiceType)\n\t\t\t\t{\n\t\t\t\t\tthis._Metacontroller = this.fable.instantiateServiceProviderWithoutRegistration(tmpServiceType,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tViewIdentifier: `FlowPanelForm-${pNodeData.Hash}`,\n\t\t\t\t\t\t\tDefaultDestinationAddress: `#${tmpContainerID}`,\n\t\t\t\t\t\t\tAutoRender: false,\n\t\t\t\t\t\t\tAutoPopulateAfterRender: true,\n\t\t\t\t\t\t\tAutoSolveBeforeRender: false\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this._Metacontroller && typeof this._Metacontroller.injectManifestAndRender === 'function')\n\t\t\t{\n\t\t\t\t// Create the FormContainer div that the metacontroller's\n\t\t\t\t// updateMetatemplateInDOM() expects when adding section wrappers.\n\t\t\t\t// Normally this is created when the metacontroller renders its own\n\t\t\t\t// metatemplate, but we skip that step since we only need the\n\t\t\t\t// injected section views, not the metacontroller's own renderable.\n\t\t\t\tlet tmpFormContainerID = `Pict-${this._Metacontroller.UUID}-FormContainer`;\n\t\t\t\tlet tmpContainerEl = pContainer.querySelector(`#${tmpContainerID}`);\n\t\t\t\tif (tmpContainerEl)\n\t\t\t\t{\n\t\t\t\t\ttmpContainerEl.innerHTML = `<div id=\"${tmpFormContainerID}\" class=\"pict-form\"></div>`;\n\t\t\t\t}\n\n\t\t\t\t// Deep clone the manifest so each panel gets its own copy\n\t\t\t\tlet tmpManifest = JSON.parse(JSON.stringify(this._Configuration.Manifest));\n\t\t\t\tthis._InjectedSectionHash = tmpManifest.Hash || null;\n\n\t\t\t\t// Use injectManifestAndRender which properly creates section views,\n\t\t\t\t// updates the metatemplate in the DOM, and renders each section view\n\t\t\t\tthis._Metacontroller.injectManifestAndRender(tmpManifest);\n\t\t\t}\n\t\t\telse if (this._Metacontroller && typeof this._Metacontroller.injectManifest === 'function')\n\t\t\t{\n\t\t\t\t// Fallback for older pict-section-form versions: inject the\n\t\t\t\t// manifest and render each section view individually\n\t\t\t\tlet tmpManifest = JSON.parse(JSON.stringify(this._Configuration.Manifest));\n\t\t\t\tlet tmpViewsToRender = this._Metacontroller.injectManifest(tmpManifest);\n\t\t\t\tthis._InjectedSectionHash = tmpManifest.Hash || null;\n\n\t\t\t\t// Create container divs for each section view and render them\n\t\t\t\tlet tmpContainerEl = pContainer.querySelector(`#${tmpContainerID}`);\n\t\t\t\tif (tmpContainerEl && tmpViewsToRender.length > 0)\n\t\t\t\t{\n\t\t\t\t\tlet tmpInnerHTML = '';\n\t\t\t\t\tfor (let i = 0; i < tmpViewsToRender.length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tlet tmpDestID = tmpViewsToRender[i].options.DefaultDestinationAddress;\n\t\t\t\t\t\tif (tmpDestID && tmpDestID.charAt(0) === '#')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttmpDestID = tmpDestID.substring(1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttmpInnerHTML += `<div id=\"${tmpDestID}\" class=\"pict-form-view\"></div>`;\n\t\t\t\t\t}\n\t\t\t\t\ttmpContainerEl.innerHTML = tmpInnerHTML;\n\n\t\t\t\t\tfor (let i = 0; i < tmpViewsToRender.length; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpViewsToRender[i].render();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpContainer.innerHTML = '<em>pict-section-form is not available. Install it in your application to use Form panels.</em>';\n\t\t\t}\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`FlowCardPropertiesPanel-Form render error: ${pError.message}`);\n\t\t\tpContainer.innerHTML = `<em>Form render error: ${pError.message}</em>`;\n\t\t}\n\t}\n\n\tmarshalFromPanel(pNodeData)\n\t{\n\t\tif (this._Metacontroller && typeof this._Metacontroller.marshalFromView === 'function')\n\t\t{\n\t\t\tthis._Metacontroller.marshalFromView();\n\t\t}\n\t}\n\n\tdestroy()\n\t{\n\t\tthis._Metacontroller = null;\n\t\tthis._InjectedSectionHash = null;\n\t\tsuper.destroy();\n\t}\n}\n\nmodule.exports = FlowCardPropertiesPanelForm;\n\nmodule.exports.default_configuration = Object.assign(\n\t{},\n\tlibPictFlowCardPropertiesPanel.default_configuration,\n\t{\n\t\tPanelType: 'Form',\n\t\tConfiguration:\n\t\t{\n\t\t\tManifest: null\n\t\t}\n\t}\n);\n","const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\n\n/**\n * FlowCardPropertiesPanel-Markdown\n *\n * Renders markdown content into the panel body using pict-section-content's\n * PictContentProvider service. When pict-section-content is installed and its\n * PictContentProvider service type has been registered with the pict instance,\n * full markdown rendering is available (headings, lists, tables, code blocks\n * with syntax highlighting, KaTeX equations, Mermaid diagrams, etc.).\n *\n * If PictContentProvider is not available, the panel falls back to displaying\n * the raw markdown as pre-formatted text.\n *\n * Configuration:\n * {\n * PanelType: 'Markdown',\n * Configuration: {\n * Markdown: '# Title\\nSome **markdown** content'\n * }\n * }\n *\n * Or use an address to pull markdown from the node's data:\n * {\n * PanelType: 'Markdown',\n * Configuration: {\n * MarkdownAddress: 'Data.MarkdownContent'\n * }\n * }\n */\nclass FlowCardPropertiesPanelMarkdown extends libPictFlowCardPropertiesPanel\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel-Markdown';\n\n\t\tthis._ContentProvider = null;\n\t}\n\n\t/**\n\t * Render markdown into the panel.\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tsuper.render(pContainer, pNodeData);\n\n\t\tthis._renderMarkdown();\n\t}\n\n\tmarshalToPanel(pNodeData)\n\t{\n\t\tsuper.marshalToPanel(pNodeData);\n\t\tthis._renderMarkdown();\n\t}\n\n\t_renderMarkdown()\n\t{\n\t\tif (!this._ContentContainer || !this._Configuration) return;\n\n\t\tlet tmpMarkdown = '';\n\n\t\tif (this._Configuration.MarkdownAddress && this._NodeData)\n\t\t{\n\t\t\t// Resolve markdown from node data using manyfest\n\t\t\ttmpMarkdown = this.fable.manifest.getValueByHash(this._NodeData, this._Configuration.MarkdownAddress) || '';\n\t\t}\n\t\telse if (this._Configuration.Markdown)\n\t\t{\n\t\t\ttmpMarkdown = this._Configuration.Markdown;\n\t\t}\n\n\t\tif (!tmpMarkdown)\n\t\t{\n\t\t\tthis._ContentContainer.innerHTML = '<em>No content</em>';\n\t\t\treturn;\n\t\t}\n\n\t\t// Use pict-section-content's PictContentProvider for markdown rendering.\n\t\t// The consuming application must register the PictContentProvider service\n\t\t// type (from pict-section-content) for this to work.\n\t\ttry\n\t\t{\n\t\t\tif (!this._ContentProvider)\n\t\t\t{\n\t\t\t\tif (this.fable.servicesMap.hasOwnProperty('PictContentProvider'))\n\t\t\t\t{\n\t\t\t\t\tthis._ContentProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictContentProvider', {});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this._ContentProvider && typeof this._ContentProvider.parseMarkdown === 'function')\n\t\t\t{\n\t\t\t\tlet tmpHTML = this._ContentProvider.parseMarkdown(tmpMarkdown);\n\t\t\t\tthis._ContentContainer.innerHTML = tmpHTML;\n\n\t\t\t\t// Post-render hooks for equations and diagrams\n\t\t\t\tif (typeof this._ContentProvider.renderKaTeXEquations === 'function')\n\t\t\t\t{\n\t\t\t\t\tthis._ContentProvider.renderKaTeXEquations(this._ContentContainer);\n\t\t\t\t}\n\t\t\t\tif (typeof this._ContentProvider.renderMermaidDiagrams === 'function')\n\t\t\t\t{\n\t\t\t\t\tthis._ContentProvider.renderMermaidDiagrams(this._ContentContainer);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// PictContentProvider not registered — render as pre-formatted text\n\t\t\t\tthis._ContentContainer.innerHTML = `<pre style=\"white-space: pre-wrap; font-family: inherit;\">${this._escapeHTML(tmpMarkdown)}</pre>`;\n\t\t\t}\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`FlowCardPropertiesPanel-Markdown render error: ${pError.message}`);\n\t\t\tthis._ContentContainer.innerHTML = `<pre style=\"white-space: pre-wrap; font-family: inherit;\">${this._escapeHTML(tmpMarkdown)}</pre>`;\n\t\t}\n\t}\n\n\t_escapeHTML(pText)\n\t{\n\t\tlet tmpDiv = document.createElement('div');\n\t\ttmpDiv.textContent = pText;\n\t\treturn tmpDiv.innerHTML;\n\t}\n\n\tdestroy()\n\t{\n\t\tthis._ContentProvider = null;\n\t\tsuper.destroy();\n\t}\n}\n\nmodule.exports = FlowCardPropertiesPanelMarkdown;\n\nmodule.exports.default_configuration = Object.assign(\n\t{},\n\tlibPictFlowCardPropertiesPanel.default_configuration,\n\t{\n\t\tPanelType: 'Markdown',\n\t\tConfiguration:\n\t\t{\n\t\t\tMarkdown: '',\n\t\t\tMarkdownAddress: ''\n\t\t}\n\t}\n);\n","const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\n\n/**\n * FlowCardPropertiesPanel-Template\n *\n * Renders pict templates into the panel body.\n *\n * Configuration:\n * {\n * PanelType: 'Template',\n * Configuration: {\n * Templates: [\n * { Hash: 'my-template', Template: '<div>{~D:Record.Data.SomeValue~}</div>' }\n * ],\n * TemplateHash: 'my-template'\n * }\n * }\n */\nclass FlowCardPropertiesPanelTemplate extends libPictFlowCardPropertiesPanel\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel-Template';\n\n\t\tthis._TemplatesRegistered = false;\n\t}\n\n\t/**\n\t * Register templates with the pict template provider and render.\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tsuper.render(pContainer, pNodeData);\n\n\t\tif (!this._Configuration || !this._Configuration.Templates) return;\n\n\t\t// Register templates with pict (only once)\n\t\tif (!this._TemplatesRegistered)\n\t\t{\n\t\t\tlet tmpTemplates = this._Configuration.Templates;\n\t\t\tfor (let i = 0; i < tmpTemplates.length; i++)\n\t\t\t{\n\t\t\t\tif (tmpTemplates[i].Hash && tmpTemplates[i].Template)\n\t\t\t\t{\n\t\t\t\t\tthis.fable.TemplateProvider.addTemplate(tmpTemplates[i].Hash, tmpTemplates[i].Template);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._TemplatesRegistered = true;\n\t\t}\n\n\t\tthis._renderTemplate();\n\t}\n\n\tmarshalToPanel(pNodeData)\n\t{\n\t\tsuper.marshalToPanel(pNodeData);\n\t\tthis._renderTemplate();\n\t}\n\n\t_renderTemplate()\n\t{\n\t\tif (!this._ContentContainer || !this._NodeData) return;\n\n\t\tlet tmpTemplateHash = this._Configuration.TemplateHash;\n\t\tif (!tmpTemplateHash) return;\n\n\t\tlet tmpRecord = this._NodeData;\n\t\tlet tmpHTML = this.fable.parseTemplateByHash(tmpTemplateHash, tmpRecord, null, [tmpRecord]);\n\t\tthis._ContentContainer.innerHTML = tmpHTML;\n\t}\n}\n\nmodule.exports = FlowCardPropertiesPanelTemplate;\n\nmodule.exports.default_configuration = Object.assign(\n\t{},\n\tlibPictFlowCardPropertiesPanel.default_configuration,\n\t{\n\t\tPanelType: 'Template',\n\t\tConfiguration:\n\t\t{\n\t\t\tTemplates: [],\n\t\t\tTemplateHash: ''\n\t\t}\n\t}\n);\n","const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\n\n/**\n * FlowCardPropertiesPanel-View\n *\n * Renders an existing registered pict-view into the panel body.\n * The view's destination is temporarily overridden to render inside\n * the panel container.\n *\n * Configuration:\n * {\n * PanelType: 'View',\n * Configuration: {\n * ViewHash: 'MyCustomViewIdentifier'\n * }\n * }\n */\nclass FlowCardPropertiesPanelView extends libPictFlowCardPropertiesPanel\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictFlowCardPropertiesPanel-View';\n\n\t\tthis._OriginalDestination = null;\n\t\tthis._ViewInstance = null;\n\t}\n\n\t/**\n\t * Render the referenced pict-view into the panel body.\n\t */\n\trender(pContainer, pNodeData)\n\t{\n\t\tsuper.render(pContainer, pNodeData);\n\n\t\tif (!this._Configuration || !this._Configuration.ViewHash)\n\t\t{\n\t\t\tpContainer.innerHTML = '<em>No ViewHash configured</em>';\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpViewHash = this._Configuration.ViewHash;\n\n\t\t// Create a unique container ID\n\t\tlet tmpContainerID = `pict-flow-panel-view-${pNodeData.Hash}`;\n\t\tpContainer.innerHTML = `<div id=\"${tmpContainerID}\"></div>`;\n\n\t\ttry\n\t\t{\n\t\t\t// Look up the view in the pict instance\n\t\t\tlet tmpPict = this.pict || this.fable;\n\t\t\tif (tmpPict.views && tmpPict.views[tmpViewHash])\n\t\t\t{\n\t\t\t\tthis._ViewInstance = tmpPict.views[tmpViewHash];\n\n\t\t\t\t// Save original destination\n\t\t\t\tthis._OriginalDestination = this._ViewInstance.options.DefaultDestinationAddress;\n\n\t\t\t\t// Override destination to our panel container\n\t\t\t\tthis._ViewInstance.options.DefaultDestinationAddress = `#${tmpContainerID}`;\n\n\t\t\t\tif (typeof this._ViewInstance.render === 'function')\n\t\t\t\t{\n\t\t\t\t\tthis._ViewInstance.render();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpContainer.innerHTML = `<em>View \"${tmpViewHash}\" not found</em>`;\n\t\t\t}\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`FlowCardPropertiesPanel-View render error: ${pError.message}`);\n\t\t\tpContainer.innerHTML = `<em>View render error: ${pError.message}</em>`;\n\t\t}\n\t}\n\n\tmarshalFromPanel(pNodeData)\n\t{\n\t\tif (this._ViewInstance && typeof this._ViewInstance.marshalFromView === 'function')\n\t\t{\n\t\t\tthis._ViewInstance.marshalFromView();\n\t\t}\n\t}\n\n\tdestroy()\n\t{\n\t\t// Restore original destination\n\t\tif (this._ViewInstance && this._OriginalDestination)\n\t\t{\n\t\t\tthis._ViewInstance.options.DefaultDestinationAddress = this._OriginalDestination;\n\t\t}\n\n\t\tthis._ViewInstance = null;\n\t\tthis._OriginalDestination = null;\n\t\tsuper.destroy();\n\t}\n}\n\nmodule.exports = FlowCardPropertiesPanelView;\n\nmodule.exports.default_configuration = Object.assign(\n\t{},\n\tlibPictFlowCardPropertiesPanel.default_configuration,\n\t{\n\t\tPanelType: 'View',\n\t\tConfiguration:\n\t\t{\n\t\t\tViewHash: ''\n\t\t}\n\t}\n);\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-CSS\n *\n * Centralized CSS provider for the flow diagram.\n * All flow-related CSS is organized into domain-specific getter methods,\n * providing a single source of truth and enabling future theming.\n */\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowCSS'\n};\n\nclass PictProviderFlowCSS extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowCSS';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t// ── Container ──────────────────────────────────────────────────────────\n\t/**\n\t * CSS for the flow container, SVG container, panning/connecting cursors, and grid pattern.\n\t * @returns {string}\n\t */\n\tgetContainerCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-container {\n\t\t\t/* ── Design Tokens ─────────────────────────────────────\n\t\t\t Override these custom properties to theme the flow diagram.\n\t\t\t Node-type classes (.pict-flow-node-{type}) can scope-override\n\t\t\t any variable for per-type variation. */\n\n\t\t\t/* Text */\n\t\t\t--pf-text-primary: #2c3e50;\n\t\t\t--pf-text-heading: #1a252f;\n\t\t\t--pf-text-secondary: #7f8c8d;\n\t\t\t--pf-text-tertiary: #8e99a4;\n\t\t\t--pf-text-placeholder: #95a5a6;\n\n\t\t\t/* Node */\n\t\t\t--pf-node-body-fill: #ffffff;\n\t\t\t--pf-node-body-stroke: #d0d4d8;\n\t\t\t--pf-node-body-stroke-hover: #b0b8c0;\n\t\t\t--pf-node-body-stroke-width: 1;\n\t\t\t--pf-node-body-radius: 8px;\n\t\t\t--pf-node-shadow: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.10));\n\t\t\t--pf-node-shadow-hover: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.15));\n\t\t\t--pf-node-shadow-selected: drop-shadow(0 2px 8px rgba(52, 152, 219, 0.25));\n\t\t\t--pf-node-shadow-dragging: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.20));\n\t\t\t--pf-node-title-fill: #ffffff;\n\t\t\t--pf-node-title-size: 11.5px;\n\t\t\t--pf-node-title-weight: 600;\n\t\t\t--pf-node-title-bar-color: #2c3e50;\n\t\t\t--pf-node-type-label-fill: #a0a8b0;\n\t\t\t--pf-node-selected-stroke: #3498db;\n\n\t\t\t/* Node Variants */\n\t\t\t--pf-node-start-fill: #eafaf1;\n\t\t\t--pf-node-start-stroke: #27ae60;\n\t\t\t--pf-node-end-fill: #e8f8f5;\n\t\t\t--pf-node-end-stroke: #1abc9c;\n\t\t\t--pf-node-halt-fill: #fdedec;\n\t\t\t--pf-node-halt-stroke: #e74c3c;\n\t\t\t--pf-node-decision-fill: #fff9e6;\n\t\t\t--pf-node-decision-stroke: #f39c12;\n\n\t\t\t/* Ports */\n\t\t\t--pf-port-input-fill: #3498db;\n\t\t\t--pf-port-output-fill: #2ecc71;\n\t\t\t--pf-port-stroke: #ffffff;\n\t\t\t--pf-port-stroke-width: 2;\n\t\t\t--pf-port-label-bg: rgba(255, 253, 240, 0.5);\n\t\t\t--pf-port-label-text: #2c3e50;\n\n\t\t\t/* Port Type Colors */\n\t\t\t--pf-port-event-in-fill: #3498db;\n\t\t\t--pf-port-event-out-fill: #2ecc71;\n\t\t\t--pf-port-setting-fill: #e67e22;\n\t\t\t--pf-port-value-fill: #f1c40f;\n\t\t\t--pf-port-error-fill: #e74c3c;\n\n\t\t\t/* Connection Type Colors (match source port) */\n\t\t\t--pf-connection-event-in-stroke: #3498db;\n\t\t\t--pf-connection-event-out-stroke: #2ecc71;\n\t\t\t--pf-connection-setting-stroke: #e67e22;\n\t\t\t--pf-connection-value-stroke: #f1c40f;\n\t\t\t--pf-connection-error-stroke: #e74c3c;\n\n\t\t\t/* Connections */\n\t\t\t--pf-connection-stroke: #95a5a6;\n\t\t\t--pf-connection-stroke-hover: #7f8c8d;\n\t\t\t--pf-connection-selected-stroke: #3498db;\n\n\t\t\t/* Panels */\n\t\t\t--pf-panel-bg: #ffffff;\n\t\t\t--pf-panel-border: #d0d4d8;\n\t\t\t--pf-panel-radius: 8px;\n\t\t\t--pf-panel-shadow: 0 4px 12px rgba(0,0,0,0.10), 0 1px 3px rgba(0,0,0,0.06);\n\t\t\t--pf-panel-titlebar-bg: #f7f8fa;\n\t\t\t--pf-panel-titlebar-border: #e8eaed;\n\t\t\t--pf-panel-title-color: #2c3e50;\n\n\t\t\t/* Tabs */\n\t\t\t--pf-tab-text: #8e99a4;\n\t\t\t--pf-tab-text-hover: #5a6a7a;\n\t\t\t--pf-tab-active-border: var(--pf-node-selected-stroke);\n\t\t\t--pf-resize-handle-hover: #e0e3e6;\n\n\t\t\t/* Forms & Inputs */\n\t\t\t--pf-input-border: #d5d8dc;\n\t\t\t--pf-input-border-focus: #3498db;\n\t\t\t--pf-divider-light: #ecf0f1;\n\t\t\t--pf-divider-medium: #e8eaed;\n\n\t\t\t/* Buttons */\n\t\t\t--pf-button-border: #bdc3c7;\n\t\t\t--pf-button-hover-border: #95a5a6;\n\t\t\t--pf-button-hover-bg: #ecf0f1;\n\t\t\t--pf-button-active-bg: #d5dbdb;\n\t\t\t--pf-button-danger-text: #e74c3c;\n\t\t\t--pf-button-danger-hover-bg: #fdedec;\n\t\t\t--pf-button-close-color: #b0b8c0;\n\n\t\t\t/* Badges */\n\t\t\t--pf-badge-category-bg: #f0f2f4;\n\t\t\t--pf-badge-category-text: #6b7b8d;\n\t\t\t--pf-badge-code-bg: #eaf2f8;\n\t\t\t--pf-badge-code-text: #2980b9;\n\n\t\t\t/* Info Panel */\n\t\t\t--pf-port-item-bg: #f8f9fa;\n\n\t\t\t/* Toolbar */\n\t\t\t--pf-toolbar-bg: #ffffff;\n\t\t\t--pf-toolbar-border: #e0e0e0;\n\n\t\t\t/* Palette Cards */\n\t\t\t--pf-card-border: #d5d8dc;\n\t\t\t--pf-card-hover-bg: #eaf2f8;\n\t\t\t--pf-card-hover-shadow: 0 1px 3px rgba(52, 152, 219, 0.15);\n\n\t\t\t/* Canvas */\n\t\t\t--pf-canvas-bg: #fafafa;\n\t\t\t--pf-grid-stroke: #e8e8e8;\n\n\t\t\tposition: relative;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tmin-height: 400px;\n\t\t\toverflow: hidden;\n\t\t\tbackground-color: var(--pf-canvas-bg);\n\t\t\tborder: 1px solid var(--pf-toolbar-border);\n\t\t\tborder-radius: 4px;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t}\n\t\t.pict-flow-svg-container {\n\t\t\tflex: 1;\n\t\t\tmin-height: 0;\n\t\t\tposition: relative;\n\t\t}\n\t\t.pict-flow-svg {\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tcursor: grab;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t}\n\t\t.pict-flow-svg.panning {\n\t\t\tcursor: grabbing;\n\t\t}\n\t\t.pict-flow-svg.connecting {\n\t\t\tcursor: crosshair;\n\t\t}\n\t\t.pict-flow-grid-pattern line {\n\t\t\tstroke: var(--pf-grid-stroke);\n\t\t\tstroke-width: 0.5;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Nodes ──────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for base node styling: body, hover/selected/dragging states, title bar, title text, type label.\n\t * @returns {string}\n\t */\n\tgetNodeCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node {\n\t\t\tcursor: pointer;\n\t\t\tfilter: var(--pf-node-shadow);\n\t\t\ttransition: filter 0.2s;\n\t\t}\n\t\t.pict-flow-node:hover {\n\t\t\tfilter: var(--pf-node-shadow-hover);\n\t\t}\n\t\t.pict-flow-node:hover .pict-flow-node-body {\n\t\t\tstroke: var(--pf-node-body-stroke-hover);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t.pict-flow-node.selected {\n\t\t\tfilter: var(--pf-node-shadow-selected);\n\t\t}\n\t\t.pict-flow-node.selected .pict-flow-node-body {\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t}\n\t\t.pict-flow-node.dragging {\n\t\t\topacity: 0.9;\n\t\t\tcursor: grabbing;\n\t\t\tfilter: var(--pf-node-shadow-dragging);\n\t\t}\n\t\t.pict-flow-node-body {\n\t\t\tfill: var(--pf-node-body-fill);\n\t\t\tstroke: var(--pf-node-body-stroke);\n\t\t\tstroke-width: var(--pf-node-body-stroke-width);\n\t\t\trx: var(--pf-node-body-radius);\n\t\t\try: var(--pf-node-body-radius);\n\t\t\ttransition: stroke 0.2s, stroke-width 0.2s;\n\t\t}\n\t\t.pict-flow-node-title-bar {\n\t\t\tfill: var(--pf-node-title-bar-color);\n\t\t\trx: var(--pf-node-body-radius);\n\t\t\try: var(--pf-node-body-radius);\n\t\t}\n\t\t.pict-flow-node-title-bar-bottom {\n\t\t\tfill: var(--pf-node-title-bar-color);\n\t\t}\n\t\t.pict-flow-node-title {\n\t\t\tfill: var(--pf-node-title-fill);\n\t\t\tfont-size: var(--pf-node-title-size);\n\t\t\tfont-weight: var(--pf-node-title-weight);\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tletter-spacing: 0.2px;\n\t\t\tpointer-events: none;\n\t\t}\n\t\t.pict-flow-node-type-label {\n\t\t\tfill: var(--pf-node-type-label-fill);\n\t\t\tfont-size: 9.5px;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.3px;\n\t\t\tpointer-events: none;\n\t\t\topacity: 0;\n\t\t\ttransition: opacity 0.2s;\n\t\t}\n\t\t.pict-flow-node:hover .pict-flow-node-type-label {\n\t\t\topacity: 1;\n\t\t}\n\t\t.pict-flow-node-card-code {\n\t\t\topacity: 0;\n\t\t\ttransition: opacity 0.2s;\n\t\t}\n\t\t.pict-flow-node:hover .pict-flow-node-card-code {\n\t\t\topacity: 1;\n\t\t}\n\t\t/* Title-bar icon: invert SVG paths to white for dark title bars */\n\t\t.pict-flow-node-title-icon {\n\t\t\tfilter: brightness(0) invert(1);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Body Content ──────────────────────────────────────────────────────\n\t/**\n\t * CSS for custom body content in nodes: SVG group, foreignObject, HTML container, canvas.\n\t * @returns {string}\n\t */\n\tgetBodyContentCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node-body-content {\n\t\t\tpointer-events: none;\n\t\t}\n\t\t.pict-flow-node-body-content-fo {\n\t\t\toverflow: hidden;\n\t\t}\n\t\t.pict-flow-node-body-content-html {\n\t\t\toverflow: hidden;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tbox-sizing: border-box;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tfont-size: 11px;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tpointer-events: auto;\n\t\t}\n\t\t.pict-flow-node-body-content-canvas {\n\t\t\tdisplay: block;\n\t\t\tpointer-events: auto;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Node Variants ──────────────────────────────────────────────────────\n\t/**\n\t * CSS overrides for specific node types: start, end, halt, decision.\n\t * @returns {string}\n\t */\n\tgetNodeVariantCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node-decision .pict-flow-node-body {\n\t\t\tfill: var(--pf-node-decision-fill);\n\t\t\tstroke: var(--pf-node-decision-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t.pict-flow-node-start .pict-flow-node-body {\n\t\t\tfill: var(--pf-node-start-fill);\n\t\t\tstroke: var(--pf-node-start-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t.pict-flow-node-end .pict-flow-node-body {\n\t\t\tfill: var(--pf-node-end-fill);\n\t\t\tstroke: var(--pf-node-end-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t.pict-flow-node-halt .pict-flow-node-body {\n\t\t\tfill: var(--pf-node-halt-fill);\n\t\t\tstroke: var(--pf-node-halt-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Ports ──────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for port circles: input/output coloring, hover states, labels.\n\t * @returns {string}\n\t */\n\tgetPortCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-port {\n\t\t\tcursor: crosshair;\n\t\t\ttransition: r 0.15s, filter 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.12));\n\t\t}\n\t\t.pict-flow-port.input {\n\t\t\tfill: var(--pf-port-input-fill);\n\t\t\tstroke: var(--pf-port-stroke);\n\t\t\tstroke-width: var(--pf-port-stroke-width);\n\t\t}\n\t\t.pict-flow-port.output {\n\t\t\tfill: var(--pf-port-output-fill);\n\t\t\tstroke: var(--pf-port-stroke);\n\t\t\tstroke-width: var(--pf-port-stroke-width);\n\t\t}\n\t\t.pict-flow-port:hover {\n\t\t\tr: 7;\n\t\t\tfilter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.20));\n\t\t}\n\t\t/* Port type color overrides */\n\t\t.pict-flow-port.port-type-event-in {\n\t\t\tfill: var(--pf-port-event-in-fill);\n\t\t}\n\t\t.pict-flow-port.port-type-event-out {\n\t\t\tfill: var(--pf-port-event-out-fill);\n\t\t}\n\t\t.pict-flow-port.port-type-setting {\n\t\t\tfill: var(--pf-port-setting-fill);\n\t\t}\n\t\t.pict-flow-port.port-type-value {\n\t\t\tfill: var(--pf-port-value-fill);\n\t\t}\n\t\t.pict-flow-port.port-type-error {\n\t\t\tfill: var(--pf-port-error-fill);\n\t\t}\n\t\t.pict-flow-port-label {\n\t\t\tfont-size: 8px;\n\t\t\tfont-weight: 600;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tpointer-events: none;\n\t\t}\n\t\t/* Port label badge background */\n\t\t.pict-flow-port-label-bg {\n\t\t\tpointer-events: none;\n\t\t}\n\t\t/* Port labels on hover: hidden by default, revealed on node hover */\n\t\t.pict-flow-node-port-labels-hover .pict-flow-port-label,\n\t\t.pict-flow-node-port-labels-hover .pict-flow-port-label-bg {\n\t\t\topacity: 0;\n\t\t\ttransition: opacity 0.2s;\n\t\t}\n\t\t.pict-flow-node-port-labels-hover:hover .pict-flow-port-label,\n\t\t.pict-flow-node-port-labels-hover:hover .pict-flow-port-label-bg {\n\t\t\topacity: 1;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Connections ────────────────────────────────────────────────────────\n\t/**\n\t * CSS for connection paths: base, hover/selected states, hitarea, drag-connection.\n\t * @returns {string}\n\t */\n\tgetConnectionCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-connection {\n\t\t\tfill: none;\n\t\t\tstroke: var(--pf-connection-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: pointer;\n\t\t\ttransition: stroke 0.15s;\n\t\t}\n\t\t.pict-flow-connection:hover {\n\t\t\tstroke: var(--pf-connection-stroke-hover);\n\t\t\tstroke-width: 3;\n\t\t}\n\t\t.pict-flow-connection.selected {\n\t\t\tstroke: var(--pf-connection-selected-stroke);\n\t\t\tstroke-width: 3;\n\t\t}\n\t\t/* Connection type color overrides (based on source port type) */\n\t\t.pict-flow-connection.conn-type-event-in {\n\t\t\tstroke: var(--pf-connection-event-in-stroke);\n\t\t}\n\t\t.pict-flow-connection.conn-type-event-out {\n\t\t\tstroke: var(--pf-connection-event-out-stroke);\n\t\t}\n\t\t.pict-flow-connection.conn-type-setting {\n\t\t\tstroke: var(--pf-connection-setting-stroke);\n\t\t}\n\t\t.pict-flow-connection.conn-type-value {\n\t\t\tstroke: var(--pf-connection-value-stroke);\n\t\t}\n\t\t.pict-flow-connection.conn-type-error {\n\t\t\tstroke: var(--pf-connection-error-stroke);\n\t\t}\n\t\t.pict-flow-connection-hitarea {\n\t\t\tfill: none;\n\t\t\tstroke: transparent;\n\t\t\tstroke-width: 12;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-drag-connection {\n\t\t\tfill: none;\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tstroke-dasharray: 6 3;\n\t\t\tpointer-events: none;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Connection Handles ─────────────────────────────────────────────────\n\t/**\n\t * CSS for connection waypoint handles and midpoint handles.\n\t * @returns {string}\n\t */\n\tgetHandleCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-connection-handle {\n\t\t\tfill: var(--pf-panel-bg);\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: grab;\n\t\t\ttransition: r 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));\n\t\t}\n\t\t.pict-flow-connection-handle:hover {\n\t\t\tr: 8;\n\t\t\tstroke-width: 2.5;\n\t\t}\n\t\t.pict-flow-connection-handle-midpoint {\n\t\t\tfill: var(--pf-panel-bg);\n\t\t\tstroke: var(--pf-port-setting-fill);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: grab;\n\t\t\ttransition: r 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));\n\t\t}\n\t\t.pict-flow-connection-handle-midpoint:hover {\n\t\t\tr: 8;\n\t\t\tstroke-width: 2.5;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Tethers ────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for tether lines, hitareas, handles, and midpoint handles.\n\t * @returns {string}\n\t */\n\tgetTetherCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-tether-line {\n\t\t\tfill: none;\n\t\t\tstroke: var(--pf-connection-stroke);\n\t\t\tstroke-width: 1.5;\n\t\t\tstroke-dasharray: 6 4;\n\t\t\tpointer-events: visibleStroke;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-tether-line.selected {\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t}\n\t\t.pict-flow-tether-hitarea {\n\t\t\tfill: none;\n\t\t\tstroke: transparent;\n\t\t\tstroke-width: 10;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-tether-handle {\n\t\t\tfill: var(--pf-panel-bg);\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: grab;\n\t\t\ttransition: r 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));\n\t\t}\n\t\t.pict-flow-tether-handle:hover {\n\t\t\tr: 8;\n\t\t\tstroke-width: 2.5;\n\t\t}\n\t\t.pict-flow-tether-handle-midpoint {\n\t\t\tfill: var(--pf-panel-bg);\n\t\t\tstroke: var(--pf-port-setting-fill);\n\t\t\tstroke-width: 2;\n\t\t\tcursor: grab;\n\t\t\ttransition: r 0.15s;\n\t\t\tfilter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));\n\t\t}\n\t\t.pict-flow-tether-handle-midpoint:hover {\n\t\t\tr: 8;\n\t\t\tstroke-width: 2.5;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Panels ─────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for property panels: foreign object, panel container, titlebar, close button, body, indicator.\n\t * @returns {string}\n\t */\n\tgetPanelCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node-panel-indicator {\n\t\t\tfill: var(--pf-node-selected-stroke);\n\t\t\tstroke: none;\n\t\t\topacity: 0.6;\n\t\t\tcursor: pointer;\n\t\t\ttransition: opacity 0.15s;\n\t\t}\n\t\t.pict-flow-node-panel-indicator:hover {\n\t\t\topacity: 1.0;\n\t\t}\n\t\t.pict-flow-panel-foreign-object {\n\t\t\toverflow: visible;\n\t\t}\n\t\t.pict-flow-panel {\n\t\t\tbackground: var(--pf-panel-bg);\n\t\t\tborder: 1px solid var(--pf-panel-border);\n\t\t\tborder-radius: var(--pf-panel-radius);\n\t\t\tbox-shadow: var(--pf-panel-shadow);\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tfont-size: 13px;\n\t\t\toverflow: hidden;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.pict-flow-panel-titlebar {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: space-between;\n\t\t\tpadding: 8px 12px;\n\t\t\tbackground: var(--pf-panel-titlebar-bg);\n\t\t\tborder-bottom: 1px solid var(--pf-panel-titlebar-border);\n\t\t\tcursor: grab;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.pict-flow-panel-titlebar.dragging {\n\t\t\tcursor: grabbing;\n\t\t}\n\t\t.pict-flow-panel-title-text {\n\t\t\tfont-weight: 600;\n\t\t\tfont-size: 12px;\n\t\t\tcolor: var(--pf-panel-title-color);\n\t\t\twhite-space: nowrap;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t\tletter-spacing: 0.1px;\n\t\t}\n\t\t.pict-flow-panel-close-btn {\n\t\t\tcursor: pointer;\n\t\t\tcolor: var(--pf-button-close-color);\n\t\t\tfont-size: 14px;\n\t\t\tline-height: 1;\n\t\t\tpadding: 4px;\n\t\t\tborder: none;\n\t\t\tbackground: none;\n\t\t\tborder-radius: 4px;\n\t\t\ttransition: background-color 0.15s, color 0.15s;\n\t\t}\n\t\t.pict-flow-panel-close-btn:hover {\n\t\t\tcolor: var(--pf-button-danger-text);\n\t\t\tbackground-color: rgba(231, 76, 60, 0.08);\n\t\t}\n\t\t.pict-flow-panel-content {\n\t\t\tflex: 1;\n\t\t\toverflow-y: auto;\n\t\t\tmin-height: 0;\n\t\t\tpadding: 0;\n\t\t}\n\t\t.pict-flow-panel-tab-pane {\n\t\t\tpadding: 10px 12px;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Info Panels ────────────────────────────────────────────────────────\n\t/**\n\t * CSS for the info/hover panel and all sub-elements: header, description, badges, sections, ports.\n\t * @returns {string}\n\t */\n\tgetInfoPanelCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-info-panel {\n\t\t\tpadding: 2px 0;\n\t\t\tfont-size: 12px;\n\t\t\tline-height: 1.5;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t}\n\t\t.pict-flow-info-panel-header {\n\t\t\tfont-size: 13px;\n\t\t\tfont-weight: 600;\n\t\t\tmargin-bottom: 6px;\n\t\t\tcolor: var(--pf-text-heading);\n\t\t}\n\t\t.pict-flow-info-panel-header.with-icon {\n\t\t\tfont-size: 14px;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 6px;\n\t\t}\n\t\t.pict-flow-info-panel-description {\n\t\t\tfont-size: 11px;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\tmargin-bottom: 10px;\n\t\t\tline-height: 1.45;\n\t\t}\n\t\t.pict-flow-info-panel-badges {\n\t\t\tmargin-bottom: 10px;\n\t\t\tdisplay: flex;\n\t\t\tflex-wrap: wrap;\n\t\t\tgap: 4px;\n\t\t}\n\t\t.pict-flow-info-panel-badge {\n\t\t\tdisplay: inline-block;\n\t\t\tpadding: 2px 8px;\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 10px;\n\t\t}\n\t\t.pict-flow-info-panel-badge.category {\n\t\t\tbackground: var(--pf-badge-category-bg);\n\t\t\tcolor: var(--pf-badge-category-text);\n\t\t}\n\t\t.pict-flow-info-panel-badge.code {\n\t\t\tbackground: var(--pf-badge-code-bg);\n\t\t\tcolor: var(--pf-badge-code-text);\n\t\t\tfont-family: \"SF Mono\", \"Fira Code\", monospace;\n\t\t}\n\t\t.pict-flow-info-panel-section {\n\t\t\tmargin-bottom: 8px;\n\t\t}\n\t\t.pict-flow-info-panel-section-title {\n\t\t\tfont-size: 10px;\n\t\t\tfont-weight: 600;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.5px;\n\t\t\tcolor: var(--pf-text-tertiary);\n\t\t\tmargin-bottom: 4px;\n\t\t\tpadding-bottom: 2px;\n\t\t\tborder-bottom: 1px solid var(--pf-divider-light);\n\t\t}\n\t\t.pict-flow-info-panel-port {\n\t\t\tpadding: 3px 8px;\n\t\t\tbackground: var(--pf-port-item-bg);\n\t\t\tmargin-bottom: 3px;\n\t\t\tfont-size: 11px;\n\t\t\tborder-radius: 3px;\n\t\t}\n\t\t.pict-flow-info-panel-port.input {\n\t\t\tborder-left: 3px solid var(--pf-port-input-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.output {\n\t\t\tborder-left: 3px solid var(--pf-port-output-fill);\n\t\t}\n\t\t/* Info panel port type color overrides */\n\t\t.pict-flow-info-panel-port.port-type-event-in {\n\t\t\tborder-left-color: var(--pf-port-event-in-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.port-type-event-out {\n\t\t\tborder-left-color: var(--pf-port-event-out-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.port-type-setting {\n\t\t\tborder-left-color: var(--pf-port-setting-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.port-type-value {\n\t\t\tborder-left-color: var(--pf-port-value-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.port-type-error {\n\t\t\tborder-left-color: var(--pf-port-error-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port-constraint {\n\t\t\tcolor: var(--pf-text-tertiary);\n\t\t\tfont-size: 10px;\n\t\t}\n\t\t/* Port summary section appended below form panels */\n\t\t.pict-flow-port-summary {\n\t\t\tmargin-top: 12px;\n\t\t\tpadding-top: 8px;\n\t\t\tborder-top: 1px solid var(--pf-divider-medium);\n\t\t}\n\t\t.pict-flow-info-panel-port.event {\n\t\t\tborder-left: 3px solid var(--pf-port-event-in-fill);\n\t\t}\n\t\t.pict-flow-info-panel-port.value {\n\t\t\tborder-left: 3px solid var(--pf-port-value-fill);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Node Properties Editor ────────────────────────────────────────────\n\t/**\n\t * CSS for the node properties editor fields used in the Appearance tab.\n\t * @returns {string}\n\t */\n\tgetNodePropsEditorCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-node-props-fields {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tgap: 6px;\n\t\t}\n\t\t.pict-flow-node-props-field {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 8px;\n\t\t}\n\t\t.pict-flow-node-props-label {\n\t\t\tfont-size: 11px;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\tmin-width: 72px;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.pict-flow-node-props-input {\n\t\t\tflex: 1;\n\t\t\tpadding: 3px 6px;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 3px;\n\t\t\tfont-size: 11px;\n\t\t\toutline: none;\n\t\t\tbox-sizing: border-box;\n\t\t\tmin-width: 0;\n\t\t}\n\t\t.pict-flow-node-props-input:focus {\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-node-props-color {\n\t\t\twidth: 28px;\n\t\t\theight: 24px;\n\t\t\tpadding: 1px;\n\t\t\tcursor: pointer;\n\t\t\tflex: 0 0 28px;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Panel Tabs & Resize ───────────────────────────────────────────────\n\t/**\n\t * CSS for the tab bar, tab panes, resize handle, and help content.\n\t * @returns {string}\n\t */\n\tgetPanelTabsCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-panel-resize-handle {\n\t\t\theight: 6px;\n\t\t\tcursor: ns-resize;\n\t\t\tbackground: transparent;\n\t\t\tflex-shrink: 0;\n\t\t\ttransition: background-color 0.15s;\n\t\t\tborder-top: 1px solid var(--pf-panel-titlebar-border);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t}\n\t\t.pict-flow-panel-resize-handle::after {\n\t\t\tcontent: '';\n\t\t\twidth: 24px;\n\t\t\theight: 2px;\n\t\t\tborder-radius: 1px;\n\t\t\tbackground: var(--pf-resize-handle-hover);\n\t\t\ttransition: background-color 0.15s, width 0.15s;\n\t\t}\n\t\t.pict-flow-panel-resize-handle:hover::after {\n\t\t\tbackground: var(--pf-button-hover-border);\n\t\t\twidth: 32px;\n\t\t}\n\t\t.pict-flow-panel-tabbar {\n\t\t\tdisplay: flex;\n\t\t\tflex-shrink: 0;\n\t\t\tborder-top: 1px solid var(--pf-panel-titlebar-border);\n\t\t\tbackground: var(--pf-panel-titlebar-bg);\n\t\t}\n\t\t.pict-flow-panel-tab {\n\t\t\tflex: 1;\n\t\t\tpadding: 5px 8px;\n\t\t\tfont-size: 11px;\n\t\t\ttext-align: center;\n\t\t\tcursor: pointer;\n\t\t\tcolor: var(--pf-tab-text);\n\t\t\tborder-top: 2px solid transparent;\n\t\t\ttransition: color 0.15s, border-top-color 0.15s;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t}\n\t\t.pict-flow-panel-tab:hover {\n\t\t\tcolor: var(--pf-tab-text-hover);\n\t\t}\n\t\t.pict-flow-panel-tab.active {\n\t\t\tborder-top-color: var(--pf-node-selected-stroke);\n\t\t\tcolor: var(--pf-panel-title-color);\n\t\t\tfont-weight: 600;\n\t\t}\n\t\t.pict-flow-panel-help-content {\n\t\t\tfont-size: 12px;\n\t\t\tline-height: 1.5;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t}\n\t\t.pict-flow-panel-help-content p {\n\t\t\tmargin: 0 0 8px 0;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Fullscreen ─────────────────────────────────────────────────────────\n\t/**\n\t * CSS for fullscreen mode.\n\t * @returns {string}\n\t */\n\tgetFullscreenCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-fullscreen {\n\t\t\tposition: fixed;\n\t\t\ttop: 0;\n\t\t\tleft: 0;\n\t\t\twidth: 100vw;\n\t\t\theight: 100vh;\n\t\t\tz-index: 9999;\n\t\t\tborder-radius: 0;\n\t\t\tborder: none;\n\t\t\tmin-height: 100vh;\n\t\t}\n\t\t.pict-flow-fullscreen .pict-flow-svg {\n\t\t\tmin-height: calc(100vh - 50px);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Toolbar ────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for the toolbar: buttons, groups, labels, selects.\n\t * @returns {string}\n\t */\n\tgetToolbarCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-toolbar {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.5em;\n\t\t\tpadding: 0.5em 0.75em;\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tborder-bottom: 1px solid var(--pf-toolbar-border);\n\t\t\tflex-wrap: wrap;\n\t\t}\n\t\t.pict-flow-toolbar-group {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.25em;\n\t\t\tpadding-right: 0.75em;\n\t\t\tborder-right: 1px solid var(--pf-toolbar-border);\n\t\t}\n\t\t.pict-flow-toolbar-group:last-child {\n\t\t\tborder-right: none;\n\t\t\tpadding-right: 0;\n\t\t}\n\t\t.pict-flow-toolbar-btn {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: 0.35em;\n\t\t\tpadding: 0.35em 0.65em;\n\t\t\tborder: 1px solid var(--pf-button-border);\n\t\t\tborder-radius: 4px;\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tfont-size: 0.85em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.15s, border-color 0.15s;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t}\n\t\t.pict-flow-toolbar-btn:focus {\n\t\t\toutline: none;\n\t\t}\n\t\t.pict-flow-toolbar-btn:hover {\n\t\t\tbackground-color: var(--pf-button-hover-bg);\n\t\t\tborder-color: var(--pf-button-hover-border);\n\t\t}\n\t\t.pict-flow-toolbar-btn:active {\n\t\t\tbackground-color: var(--pf-button-active-bg);\n\t\t}\n\t\t.pict-flow-toolbar-btn-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-toolbar-btn-icon svg {\n\t\t\tdisplay: block;\n\t\t}\n\t\t.pict-flow-toolbar-btn-text {\n\t\t\twhite-space: nowrap;\n\t\t}\n\t\t.pict-flow-toolbar-btn-chevron {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tmargin-left: 0.15em;\n\t\t}\n\t\t.pict-flow-toolbar-right {\n\t\t\tmargin-left: auto;\n\t\t\tborder-right: none;\n\t\t\tpadding-right: 0;\n\t\t}\n\t\t.pict-flow-toolbar-label {\n\t\t\tfont-size: 0.8em;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\tmargin-right: 0.25em;\n\t\t}\n\t\t.pict-flow-toolbar-select {\n\t\t\tpadding: 0.3em 0.5em;\n\t\t\tborder: 1px solid var(--pf-button-border);\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 0.85em;\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tcolor: var(--pf-text-primary);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Card Palette ───────────────────────────────────────────────────────\n\t/**\n\t * CSS for the card palette: toggle, body, categories, cards, swatches.\n\t * @returns {string}\n\t */\n\tgetPaletteCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-palette-category {\n\t\t\tmargin-bottom: 0.5em;\n\t\t}\n\t\t.pict-flow-palette-category:last-child {\n\t\t\tmargin-bottom: 0;\n\t\t}\n\t\t.pict-flow-palette-category-label {\n\t\t\tfont-size: 0.7em;\n\t\t\tfont-weight: 700;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.05em;\n\t\t\tcolor: var(--pf-text-placeholder);\n\t\t\tmargin-bottom: 0.35em;\n\t\t\tpadding-bottom: 0.2em;\n\t\t\tborder-bottom: 1px solid var(--pf-divider-light);\n\t\t}\n\t\t.pict-flow-palette-cards {\n\t\t\tdisplay: flex;\n\t\t\tflex-wrap: wrap;\n\t\t\tgap: 0.35em;\n\t\t}\n\t\t.pict-flow-palette-card {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.35em;\n\t\t\tpadding: 0.35em 0.6em;\n\t\t\tborder: 1px solid var(--pf-card-border);\n\t\t\tborder-radius: 4px;\n\t\t\tbackground-color: var(--pf-panel-bg);\n\t\t\tfont-size: 0.8em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;\n\t\t\tuser-select: none;\n\t\t\t-webkit-user-select: none;\n\t\t\tposition: relative;\n\t\t}\n\t\t.pict-flow-palette-card:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t\tborder-color: var(--pf-node-selected-stroke);\n\t\t\tbox-shadow: var(--pf-card-hover-shadow);\n\t\t}\n\t\t.pict-flow-palette-card.disabled {\n\t\t\topacity: 0.45;\n\t\t\tpointer-events: none;\n\t\t\tcursor: default;\n\t\t}\n\t\t.pict-flow-palette-card-icon {\n\t\t\tfont-size: 1.1em;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-palette-card-swatch {\n\t\t\twidth: 10px;\n\t\t\theight: 10px;\n\t\t\tborder-radius: 2px;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.pict-flow-palette-card-title {\n\t\t\tfont-weight: 500;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\twhite-space: nowrap;\n\t\t}\n\t\t.pict-flow-palette-card-code {\n\t\t\tfont-size: 0.8em;\n\t\t\tcolor: var(--pf-text-placeholder);\n\t\t\tfont-family: monospace;\n\t\t}\n\t\t.pict-flow-toolbar-select.layout-select {\n\t\t\tmin-width: 120px;\n\t\t\tmax-width: 200px;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Popups ────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for toolbar dropdown popups (Add Node, Cards, Layout).\n\t * @returns {string}\n\t */\n\tgetPopupCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-toolbar-popup-anchor {\n\t\t\tposition: relative;\n\t\t}\n\t\t.pict-flow-toolbar-popup {\n\t\t\tposition: absolute;\n\t\t\tz-index: 1000;\n\t\t\tbackground: var(--pf-panel-bg);\n\t\t\tborder: 1px solid var(--pf-card-border);\n\t\t\tborder-radius: 6px;\n\t\t\tbox-shadow: 0 4px 16px rgba(0,0,0,0.12);\n\t\t\tmin-width: 240px;\n\t\t\tmax-height: 340px;\n\t\t\toverflow-y: auto;\n\t\t\tpadding: 0.35em 0;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n\t\t\tfont-size: 13px;\n\t\t}\n\t\t.pict-flow-popup-search-wrapper {\n\t\t\tposition: relative;\n\t\t\tpadding: 0.4em 0.5em;\n\t\t\tborder-bottom: 1px solid var(--pf-divider-light);\n\t\t}\n\t\t.pict-flow-popup-search-icon {\n\t\t\tposition: absolute;\n\t\t\tleft: 0.85em;\n\t\t\ttop: 50%;\n\t\t\ttransform: translateY(-50%);\n\t\t\tpointer-events: none;\n\t\t\tline-height: 1;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t}\n\t\t.pict-flow-popup-search {\n\t\t\twidth: 100%;\n\t\t\tpadding: 0.4em 0.5em 0.4em 2em;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 0.9em;\n\t\t\toutline: none;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.pict-flow-popup-search:focus {\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-popup-list-item {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.5em;\n\t\t\tpadding: 0.45em 0.75em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.1s;\n\t\t}\n\t\t.pict-flow-popup-list-item:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t}\n\t\t.pict-flow-popup-list-item-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tflex-shrink: 0;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-popup-list-item-label {\n\t\t\tflex: 1;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tfont-weight: 500;\n\t\t}\n\t\t.pict-flow-popup-list-item-code {\n\t\t\tfont-size: 0.8em;\n\t\t\tcolor: var(--pf-text-placeholder);\n\t\t\tfont-family: monospace;\n\t\t\tbackground: var(--pf-badge-category-bg);\n\t\t\tpadding: 0.1em 0.4em;\n\t\t\tborder-radius: 3px;\n\t\t}\n\t\t.pict-flow-popup-divider {\n\t\t\theight: 1px;\n\t\t\tbackground: var(--pf-divider-light);\n\t\t\tmargin: 0.25em 0;\n\t\t}\n\t\t.pict-flow-popup-list-empty {\n\t\t\ttext-align: center;\n\t\t\tcolor: var(--pf-text-placeholder);\n\t\t\tpadding: 1.5em 0.75em;\n\t\t\tfont-size: 0.9em;\n\t\t}\n\t\t.pict-flow-popup-layout-save {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.5em;\n\t\t\tpadding: 0.45em 0.75em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.1s;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tfont-weight: 500;\n\t\t}\n\t\t.pict-flow-popup-layout-save:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t}\n\t\t.pict-flow-popup-layout-save-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tflex-shrink: 0;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-popup-layout-save-input-row {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.35em;\n\t\t\tpadding: 0.35em 0.5em;\n\t\t}\n\t\t.pict-flow-popup-layout-save-input {\n\t\t\tflex: 1;\n\t\t\tpadding: 0.35em 0.5em;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 0.9em;\n\t\t\toutline: none;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.pict-flow-popup-layout-save-input:focus {\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-popup-layout-save-confirm {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\twidth: 28px;\n\t\t\theight: 28px;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 4px;\n\t\t\tbackground: var(--pf-panel-bg);\n\t\t\tcursor: pointer;\n\t\t\tflex-shrink: 0;\n\t\t\ttransition: background-color 0.15s, border-color 0.15s;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-popup-layout-save-confirm:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-popup-layout-row {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tpadding: 0.45em 0.75em;\n\t\t\tcursor: pointer;\n\t\t\ttransition: background-color 0.1s;\n\t\t}\n\t\t.pict-flow-popup-layout-row:hover {\n\t\t\tbackground-color: var(--pf-card-hover-bg);\n\t\t}\n\t\t.pict-flow-popup-layout-name {\n\t\t\tflex: 1;\n\t\t\tcolor: var(--pf-text-primary);\n\t\t}\n\t\t.pict-flow-popup-layout-delete {\n\t\t\tdisplay: none;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tborder: none;\n\t\t\tbackground: none;\n\t\t\tcolor: var(--pf-button-danger-text);\n\t\t\tcursor: pointer;\n\t\t\tpadding: 2px 4px;\n\t\t\tborder-radius: 3px;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-popup-layout-row:hover .pict-flow-popup-layout-delete {\n\t\t\tdisplay: inline-flex;\n\t\t}\n\t\t.pict-flow-popup-layout-delete:hover {\n\t\t\tbackground-color: var(--pf-button-danger-hover-bg);\n\t\t}\n\t\t.pict-flow-popup-settings-section {\n\t\t\tpadding: 0.5em 0.75em;\n\t\t}\n\t\t.pict-flow-popup-settings-label {\n\t\t\tdisplay: block;\n\t\t\tfont-size: 0.8em;\n\t\t\tfont-weight: 600;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.05em;\n\t\t\tmargin-bottom: 0.35em;\n\t\t}\n\t\t.pict-flow-popup-settings-select {\n\t\t\twidth: 100%;\n\t\t\tpadding: 0.4em 0.5em;\n\t\t\tborder: 1px solid var(--pf-input-border);\n\t\t\tborder-radius: 4px;\n\t\t\tfont-size: 0.9em;\n\t\t\tbackground: var(--pf-panel-bg);\n\t\t\tcolor: var(--pf-text-primary);\n\t\t\tcursor: pointer;\n\t\t\toutline: none;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.pict-flow-popup-settings-select:focus {\n\t\t\tborder-color: var(--pf-input-border-focus);\n\t\t}\n\t\t.pict-flow-popup-settings-slider-row {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.5em;\n\t\t}\n\t\t.pict-flow-popup-settings-slider {\n\t\t\tflex: 1;\n\t\t\t-webkit-appearance: none;\n\t\t\tappearance: none;\n\t\t\theight: 4px;\n\t\t\tbackground: var(--pf-input-border);\n\t\t\tborder-radius: 2px;\n\t\t\toutline: none;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-popup-settings-slider::-webkit-slider-thumb {\n\t\t\t-webkit-appearance: none;\n\t\t\tappearance: none;\n\t\t\twidth: 14px;\n\t\t\theight: 14px;\n\t\t\tbackground: var(--pf-node-selected-stroke);\n\t\t\tborder-radius: 50%;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.pict-flow-popup-settings-slider::-moz-range-thumb {\n\t\t\twidth: 14px;\n\t\t\theight: 14px;\n\t\t\tbackground: var(--pf-node-selected-stroke);\n\t\t\tborder-radius: 50%;\n\t\t\tcursor: pointer;\n\t\t\tborder: none;\n\t\t}\n\t\t.pict-flow-popup-settings-slider-value {\n\t\t\tfont-size: 0.85em;\n\t\t\tcolor: var(--pf-text-secondary);\n\t\t\tmin-width: 2.5em;\n\t\t\ttext-align: right;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Collapsed Toolbar ─────────────────────────────────────────────────\n\t/**\n\t * CSS for the collapsed toolbar state (small expand button in corner).\n\t * @returns {string}\n\t */\n\tgetCollapsedToolbarCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-toolbar-collapsed {\n\t\t\tposition: absolute;\n\t\t\ttop: 8px;\n\t\t\tright: 8px;\n\t\t\tz-index: 100;\n\t\t\tdisplay: none;\n\t\t}\n\t\t.pict-flow-toolbar-collapsed.visible {\n\t\t\tdisplay: block;\n\t\t}\n\t\t.pict-flow-toolbar-expand-btn {\n\t\t\twidth: 36px;\n\t\t\theight: 36px;\n\t\t\tborder-radius: 6px;\n\t\t\tborder: 1px solid var(--pf-card-border);\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tbox-shadow: 0 2px 6px rgba(0,0,0,0.1);\n\t\t\tcursor: pointer;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\ttransition: background-color 0.15s, box-shadow 0.15s;\n\t\t}\n\t\t.pict-flow-toolbar-expand-btn:hover {\n\t\t\tbackground-color: var(--pf-button-hover-bg);\n\t\t\tbox-shadow: 0 2px 8px rgba(0,0,0,0.15);\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Floating Toolbar ──────────────────────────────────────────────────\n\t/**\n\t * CSS for the floating draggable toolbar.\n\t * @returns {string}\n\t */\n\tgetFloatingToolbarCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-floating-toolbar {\n\t\t\tposition: absolute;\n\t\t\tz-index: 100;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tgap: 2px;\n\t\t\tpadding: 4px;\n\t\t\tborder-radius: 8px;\n\t\t\tborder: 1px solid var(--pf-card-border);\n\t\t\tbackground-color: var(--pf-toolbar-bg);\n\t\t\tbox-shadow: 0 4px 16px rgba(0,0,0,0.12);\n\t\t\tpointer-events: auto;\n\t\t}\n\t\t.pict-flow-floating-grip {\n\t\t\tcursor: grab;\n\t\t\tpadding: 4px;\n\t\t\tborder-radius: 4px;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\ttransition: background-color 0.15s;\n\t\t}\n\t\t.pict-flow-floating-grip:hover {\n\t\t\tbackground-color: var(--pf-button-hover-bg);\n\t\t}\n\t\t.pict-flow-floating-grip:active {\n\t\t\tcursor: grabbing;\n\t\t}\n\t\t.pict-flow-floating-btn {\n\t\t\twidth: 32px;\n\t\t\theight: 32px;\n\t\t\tborder: none;\n\t\t\tborder-radius: 4px;\n\t\t\tbackground-color: transparent;\n\t\t\tcursor: pointer;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\ttransition: background-color 0.15s;\n\t\t}\n\t\t.pict-flow-floating-btn:focus {\n\t\t\toutline: none;\n\t\t}\n\t\t.pict-flow-floating-btn:hover {\n\t\t\tbackground-color: var(--pf-button-hover-bg);\n\t\t}\n\t\t.pict-flow-floating-separator {\n\t\t\theight: 1px;\n\t\t\tbackground-color: var(--pf-divider-light);\n\t\t\tmargin: 2px 4px;\n\t\t}\n\t\t/* Collapsed floating toolbar — grip-only draggable square */\n\t\t.pict-flow-floating-toolbar.collapsed .pict-flow-floating-btn,\n\t\t.pict-flow-floating-toolbar.collapsed .pict-flow-floating-separator {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.pict-flow-floating-toolbar.collapsed {\n\t\t\tpadding: 0;\n\t\t\tborder-radius: 6px;\n\t\t}\n\t\t.pict-flow-floating-toolbar.collapsed .pict-flow-floating-grip {\n\t\t\twidth: 32px;\n\t\t\theight: 32px;\n\t\t\tpadding: 0;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t}\n\t\t.pict-flow-floating-toolbar.collapsed .pict-flow-floating-grip span {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tline-height: 1;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Icons ─────────────────────────────────────────────────────────────\n\t/**\n\t * CSS for inline SVG icons in palette cards, toolbar buttons, info panel headers, and panel close buttons.\n\t * @returns {string}\n\t */\n\tgetIconCSS()\n\t{\n\t\treturn /*css*/`\n\t\t.pict-flow-icon-svg {\n\t\t\tpointer-events: none;\n\t\t}\n\t\t.pict-flow-palette-card-icon svg {\n\t\t\tdisplay: inline-block;\n\t\t\tvertical-align: middle;\n\t\t}\n\t\t.pict-flow-toolbar-btn-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-toolbar-btn-icon svg {\n\t\t\tdisplay: block;\n\t\t\tvertical-align: middle;\n\t\t}\n\t\t.pict-flow-info-panel-header.with-icon svg {\n\t\t\tdisplay: inline-block;\n\t\t\tvertical-align: middle;\n\t\t\tmargin-right: 4px;\n\t\t}\n\t\t.pict-flow-panel-close-icon {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tline-height: 1;\n\t\t}\n\t\t.pict-flow-panel-close-icon svg {\n\t\t\tdisplay: block;\n\t\t}\n\t\t.pict-flow-palette-toggle-arrow svg {\n\t\t\tdisplay: block;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Bracket Node CSS ──────────────────────────────────────────────────\n\t/**\n\t * CSS for bracket-style node bodies used by sketch/blueprint themes.\n\t *\n\t * The bracket fill rects share class `.pict-flow-node-body` for fill\n\t * inheritance, so these rules must use parent-qualified selectors\n\t * (specificity ≥ 0,2,0) to override the base, variant, hover, and\n\t * selected rules that set stroke and rx/ry on `.pict-flow-node-body`.\n\t *\n\t * @returns {string}\n\t */\n\tgetBracketNodeCSS()\n\t{\n\t\treturn /*css*/`\n\t\t/* Bracket outline path */\n\t\t.pict-flow-node-bracket {\n\t\t\tfill: none;\n\t\t\tstroke: var(--pf-node-body-stroke);\n\t\t\tstroke-width: 2;\n\t\t\tstroke-linecap: round;\n\t\t\tstroke-linejoin: round;\n\t\t}\n\t\t.pict-flow-node.selected .pict-flow-node-bracket {\n\t\t\tstroke: var(--pf-node-selected-stroke);\n\t\t\tstroke-width: 2;\n\t\t}\n\t\t.pict-flow-node:hover .pict-flow-node-bracket {\n\t\t\tstroke: var(--pf-node-body-stroke-hover);\n\t\t\tstroke-width: 1.5;\n\t\t}\n\n\t\t/* Bracket fill rects: no stroke, no rounded corners.\n\t\t Uses parent-qualified selectors to beat variant rules\n\t\t (e.g. .pict-flow-node-start .pict-flow-node-body). */\n\t\t.pict-flow-node .pict-flow-node-bracket-fill,\n\t\t.pict-flow-node .pict-flow-node-bracket-title-fill {\n\t\t\tstroke: none;\n\t\t\tstroke-width: 0;\n\t\t\trx: 0;\n\t\t\try: 0;\n\t\t}\n\t\t/* Beat hover rule: .pict-flow-node:hover .pict-flow-node-body */\n\t\t.pict-flow-node:hover .pict-flow-node-bracket-fill,\n\t\t.pict-flow-node:hover .pict-flow-node-bracket-title-fill {\n\t\t\tstroke: none;\n\t\t\tstroke-width: 0;\n\t\t}\n\t\t/* Beat selected rule: .pict-flow-node.selected .pict-flow-node-body */\n\t\t.pict-flow-node.selected .pict-flow-node-bracket-fill,\n\t\t.pict-flow-node.selected .pict-flow-node-bracket-title-fill {\n\t\t\tstroke: none;\n\t\t\tstroke-width: 0;\n\t\t}\n\t\t`;\n\t}\n\n\t// ── Aggregate Methods ──────────────────────────────────────────────────\n\n\t/**\n\t * Concatenate all domain CSS getters into a single CSS string.\n\t * Includes theme overrides if a theme provider is active.\n\t * @returns {string}\n\t */\n\tgenerateCSS()\n\t{\n\t\tlet tmpBaseCSS = (\n\t\t\tthis.getContainerCSS() +\n\t\t\tthis.getNodeCSS() +\n\t\t\tthis.getBodyContentCSS() +\n\t\t\tthis.getNodeVariantCSS() +\n\t\t\tthis.getPortCSS() +\n\t\t\tthis.getConnectionCSS() +\n\t\t\tthis.getHandleCSS() +\n\t\t\tthis.getTetherCSS() +\n\t\t\tthis.getPanelCSS() +\n\t\t\tthis.getInfoPanelCSS() +\n\t\t\tthis.getNodePropsEditorCSS() +\n\t\t\tthis.getPanelTabsCSS() +\n\t\t\tthis.getBracketNodeCSS() +\n\t\t\tthis.getFullscreenCSS() +\n\t\t\tthis.getToolbarCSS() +\n\t\t\tthis.getPaletteCSS() +\n\t\t\tthis.getPopupCSS() +\n\t\t\tthis.getCollapsedToolbarCSS() +\n\t\t\tthis.getFloatingToolbarCSS() +\n\t\t\tthis.getIconCSS()\n\t\t);\n\n\t\t// Apply theme overrides if a theme provider exists\n\t\tif (this._FlowView && this._FlowView._ThemeProvider)\n\t\t{\n\t\t\tlet tmpTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\t\tif (tmpTheme && tmpTheme.CSSVariables && Object.keys(tmpTheme.CSSVariables).length > 0)\n\t\t\t{\n\t\t\t\tlet tmpOverrides = '.pict-flow-container {\\n';\n\t\t\t\tfor (let tmpKey in tmpTheme.CSSVariables)\n\t\t\t\t{\n\t\t\t\t\ttmpOverrides += '\\t' + tmpKey + ': ' + tmpTheme.CSSVariables[tmpKey] + ';\\n';\n\t\t\t\t}\n\t\t\t\ttmpOverrides += '}\\n';\n\t\t\t\ttmpBaseCSS += tmpOverrides;\n\t\t\t}\n\t\t\tif (tmpTheme && tmpTheme.AdditionalCSS)\n\t\t\t{\n\t\t\t\ttmpBaseCSS += tmpTheme.AdditionalCSS;\n\t\t\t}\n\t\t}\n\n\t\treturn tmpBaseCSS;\n\t}\n\n\t/**\n\t * Register all flow CSS with pict's CSSMap service.\n\t * Uses correct parameter ordering: (hash, content, priority, provider).\n\t * Removes existing CSS first to allow theme re-registration,\n\t * then re-injects into the DOM.\n\t */\n\tregisterCSS()\n\t{\n\t\tif (this.fable && this.fable.CSSMap)\n\t\t{\n\t\t\t// Remove existing CSS first so we can re-register with updated theme overrides\n\t\t\tthis.fable.CSSMap.removeCSS('PictSectionFlow-CSS');\n\t\t\tthis.fable.CSSMap.addCSS('PictSectionFlow-CSS', this.generateCSS(), 500, 'PictProviderFlowCSS');\n\t\t\t// Re-inject into the DOM to apply the updated CSS\n\t\t\tthis.fable.CSSMap.injectCSS();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowCSS: CSSMap not available; CSS not registered.');\n\t\t}\n\t}\n}\n\nmodule.exports = PictProviderFlowCSS;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nclass PictProviderFlowConnectorShapes extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowConnectorShapes';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Default shape configurations — each keyed by a shape identifier\n\t\t// _OriginalShapes stores a deep copy for reset when switching themes.\n\t\tthis._DefaultShapes =\n\t\t{\n\t\t\t'port':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '5' },\n\t\t\t\tClassName: 'pict-flow-port'\n\t\t\t},\n\t\t\t'panel-indicator':\n\t\t\t{\n\t\t\t\tElementType: 'rect',\n\t\t\t\tAttributes: { rx: '2', ry: '2' },\n\t\t\t\tClassName: 'pict-flow-node-panel-indicator'\n\t\t\t},\n\t\t\t'connection-path':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-connection'\n\t\t\t},\n\t\t\t'connection-hitarea':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-connection-hitarea'\n\t\t\t},\n\t\t\t'connection-handle':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '6' },\n\t\t\t\tClassName: 'pict-flow-connection-handle'\n\t\t\t},\n\t\t\t'connection-handle-midpoint':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '6' },\n\t\t\t\tClassName: 'pict-flow-connection-handle-midpoint'\n\t\t\t},\n\t\t\t'drag-connection':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-drag-connection'\n\t\t\t},\n\t\t\t'tether-path':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-tether-line'\n\t\t\t},\n\t\t\t'tether-hitarea':\n\t\t\t{\n\t\t\t\tElementType: 'path',\n\t\t\t\tAttributes: {},\n\t\t\t\tClassName: 'pict-flow-tether-hitarea'\n\t\t\t},\n\t\t\t'tether-handle':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '6' },\n\t\t\t\tClassName: 'pict-flow-tether-handle'\n\t\t\t},\n\t\t\t'tether-handle-midpoint':\n\t\t\t{\n\t\t\t\tElementType: 'circle',\n\t\t\t\tAttributes: { r: '6' },\n\t\t\t\tClassName: 'pict-flow-tether-handle-midpoint'\n\t\t\t},\n\t\t\t'arrowhead-connection':\n\t\t\t{\n\t\t\t\tMarkerWidth: '5',\n\t\t\t\tMarkerHeight: '7',\n\t\t\t\tRefX: '7.5',\n\t\t\t\tRefY: '3.5',\n\t\t\t\tPoints: '0 0, 5 3.5, 0 7',\n\t\t\t\tFill: '#95a5a6'\n\t\t\t},\n\t\t\t'arrowhead-connection-selected':\n\t\t\t{\n\t\t\t\tMarkerWidth: '5',\n\t\t\t\tMarkerHeight: '7',\n\t\t\t\tRefX: '7.5',\n\t\t\t\tRefY: '3.5',\n\t\t\t\tPoints: '0 0, 5 3.5, 0 7',\n\t\t\t\tFill: '#3498db'\n\t\t\t},\n\t\t\t'arrowhead-tether':\n\t\t\t{\n\t\t\t\tMarkerWidth: '4',\n\t\t\t\tMarkerHeight: '6',\n\t\t\t\tRefX: '6',\n\t\t\t\tRefY: '3',\n\t\t\t\tPoints: '0 0, 4 3, 0 6',\n\t\t\t\tFill: '#95a5a6'\n\t\t\t}\n\t\t};\n\n\t\t// Store a deep copy for resetting when switching themes\n\t\tthis._OriginalShapes = JSON.parse(JSON.stringify(this._DefaultShapes));\n\t}\n\n\t// ── Theme Override Methods ─────────────────────────────────────────────\n\n\t/**\n\t * Apply theme-specific shape overrides.\n\t * Merges the overrides into the active shape configs.\n\t * @param {Object} pOverrides - Map of shape key to partial config\n\t */\n\tapplyThemeOverrides(pOverrides)\n\t{\n\t\tif (!pOverrides || typeof pOverrides !== 'object')\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tfor (let tmpKey in pOverrides)\n\t\t{\n\t\t\tif (this._DefaultShapes.hasOwnProperty(tmpKey))\n\t\t\t{\n\t\t\t\tObject.assign(this._DefaultShapes[tmpKey], pOverrides[tmpKey]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tthis._DefaultShapes[tmpKey] = pOverrides[tmpKey];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Reset all shape configs to their original defaults.\n\t * Called before applying new theme overrides to prevent accumulation.\n\t */\n\tresetToDefaults()\n\t{\n\t\tthis._DefaultShapes = JSON.parse(JSON.stringify(this._OriginalShapes));\n\t}\n\n\t/**\n\t * Get a shape configuration by key.\n\t * @param {string} pShapeKey\n\t * @returns {Object|null}\n\t */\n\tgetShapeConfig(pShapeKey)\n\t{\n\t\tif (this._DefaultShapes.hasOwnProperty(pShapeKey))\n\t\t{\n\t\t\treturn this._DefaultShapes[pShapeKey];\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Override or add a shape configuration.\n\t * Consumers can call this to customize connector shapes without subclassing.\n\t * @param {string} pShapeKey\n\t * @param {Object} pConfig\n\t */\n\tsetShapeConfig(pShapeKey, pConfig)\n\t{\n\t\tthis._DefaultShapes[pShapeKey] = pConfig;\n\t}\n\n\t/**\n\t * Get all registered shape keys.\n\t * @returns {string[]}\n\t */\n\tgetShapeKeys()\n\t{\n\t\treturn Object.keys(this._DefaultShapes);\n\t}\n\n\t// ---- Factory Methods ----\n\n\t/**\n\t * Create a port SVG element.\n\t * @param {Object} pPortData - Port data with Hash, Direction\n\t * @param {{x: number, y: number}} pPosition - Local position within the node group\n\t * @param {string} pNodeHash - The owning node hash\n\t * @returns {SVGCircleElement}\n\t */\n\tcreatePortElement(pPortData, pPosition, pNodeHash)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['port'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\tlet tmpClassName = tmpConfig.ClassName + ' ' + pPortData.Direction;\n\t\tif (pPortData.PortType)\n\t\t{\n\t\t\ttmpClassName += ' port-type-' + pPortData.PortType;\n\t\t}\n\t\ttmpElement.setAttribute('class', tmpClassName);\n\t\ttmpElement.setAttribute('cx', String(pPosition.x));\n\t\ttmpElement.setAttribute('cy', String(pPosition.y));\n\t\t// Apply config attributes (r, etc.)\n\t\tfor (let tmpKey in tmpConfig.Attributes)\n\t\t{\n\t\t\ttmpElement.setAttribute(tmpKey, tmpConfig.Attributes[tmpKey]);\n\t\t}\n\t\ttmpElement.setAttribute('data-port-hash', pPortData.Hash);\n\t\ttmpElement.setAttribute('data-node-hash', pNodeHash);\n\t\ttmpElement.setAttribute('data-port-direction', pPortData.Direction);\n\t\tif (pPortData.PortType)\n\t\t{\n\t\t\ttmpElement.setAttribute('data-port-type', pPortData.PortType);\n\t\t}\n\t\ttmpElement.setAttribute('data-element-type', 'port');\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a panel indicator SVG element.\n\t * @param {string} pNodeHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @returns {SVGRectElement}\n\t */\n\tcreatePanelIndicatorElement(pNodeHash, pX, pY, pWidth, pHeight)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['panel-indicator'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('x', String(pX));\n\t\ttmpElement.setAttribute('y', String(pY));\n\t\ttmpElement.setAttribute('width', String(pWidth));\n\t\ttmpElement.setAttribute('height', String(pHeight));\n\t\t// Apply config attributes (rx, ry, etc.)\n\t\tfor (let tmpKey in tmpConfig.Attributes)\n\t\t{\n\t\t\ttmpElement.setAttribute(tmpKey, tmpConfig.Attributes[tmpKey]);\n\t\t}\n\t\ttmpElement.setAttribute('data-node-hash', pNodeHash);\n\t\ttmpElement.setAttribute('data-element-type', 'panel-indicator');\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a visible connection path SVG element.\n\t * @param {string} pPath - The SVG path d-string\n\t * @param {string} pConnectionHash\n\t * @param {boolean} pIsSelected\n\t * @param {string} pViewIdentifier\n\t * @returns {SVGPathElement}\n\t */\n\tcreateConnectionPathElement(pPath, pConnectionHash, pIsSelected, pViewIdentifier)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['connection-path'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName + (pIsSelected ? ' selected' : ''));\n\t\ttmpElement.setAttribute('d', pPath);\n\t\ttmpElement.setAttribute('data-connection-hash', pConnectionHash);\n\t\ttmpElement.setAttribute('data-element-type', 'connection');\n\n\t\t// Arrow marker\n\t\tlet tmpMarkerConfig = pIsSelected\n\t\t\t? this._DefaultShapes['arrowhead-connection-selected']\n\t\t\t: this._DefaultShapes['arrowhead-connection'];\n\t\t// The marker id follows the naming convention used in generateMarkerDefs\n\t\tlet tmpMarkerId = pIsSelected\n\t\t\t? ('flow-arrowhead-selected-' + pViewIdentifier)\n\t\t\t: ('flow-arrowhead-' + pViewIdentifier);\n\t\ttmpElement.setAttribute('marker-end', 'url(#' + tmpMarkerId + ')');\n\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a connection hit area SVG element (wider invisible path for click targeting).\n\t * @param {string} pPath - The SVG path d-string\n\t * @param {string} pConnectionHash\n\t * @returns {SVGPathElement}\n\t */\n\tcreateConnectionHitAreaElement(pPath, pConnectionHash)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['connection-hitarea'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('d', pPath);\n\t\ttmpElement.setAttribute('data-connection-hash', pConnectionHash);\n\t\ttmpElement.setAttribute('data-element-type', 'connection-hitarea');\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a drag handle circle element.\n\t * Works for both connection handles and tether handles.\n\t * @param {string} pOwnerHash - Connection hash or panel hash\n\t * @param {string} pHandleType - e.g. 'ortho-corner1', 'bezier-midpoint'\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {string} pShapeKey - 'connection-handle', 'connection-handle-midpoint', 'tether-handle', 'tether-handle-midpoint'\n\t * @returns {SVGCircleElement}\n\t */\n\tcreateHandleElement(pOwnerHash, pHandleType, pX, pY, pShapeKey)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes[pShapeKey] || this._DefaultShapes['connection-handle'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('cx', String(pX));\n\t\ttmpElement.setAttribute('cy', String(pY));\n\t\t// Apply config attributes (r, etc.)\n\t\tfor (let tmpKey in tmpConfig.Attributes)\n\t\t{\n\t\t\ttmpElement.setAttribute(tmpKey, tmpConfig.Attributes[tmpKey]);\n\t\t}\n\t\ttmpElement.setAttribute('data-handle-type', pHandleType);\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a complete drag handle element with all data attributes set and\n\t * append it to the given layer. Unifies the handle-creation logic that\n\t * was previously duplicated in ConnectionRenderer and TetherService.\n\t *\n\t * @param {SVGGElement} pLayer - The SVG group to append the handle to\n\t * @param {string} pOwnerHash - Connection hash or panel hash\n\t * @param {string} pHandleType - e.g. 'ortho-corner1', 'bezier-midpoint'\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {string} pShapeKey - Shape config key ('connection-handle', 'tether-handle-midpoint', etc.)\n\t * @param {string} pElementType - data-element-type value ('connection-handle' or 'tether-handle')\n\t * @param {string} pOwnerAttrName - data attribute name for the owner ('data-connection-hash' or 'data-panel-hash')\n\t * @returns {SVGElement}\n\t */\n\tcreateFullHandle(pLayer, pOwnerHash, pHandleType, pX, pY, pShapeKey, pElementType, pOwnerAttrName)\n\t{\n\t\tlet tmpHandle = this.createHandleElement(pOwnerHash, pHandleType, pX, pY, pShapeKey);\n\t\ttmpHandle.setAttribute('data-element-type', pElementType);\n\t\ttmpHandle.setAttribute(pOwnerAttrName, pOwnerHash);\n\t\tpLayer.appendChild(tmpHandle);\n\t\treturn tmpHandle;\n\t}\n\n\t/**\n\t * Create a temporary drag connection path element.\n\t * @param {string} pPath - The SVG path d-string\n\t * @returns {SVGPathElement}\n\t */\n\tcreateDragConnectionElement(pPath)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['drag-connection'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('d', pPath);\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a visible tether path SVG element.\n\t * @param {string} pPath - The SVG path d-string\n\t * @param {string} pPanelHash\n\t * @param {boolean} pIsSelected\n\t * @param {string} pViewIdentifier\n\t * @returns {SVGPathElement}\n\t */\n\tcreateTetherPathElement(pPath, pPanelHash, pIsSelected, pViewIdentifier)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['tether-path'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName + (pIsSelected ? ' selected' : ''));\n\t\ttmpElement.setAttribute('d', pPath);\n\t\ttmpElement.setAttribute('marker-end', 'url(#flow-tether-arrowhead-' + pViewIdentifier + ')');\n\t\ttmpElement.setAttribute('data-element-type', 'tether');\n\t\ttmpElement.setAttribute('data-panel-hash', pPanelHash);\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Create a tether hit area SVG element.\n\t * @param {string} pPath - The SVG path d-string\n\t * @param {string} pPanelHash\n\t * @returns {SVGPathElement}\n\t */\n\tcreateTetherHitAreaElement(pPath, pPanelHash)\n\t{\n\t\tlet tmpConfig = this._DefaultShapes['tether-hitarea'];\n\t\tlet tmpElement = this._FlowView._SVGHelperProvider.createSVGElement(tmpConfig.ElementType);\n\t\ttmpElement.setAttribute('class', tmpConfig.ClassName);\n\t\ttmpElement.setAttribute('d', pPath);\n\t\ttmpElement.setAttribute('data-element-type', 'tether-hitarea');\n\t\ttmpElement.setAttribute('data-panel-hash', pPanelHash);\n\t\treturn tmpElement;\n\t}\n\n\t/**\n\t * Generate SVG marker definition markup for all arrowhead types.\n\t * Returns raw SVG markup to be injected into the <defs> section.\n\t * @param {string} pViewIdentifier\n\t * @returns {string}\n\t */\n\tgenerateMarkerDefs(pViewIdentifier)\n\t{\n\t\tlet tmpConnectionMarker = this._DefaultShapes['arrowhead-connection'];\n\t\tlet tmpSelectedMarker = this._DefaultShapes['arrowhead-connection-selected'];\n\t\tlet tmpTetherMarker = this._DefaultShapes['arrowhead-tether'];\n\n\t\tlet tmpMarkup = '';\n\n\t\t// Normal connection arrowhead (default gray)\n\t\ttmpMarkup += '<marker id=\"flow-arrowhead-' + pViewIdentifier + '\"'\n\t\t\t+ ' markerWidth=\"' + tmpConnectionMarker.MarkerWidth + '\"'\n\t\t\t+ ' markerHeight=\"' + tmpConnectionMarker.MarkerHeight + '\"'\n\t\t\t+ ' refX=\"' + tmpConnectionMarker.RefX + '\"'\n\t\t\t+ ' refY=\"' + tmpConnectionMarker.RefY + '\"'\n\t\t\t+ ' orient=\"auto\" markerUnits=\"strokeWidth\">'\n\t\t\t+ '<polygon points=\"' + tmpConnectionMarker.Points + '\" fill=\"' + tmpConnectionMarker.Fill + '\" />'\n\t\t\t+ '</marker>';\n\n\t\t// Per-port-type connection arrowheads\n\t\tlet tmpPortTypeColors =\n\t\t{\n\t\t\t'event-in': '#3498db',\n\t\t\t'event-out': '#2ecc71',\n\t\t\t'setting': '#e67e22',\n\t\t\t'value': '#f1c40f',\n\t\t\t'error': '#e74c3c'\n\t\t};\n\n\t\tfor (let tmpType in tmpPortTypeColors)\n\t\t{\n\t\t\ttmpMarkup += '<marker id=\"flow-arrowhead-' + tmpType + '-' + pViewIdentifier + '\"'\n\t\t\t\t+ ' markerWidth=\"' + tmpConnectionMarker.MarkerWidth + '\"'\n\t\t\t\t+ ' markerHeight=\"' + tmpConnectionMarker.MarkerHeight + '\"'\n\t\t\t\t+ ' refX=\"' + tmpConnectionMarker.RefX + '\"'\n\t\t\t\t+ ' refY=\"' + tmpConnectionMarker.RefY + '\"'\n\t\t\t\t+ ' orient=\"auto\" markerUnits=\"strokeWidth\">'\n\t\t\t\t+ '<polygon points=\"' + tmpConnectionMarker.Points + '\" fill=\"' + tmpPortTypeColors[tmpType] + '\" />'\n\t\t\t\t+ '</marker>';\n\t\t}\n\n\t\t// Selected connection arrowhead\n\t\ttmpMarkup += '<marker id=\"flow-arrowhead-selected-' + pViewIdentifier + '\"'\n\t\t\t+ ' markerWidth=\"' + tmpSelectedMarker.MarkerWidth + '\"'\n\t\t\t+ ' markerHeight=\"' + tmpSelectedMarker.MarkerHeight + '\"'\n\t\t\t+ ' refX=\"' + tmpSelectedMarker.RefX + '\"'\n\t\t\t+ ' refY=\"' + tmpSelectedMarker.RefY + '\"'\n\t\t\t+ ' orient=\"auto\" markerUnits=\"strokeWidth\">'\n\t\t\t+ '<polygon points=\"' + tmpSelectedMarker.Points + '\" fill=\"' + tmpSelectedMarker.Fill + '\" />'\n\t\t\t+ '</marker>';\n\n\t\t// Tether arrowhead\n\t\ttmpMarkup += '<marker id=\"flow-tether-arrowhead-' + pViewIdentifier + '\"'\n\t\t\t+ ' markerWidth=\"' + tmpTetherMarker.MarkerWidth + '\"'\n\t\t\t+ ' markerHeight=\"' + tmpTetherMarker.MarkerHeight + '\"'\n\t\t\t+ ' refX=\"' + tmpTetherMarker.RefX + '\"'\n\t\t\t+ ' refY=\"' + tmpTetherMarker.RefY + '\"'\n\t\t\t+ ' orient=\"auto\" markerUnits=\"strokeWidth\">'\n\t\t\t+ '<polygon points=\"' + tmpTetherMarker.Points + '\" fill=\"' + tmpTetherMarker.Fill + '\" />'\n\t\t\t+ '</marker>';\n\n\t\treturn tmpMarkup;\n\t}\n}\n\nmodule.exports = PictProviderFlowConnectorShapes;\n","const libPictProvider = require('pict-provider');\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowEventHandler'\n};\n\n/**\n * Event handler provider for the flow diagram.\n * Provides hook points for extensibility - consumers can register handlers\n * for flow events like node selection, movement, connection creation, etc.\n *\n * Supported events:\n * - onNodeSelected(node) - A node was selected (or null for deselection)\n * - onNodeAdded(node) - A new node was added\n * - onNodeRemoved(node) - A node was removed\n * - onNodeMoved(node) - A node was moved to a new position\n * - onConnectionSelected(conn) - A connection was selected\n * - onConnectionCreated(conn) - A new connection was created\n * - onConnectionRemoved(conn) - A connection was removed\n * - onConnectionHandleMoved(conn) - A connection's drag handle was repositioned\n * - onConnectionModeChanged(conn) - A connection's line mode was toggled (bezier/orthogonal)\n * - onPanelOpened(panelData) - A properties panel was opened\n * - onPanelClosed(panelData) - A properties panel was closed\n * - onPanelMoved(panelData) - A properties panel was moved\n * - onTetherSelected(panelData) - A tether line was selected (or null for deselection)\n * - onTetherHandleMoved(panelData) - A tether's drag handle was repositioned\n * - onTetherModeChanged(panelData) - A tether's line mode was toggled (bezier/orthogonal)\n * - onLayoutSaved(layoutData) - A layout snapshot was saved\n * - onLayoutRestored(layoutData) - A saved layout was restored\n * - onLayoutDeleted(layoutData) - A saved layout was deleted\n * - onFlowChanged(flowData) - The flow data changed (catch-all)\n */\nclass PictProviderFlowEventHandler extends libPictProvider\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowEventHandler';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Event handler registry\n\t\tthis._Handlers = {};\n\t}\n\n\t/**\n\t * Register an event handler\n\t * @param {string} pEventName - The event name (e.g., 'onNodeSelected')\n\t * @param {Function} pHandler - The handler function\n\t * @param {string} [pHandlerHash] - Optional unique identifier for this handler (for later removal)\n\t * @returns {string} The handler hash\n\t */\n\tregisterHandler(pEventName, pHandler, pHandlerHash)\n\t{\n\t\tif (typeof pHandler !== 'function')\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowEventHandler registerHandler: handler for '${pEventName}' is not a function`);\n\t\t\treturn null;\n\t\t}\n\n\t\tif (!this._Handlers[pEventName])\n\t\t{\n\t\t\tthis._Handlers[pEventName] = [];\n\t\t}\n\n\t\tlet tmpHash = pHandlerHash || `handler-${this.fable.getUUID()}`;\n\n\t\tthis._Handlers[pEventName].push({\n\t\t\tHash: tmpHash,\n\t\t\tHandler: pHandler\n\t\t});\n\n\t\tthis.log.trace(`PictProviderFlowEventHandler registered handler '${tmpHash}' for event '${pEventName}'`);\n\n\t\treturn tmpHash;\n\t}\n\n\t/**\n\t * Remove a specific event handler\n\t * @param {string} pEventName - The event name\n\t * @param {string} pHandlerHash - The handler hash to remove\n\t * @returns {boolean}\n\t */\n\tremoveHandler(pEventName, pHandlerHash)\n\t{\n\t\tif (!this._Handlers[pEventName]) return false;\n\n\t\tlet tmpIndex = this._Handlers[pEventName].findIndex((pH) => pH.Hash === pHandlerHash);\n\t\tif (tmpIndex >= 0)\n\t\t{\n\t\t\tthis._Handlers[pEventName].splice(tmpIndex, 1);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Remove all handlers for a specific event\n\t * @param {string} pEventName\n\t */\n\tremoveAllHandlers(pEventName)\n\t{\n\t\tif (pEventName)\n\t\t{\n\t\t\tthis._Handlers[pEventName] = [];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._Handlers = {};\n\t\t}\n\t}\n\n\t/**\n\t * Fire an event, calling all registered handlers\n\t * @param {string} pEventName - The event name\n\t * @param {*} pEventData - The event data to pass to handlers\n\t */\n\tfireEvent(pEventName, pEventData)\n\t{\n\t\tthis.log.trace(`PictProviderFlowEventHandler firing event '${pEventName}'`);\n\n\t\tif (!this._Handlers[pEventName] || this._Handlers[pEventName].length === 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tfor (let i = 0; i < this._Handlers[pEventName].length; i++)\n\t\t{\n\t\t\tlet tmpEntry = this._Handlers[pEventName][i];\n\t\t\ttry\n\t\t\t{\n\t\t\t\ttmpEntry.Handler(pEventData, this._FlowView);\n\t\t\t}\n\t\t\tcatch (pError)\n\t\t\t{\n\t\t\t\tthis.log.error(`PictProviderFlowEventHandler error in handler '${tmpEntry.Hash}' for event '${pEventName}': ${pError.message}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check if any handlers are registered for a specific event\n\t * @param {string} pEventName\n\t * @returns {boolean}\n\t */\n\thasHandlers(pEventName)\n\t{\n\t\treturn !!(this._Handlers[pEventName] && this._Handlers[pEventName].length > 0);\n\t}\n\n\t/**\n\t * Get the count of handlers for a specific event\n\t * @param {string} pEventName\n\t * @returns {number}\n\t */\n\tgetHandlerCount(pEventName)\n\t{\n\t\tif (!this._Handlers[pEventName]) return 0;\n\t\treturn this._Handlers[pEventName].length;\n\t}\n}\n\nmodule.exports = PictProviderFlowEventHandler;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-Geometry\n *\n * Shared geometry utilities for the flow diagram.\n * Provides direction vectors and edge center calculations used by\n * connections, tethers, and other flow components.\n *\n * Port Side values (12 positions):\n *\n * Top edge: 'top-left' 'top' 'top-right'\n * Left edge: 'left-top' 'left' 'left-bottom'\n * Right edge: 'right-top' 'right' 'right-bottom'\n * Bottom edge: 'bottom-left' 'bottom' 'bottom-right'\n *\n * The old 4-value sides ('left', 'right', 'top', 'bottom') map to\n * the middle position on each edge for backward compatibility.\n */\nclass PictProviderFlowGeometry extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowGeometry';\n\t}\n\n\t/**\n\t * Extract the edge name from a Side value.\n\t *\n\t * Maps all 12 positions (and the 4 legacy values) back to\n\t * the edge they sit on: 'left', 'right', 'top', or 'bottom'.\n\t *\n\t * @param {string} pSide - Any valid Side value\n\t * @returns {string} The edge: 'left', 'right', 'top', or 'bottom'\n\t */\n\tgetEdgeFromSide(pSide)\n\t{\n\t\tswitch (pSide)\n\t\t{\n\t\t\tcase 'left-top':\n\t\t\tcase 'left':\n\t\t\tcase 'left-bottom':\n\t\t\t\treturn 'left';\n\n\t\t\tcase 'right-top':\n\t\t\tcase 'right':\n\t\t\tcase 'right-bottom':\n\t\t\t\treturn 'right';\n\n\t\t\tcase 'top-left':\n\t\t\tcase 'top':\n\t\t\tcase 'top-right':\n\t\t\t\treturn 'top';\n\n\t\t\tcase 'bottom-left':\n\t\t\tcase 'bottom':\n\t\t\tcase 'bottom-right':\n\t\t\t\treturn 'bottom';\n\n\t\t\tdefault:\n\t\t\t\treturn 'right';\n\t\t}\n\t}\n\n\t/**\n\t * Get the outward unit direction vector for a given side.\n\t *\n\t * All positions on the same edge share the same direction vector.\n\t *\n\t * @param {string} pSide - Any valid Side value (12 positions or 4 legacy)\n\t * @returns {{dx: number, dy: number}}\n\t */\n\tsideDirection(pSide)\n\t{\n\t\tswitch (this.getEdgeFromSide(pSide))\n\t\t{\n\t\t\tcase 'left': return { dx: -1, dy: 0 };\n\t\t\tcase 'right': return { dx: 1, dy: 0 };\n\t\t\tcase 'top': return { dx: 0, dy: -1 };\n\t\t\tcase 'bottom': return { dx: 0, dy: 1 };\n\t\t\tdefault: return { dx: 1, dy: 0 };\n\t\t}\n\t}\n\n\t/**\n\t * Get the center point of a rectangle's edge.\n\t * Works for any object with X, Y, Width, Height properties\n\t * (nodes, panels, or any rectangular element).\n\t *\n\t * @param {Object} pRectData - Object with X, Y, Width, Height\n\t * @param {string} pSide - 'left', 'right', 'top', 'bottom'\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetEdgeCenter(pRectData, pSide)\n\t{\n\t\tswitch (pSide)\n\t\t{\n\t\t\tcase 'left':\n\t\t\t\treturn { x: pRectData.X, y: pRectData.Y + pRectData.Height / 2 };\n\t\t\tcase 'right':\n\t\t\t\treturn { x: pRectData.X + pRectData.Width, y: pRectData.Y + pRectData.Height / 2 };\n\t\t\tcase 'top':\n\t\t\t\treturn { x: pRectData.X + pRectData.Width / 2, y: pRectData.Y };\n\t\t\tcase 'bottom':\n\t\t\t\treturn { x: pRectData.X + pRectData.Width / 2, y: pRectData.Y + pRectData.Height };\n\t\t\tdefault:\n\t\t\t\treturn { x: pRectData.X + pRectData.Width, y: pRectData.Y + pRectData.Height / 2 };\n\t\t}\n\t}\n\n\t/**\n\t * Calculate a port's local position relative to node origin.\n\t *\n\t * Supports 12 positions (3 zones per edge). For left/right edges,\n\t * the body area below the title bar is divided into vertical zones.\n\t * For top/bottom edges, the full width is divided into horizontal zones.\n\t *\n\t * When pPortCountsBySide is provided, zone fractions are computed\n\t * proportionally based on actual port counts (adaptive zones).\n\t * Otherwise, fixed 1/3 zones are used for backward compatibility.\n\t *\n\t * Multiple ports sharing the same Side value distribute evenly within\n\t * their zone.\n\t *\n\t * @param {string} pSide - Side value (any of 12 positions or 4 legacy)\n\t * @param {number} pIndex - Index of this port within its Side group\n\t * @param {number} pTotal - Total ports with this Side value\n\t * @param {number} pWidth - Node width\n\t * @param {number} pHeight - Node height\n\t * @param {number} pTitleBarHeight - Height of the node title bar\n\t * @param {Object} [pPortCountsBySide] - Optional map of Side → port count\n\t * for all ports on the node. Enables adaptive zone sizing.\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetPortLocalPosition(pSide, pIndex, pTotal, pWidth, pHeight, pTitleBarHeight, pPortCountsBySide)\n\t{\n\t\tlet tmpEdge = this.getEdgeFromSide(pSide);\n\t\tlet tmpZone = pPortCountsBySide\n\t\t\t? this._computeAdaptiveZone(pSide, pPortCountsBySide)\n\t\t\t: this._getZoneFromSide(pSide);\n\n\t\t// Use the fixed zone to decide alignment intent (start/center/end)\n\t\t// because adaptive zones shift boundaries when neighbouring zones\n\t\t// are empty, which would break alignment decisions.\n\t\tlet tmpFixedZone = this._getZoneFromSide(pSide);\n\n\t\t// Minimum spacing between port centers (px)\n\t\tlet tmpMinSpacing = 16;\n\n\t\t// Reserve space at the bottom of the body so that port badges\n\t\t// never overlap the panel-indicator icon (10×10 rect at bottom-right)\n\t\t// and always leave a visible gap above the node bottom edge.\n\t\tlet tmpBottomPad = 16;\n\n\t\t// Determine alignment from the fixed zone position:\n\t\t// start zone (0.000 – 0.333) → start-align (offset 0)\n\t\t// middle zone (0.333 – 0.667) → center\n\t\t// end zone (0.667 – 1.000) → end-align\n\t\tlet tmpAlignment = 'start';\n\t\tif (tmpFixedZone.start >= 0.5)\n\t\t{\n\t\t\ttmpAlignment = 'end';\n\t\t}\n\t\telse if (tmpFixedZone.start >= 0.17)\n\t\t{\n\t\t\ttmpAlignment = 'center';\n\t\t}\n\n\t\tif (tmpEdge === 'left' || tmpEdge === 'right')\n\t\t{\n\t\t\tlet tmpX = (tmpEdge === 'left') ? 0 : pWidth;\n\t\t\tlet tmpBodyHeight = pHeight - pTitleBarHeight - tmpBottomPad;\n\t\t\tlet tmpZoneStart = pTitleBarHeight + tmpBodyHeight * tmpZone.start;\n\t\t\tlet tmpZoneHeight = tmpBodyHeight * (tmpZone.end - tmpZone.start);\n\n\t\t\t// Use fixed spacing so port gaps stay consistent across cards\n\t\t\t// even when one edge drives the card height beyond what the\n\t\t\t// other needs.\n\t\t\tlet tmpSpacing = tmpMinSpacing;\n\t\t\tlet tmpGroupHeight = tmpSpacing * (pTotal + 1);\n\t\t\tlet tmpSlack = tmpZoneHeight - tmpGroupHeight;\n\t\t\tif (tmpSlack < 0)\n\t\t\t{\n\t\t\t\ttmpSlack = 0;\n\t\t\t}\n\n\t\t\tlet tmpAlignOffset = 0;\n\t\t\tif (tmpAlignment === 'end')\n\t\t\t{\n\t\t\t\ttmpAlignOffset = tmpSlack;\n\t\t\t}\n\t\t\telse if (tmpAlignment === 'center')\n\t\t\t{\n\t\t\t\ttmpAlignOffset = tmpSlack / 2;\n\t\t\t}\n\n\t\t\tlet tmpY = tmpZoneStart + tmpAlignOffset + tmpSpacing * (pIndex + 1);\n\t\t\treturn { x: tmpX, y: tmpY };\n\t\t}\n\n\t\t// top or bottom\n\t\tlet tmpY = (tmpEdge === 'top') ? 0 : pHeight;\n\t\tlet tmpZoneStart = pWidth * tmpZone.start;\n\t\tlet tmpZoneWidth = pWidth * (tmpZone.end - tmpZone.start);\n\n\t\tlet tmpSpacing = tmpMinSpacing;\n\t\tlet tmpGroupWidth = tmpSpacing * (pTotal + 1);\n\t\tlet tmpSlack = tmpZoneWidth - tmpGroupWidth;\n\t\tif (tmpSlack < 0)\n\t\t{\n\t\t\ttmpSlack = 0;\n\t\t}\n\n\t\tlet tmpAlignOffset = 0;\n\t\tif (tmpAlignment === 'end')\n\t\t{\n\t\t\ttmpAlignOffset = tmpSlack;\n\t\t}\n\t\telse if (tmpAlignment === 'center')\n\t\t{\n\t\t\ttmpAlignOffset = tmpSlack / 2;\n\t\t}\n\n\t\tlet tmpX = tmpZoneStart + tmpAlignOffset + tmpSpacing * (pIndex + 1);\n\t\treturn { x: tmpX, y: tmpY };\n\t}\n\n\t/**\n\t * Get the zone fraction (start, end) for a Side value.\n\t *\n\t * Each edge is divided into three zones of equal size:\n\t * start: 0.0 — 0.333\n\t * middle: 0.333 — 0.667\n\t * end: 0.667 — 1.0\n\t *\n\t * Used as fallback when adaptive zones are not available.\n\t *\n\t * @param {string} pSide\n\t * @returns {{start: number, end: number}}\n\t */\n\t_getZoneFromSide(pSide)\n\t{\n\t\tswitch (pSide)\n\t\t{\n\t\t\t// Left edge: top, middle, bottom\n\t\t\tcase 'left-top': return { start: 0.0, end: 0.333 };\n\t\t\tcase 'left': return { start: 0.333, end: 0.667 };\n\t\t\tcase 'left-bottom': return { start: 0.667, end: 1.0 };\n\n\t\t\t// Right edge: top, middle, bottom\n\t\t\tcase 'right-top': return { start: 0.0, end: 0.333 };\n\t\t\tcase 'right': return { start: 0.333, end: 0.667 };\n\t\t\tcase 'right-bottom': return { start: 0.667, end: 1.0 };\n\n\t\t\t// Top edge: left, middle, right\n\t\t\tcase 'top-left': return { start: 0.0, end: 0.333 };\n\t\t\tcase 'top': return { start: 0.333, end: 0.667 };\n\t\t\tcase 'top-right': return { start: 0.667, end: 1.0 };\n\n\t\t\t// Bottom edge: left, middle, right\n\t\t\tcase 'bottom-left': return { start: 0.0, end: 0.333 };\n\t\t\tcase 'bottom': return { start: 0.333, end: 0.667 };\n\t\t\tcase 'bottom-right': return { start: 0.667, end: 1.0 };\n\n\t\t\t// Fallback: full range (legacy behavior)\n\t\t\tdefault: return { start: 0.0, end: 1.0 };\n\t\t}\n\t}\n\n\t/**\n\t * Get the three zone Side keys for a given edge, in order.\n\t *\n\t * @param {string} pEdge - 'left', 'right', 'top', or 'bottom'\n\t * @returns {Array<string>} Three Side keys in start-to-end order\n\t */\n\t_getZoneKeysForEdge(pEdge)\n\t{\n\t\tswitch (pEdge)\n\t\t{\n\t\t\tcase 'left': return ['left-top', 'left', 'left-bottom'];\n\t\t\tcase 'right': return ['right-top', 'right', 'right-bottom'];\n\t\t\tcase 'top': return ['top-left', 'top', 'top-right'];\n\t\t\tcase 'bottom': return ['bottom-left', 'bottom', 'bottom-right'];\n\t\t\tdefault: return ['right-top', 'right', 'right-bottom'];\n\t\t}\n\t}\n\n\t/**\n\t * Compute an adaptive zone fraction for a Side value based on the\n\t * actual port distribution across all zones on the same edge.\n\t *\n\t * Instead of fixed 1/3 splits, zones are sized proportionally to the\n\t * space each zone needs (minSpacing * (portCount + 1)). Zones with\n\t * zero ports collapse to zero, giving occupied zones more room.\n\t *\n\t * @param {string} pSide - The Side value to compute a zone for\n\t * @param {Object} pPortCountsBySide - Map of Side → number of ports\n\t * @returns {{start: number, end: number}}\n\t */\n\t_computeAdaptiveZone(pSide, pPortCountsBySide)\n\t{\n\t\tlet tmpEdge = this.getEdgeFromSide(pSide);\n\t\tlet tmpZoneKeys = this._getZoneKeysForEdge(tmpEdge);\n\n\t\tlet tmpMinSpacing = 16;\n\n\t\t// Compute the space each zone needs: minSpacing * (count + 1)\n\t\t// The +1 provides padding at both ends of the zone.\n\t\tlet tmpTotalSpace = 0;\n\t\tlet tmpSpaceByZone = {};\n\t\tfor (let i = 0; i < tmpZoneKeys.length; i++)\n\t\t{\n\t\t\tlet tmpKey = tmpZoneKeys[i];\n\t\t\tlet tmpCount = pPortCountsBySide[tmpKey] || 0;\n\t\t\tlet tmpSpace = (tmpCount > 0) ? (tmpMinSpacing * (tmpCount + 1)) : 0;\n\t\t\ttmpSpaceByZone[tmpKey] = tmpSpace;\n\t\t\ttmpTotalSpace += tmpSpace;\n\t\t}\n\n\t\t// If no ports on this edge at all, fall back to fixed zones\n\t\tif (tmpTotalSpace === 0)\n\t\t{\n\t\t\treturn this._getZoneFromSide(pSide);\n\t\t}\n\n\t\t// Compute proportional start/end for the requested zone\n\t\tlet tmpCumulativeStart = 0;\n\t\tfor (let i = 0; i < tmpZoneKeys.length; i++)\n\t\t{\n\t\t\tlet tmpKey = tmpZoneKeys[i];\n\t\t\tlet tmpFraction = tmpSpaceByZone[tmpKey] / tmpTotalSpace;\n\t\t\tif (tmpKey === pSide)\n\t\t\t{\n\t\t\t\treturn { start: tmpCumulativeStart, end: tmpCumulativeStart + tmpFraction };\n\t\t\t}\n\t\t\ttmpCumulativeStart += tmpFraction;\n\t\t}\n\n\t\t// Should not reach here; fall back to fixed zones\n\t\treturn this._getZoneFromSide(pSide);\n\t}\n\n\t/**\n\t * Build a map of Side → port count from an array of port objects.\n\t *\n\t * Convenience method for callers that need to pass port counts\n\t * to getPortLocalPosition or computeMinimumNodeHeight.\n\t *\n\t * @param {Array} pPorts - Array of port objects with Side, Direction\n\t * @returns {Object} Map of Side value → count\n\t */\n\tbuildPortCountsBySide(pPorts)\n\t{\n\t\tlet tmpCounts = {};\n\t\tif (!pPorts || !Array.isArray(pPorts))\n\t\t{\n\t\t\treturn tmpCounts;\n\t\t}\n\t\tfor (let i = 0; i < pPorts.length; i++)\n\t\t{\n\t\t\tlet tmpSide = pPorts[i].Side || (pPorts[i].Direction === 'input' ? 'left' : 'right');\n\t\t\tif (!tmpCounts[tmpSide])\n\t\t\t{\n\t\t\t\ttmpCounts[tmpSide] = 0;\n\t\t\t}\n\t\t\ttmpCounts[tmpSide]++;\n\t\t}\n\t\treturn tmpCounts;\n\t}\n\n\t/**\n\t * Compute the minimum node height required so that all ports\n\t * (with their badges) fit within the node boundary.\n\t *\n\t * Uses adaptive zone sizing: instead of assuming each zone gets\n\t * a fixed 1/3 of the body, sums the space needed by all occupied\n\t * zones on each left/right edge. This produces compact cards\n\t * whose height scales linearly with total port count.\n\t *\n\t * @param {Array} pPorts - Array of port objects with Side, Direction\n\t * @param {number} pTitleBarHeight - Height of the title bar\n\t * @returns {number} Minimum node height in pixels (0 if no ports)\n\t */\n\tcomputeMinimumNodeHeight(pPorts, pTitleBarHeight)\n\t{\n\t\tif (!pPorts || !Array.isArray(pPorts) || pPorts.length === 0)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\n\t\tlet tmpMinSpacing = 16;\n\t\tlet tmpBottomPad = 16;\n\n\t\t// Count ports per Side value\n\t\tlet tmpCountBySide = this.buildPortCountsBySide(pPorts);\n\n\t\t// Sum the space needed per edge (left, right) across all zones.\n\t\t// Each zone needs minSpacing * (count + 1) pixels.\n\t\tlet tmpSpacePerEdge = {};\n\t\tfor (let tmpSide in tmpCountBySide)\n\t\t{\n\t\t\tlet tmpEdge = this.getEdgeFromSide(tmpSide);\n\n\t\t\t// Only left/right edge zones affect required height\n\t\t\tif (tmpEdge !== 'left' && tmpEdge !== 'right')\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet tmpCount = tmpCountBySide[tmpSide];\n\t\t\tlet tmpZoneSpace = tmpMinSpacing * (tmpCount + 1);\n\n\t\t\tif (!tmpSpacePerEdge[tmpEdge])\n\t\t\t{\n\t\t\t\ttmpSpacePerEdge[tmpEdge] = 0;\n\t\t\t}\n\t\t\ttmpSpacePerEdge[tmpEdge] += tmpZoneSpace;\n\t\t}\n\n\t\t// The minimum height is titleBar + bottomPad + max edge space\n\t\tlet tmpMinHeight = 0;\n\t\tfor (let tmpEdge in tmpSpacePerEdge)\n\t\t{\n\t\t\tlet tmpRequired = pTitleBarHeight + tmpBottomPad + tmpSpacePerEdge[tmpEdge];\n\t\t\tif (tmpRequired > tmpMinHeight)\n\t\t\t{\n\t\t\t\ttmpMinHeight = tmpRequired;\n\t\t\t}\n\t\t}\n\n\t\treturn Math.ceil(tmpMinHeight);\n\t}\n}\n\nmodule.exports = PictProviderFlowGeometry;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-Icons\n *\n * Centralized SVG icon provider for the flow diagram.\n * All icons use a duotone style: 2px outline (#2c3e50) with\n * subtle filled accent shapes (#d5e8f7).\n *\n * Each icon is registered as a pict template with hash `Flow-Icon-{key}`,\n * making them individually overridable by consumers.\n */\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowIcons'\n};\n\n// ── Default Icon SVG Markup ────────────────────────────────────────────────\n// All icons: viewBox=\"0 0 24 24\", duotone style\n// Accent fill: #d5e8f7, Stroke: #2c3e50, Stroke-width: 2\n//\n// The {FlowIconSize} placeholder is replaced at render time with the\n// requested pixel size. Each template is a self-contained <svg> element.\n\nconst _DefaultIcons =\n{\n\t// ── FlowCard Icons ─────────────────────────────────────────────────────\n\n\t'ITE': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"6\" r=\"3\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><circle cx=\"6\" cy=\"18\" r=\"2.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><circle cx=\"18\" cy=\"18\" r=\"2.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 9v2M9.5 12.5L6 15.5M14.5 12.5L18 15.5\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'SW': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M3 12h5M16 12h5M14.8 9.2l3.7-5.2M14.8 14.8l3.7 5.2\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'EACH': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'FREAD': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-7-7z\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M13 2v7h7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M9 13h6M9 17h4\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'FWRITE': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-7-7z\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M13 2v7h7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 13v5M9.5 15.5L12 13l2.5 2.5\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'LOG': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><circle cx=\"7.5\" cy=\"8\" r=\"1\" fill=\"#2c3e50\"/><circle cx=\"7.5\" cy=\"12\" r=\"1\" fill=\"#2c3e50\"/><circle cx=\"7.5\" cy=\"16\" r=\"1\" fill=\"#2c3e50\"/><path d=\"M11 8h5.5M11 12h5.5M11 16h3.5\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'GET': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M21 21l-5.15-5.15\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'SET': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4.5 1.5L4 16Z\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M14 6l3 3\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t// ── UI Icons ───────────────────────────────────────────────────────────\n\n\t'fullscreen': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 3h6v6\"/><path d=\"M9 21H3v-6\"/><path d=\"M21 3l-7 7\"/><path d=\"M3 21l7-7\"/></svg>',\n\n\t'exit-fullscreen': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 14h6v6\"/><path d=\"M20 10h-6V4\"/><path d=\"M14 10l7-7\"/><path d=\"M3 21l7-7\"/></svg>',\n\n\t'close': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>',\n\n\t'chevron-down': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>',\n\n\t// ── Toolbar & Popup Icons ─────────────────────────────────────────────\n\n\t'search': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"7\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M21 21l-4.35-4.35\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'cards': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"7\" width=\"16\" height=\"12\" rx=\"2\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M6 7V5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-2\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'layout': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"8\" height=\"10\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"13\" y=\"3\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"3\" y=\"15\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"13\" y=\"11\" width=\"8\" height=\"10\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'collapse': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M8 12h8\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'expand': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 8v8M8 12h8\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'grip': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"#2c3e50\"><circle cx=\"9\" cy=\"5\" r=\"1.5\"/><circle cx=\"15\" cy=\"5\" r=\"1.5\"/><circle cx=\"9\" cy=\"12\" r=\"1.5\"/><circle cx=\"15\" cy=\"12\" r=\"1.5\"/><circle cx=\"9\" cy=\"19\" r=\"1.5\"/><circle cx=\"15\" cy=\"19\" r=\"1.5\"/></svg>',\n\n\t'settings': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\" fill=\"#d5e8f7\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"/></svg>',\n\n\t'plus': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 8v8M8 12h8\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'trash': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"5\" y=\"6\" width=\"14\" height=\"14\" rx=\"2\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M10 11v6M14 11v6\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'save': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M17 21v-8H7v8\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M7 3v5h8\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'auto-layout': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"8\" y=\"2\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"2\" y=\"16\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"14\" y=\"16\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 8v4M6 16v-4h12v4\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'zoom-in': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"7\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M21 21l-4.35-4.35\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M11 8v6M8 11h6\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'zoom-out': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"7\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M21 21l-4.35-4.35\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M8 11h6\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'zoom-fit': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 7V3h4\"/><path d=\"M17 3h4v4\"/><path d=\"M21 17v4h-4\"/><path d=\"M7 21H3v-4\"/><rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" rx=\"1.5\" fill=\"#d5e8f7\"/></svg>',\n\n\t'dock': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"4\" rx=\"1.5\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M12 20V11M8 14l4-4 4 4\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t'restore': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#2c3e50\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 12a9 9 0 1 0 3-6.7L3 8\"/><path d=\"M3 3v5h5\"/></svg>',\n\n\t'delete-node': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" stroke=\"#2c3e50\" stroke-width=\"2\"/><rect x=\"5\" y=\"6\" width=\"14\" height=\"14\" rx=\"2\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><path d=\"M10 11v6M14 11v6\" stroke=\"#2c3e50\" stroke-width=\"2\"/></svg>',\n\n\t// ── Fallback ───────────────────────────────────────────────────────────\n\n\t'default': '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{FlowIconSize}\" height=\"{FlowIconSize}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"4\" fill=\"#d5e8f7\" stroke=\"#2c3e50\" stroke-width=\"2\"/><circle cx=\"12\" cy=\"12\" r=\"2.5\" fill=\"#2c3e50\"/></svg>'\n};\n\nclass PictProviderFlowIcons extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowIcons';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Deep copy the default icons\n\t\tthis._Icons = JSON.parse(JSON.stringify(_DefaultIcons));\n\n\t\t// Merge any additional icons passed via options\n\t\tif (pOptions && pOptions.AdditionalIcons && typeof pOptions.AdditionalIcons === 'object')\n\t\t{\n\t\t\tlet tmpKeys = Object.keys(pOptions.AdditionalIcons);\n\t\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t\t{\n\t\t\t\tthis._Icons[tmpKeys[i]] = pOptions.AdditionalIcons[tmpKeys[i]];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Register all icons as pict templates with hash Flow-Icon-{key}.\n\t * Consumers can override any icon by registering a template with\n\t * the same hash before the flow view renders.\n\t */\n\tregisterIconTemplates()\n\t{\n\t\tif (!this.fable || !this.fable.TemplateProvider)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowIcons: TemplateProvider not available; icon templates not registered.');\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpKeys = Object.keys(this._Icons);\n\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t{\n\t\t\tlet tmpHash = 'Flow-Icon-' + tmpKeys[i];\n\n\t\t\t// Only register if not already present (allow consumer overrides)\n\t\t\tif (!this.fable.TemplateProvider.getTemplate(tmpHash))\n\t\t\t{\n\t\t\t\tthis.fable.TemplateProvider.addTemplate(tmpHash, this._Icons[tmpKeys[i]]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Determine if the given icon string is an emoji (legacy) or an icon key.\n\t * Returns true if the string contains characters with code points above U+00FF,\n\t * indicating emoji or Unicode symbol characters.\n\t *\n\t * @param {string} pIconValue - The icon value to check\n\t * @returns {boolean}\n\t */\n\tisEmojiIcon(pIconValue)\n\t{\n\t\tif (!pIconValue || typeof pIconValue !== 'string')\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (let i = 0; i < pIconValue.length; i++)\n\t\t{\n\t\t\tif (pIconValue.charCodeAt(i) > 255)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Resolve the icon key to use for a given CardMetadata object.\n\t * Tries Icon field first, then Code field, then falls back to 'default'.\n\t *\n\t * @param {Object} pCardMetadata - The CardMetadata object\n\t * @returns {string} The icon key\n\t */\n\tresolveIconKey(pCardMetadata)\n\t{\n\t\tif (!pCardMetadata)\n\t\t{\n\t\t\treturn 'default';\n\t\t}\n\n\t\t// If Icon is a known key, use it\n\t\tif (pCardMetadata.Icon && this._Icons.hasOwnProperty(pCardMetadata.Icon))\n\t\t{\n\t\t\treturn pCardMetadata.Icon;\n\t\t}\n\n\t\t// If Icon is a non-emoji string, check if it matches a registered template\n\t\tif (pCardMetadata.Icon && !this.isEmojiIcon(pCardMetadata.Icon))\n\t\t{\n\t\t\treturn pCardMetadata.Icon;\n\t\t}\n\n\t\t// Fall back to Code field\n\t\tif (pCardMetadata.Code && this._Icons.hasOwnProperty(pCardMetadata.Code))\n\t\t{\n\t\t\treturn pCardMetadata.Code;\n\t\t}\n\n\t\treturn 'default';\n\t}\n\n\t/**\n\t * Get the raw SVG markup string for a given icon key, with size applied.\n\t *\n\t * @param {string} pIconKey - The icon key\n\t * @param {number} pSize - Pixel size (default 16)\n\t * @returns {string} The SVG markup string\n\t */\n\tgetIconSVGMarkup(pIconKey, pSize)\n\t{\n\t\tlet tmpSize = pSize || 16;\n\t\tlet tmpKey = pIconKey || 'default';\n\t\tlet tmpMarkup = this._Icons[tmpKey] || this._Icons['default'];\n\n\t\t// Replace the size placeholder\n\t\treturn tmpMarkup.replace(/\\{FlowIconSize\\}/g, String(tmpSize));\n\t}\n\n\t/**\n\t * Render an icon into an SVG canvas context using createElementNS.\n\t * Creates a <g> element containing the icon paths, positioned at (pX, pY)\n\t * with the given size via a scale transform.\n\t *\n\t * @param {string} pIconKey - The icon key\n\t * @param {SVGElement} pParentGroup - The parent SVG group to append to\n\t * @param {number} pX - X position (top-left of icon bounding box)\n\t * @param {number} pY - Y position (top-left of icon bounding box)\n\t * @param {number} pSize - Pixel size (default 16)\n\t * @returns {SVGGElement|null} The created group element, or null on failure\n\t */\n\trenderIconIntoSVGGroup(pIconKey, pParentGroup, pX, pY, pSize)\n\t{\n\t\tif (!pParentGroup)\n\t\t{\n\t\t\treturn null;\n\t\t}\n\n\t\tlet tmpSize = pSize || 16;\n\t\tlet tmpScale = tmpSize / 24;\n\t\tlet tmpKey = pIconKey || 'default';\n\t\tlet tmpMarkup = this._Icons[tmpKey] || this._Icons['default'];\n\n\t\t// Replace size placeholder (for consistency, though we scale via transform)\n\t\ttmpMarkup = tmpMarkup.replace(/\\{FlowIconSize\\}/g, '24');\n\n\t\ttry\n\t\t{\n\t\t\t// Create a temporary SVG element to parse the icon markup\n\t\t\tlet tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\t\t\ttmpTempSVG.innerHTML = tmpMarkup;\n\n\t\t\t// Find the inner SVG (the icon's root <svg> element)\n\t\t\tlet tmpInnerSVG = tmpTempSVG.querySelector('svg');\n\t\t\tif (!tmpInnerSVG)\n\t\t\t{\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Create a group to hold the icon content\n\t\t\tlet tmpGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n\t\t\ttmpGroup.setAttribute('transform', 'translate(' + pX + ',' + pY + ') scale(' + tmpScale + ')');\n\t\t\ttmpGroup.setAttribute('pointer-events', 'none');\n\t\t\ttmpGroup.setAttribute('class', 'pict-flow-icon-svg');\n\n\t\t\t// Move all children from the parsed SVG into the group\n\t\t\twhile (tmpInnerSVG.childNodes.length > 0)\n\t\t\t{\n\t\t\t\ttmpGroup.appendChild(tmpInnerSVG.childNodes[0]);\n\t\t\t}\n\n\t\t\tpParentGroup.appendChild(tmpGroup);\n\t\t\treturn tmpGroup;\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowIcons renderIconIntoSVGGroup error: ' + pError.message);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get all registered icon keys.\n\t * @returns {Array<string>}\n\t */\n\tgetIconKeys()\n\t{\n\t\treturn Object.keys(this._Icons);\n\t}\n\n\t/**\n\t * Check if a given key has a registered icon.\n\t * @param {string} pIconKey\n\t * @returns {boolean}\n\t */\n\thasIcon(pIconKey)\n\t{\n\t\treturn this._Icons.hasOwnProperty(pIconKey);\n\t}\n\n\t/**\n\t * Register a new icon or override an existing one.\n\t * @param {string} pIconKey - The icon key\n\t * @param {string} pSVGMarkup - The SVG markup string (must contain {FlowIconSize} placeholders)\n\t * @returns {boolean}\n\t */\n\tregisterIcon(pIconKey, pSVGMarkup)\n\t{\n\t\tif (!pIconKey || !pSVGMarkup)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._Icons[pIconKey] = pSVGMarkup;\n\n\t\t// Also update the pict template if TemplateProvider is available\n\t\tif (this.fable && this.fable.TemplateProvider)\n\t\t{\n\t\t\tthis.fable.TemplateProvider.addTemplate('Flow-Icon-' + pIconKey, pSVGMarkup);\n\t\t}\n\n\t\treturn true;\n\t}\n}\n\nmodule.exports = PictProviderFlowIcons;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\nmodule.exports.DefaultIcons = _DefaultIcons;\n","const libPictProvider = require('pict-provider');\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowLayouts'\n};\n\n/**\n * Provider for managing saved flow diagram layouts.\n *\n * Layouts capture the spatial arrangement of nodes and panels on the canvas\n * without storing any card content. When a layout is restored, nodes that\n * still exist are placed at their saved positions and any new nodes are\n * auto-laid-out to the right.\n *\n * ## Persistence\n *\n * By default, layouts are persisted to the browser's `localStorage` using a\n * key derived from the flow view identifier. This means layouts survive page\n * refreshes out of the box.\n *\n * Developers can override the storage backend (e.g., to use a REST API or\n * IndexedDB) by replacing the three storage hook methods on the instance or\n * in a subclass:\n *\n * - `storageWrite(pLayouts, fCallback)` — persist the full layout array\n * - `storageRead(fCallback)` — load the persisted layout array\n * - `storageDelete(fCallback)` — remove all persisted layouts\n *\n * Each callback follows the Node convention: `fCallback(pError, pResult)`.\n *\n * Saved layout data structure:\n * {\n * Hash: \"layout-<UUID>\",\n * Name: \"My Layout\",\n * CreatedAt: \"2026-02-26T12:00:00.000Z\",\n * NodePositions: { \"node-hash\": { X, Y, Width, Height } },\n * PanelPositions: { \"node-hash\": { X, Y, Width, Height } },\n * ViewState: { PanX, PanY, Zoom }\n * }\n */\nclass PictProviderFlowLayouts extends libPictProvider\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowLayouts';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Storage key for localStorage persistence.\n\t\t// Defaults to a key derived from the FlowView identifier, or can be\n\t\t// set via options.StorageKey. Pass `false` to disable localStorage.\n\t\tif (pOptions && pOptions.StorageKey !== undefined)\n\t\t{\n\t\t\tthis._StorageKey = pOptions.StorageKey;\n\t\t}\n\t\telse if (this._FlowView && this._FlowView.options && this._FlowView.options.ViewIdentifier)\n\t\t{\n\t\t\tthis._StorageKey = `pict-flow-layouts-${this._FlowView.options.ViewIdentifier}`;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._StorageKey = 'pict-flow-layouts';\n\t\t}\n\t}\n\n\t// ── Storage Hooks ─────────────────────────────────────────────────────\n\t// These three methods form the persistence contract. The default\n\t// implementation uses localStorage. Override them on the instance or\n\t// subclass to use a REST API, IndexedDB, or any other backend.\n\t//\n\t// All callbacks follow `fCallback(pError, pResult)`.\n\n\t/**\n\t * Persist the full array of layout objects.\n\t *\n\t * Default implementation writes JSON to `localStorage`.\n\t *\n\t * @param {Array} pLayouts - The array of layout objects to persist\n\t * @param {Function} fCallback - `function(pError)` called when done\n\t */\n\tstorageWrite(pLayouts, fCallback)\n\t{\n\t\tif (this._StorageKey === false)\n\t\t{\n\t\t\treturn fCallback(null);\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (typeof localStorage !== 'undefined')\n\t\t\t{\n\t\t\t\tlocalStorage.setItem(this._StorageKey, JSON.stringify(pLayouts));\n\t\t\t}\n\t\t\treturn fCallback(null);\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts storageWrite error: ${pError.message}`);\n\t\t\treturn fCallback(pError);\n\t\t}\n\t}\n\n\t/**\n\t * Load the persisted array of layout objects.\n\t *\n\t * Default implementation reads JSON from `localStorage`.\n\t *\n\t * @param {Function} fCallback - `function(pError, pLayouts)` where\n\t * pLayouts is an Array (or null/empty if nothing stored)\n\t */\n\tstorageRead(fCallback)\n\t{\n\t\tif (this._StorageKey === false)\n\t\t{\n\t\t\treturn fCallback(null, []);\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (typeof localStorage !== 'undefined')\n\t\t\t{\n\t\t\t\tlet tmpRaw = localStorage.getItem(this._StorageKey);\n\t\t\t\tif (tmpRaw)\n\t\t\t\t{\n\t\t\t\t\tlet tmpParsed = JSON.parse(tmpRaw);\n\t\t\t\t\tif (Array.isArray(tmpParsed))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn fCallback(null, tmpParsed);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fCallback(null, []);\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts storageRead error: ${pError.message}`);\n\t\t\treturn fCallback(pError, []);\n\t\t}\n\t}\n\n\t/**\n\t * Remove all persisted layout data.\n\t *\n\t * Default implementation removes the key from `localStorage`.\n\t *\n\t * @param {Function} fCallback - `function(pError)` called when done\n\t */\n\tstorageDelete(fCallback)\n\t{\n\t\tif (this._StorageKey === false)\n\t\t{\n\t\t\treturn fCallback(null);\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tif (typeof localStorage !== 'undefined')\n\t\t\t{\n\t\t\t\tlocalStorage.removeItem(this._StorageKey);\n\t\t\t}\n\t\t\treturn fCallback(null);\n\t\t}\n\t\tcatch (pError)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts storageDelete error: ${pError.message}`);\n\t\t\treturn fCallback(pError);\n\t\t}\n\t}\n\n\t// ── Initialization ────────────────────────────────────────────────────\n\n\t/**\n\t * Load persisted layouts and merge them into _FlowData.SavedLayouts.\n\t * Layouts already present in _FlowData (e.g., from setFlowData) are\n\t * kept; persisted layouts with new hashes are appended.\n\t *\n\t * Call this after _FlowData is populated.\n\t */\n\tloadPersistedLayouts()\n\t{\n\t\tthis.storageRead((pError, pLayouts) =>\n\t\t{\n\t\t\tif (pError || !Array.isArray(pLayouts) || pLayouts.length === 0)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!this._FlowView || !this._FlowView._FlowData)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet tmpExisting = this._FlowView._FlowData.SavedLayouts;\n\t\t\tlet tmpExistingHashes = {};\n\t\t\tfor (let i = 0; i < tmpExisting.length; i++)\n\t\t\t{\n\t\t\t\ttmpExistingHashes[tmpExisting[i].Hash] = true;\n\t\t\t}\n\n\t\t\tlet tmpAdded = 0;\n\t\t\tfor (let i = 0; i < pLayouts.length; i++)\n\t\t\t{\n\t\t\t\tif (!tmpExistingHashes[pLayouts[i].Hash])\n\t\t\t\t{\n\t\t\t\t\ttmpExisting.push(pLayouts[i]);\n\t\t\t\t\ttmpAdded++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tmpAdded > 0)\n\t\t\t{\n\t\t\t\tthis.log.trace(`PictProviderFlowLayouts loaded ${tmpAdded} persisted layout(s)`);\n\t\t\t}\n\t\t});\n\t}\n\n\t// ── Public API ────────────────────────────────────────────────────────\n\n\t/**\n\t * Save the current node and panel positions as a named layout.\n\t * @param {string} pName - The display name for this layout\n\t * @returns {Object} The saved layout entry\n\t */\n\tsaveLayout(pName)\n\t{\n\t\tif (!this._FlowView)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowLayouts saveLayout: no FlowView reference');\n\t\t\treturn null;\n\t\t}\n\n\t\tlet tmpFlowData = this._FlowView._FlowData;\n\t\tlet tmpLayoutHash = `layout-${this.fable.getUUID()}`;\n\t\tlet tmpNodePositions = {};\n\t\tlet tmpPanelPositions = {};\n\n\t\t// Capture node positions and per-instance overrides (Title, Style)\n\t\tfor (let i = 0; i < tmpFlowData.Nodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = tmpFlowData.Nodes[i];\n\t\t\ttmpNodePositions[tmpNode.Hash] =\n\t\t\t{\n\t\t\t\tX: tmpNode.X,\n\t\t\t\tY: tmpNode.Y,\n\t\t\t\tWidth: tmpNode.Width,\n\t\t\t\tHeight: tmpNode.Height,\n\t\t\t\tTitle: tmpNode.Title\n\t\t\t};\n\n\t\t\t// Only include Style if it has been customized\n\t\t\tif (tmpNode.Style && Object.keys(tmpNode.Style).length > 0)\n\t\t\t{\n\t\t\t\ttmpNodePositions[tmpNode.Hash].Style = JSON.parse(JSON.stringify(tmpNode.Style));\n\t\t\t}\n\t\t}\n\n\t\t// Capture panel positions keyed by NodeHash (panels get new hashes on each open)\n\t\tfor (let i = 0; i < tmpFlowData.OpenPanels.length; i++)\n\t\t{\n\t\t\tlet tmpPanel = tmpFlowData.OpenPanels[i];\n\t\t\ttmpPanelPositions[tmpPanel.NodeHash] =\n\t\t\t{\n\t\t\t\tX: tmpPanel.X,\n\t\t\t\tY: tmpPanel.Y,\n\t\t\t\tWidth: tmpPanel.Width,\n\t\t\t\tHeight: tmpPanel.Height\n\t\t\t};\n\t\t}\n\n\t\tlet tmpLayout =\n\t\t{\n\t\t\tHash: tmpLayoutHash,\n\t\t\tName: pName || 'Untitled Layout',\n\t\t\tCreatedAt: new Date().toISOString(),\n\t\t\tNodePositions: tmpNodePositions,\n\t\t\tPanelPositions: tmpPanelPositions,\n\t\t\tViewState:\n\t\t\t{\n\t\t\t\tPanX: tmpFlowData.ViewState.PanX,\n\t\t\t\tPanY: tmpFlowData.ViewState.PanY,\n\t\t\t\tZoom: tmpFlowData.ViewState.Zoom\n\t\t\t}\n\t\t};\n\n\t\ttmpFlowData.SavedLayouts.push(tmpLayout);\n\t\tthis._FlowView.marshalFromView();\n\n\t\t// Persist to storage\n\t\tthis.storageWrite(tmpFlowData.SavedLayouts, (pError) =>\n\t\t{\n\t\t\tif (pError)\n\t\t\t{\n\t\t\t\tthis.log.warn(`PictProviderFlowLayouts: failed to persist after save: ${pError.message}`);\n\t\t\t}\n\t\t});\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onLayoutSaved', tmpLayout);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);\n\t\t}\n\n\t\tthis.log.trace(`PictProviderFlowLayouts saved layout '${tmpLayout.Name}' (${tmpLayout.Hash})`);\n\n\t\treturn tmpLayout;\n\t}\n\n\t/**\n\t * Restore a saved layout by hash.\n\t * Nodes present in the saved layout are placed at their saved positions.\n\t * Nodes not in the saved layout are auto-laid-out to the right of the\n\t * positioned nodes.\n\t * @param {string} pLayoutHash - The hash of the layout to restore\n\t * @returns {boolean} Whether the layout was restored\n\t */\n\trestoreLayout(pLayoutHash)\n\t{\n\t\tif (!this._FlowView)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowLayouts restoreLayout: no FlowView reference');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpFlowData = this._FlowView._FlowData;\n\t\tlet tmpLayout = tmpFlowData.SavedLayouts.find(\n\t\t\t(pLayout) => pLayout.Hash === pLayoutHash\n\t\t);\n\n\t\tif (!tmpLayout)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts restoreLayout: layout '${pLayoutHash}' not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpMatchedNodes = [];\n\t\tlet tmpUnmatchedNodes = [];\n\n\t\t// Apply saved positions to matched nodes; collect unmatched ones\n\t\tfor (let i = 0; i < tmpFlowData.Nodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = tmpFlowData.Nodes[i];\n\t\t\tlet tmpSaved = tmpLayout.NodePositions[tmpNode.Hash];\n\n\t\t\tif (tmpSaved)\n\t\t\t{\n\t\t\t\ttmpNode.X = tmpSaved.X;\n\t\t\t\ttmpNode.Y = tmpSaved.Y;\n\t\t\t\tif (typeof tmpSaved.Width === 'number') tmpNode.Width = tmpSaved.Width;\n\t\t\t\tif (typeof tmpSaved.Height === 'number') tmpNode.Height = tmpSaved.Height;\n\t\t\t\tif (typeof tmpSaved.Title === 'string') tmpNode.Title = tmpSaved.Title;\n\t\t\t\tif (tmpSaved.Style && typeof tmpSaved.Style === 'object')\n\t\t\t\t{\n\t\t\t\t\ttmpNode.Style = JSON.parse(JSON.stringify(tmpSaved.Style));\n\t\t\t\t}\n\t\t\t\ttmpMatchedNodes.push(tmpNode);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpUnmatchedNodes.push(tmpNode);\n\t\t\t}\n\t\t}\n\n\t\t// Apply saved panel positions (keyed by NodeHash)\n\t\tif (tmpLayout.PanelPositions)\n\t\t{\n\t\t\tfor (let i = 0; i < tmpFlowData.OpenPanels.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPanel = tmpFlowData.OpenPanels[i];\n\t\t\t\tlet tmpSavedPanel = tmpLayout.PanelPositions[tmpPanel.NodeHash];\n\n\t\t\t\tif (tmpSavedPanel)\n\t\t\t\t{\n\t\t\t\t\ttmpPanel.X = tmpSavedPanel.X;\n\t\t\t\t\ttmpPanel.Y = tmpSavedPanel.Y;\n\t\t\t\t\tif (typeof tmpSavedPanel.Width === 'number') tmpPanel.Width = tmpSavedPanel.Width;\n\t\t\t\t\tif (typeof tmpSavedPanel.Height === 'number') tmpPanel.Height = tmpSavedPanel.Height;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Auto-layout unmatched nodes to the right of positioned nodes\n\t\tif (tmpUnmatchedNodes.length > 0 && this._FlowView._LayoutService)\n\t\t{\n\t\t\tthis._FlowView._LayoutService.autoLayoutSubset(\n\t\t\t\ttmpUnmatchedNodes,\n\t\t\t\ttmpMatchedNodes,\n\t\t\t\ttmpFlowData.Connections\n\t\t\t);\n\t\t}\n\n\t\t// Restore view state (camera position)\n\t\tif (tmpLayout.ViewState)\n\t\t{\n\t\t\tif (typeof tmpLayout.ViewState.PanX === 'number')\n\t\t\t{\n\t\t\t\ttmpFlowData.ViewState.PanX = tmpLayout.ViewState.PanX;\n\t\t\t}\n\t\t\tif (typeof tmpLayout.ViewState.PanY === 'number')\n\t\t\t{\n\t\t\t\ttmpFlowData.ViewState.PanY = tmpLayout.ViewState.PanY;\n\t\t\t}\n\t\t\tif (typeof tmpLayout.ViewState.Zoom === 'number')\n\t\t\t{\n\t\t\t\ttmpFlowData.ViewState.Zoom = tmpLayout.ViewState.Zoom;\n\t\t\t}\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onLayoutRestored', tmpLayout);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);\n\t\t}\n\n\t\tthis.log.trace(`PictProviderFlowLayouts restored layout '${tmpLayout.Name}' (${tmpLayout.Hash})`);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delete a saved layout by hash.\n\t * @param {string} pLayoutHash - The hash of the layout to delete\n\t * @returns {boolean} Whether the layout was deleted\n\t */\n\tdeleteLayout(pLayoutHash)\n\t{\n\t\tif (!this._FlowView)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowLayouts deleteLayout: no FlowView reference');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpFlowData = this._FlowView._FlowData;\n\t\tlet tmpIndex = tmpFlowData.SavedLayouts.findIndex(\n\t\t\t(pLayout) => pLayout.Hash === pLayoutHash\n\t\t);\n\n\t\tif (tmpIndex < 0)\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowLayouts deleteLayout: layout '${pLayoutHash}' not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpRemovedLayout = tmpFlowData.SavedLayouts.splice(tmpIndex, 1)[0];\n\t\tthis._FlowView.marshalFromView();\n\n\t\t// Persist to storage (with the layout removed)\n\t\tthis.storageWrite(tmpFlowData.SavedLayouts, (pError) =>\n\t\t{\n\t\t\tif (pError)\n\t\t\t{\n\t\t\t\tthis.log.warn(`PictProviderFlowLayouts: failed to persist after delete: ${pError.message}`);\n\t\t\t}\n\t\t});\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onLayoutDeleted', tmpRemovedLayout);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);\n\t\t}\n\n\t\tthis.log.trace(`PictProviderFlowLayouts deleted layout '${tmpRemovedLayout.Name}' (${tmpRemovedLayout.Hash})`);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get the list of saved layouts.\n\t * @returns {Array} Array of saved layout objects\n\t */\n\tgetLayouts()\n\t{\n\t\tif (!this._FlowView) return [];\n\t\treturn this._FlowView._FlowData.SavedLayouts;\n\t}\n\n\t/**\n\t * Get a specific saved layout by hash.\n\t * @param {string} pLayoutHash\n\t * @returns {Object|null}\n\t */\n\tgetLayout(pLayoutHash)\n\t{\n\t\tif (!this._FlowView) return null;\n\t\treturn this._FlowView._FlowData.SavedLayouts.find(\n\t\t\t(pLayout) => pLayout.Hash === pLayoutHash\n\t\t) || null;\n\t}\n}\n\nmodule.exports = PictProviderFlowLayouts;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n","const libPictProvider = require('pict-provider');\n\nconst _DefaultNodeTypes =\n{\n\t'default':\n\t{\n\t\tHash: 'default',\n\t\tLabel: 'Default',\n\t\tDefaultWidth: 180,\n\t\tDefaultHeight: 80,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' },\n\t\t\t{ Hash: null, Direction: 'output', Side: 'right', Label: 'Out' }\n\t\t],\n\t\tTitleBarColor: '#2c3e50',\n\t\tBodyStyle: {}\n\t},\n\t'start':\n\t{\n\t\tHash: 'start',\n\t\tLabel: 'Start',\n\t\tDefaultWidth: 140,\n\t\tDefaultHeight: 80,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'output', Side: 'right', Label: 'Out' }\n\t\t],\n\t\tTitleBarColor: '#27ae60',\n\t\tBodyStyle:\n\t\t{\n\t\t\t'fill': '#eafaf1',\n\t\t\t'stroke': '#27ae60'\n\t\t}\n\t},\n\t'end':\n\t{\n\t\tHash: 'end',\n\t\tLabel: 'End',\n\t\tDefaultWidth: 140,\n\t\tDefaultHeight: 80,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' }\n\t\t],\n\t\tTitleBarColor: '#1abc9c',\n\t\tBodyStyle:\n\t\t{\n\t\t\t'fill': '#e8f8f5',\n\t\t\t'stroke': '#1abc9c'\n\t\t}\n\t},\n\t'halt':\n\t{\n\t\tHash: 'halt',\n\t\tLabel: 'Halt',\n\t\tDefaultWidth: 140,\n\t\tDefaultHeight: 80,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' }\n\t\t],\n\t\tTitleBarColor: '#e74c3c',\n\t\tBodyStyle:\n\t\t{\n\t\t\t'fill': '#fdedec',\n\t\t\t'stroke': '#e74c3c'\n\t\t}\n\t},\n\t'decision':\n\t{\n\t\tHash: 'decision',\n\t\tLabel: 'Decision',\n\t\tDefaultWidth: 200,\n\t\tDefaultHeight: 100,\n\t\tDefaultPorts:\n\t\t[\n\t\t\t{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' },\n\t\t\t{ Hash: null, Direction: 'output', Side: 'right', Label: 'Yes' },\n\t\t\t{ Hash: null, Direction: 'output', Side: 'bottom', Label: 'No' }\n\t\t],\n\t\tTitleBarColor: '#f39c12',\n\t\tBodyStyle:\n\t\t{\n\t\t\t'fill': '#fff9e6',\n\t\t\t'stroke': '#f39c12'\n\t\t}\n\t}\n};\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowNodeTypes'\n};\n\nclass PictProviderFlowNodeTypes extends libPictProvider\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowNodeTypes';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Initialize with default node types unless explicitly disabled\n\t\tif (pOptions && pOptions.IncludeDefaultNodeTypes === false)\n\t\t{\n\t\t\tthis._NodeTypes = {};\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._NodeTypes = JSON.parse(JSON.stringify(_DefaultNodeTypes));\n\t\t}\n\n\t\t// Merge any additional node types passed in via options\n\t\tif (pOptions && pOptions.AdditionalNodeTypes && typeof pOptions.AdditionalNodeTypes === 'object')\n\t\t{\n\t\t\tlet tmpAdditionalKeys = Object.keys(pOptions.AdditionalNodeTypes);\n\t\t\tfor (let i = 0; i < tmpAdditionalKeys.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpOriginal = pOptions.AdditionalNodeTypes[tmpAdditionalKeys[i]];\n\t\t\t\tthis._NodeTypes[tmpAdditionalKeys[i]] = Object.assign(\n\t\t\t\t\t{},\n\t\t\t\t\tthis._NodeTypes[tmpAdditionalKeys[i]] || {},\n\t\t\t\t\tJSON.parse(JSON.stringify(tmpOriginal))\n\t\t\t\t);\n\t\t\t\t// Preserve BodyContent.RenderCallback (functions are stripped by JSON serialization)\n\t\t\t\tif (tmpOriginal.BodyContent && typeof tmpOriginal.BodyContent.RenderCallback === 'function')\n\t\t\t\t{\n\t\t\t\t\tif (!this._NodeTypes[tmpAdditionalKeys[i]].BodyContent)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._NodeTypes[tmpAdditionalKeys[i]].BodyContent = {};\n\t\t\t\t\t}\n\t\t\t\t\tthis._NodeTypes[tmpAdditionalKeys[i]].BodyContent.RenderCallback = tmpOriginal.BodyContent.RenderCallback;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get a node type configuration by hash\n\t * @param {string} pTypeHash - The node type hash\n\t * @returns {Object|null} The node type configuration\n\t */\n\tgetNodeType(pTypeHash)\n\t{\n\t\treturn this._NodeTypes[pTypeHash] || this._NodeTypes['default'];\n\t}\n\n\t/**\n\t * Register a new node type or override an existing one\n\t * @param {Object} pNodeTypeConfig - The node type configuration\n\t * @returns {boolean}\n\t */\n\tregisterNodeType(pNodeTypeConfig)\n\t{\n\t\tif (!pNodeTypeConfig || !pNodeTypeConfig.Hash)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowNodeTypes registerNodeType: invalid config (missing Hash)');\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._NodeTypes[pNodeTypeConfig.Hash] = Object.assign(\n\t\t\t{},\n\t\t\tthis._NodeTypes[pNodeTypeConfig.Hash] || {},\n\t\t\tpNodeTypeConfig\n\t\t);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Remove a node type\n\t * @param {string} pTypeHash\n\t * @returns {boolean}\n\t */\n\tremoveNodeType(pTypeHash)\n\t{\n\t\tif (pTypeHash === 'default')\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowNodeTypes: cannot remove the default node type');\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this._NodeTypes[pTypeHash])\n\t\t{\n\t\t\tdelete this._NodeTypes[pTypeHash];\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Get all registered node types\n\t * @returns {Object} Map of type hash to type configuration\n\t */\n\tgetNodeTypes()\n\t{\n\t\treturn JSON.parse(JSON.stringify(this._NodeTypes));\n\t}\n\n\t/**\n\t * Get a list of node type hashes\n\t * @returns {Array<string>}\n\t */\n\tgetNodeTypeList()\n\t{\n\t\treturn Object.keys(this._NodeTypes);\n\t}\n\n\t/**\n\t * Get all enabled node types that have FlowCard metadata.\n\t * Returns only types that are cards and whose Enabled flag is true.\n\t * @returns {Array<Object>} Array of node type configurations\n\t */\n\tgetEnabledCards()\n\t{\n\t\tlet tmpCards = [];\n\t\tlet tmpKeys = Object.keys(this._NodeTypes);\n\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t{\n\t\t\tlet tmpType = this._NodeTypes[tmpKeys[i]];\n\t\t\tif (tmpType.CardMetadata)\n\t\t\t{\n\t\t\t\tif (tmpType.CardMetadata.Enabled !== false)\n\t\t\t\t{\n\t\t\t\t\ttmpCards.push(JSON.parse(JSON.stringify(tmpType)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn tmpCards;\n\t}\n\n\t/**\n\t * Get all enabled cards grouped by category.\n\t * @returns {Object} Map of category name to array of node type configurations\n\t */\n\tgetCardsByCategory()\n\t{\n\t\tlet tmpCards = this.getEnabledCards();\n\t\tlet tmpCategories = {};\n\t\tfor (let i = 0; i < tmpCards.length; i++)\n\t\t{\n\t\t\tlet tmpCategory = (tmpCards[i].CardMetadata && tmpCards[i].CardMetadata.Category)\n\t\t\t\t? tmpCards[i].CardMetadata.Category\n\t\t\t\t: 'General';\n\t\t\tif (!tmpCategories[tmpCategory])\n\t\t\t{\n\t\t\t\ttmpCategories[tmpCategory] = [];\n\t\t\t}\n\t\t\ttmpCategories[tmpCategory].push(tmpCards[i]);\n\t\t}\n\t\treturn tmpCategories;\n\t}\n\n\t/**\n\t * Check whether a node type hash refers to a FlowCard (has CardMetadata).\n\t * @param {string} pTypeHash\n\t * @returns {boolean}\n\t */\n\tisFlowCard(pTypeHash)\n\t{\n\t\tlet tmpType = this._NodeTypes[pTypeHash];\n\t\treturn !!(tmpType && tmpType.CardMetadata);\n\t}\n}\n\nmodule.exports = PictProviderFlowNodeTypes;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\nmodule.exports.DefaultNodeTypes = _DefaultNodeTypes;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowNoise'\n};\n\n/**\n * PictProvider-Flow-Noise\n *\n * Deterministic noise/jitter generator for hand-drawn visual effects.\n *\n * Uses seeded pseudo-random number generation so that the same node or\n * connection always receives the same jitter values across re-renders,\n * preventing visual \"jumping\" while still looking organic.\n */\nclass PictProviderFlowNoise extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowNoise';\n\t}\n\n\t// ── Hashing / PRNG ────────────────────────────────────────────────────\n\n\t/**\n\t * Convert a string to a 32-bit integer hash (djb2 algorithm).\n\t * @param {string} pStr\n\t * @returns {number}\n\t */\n\thashString(pStr)\n\t{\n\t\tlet tmpHash = 5381;\n\t\tfor (let i = 0; i < pStr.length; i++)\n\t\t{\n\t\t\ttmpHash = ((tmpHash << 5) + tmpHash) + pStr.charCodeAt(i);\n\t\t\ttmpHash = tmpHash & tmpHash; // Convert to 32-bit integer\n\t\t}\n\t\treturn tmpHash >>> 0; // Ensure unsigned\n\t}\n\n\t/**\n\t * Create a seeded pseudo-random number generator (Mulberry32).\n\t * Returns a function that produces deterministic floats in [0, 1).\n\t * @param {number} pSeed - 32-bit integer seed\n\t * @returns {Function}\n\t */\n\tseededRandom(pSeed)\n\t{\n\t\tlet tmpSeed = pSeed | 0;\n\t\treturn function()\n\t\t{\n\t\t\ttmpSeed = tmpSeed + 0x6D2B79F5 | 0;\n\t\t\tlet t = Math.imul(tmpSeed ^ tmpSeed >>> 15, 1 | tmpSeed);\n\t\t\tt = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;\n\t\t\treturn ((t ^ t >>> 14) >>> 0) / 4294967296;\n\t\t};\n\t}\n\n\t// ── Point Jitter ──────────────────────────────────────────────────────\n\n\t/**\n\t * Apply random jitter to a point.\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {number} pAmplitude - Maximum offset in pixels\n\t * @param {Function} pRNG - Seeded random function\n\t * @returns {{x: number, y: number}}\n\t */\n\tjitterPoint(pX, pY, pAmplitude, pRNG)\n\t{\n\t\tif (pAmplitude <= 0)\n\t\t{\n\t\t\treturn { x: pX, y: pY };\n\t\t}\n\t\treturn {\n\t\t\tx: pX + pAmplitude * (pRNG() - 0.5) * 2,\n\t\t\ty: pY + pAmplitude * (pRNG() - 0.5) * 2\n\t\t};\n\t}\n\n\t// ── Bracket Path Generation ───────────────────────────────────────────\n\n\t/**\n\t * Generate an SVG path `d` string for a bracket-shaped node border.\n\t *\n\t * Draws true bracket shapes — `[` on the left and `]` on the right —\n\t * with NO top/bottom connecting lines. The serifs (horizontal turns)\n\t * extend inward from each corner, giving a distinctive hand-drawn\n\t * technical-diagram look that is immediately distinguishable from a\n\t * regular rectangle at any zoom level.\n\t *\n\t * The bracket consists of:\n\t * - Left bracket `[`: top serif → vertical left side → bottom serif\n\t * - Right bracket `]`: top serif → vertical right side → bottom serif\n\t * - Optional title divider line across the full width\n\t *\n\t * @param {number} pWidth - Node width\n\t * @param {number} pHeight - Node height\n\t * @param {number} pSerifLength - Length of corner serifs in px\n\t * @param {number} pTitleBarHeight - Height of title bar (0 to skip divider)\n\t * @param {number} pAmplitude - Noise amplitude (0 = precise)\n\t * @param {string} pSeedString - Hash string for deterministic noise\n\t * @returns {string} SVG path d attribute\n\t */\n\tgenerateBracketPath(pWidth, pHeight, pSerifLength, pTitleBarHeight, pAmplitude, pSeedString)\n\t{\n\t\tlet tmpRNG = this.seededRandom(this.hashString(pSeedString || 'default'));\n\t\tlet tmpS = pSerifLength || 18;\n\t\tlet tmpW = pWidth;\n\t\tlet tmpH = pHeight;\n\n\t\tlet tmpJ = (pX, pY) =>\n\t\t{\n\t\t\treturn this.jitterPoint(pX, pY, pAmplitude, tmpRNG);\n\t\t};\n\n\t\t// Left bracket `[`: top serif → down left side → bottom serif\n\t\tlet tmpTL_serif = tmpJ(tmpS, 0);\n\t\tlet tmpTL_corner = tmpJ(0, 0);\n\t\tlet tmpBL_corner = tmpJ(0, tmpH);\n\t\tlet tmpBL_serif = tmpJ(tmpS, tmpH);\n\n\t\t// Right bracket `]`: top serif → down right side → bottom serif\n\t\tlet tmpTR_serif = tmpJ(tmpW - tmpS, 0);\n\t\tlet tmpTR_corner = tmpJ(tmpW, 0);\n\t\tlet tmpBR_corner = tmpJ(tmpW, tmpH);\n\t\tlet tmpBR_serif = tmpJ(tmpW - tmpS, tmpH);\n\n\t\tlet tmpPath = '';\n\n\t\t// Left bracket `[`\n\t\ttmpPath += `M ${tmpTL_serif.x.toFixed(1)} ${tmpTL_serif.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpTL_corner.x.toFixed(1)} ${tmpTL_corner.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpBL_corner.x.toFixed(1)} ${tmpBL_corner.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpBL_serif.x.toFixed(1)} ${tmpBL_serif.y.toFixed(1)}`;\n\n\t\t// Right bracket `]`\n\t\ttmpPath += ` M ${tmpTR_serif.x.toFixed(1)} ${tmpTR_serif.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpTR_corner.x.toFixed(1)} ${tmpTR_corner.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpBR_corner.x.toFixed(1)} ${tmpBR_corner.y.toFixed(1)}`;\n\t\ttmpPath += ` L ${tmpBR_serif.x.toFixed(1)} ${tmpBR_serif.y.toFixed(1)}`;\n\n\t\t// No horizontal lines — the title bar fill rect provides visual\n\t\t// separation via its background color. The bracket outline is\n\t\t// purely the `[` and `]` shapes on the sides.\n\n\t\treturn tmpPath;\n\t}\n\n\t// ── Path Jitter (for connections) ─────────────────────────────────────\n\n\t/**\n\t * Apply jitter to an existing SVG path string by offsetting coordinate\n\t * pairs. The first M and last coordinate pair receive reduced jitter\n\t * to keep connections aligned with their port anchors.\n\t *\n\t * @param {string} pPathString - SVG path d attribute\n\t * @param {number} pAmplitude - Noise amplitude (0 = no change)\n\t * @param {string} pSeedString - Hash string for deterministic noise\n\t * @returns {string} Modified path string\n\t */\n\tjitterPath(pPathString, pAmplitude, pSeedString)\n\t{\n\t\tif (pAmplitude <= 0 || !pPathString)\n\t\t{\n\t\t\treturn pPathString;\n\t\t}\n\n\t\tlet tmpRNG = this.seededRandom(this.hashString(pSeedString || 'path'));\n\n\t\t// Parse path into tokens: commands and numbers\n\t\tlet tmpTokens = pPathString.match(/[MLCQZmlcqz]|[-+]?[0-9]*\\.?[0-9]+/g);\n\t\tif (!tmpTokens)\n\t\t{\n\t\t\treturn pPathString;\n\t\t}\n\n\t\t// Collect all numeric coordinate indices\n\t\tlet tmpNumericIndices = [];\n\t\tfor (let i = 0; i < tmpTokens.length; i++)\n\t\t{\n\t\t\tif (/^[-+]?[0-9]*\\.?[0-9]+$/.test(tmpTokens[i]))\n\t\t\t{\n\t\t\t\ttmpNumericIndices.push(i);\n\t\t\t}\n\t\t}\n\n\t\t// Process pairs of coordinates (x, y)\n\t\tfor (let i = 0; i < tmpNumericIndices.length - 1; i += 2)\n\t\t{\n\t\t\tlet tmpXIdx = tmpNumericIndices[i];\n\t\t\tlet tmpYIdx = tmpNumericIndices[i + 1];\n\n\t\t\t// Reduce jitter for first and last coordinate pairs (port anchors)\n\t\t\tlet tmpLocalAmplitude = pAmplitude;\n\t\t\tif (i === 0 || i >= tmpNumericIndices.length - 2)\n\t\t\t{\n\t\t\t\ttmpLocalAmplitude = pAmplitude * 0.15; // Minimal anchor jitter\n\t\t\t}\n\t\t\telse if (i === 2 || i >= tmpNumericIndices.length - 4)\n\t\t\t{\n\t\t\t\ttmpLocalAmplitude = pAmplitude * 0.5; // Reduced near anchors\n\t\t\t}\n\n\t\t\tlet tmpX = parseFloat(tmpTokens[tmpXIdx]);\n\t\t\tlet tmpY = parseFloat(tmpTokens[tmpYIdx]);\n\t\t\tlet tmpJittered = this.jitterPoint(tmpX, tmpY, tmpLocalAmplitude, tmpRNG);\n\n\t\t\ttmpTokens[tmpXIdx] = tmpJittered.x.toFixed(1);\n\t\t\ttmpTokens[tmpYIdx] = tmpJittered.y.toFixed(1);\n\t\t}\n\n\t\t// Reassemble path string with spaces\n\t\tlet tmpResult = '';\n\t\tfor (let i = 0; i < tmpTokens.length; i++)\n\t\t{\n\t\t\tif (i > 0 && /^[MLCQZmlcqz]$/.test(tmpTokens[i]))\n\t\t\t{\n\t\t\t\ttmpResult += ' ' + tmpTokens[i];\n\t\t\t}\n\t\t\telse if (i > 0)\n\t\t\t{\n\t\t\t\ttmpResult += ' ' + tmpTokens[i];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpResult += tmpTokens[i];\n\t\t\t}\n\t\t}\n\n\t\treturn tmpResult;\n\t}\n}\n\nmodule.exports = PictProviderFlowNoise;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-PanelChrome\n *\n * Template-based provider for creating the panel chrome (the foreignObject\n * wrapper, title bar, close button, and body container) for properties panels.\n *\n * Replaces the raw DOM API approach with a configuration template\n * (Flow-PanelChrome-Template) registered in the FlowView's template set.\n */\nclass PictProviderFlowPanelChrome extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowPanelChrome';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Create a foreignObject containing the panel chrome and empty body container.\n\t *\n\t * Uses the Flow-PanelChrome-Template registered in the FlowView configuration\n\t * to render the inner HTML (title bar, close button, body container), then\n\t * attaches event isolation listeners so pointer and wheel events inside the\n\t * panel body do not propagate to the SVG interaction layer.\n\t *\n\t * @param {Object} pPanelData - Panel data from OpenPanels (Hash, NodeHash, X, Y, Width, Height, Title)\n\t * @param {SVGGElement} pPanelsLayer - The SVG <g> for panel foreignObjects\n\t * @returns {HTMLDivElement} The panel body container element for content rendering\n\t */\n\tcreatePanelForeignObject(pPanelData, pPanelsLayer)\n\t{\n\t\tlet tmpSVGHelper = this._FlowView._SVGHelperProvider;\n\n\t\t// Create the SVG foreignObject wrapper\n\t\tlet tmpFO = tmpSVGHelper.createSVGElement('foreignObject');\n\t\ttmpFO.setAttribute('class', 'pict-flow-panel-foreign-object');\n\t\ttmpFO.setAttribute('data-panel-hash', pPanelData.Hash);\n\t\ttmpFO.setAttribute('data-node-hash', pPanelData.NodeHash);\n\t\ttmpFO.setAttribute('x', String(pPanelData.X));\n\t\ttmpFO.setAttribute('y', String(pPanelData.Y));\n\t\ttmpFO.setAttribute('width', String(pPanelData.Width));\n\t\ttmpFO.setAttribute('height', String(pPanelData.Height));\n\n\t\t// Render the panel chrome from the configuration template\n\t\tlet tmpPict = this._FlowView.pict || this._FlowView.fable;\n\t\tlet tmpTitle = pPanelData.Title || 'Properties';\n\t\tlet tmpChromeHTML = tmpPict.parseTemplateByHash('Flow-PanelChrome-Template',\n\t\t\t{ Hash: pPanelData.Hash, Title: tmpTitle });\n\n\t\ttmpFO.innerHTML = tmpChromeHTML;\n\n\t\t// Populate the close button icon\n\t\tlet tmpCloseIcon = tmpFO.querySelector('.pict-flow-panel-close-icon');\n\t\tif (tmpCloseIcon && this._FlowView && this._FlowView._IconProvider)\n\t\t{\n\t\t\ttmpCloseIcon.innerHTML = this._FlowView._IconProvider.getIconSVGMarkup('close', 12);\n\t\t}\n\t\telse if (tmpCloseIcon)\n\t\t{\n\t\t\ttmpCloseIcon.textContent = '\\u2715';\n\t\t}\n\n\t\t// Attach event isolation to the scrollable content area so\n\t\t// pointer/wheel events inside the panel do not trigger SVG interactions\n\t\tlet tmpContent = tmpFO.querySelector('.pict-flow-panel-content');\n\t\tif (tmpContent)\n\t\t{\n\t\t\ttmpContent.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\t\ttmpContent.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });\n\t\t}\n\n\t\t// Isolate events on the tab bar\n\t\tlet tmpTabbar = tmpFO.querySelector('.pict-flow-panel-tabbar');\n\t\tif (tmpTabbar)\n\t\t{\n\t\t\ttmpTabbar.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\t\ttmpTabbar.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });\n\t\t}\n\n\t\tpPanelsLayer.appendChild(tmpFO);\n\n\t\t// Return the properties tab pane as the body container for content rendering\n\t\tlet tmpPropertiesPane = tmpFO.querySelector('.pict-flow-panel-tab-pane[data-tab=\"properties\"]');\n\t\treturn tmpPropertiesPane;\n\t}\n}\n\nmodule.exports = PictProviderFlowPanelChrome;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictProvider-Flow-SVGHelpers\n *\n * Shared SVG element creation utility used by all flow components\n * that need to create SVG namespace elements (nodes, connections, tethers).\n */\nclass PictProviderFlowSVGHelpers extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowSVGHelpers';\n\t}\n\n\t/**\n\t * Create an SVG namespace element.\n\t *\n\t * @param {string} pTagName - The SVG element tag name (e.g., 'path', 'circle', 'g', 'rect', 'text')\n\t * @returns {SVGElement}\n\t */\n\tcreateSVGElement(pTagName)\n\t{\n\t\treturn document.createElementNS('http://www.w3.org/2000/svg', pTagName);\n\t}\n}\n\nmodule.exports = PictProviderFlowSVGHelpers;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nconst _ProviderConfiguration =\n{\n\tProviderIdentifier: 'PictProviderFlowTheme'\n};\n\n/**\n * PictProvider-Flow-Theme\n *\n * Central orchestrator for the flow diagram theming system.\n *\n * Holds a registry of theme definitions, manages the active theme,\n * and provides hooks for node body rendering and path noise processing.\n *\n * ## Usage\n *\n * ```javascript\n * flowView.setTheme('sketch'); // Switch to hand-drawn style\n * flowView.setNoiseLevel(0.6); // Increase bracket/connection wobble\n * flowView.setTheme('default'); // Restore modern style\n * ```\n *\n * ## Custom Themes\n *\n * ```javascript\n * flowView._ThemeProvider.registerTheme('custom', {\n * Key: 'custom',\n * Label: 'My Custom Theme',\n * CSSVariables: { '--pf-canvas-bg': '#222' },\n * NodeBodyMode: 'rect',\n * NoiseConfig: { Enabled: false }\n * });\n * flowView.setTheme('custom');\n * ```\n */\nclass PictProviderFlowTheme extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictProviderFlowTheme';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\tthis._ActiveThemeKey = 'default';\n\t\tthis._NoiseLevel = 0;\n\t\tthis._Themes = {};\n\n\t\tthis._registerBuiltInThemes();\n\t}\n\n\t// ── Theme Registry ────────────────────────────────────────────────────\n\n\t_registerBuiltInThemes()\n\t{\n\t\t// ── 1. Default (Modern) ──────────────────────────────────────\n\t\tthis._Themes['default'] =\n\t\t{\n\t\t\tKey: 'default',\n\t\t\tLabel: 'Modern',\n\t\t\tCSSVariables: {},\n\t\t\tAdditionalCSS: '',\n\t\t\tNodeBodyMode: 'rect',\n\t\t\tBracketConfig: null,\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 2,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides: {}\n\t\t};\n\n\t\t// ── 2. Sketch (Hand-drawn) ───────────────────────────────────\n\t\tthis._Themes['sketch'] =\n\t\t{\n\t\t\tKey: 'sketch',\n\t\t\tLabel: 'Sketch',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': '#fffef5',\n\t\t\t\t'--pf-node-body-stroke': '#444444',\n\t\t\t\t'--pf-node-body-stroke-width': '1.5',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'none',\n\t\t\t\t'--pf-node-shadow-hover': 'none',\n\t\t\t\t'--pf-node-shadow-selected': 'none',\n\t\t\t\t'--pf-node-shadow-dragging': 'none',\n\t\t\t\t'--pf-node-title-fill': '#333333',\n\t\t\t\t'--pf-node-title-size': '12px',\n\t\t\t\t'--pf-node-title-weight': '400',\n\t\t\t\t'--pf-node-title-bar-color': '#f0ece0',\n\t\t\t\t'--pf-node-type-label-fill': '#888888',\n\t\t\t\t'--pf-node-selected-stroke': '#2255aa',\n\t\t\t\t'--pf-port-input-fill': '#5577bb',\n\t\t\t\t'--pf-port-output-fill': '#55aa77',\n\t\t\t\t'--pf-port-stroke': '#fffef5',\n\t\t\t\t'--pf-connection-stroke': '#555555',\n\t\t\t\t'--pf-connection-selected-stroke': '#2255aa',\n\t\t\t\t'--pf-canvas-bg': '#fffef5',\n\t\t\t\t'--pf-grid-stroke': '#e8e4d8',\n\t\t\t\t'--pf-panel-bg': '#fffef5',\n\t\t\t\t'--pf-panel-border': '#ccccaa',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': '2px 2px 0px rgba(0,0,0,0.08)',\n\t\t\t\t'--pf-panel-titlebar-bg': '#f0ece0',\n\t\t\t\t'--pf-panel-titlebar-border': '#ccccaa',\n\t\t\t\t'--pf-panel-title-color': '#333333'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: \"Courier New\", \"Courier\", monospace !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-panel-title-text,\n\t\t\t\t.pict-flow-panel-node-props-title,\n\t\t\t\t.pict-flow-info-panel {\n\t\t\t\t\tfont-family: \"Courier New\", \"Courier\", monospace !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) !important;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'bracket',\n\t\t\tBracketConfig:\n\t\t\t{\n\t\t\t\tSerifLength: 20,\n\t\t\t\tTitleSeparator: true\n\t\t\t},\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 1.5,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: true,\n\t\t\t\tDefaultLevel: 0.4,\n\t\t\t\tMaxJitterPx: 4,\n\t\t\t\tAffectsNodes: true,\n\t\t\t\tAffectsConnections: true\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#555555' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#2255aa' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 3. Blueprint (Technical) ─────────────────────────────────\n\t\tthis._Themes['blueprint'] =\n\t\t{\n\t\t\tKey: 'blueprint',\n\t\t\tLabel: 'Blueprint',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': 'rgba(255,255,255,0.05)',\n\t\t\t\t'--pf-node-body-stroke': '#ffffff',\n\t\t\t\t'--pf-node-body-stroke-width': '1',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'none',\n\t\t\t\t'--pf-node-shadow-hover': 'none',\n\t\t\t\t'--pf-node-shadow-selected': 'none',\n\t\t\t\t'--pf-node-shadow-dragging': 'none',\n\t\t\t\t'--pf-node-title-fill': '#ffffff',\n\t\t\t\t'--pf-node-title-size': '11px',\n\t\t\t\t'--pf-node-title-weight': '400',\n\t\t\t\t'--pf-node-title-bar-color': 'rgba(255,255,255,0.1)',\n\t\t\t\t'--pf-node-type-label-fill': 'rgba(255,255,255,0.5)',\n\t\t\t\t'--pf-node-selected-stroke': '#ffdd44',\n\t\t\t\t'--pf-port-input-fill': '#88bbff',\n\t\t\t\t'--pf-port-output-fill': '#88ffbb',\n\t\t\t\t'--pf-port-stroke': '#1a3a6a',\n\t\t\t\t'--pf-connection-stroke': 'rgba(255,255,255,0.6)',\n\t\t\t\t'--pf-connection-selected-stroke': '#ffdd44',\n\t\t\t\t'--pf-canvas-bg': '#1a3a6a',\n\t\t\t\t'--pf-grid-stroke': 'rgba(255,255,255,0.08)',\n\t\t\t\t'--pf-panel-bg': '#1a3a6a',\n\t\t\t\t'--pf-panel-border': 'rgba(255,255,255,0.3)',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': 'none',\n\t\t\t\t'--pf-panel-titlebar-bg': 'rgba(255,255,255,0.05)',\n\t\t\t\t'--pf-panel-titlebar-border': 'rgba(255,255,255,0.15)',\n\t\t\t\t'--pf-panel-title-color': '#ffffff'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: \"Courier New\", monospace !important;\n\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\tletter-spacing: 1px;\n\t\t\t\t}\n\t\t\t\t.pict-flow-container {\n\t\t\t\t\tborder-color: #0d2244;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) invert(1) !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar {\n\t\t\t\t\tbackground-color: #142e54;\n\t\t\t\t\tborder-bottom-color: rgba(255,255,255,0.15);\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn {\n\t\t\t\t\tbackground-color: rgba(255,255,255,0.05);\n\t\t\t\t\tborder-color: rgba(255,255,255,0.2);\n\t\t\t\t\tcolor: #ffffff;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn:hover {\n\t\t\t\t\tbackground-color: rgba(255,255,255,0.1);\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'bracket',\n\t\t\tBracketConfig:\n\t\t\t{\n\t\t\t\tSerifLength: 18,\n\t\t\t\tTitleSeparator: true\n\t\t\t},\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: '8 4',\n\t\t\t\tStrokeWidth: 1,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: 'rgba(255,255,255,0.6)' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#ffdd44' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 4. Mono (Black & White) ──────────────────────────────────\n\t\tthis._Themes['mono'] =\n\t\t{\n\t\t\tKey: 'mono',\n\t\t\tLabel: 'Monochrome',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': '#ffffff',\n\t\t\t\t'--pf-node-body-stroke': '#000000',\n\t\t\t\t'--pf-node-body-stroke-width': '1',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'none',\n\t\t\t\t'--pf-node-shadow-hover': 'none',\n\t\t\t\t'--pf-node-shadow-selected': 'none',\n\t\t\t\t'--pf-node-shadow-dragging': 'none',\n\t\t\t\t'--pf-node-title-fill': '#ffffff',\n\t\t\t\t'--pf-node-title-size': '11px',\n\t\t\t\t'--pf-node-title-weight': '600',\n\t\t\t\t'--pf-node-title-bar-color': '#000000',\n\t\t\t\t'--pf-node-type-label-fill': '#888888',\n\t\t\t\t'--pf-node-selected-stroke': '#444444',\n\t\t\t\t'--pf-port-input-fill': '#000000',\n\t\t\t\t'--pf-port-output-fill': '#666666',\n\t\t\t\t'--pf-port-stroke': '#ffffff',\n\t\t\t\t'--pf-connection-stroke': '#000000',\n\t\t\t\t'--pf-connection-selected-stroke': '#444444',\n\t\t\t\t'--pf-canvas-bg': '#ffffff',\n\t\t\t\t'--pf-grid-stroke': '#eeeeee',\n\t\t\t\t'--pf-panel-bg': '#ffffff',\n\t\t\t\t'--pf-panel-border': '#000000',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': 'none',\n\t\t\t\t'--pf-panel-titlebar-bg': '#f0f0f0',\n\t\t\t\t'--pf-panel-titlebar-border': '#000000',\n\t\t\t\t'--pf-panel-title-color': '#000000'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title {\n\t\t\t\t\tfont-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) invert(1) !important;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'rect',\n\t\t\tBracketConfig: null,\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 1,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#000000' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#444444' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 5. Retro 80s (Neon) ──────────────────────────────────────\n\t\tthis._Themes['retro-80s'] =\n\t\t{\n\t\t\tKey: 'retro-80s',\n\t\t\tLabel: '80s Retro',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': '#1a0a2e',\n\t\t\t\t'--pf-node-body-stroke': '#ff00ff',\n\t\t\t\t'--pf-node-body-stroke-width': '2',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'drop-shadow(0 0 8px rgba(255,0,255,0.4))',\n\t\t\t\t'--pf-node-shadow-hover': 'drop-shadow(0 0 12px rgba(255,0,255,0.6))',\n\t\t\t\t'--pf-node-shadow-selected': 'drop-shadow(0 0 16px rgba(0,255,255,0.5))',\n\t\t\t\t'--pf-node-shadow-dragging': 'drop-shadow(0 0 20px rgba(255,0,255,0.7))',\n\t\t\t\t'--pf-node-title-fill': '#00ffff',\n\t\t\t\t'--pf-node-title-size': '11px',\n\t\t\t\t'--pf-node-title-weight': '700',\n\t\t\t\t'--pf-node-title-bar-color': '#2a0a4e',\n\t\t\t\t'--pf-node-type-label-fill': '#ff66ff',\n\t\t\t\t'--pf-node-selected-stroke': '#00ffff',\n\t\t\t\t'--pf-port-input-fill': '#ff00ff',\n\t\t\t\t'--pf-port-output-fill': '#00ff66',\n\t\t\t\t'--pf-port-stroke': '#1a0a2e',\n\t\t\t\t'--pf-connection-stroke': '#ff00ff',\n\t\t\t\t'--pf-connection-selected-stroke': '#00ffff',\n\t\t\t\t'--pf-canvas-bg': '#0a0015',\n\t\t\t\t'--pf-grid-stroke': '#1a0a2e',\n\t\t\t\t'--pf-panel-bg': '#1a0a2e',\n\t\t\t\t'--pf-panel-border': '#ff00ff',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': '0 0 20px rgba(255,0,255,0.3)',\n\t\t\t\t'--pf-panel-titlebar-bg': '#2a0a4e',\n\t\t\t\t'--pf-panel-titlebar-border': '#ff00ff',\n\t\t\t\t'--pf-panel-title-color': '#00ffff'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: \"Courier New\", monospace !important;\n\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\tletter-spacing: 0.5px;\n\t\t\t\t}\n\t\t\t\t.pict-flow-connection {\n\t\t\t\t\tfilter: drop-shadow(0 0 3px rgba(255,0,255,0.4));\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) invert(1) hue-rotate(180deg) !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar {\n\t\t\t\t\tbackground-color: #1a0a2e;\n\t\t\t\t\tborder-bottom-color: #ff00ff;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn {\n\t\t\t\t\tbackground-color: #1a0a2e;\n\t\t\t\t\tborder-color: #ff00ff;\n\t\t\t\t\tcolor: #00ffff;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn:hover {\n\t\t\t\t\tbackground-color: #2a0a4e;\n\t\t\t\t}\n\t\t\t\t.pict-flow-container {\n\t\t\t\t\tborder-color: #ff00ff;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'rect',\n\t\t\tBracketConfig: null,\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 2,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#ff00ff' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#00ffff' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 6. Retro 90s (Windows) ───────────────────────────────────\n\t\tthis._Themes['retro-90s'] =\n\t\t{\n\t\t\tKey: 'retro-90s',\n\t\t\tLabel: '90s Retro',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': '#c0c0c0',\n\t\t\t\t'--pf-node-body-stroke': '#808080',\n\t\t\t\t'--pf-node-body-stroke-width': '1',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'drop-shadow(2px 2px 0px #404040)',\n\t\t\t\t'--pf-node-shadow-hover': 'drop-shadow(3px 3px 0px #404040)',\n\t\t\t\t'--pf-node-shadow-selected': 'drop-shadow(2px 2px 0px #008080)',\n\t\t\t\t'--pf-node-shadow-dragging': 'drop-shadow(4px 4px 0px #404040)',\n\t\t\t\t'--pf-node-title-fill': '#ffffff',\n\t\t\t\t'--pf-node-title-size': '11px',\n\t\t\t\t'--pf-node-title-weight': '700',\n\t\t\t\t'--pf-node-title-bar-color': '#000080',\n\t\t\t\t'--pf-node-type-label-fill': '#606060',\n\t\t\t\t'--pf-node-selected-stroke': '#008080',\n\t\t\t\t'--pf-port-input-fill': '#000080',\n\t\t\t\t'--pf-port-output-fill': '#008000',\n\t\t\t\t'--pf-port-stroke': '#c0c0c0',\n\t\t\t\t'--pf-connection-stroke': '#808080',\n\t\t\t\t'--pf-connection-selected-stroke': '#008080',\n\t\t\t\t'--pf-canvas-bg': '#008080',\n\t\t\t\t'--pf-grid-stroke': 'rgba(0,0,0,0.06)',\n\t\t\t\t'--pf-panel-bg': '#c0c0c0',\n\t\t\t\t'--pf-panel-border': '#808080',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': '2px 2px 0px #404040',\n\t\t\t\t'--pf-panel-titlebar-bg': '#000080',\n\t\t\t\t'--pf-panel-titlebar-border': '#c0c0c0',\n\t\t\t\t'--pf-panel-title-color': '#ffffff'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: \"MS Sans Serif\", \"Arial\", sans-serif !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: brightness(0) invert(1) !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar {\n\t\t\t\t\tbackground-color: #c0c0c0;\n\t\t\t\t\tborder-bottom: 2px solid #808080;\n\t\t\t\t\tborder-top: 1px solid #ffffff;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn {\n\t\t\t\t\tbackground-color: #c0c0c0;\n\t\t\t\t\tborder: 2px outset #c0c0c0;\n\t\t\t\t\tborder-radius: 0;\n\t\t\t\t\tcolor: #000000;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn:hover {\n\t\t\t\t\tbackground-color: #d0d0d0;\n\t\t\t\t}\n\t\t\t\t.pict-flow-toolbar-btn:active {\n\t\t\t\t\tborder-style: inset;\n\t\t\t\t}\n\t\t\t\t.pict-flow-container {\n\t\t\t\t\tborder: 2px outset #c0c0c0;\n\t\t\t\t\tborder-radius: 0;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'rect',\n\t\t\tBracketConfig: null,\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 2,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: false,\n\t\t\t\tDefaultLevel: 0,\n\t\t\t\tMaxJitterPx: 0,\n\t\t\t\tAffectsNodes: false,\n\t\t\t\tAffectsConnections: false\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#808080' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#008080' }\n\t\t\t}\n\t\t};\n\n\t\t// ── 7. Whiteboard (Minimal brackets, no fills) ──────────────\n\t\tthis._Themes['whiteboard'] =\n\t\t{\n\t\t\tKey: 'whiteboard',\n\t\t\tLabel: 'Whiteboard',\n\t\t\tCSSVariables:\n\t\t\t{\n\t\t\t\t'--pf-node-body-fill': 'transparent',\n\t\t\t\t'--pf-node-body-stroke': '#555555',\n\t\t\t\t'--pf-node-body-stroke-width': '2',\n\t\t\t\t'--pf-node-body-radius': '0px',\n\t\t\t\t'--pf-node-shadow': 'none',\n\t\t\t\t'--pf-node-shadow-hover': 'none',\n\t\t\t\t'--pf-node-shadow-selected': 'none',\n\t\t\t\t'--pf-node-shadow-dragging': 'none',\n\t\t\t\t'--pf-node-title-fill': '#333333',\n\t\t\t\t'--pf-node-title-size': '12px',\n\t\t\t\t'--pf-node-title-weight': '600',\n\t\t\t\t'--pf-node-title-bar-color': 'transparent',\n\t\t\t\t'--pf-node-type-label-fill': '#999999',\n\t\t\t\t'--pf-node-selected-stroke': '#2255aa',\n\t\t\t\t'--pf-port-input-fill': '#5577bb',\n\t\t\t\t'--pf-port-output-fill': '#55aa77',\n\t\t\t\t'--pf-port-stroke': '#ffffff',\n\t\t\t\t'--pf-connection-stroke': '#888888',\n\t\t\t\t'--pf-connection-selected-stroke': '#2255aa',\n\t\t\t\t'--pf-canvas-bg': '#ffffff',\n\t\t\t\t'--pf-grid-stroke': '#f0f0f0',\n\t\t\t\t'--pf-panel-bg': '#ffffff',\n\t\t\t\t'--pf-panel-border': '#cccccc',\n\t\t\t\t'--pf-panel-radius': '0px',\n\t\t\t\t'--pf-panel-shadow': '2px 2px 0px rgba(0,0,0,0.06)',\n\t\t\t\t'--pf-panel-titlebar-bg': '#f8f8f8',\n\t\t\t\t'--pf-panel-titlebar-border': '#e0e0e0',\n\t\t\t\t'--pf-panel-title-color': '#333333'\n\t\t\t},\n\t\t\tAdditionalCSS: `\n\t\t\t\t.pict-flow-node-title,\n\t\t\t\t.pict-flow-node-type-label,\n\t\t\t\t.pict-flow-port-label,\n\t\t\t\t.pict-flow-node-card-code {\n\t\t\t\t\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif !important;\n\t\t\t\t}\n\t\t\t\t/* Node-type bracket colors — each type gets its own bracket color */\n\t\t\t\t.pict-flow-node-start .pict-flow-node-bracket { stroke: #27ae60; }\n\t\t\t\t.pict-flow-node-end .pict-flow-node-bracket { stroke: #1abc9c; }\n\t\t\t\t.pict-flow-node-halt .pict-flow-node-bracket { stroke: #e74c3c; }\n\t\t\t\t.pict-flow-node-decision .pict-flow-node-bracket { stroke: #f39c12; }\n\t\t\t\t.pict-flow-node-default .pict-flow-node-bracket { stroke: #3498db; }\n\t\t\t\t.pict-flow-node-action .pict-flow-node-bracket { stroke: #2c3e50; }\n\t\t\t\t/* Override variant rules: no fills/strokes on body rects in whiteboard */\n\t\t\t\t.pict-flow-node-decision .pict-flow-node-body,\n\t\t\t\t.pict-flow-node-start .pict-flow-node-body,\n\t\t\t\t.pict-flow-node-end .pict-flow-node-body,\n\t\t\t\t.pict-flow-node-halt .pict-flow-node-body {\n\t\t\t\t\tfill: transparent;\n\t\t\t\t\tstroke: transparent;\n\t\t\t\t\tstroke-width: 0;\n\t\t\t\t}\n\t\t\t\t/* Title bar fills transparent too */\n\t\t\t\t.pict-flow-node .pict-flow-node-bracket-title-fill {\n\t\t\t\t\tfill: transparent !important;\n\t\t\t\t}\n\t\t\t\t.pict-flow-node-title-icon {\n\t\t\t\t\tfilter: none !important;\n\t\t\t\t}\n\t\t\t`,\n\t\t\tNodeBodyMode: 'bracket',\n\t\t\tBracketConfig:\n\t\t\t{\n\t\t\t\tSerifLength: 22,\n\t\t\t\tTitleSeparator: false\n\t\t\t},\n\t\t\tConnectionConfig:\n\t\t\t{\n\t\t\t\tStrokeDashArray: null,\n\t\t\t\tStrokeWidth: 1.5,\n\t\t\t\tArrowheadStyle: 'triangle'\n\t\t\t},\n\t\t\tNoiseConfig:\n\t\t\t{\n\t\t\t\tEnabled: true,\n\t\t\t\tDefaultLevel: 0.3,\n\t\t\t\tMaxJitterPx: 3,\n\t\t\t\tAffectsNodes: true,\n\t\t\t\tAffectsConnections: true\n\t\t\t},\n\t\t\tShapeOverrides:\n\t\t\t{\n\t\t\t\t'arrowhead-connection': { Fill: '#888888' },\n\t\t\t\t'arrowhead-connection-selected': { Fill: '#2255aa' }\n\t\t\t}\n\t\t};\n\t}\n\n\t// ── Public API ────────────────────────────────────────────────────────\n\n\t/**\n\t * Get the active theme definition.\n\t * @returns {Object}\n\t */\n\tgetActiveTheme()\n\t{\n\t\treturn this._Themes[this._ActiveThemeKey] || this._Themes['default'];\n\t}\n\n\t/**\n\t * Get the active theme key.\n\t * @returns {string}\n\t */\n\tgetActiveThemeKey()\n\t{\n\t\treturn this._ActiveThemeKey;\n\t}\n\n\t/**\n\t * Switch the active theme.\n\t * This updates the internal key and applies shape overrides.\n\t * The caller (FlowView.setTheme) is responsible for re-registering\n\t * CSS and triggering a full re-render.\n\t *\n\t * @param {string} pThemeKey\n\t * @returns {boolean} Whether the theme was found and applied\n\t */\n\tsetTheme(pThemeKey)\n\t{\n\t\tif (!this._Themes[pThemeKey])\n\t\t{\n\t\t\tthis.log.warn(`PictProviderFlowTheme: theme '${pThemeKey}' not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._ActiveThemeKey = pThemeKey;\n\t\tlet tmpTheme = this._Themes[pThemeKey];\n\n\t\t// Apply noise defaults from theme\n\t\tif (tmpTheme.NoiseConfig && typeof tmpTheme.NoiseConfig.DefaultLevel === 'number')\n\t\t{\n\t\t\tthis._NoiseLevel = tmpTheme.NoiseConfig.DefaultLevel;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._NoiseLevel = 0;\n\t\t}\n\n\t\t// Apply shape overrides\n\t\tif (this._FlowView && this._FlowView._ConnectorShapesProvider)\n\t\t{\n\t\t\tthis._FlowView._ConnectorShapesProvider.resetToDefaults();\n\t\t\tif (tmpTheme.ShapeOverrides && Object.keys(tmpTheme.ShapeOverrides).length > 0)\n\t\t\t{\n\t\t\t\tthis._FlowView._ConnectorShapesProvider.applyThemeOverrides(tmpTheme.ShapeOverrides);\n\t\t\t}\n\t\t}\n\n\t\tthis.log.trace(`PictProviderFlowTheme: switched to '${pThemeKey}'`);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get the current noise level (0 to 1).\n\t * @returns {number}\n\t */\n\tgetNoiseLevel()\n\t{\n\t\treturn this._NoiseLevel;\n\t}\n\n\t/**\n\t * Set the noise level (0 to 1).\n\t * @param {number} pLevel\n\t */\n\tsetNoiseLevel(pLevel)\n\t{\n\t\tthis._NoiseLevel = Math.max(0, Math.min(1, pLevel || 0));\n\t}\n\n\t/**\n\t * Register a custom theme.\n\t * @param {string} pKey\n\t * @param {Object} pThemeDefinition\n\t */\n\tregisterTheme(pKey, pThemeDefinition)\n\t{\n\t\tif (!pKey || !pThemeDefinition)\n\t\t{\n\t\t\tthis.log.warn('PictProviderFlowTheme: registerTheme requires key and definition');\n\t\t\treturn;\n\t\t}\n\t\tpThemeDefinition.Key = pKey;\n\t\tthis._Themes[pKey] = pThemeDefinition;\n\t}\n\n\t/**\n\t * Get all registered theme keys.\n\t * @returns {Array<string>}\n\t */\n\tgetThemeKeys()\n\t{\n\t\treturn Object.keys(this._Themes);\n\t}\n\n\t// ── Rendering Hooks ───────────────────────────────────────────────────\n\n\t/**\n\t * Post-process an SVG path string to apply noise/jitter if the\n\t * active theme has noise enabled for connections.\n\t *\n\t * @param {string} pPathString - SVG path d attribute\n\t * @param {string} pSeedString - Hash for deterministic noise\n\t * @returns {string} Possibly-modified path string\n\t */\n\tprocessPathString(pPathString, pSeedString)\n\t{\n\t\tlet tmpTheme = this.getActiveTheme();\n\t\tif (!tmpTheme || !tmpTheme.NoiseConfig || !tmpTheme.NoiseConfig.Enabled || !tmpTheme.NoiseConfig.AffectsConnections)\n\t\t{\n\t\t\treturn pPathString;\n\t\t}\n\n\t\tlet tmpAmplitude = this._NoiseLevel * (tmpTheme.NoiseConfig.MaxJitterPx || 3);\n\t\tif (tmpAmplitude <= 0)\n\t\t{\n\t\t\treturn pPathString;\n\t\t}\n\n\t\tif (this._FlowView && this._FlowView._NoiseProvider)\n\t\t{\n\t\t\treturn this._FlowView._NoiseProvider.jitterPath(pPathString, tmpAmplitude, pSeedString);\n\t\t}\n\n\t\treturn pPathString;\n\t}\n\n\t/**\n\t * Get the noise amplitude for node bracket rendering.\n\t * Returns 0 if noise is not enabled for nodes in the active theme.\n\t * @returns {number}\n\t */\n\tgetNodeNoiseAmplitude()\n\t{\n\t\tlet tmpTheme = this.getActiveTheme();\n\t\tif (!tmpTheme || !tmpTheme.NoiseConfig || !tmpTheme.NoiseConfig.Enabled || !tmpTheme.NoiseConfig.AffectsNodes)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\treturn this._NoiseLevel * (tmpTheme.NoiseConfig.MaxJitterPx || 3);\n\t}\n}\n\nmodule.exports = PictProviderFlowTheme;\n\nmodule.exports.default_configuration = _ProviderConfiguration;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-ConnectionHandleManager\n *\n * Manages connection handle lifecycle: dragging, adding, removing,\n * and resetting bezier/orthogonal handles on connections and tethers.\n *\n * Extracted from PictView-Flow.js to isolate handle CRUD operations\n * from the main view.\n */\nclass PictServiceFlowConnectionHandleManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowConnectionHandleManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Update a connection handle position during drag (for real-time feedback).\n\t * @param {string} pConnectionHash\n\t * @param {string} pHandleType - 'bezier-midpoint', 'bezier-handle-N', 'ortho-corner1', 'ortho-corner2', 'ortho-midpoint'\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdateConnectionHandle(pConnectionHash, pHandleType, pX, pY)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection) return;\n\n\t\tif (!tmpConnection.Data) tmpConnection.Data = {};\n\t\ttmpConnection.Data.HandleCustomized = true;\n\n\t\t// Multi-handle bezier: handle type is 'bezier-handle-N'\n\t\tif (pHandleType && pHandleType.startsWith('bezier-handle-'))\n\t\t{\n\t\t\tlet tmpIndex = parseInt(pHandleType.replace('bezier-handle-', ''), 10);\n\t\t\tif (!isNaN(tmpIndex) && Array.isArray(tmpConnection.Data.BezierHandles)\n\t\t\t\t&& tmpIndex < tmpConnection.Data.BezierHandles.length)\n\t\t\t{\n\t\t\t\ttmpConnection.Data.BezierHandles[tmpIndex].x = pX;\n\t\t\t\ttmpConnection.Data.BezierHandles[tmpIndex].y = pY;\n\t\t\t}\n\t\t\tthis._FlowView._renderSingleConnection(pConnectionHash);\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (pHandleType)\n\t\t{\n\t\t\tcase 'bezier-midpoint':\n\t\t\t\t// Legacy single-handle: migrate to BezierHandles array\n\t\t\t\tif (!Array.isArray(tmpConnection.Data.BezierHandles)\n\t\t\t\t\t|| tmpConnection.Data.BezierHandles.length === 0)\n\t\t\t\t{\n\t\t\t\t\ttmpConnection.Data.BezierHandles = [{ x: pX, y: pY }];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttmpConnection.Data.BezierHandles[0].x = pX;\n\t\t\t\t\ttmpConnection.Data.BezierHandles[0].y = pY;\n\t\t\t\t}\n\t\t\t\t// Keep legacy fields in sync for backward compat\n\t\t\t\ttmpConnection.Data.BezierHandleX = pX;\n\t\t\t\ttmpConnection.Data.BezierHandleY = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-corner1':\n\t\t\t\ttmpConnection.Data.OrthoCorner1X = pX;\n\t\t\t\ttmpConnection.Data.OrthoCorner1Y = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-corner2':\n\t\t\t\ttmpConnection.Data.OrthoCorner2X = pX;\n\t\t\t\ttmpConnection.Data.OrthoCorner2Y = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-midpoint':\n\t\t\t{\n\t\t\t\t// Midpoint drag shifts the corridor offset\n\t\t\t\tlet tmpSourcePos = this._FlowView.getPortPosition(tmpConnection.SourceNodeHash, tmpConnection.SourcePortHash);\n\t\t\t\tlet tmpTargetPos = this._FlowView.getPortPosition(tmpConnection.TargetNodeHash, tmpConnection.TargetPortHash);\n\t\t\t\tif (tmpSourcePos && tmpTargetPos)\n\t\t\t\t{\n\t\t\t\t\tlet tmpGeom = this._FlowView._ConnectionRenderer._computeDirectionalGeometry(tmpSourcePos, tmpTargetPos);\n\t\t\t\t\tlet tmpStartDir = tmpGeom.startDir;\n\n\t\t\t\t\t// Compute offset along the corridor axis\n\t\t\t\t\tif (Math.abs(tmpStartDir.dx) > Math.abs(tmpStartDir.dy))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Horizontal departure — corridor is vertical, shift is along X\n\t\t\t\t\t\tlet tmpAutoMidX = (tmpGeom.departX + tmpGeom.approachX) / 2;\n\t\t\t\t\t\ttmpConnection.Data.OrthoMidOffset = pX - tmpAutoMidX;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Vertical departure — corridor is horizontal, shift is along Y\n\t\t\t\t\t\tlet tmpAutoMidY = (tmpGeom.departY + tmpGeom.approachY) / 2;\n\t\t\t\t\t\ttmpConnection.Data.OrthoMidOffset = pY - tmpAutoMidY;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis._FlowView._renderSingleConnection(pConnectionHash);\n\t}\n\n\t/**\n\t * Add a bezier handle to a connection at the specified position.\n\t * The handle is inserted at the correct index based on which\n\t * segment of the curve the click point is closest to.\n\t *\n\t * @param {string} pConnectionHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\taddConnectionHandle(pConnectionHash, pX, pY)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection) return;\n\n\t\tif (!tmpConnection.Data) tmpConnection.Data = {};\n\n\t\t// Ensure BezierHandles array exists (migrate from legacy if needed)\n\t\tif (!Array.isArray(tmpConnection.Data.BezierHandles))\n\t\t{\n\t\t\ttmpConnection.Data.BezierHandles = [];\n\t\t\tif (tmpConnection.Data.BezierHandleX != null && tmpConnection.Data.BezierHandleY != null)\n\t\t\t{\n\t\t\t\ttmpConnection.Data.BezierHandles.push({\n\t\t\t\t\tx: tmpConnection.Data.BezierHandleX,\n\t\t\t\t\ty: tmpConnection.Data.BezierHandleY\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Ensure bezier mode\n\t\ttmpConnection.Data.LineMode = 'bezier';\n\n\t\tlet tmpSourcePos = this._FlowView.getPortPosition(tmpConnection.SourceNodeHash, tmpConnection.SourcePortHash);\n\t\tlet tmpTargetPos = this._FlowView.getPortPosition(tmpConnection.TargetNodeHash, tmpConnection.TargetPortHash);\n\n\t\tlet tmpInsertIndex = 0;\n\t\tif (tmpSourcePos && tmpTargetPos && this._FlowView._ConnectionRenderer)\n\t\t{\n\t\t\ttmpInsertIndex = this._FlowView._ConnectionRenderer.computeInsertionIndex(\n\t\t\t\ttmpConnection.Data.BezierHandles,\n\t\t\t\t{ x: pX, y: pY },\n\t\t\t\ttmpSourcePos,\n\t\t\t\ttmpTargetPos\n\t\t\t);\n\t\t}\n\n\t\ttmpConnection.Data.BezierHandles.splice(tmpInsertIndex, 0, { x: pX, y: pY });\n\t\ttmpConnection.Data.HandleCustomized = true;\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Remove a bezier handle from a connection by index.\n\t *\n\t * @param {string} pConnectionHash\n\t * @param {number} pIndex - Index in the BezierHandles array\n\t */\n\tremoveConnectionHandle(pConnectionHash, pIndex)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection || !tmpConnection.Data) return;\n\n\t\tif (!Array.isArray(tmpConnection.Data.BezierHandles)) return;\n\t\tif (pIndex < 0 || pIndex >= tmpConnection.Data.BezierHandles.length) return;\n\n\t\ttmpConnection.Data.BezierHandles.splice(pIndex, 1);\n\n\t\tif (tmpConnection.Data.BezierHandles.length === 0)\n\t\t{\n\t\t\ttmpConnection.Data.HandleCustomized = false;\n\t\t\ttmpConnection.Data.BezierHandleX = null;\n\t\t\ttmpConnection.Data.BezierHandleY = null;\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Reset handle positions for all connections/tethers involving a node.\n\t * Called when a node moves. Preserves LineMode but resets handle coordinates to auto.\n\t * @param {string} pNodeHash\n\t */\n\tresetHandlesForNode(pNodeHash)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\t// Reset connection handles\n\t\tfor (let i = 0; i < this._FlowView._FlowData.Connections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = this._FlowView._FlowData.Connections[i];\n\t\t\tif (tmpConn.SourceNodeHash === pNodeHash || tmpConn.TargetNodeHash === pNodeHash)\n\t\t\t{\n\t\t\t\tif (tmpConn.Data && tmpConn.Data.HandleCustomized)\n\t\t\t\t{\n\t\t\t\t\ttmpConn.Data.HandleCustomized = false;\n\t\t\t\t\t// Clear multi-handle array (current format)\n\t\t\t\t\ttmpConn.Data.BezierHandles = [];\n\t\t\t\t\t// Clear legacy single-handle fields\n\t\t\t\t\ttmpConn.Data.BezierHandleX = null;\n\t\t\t\t\ttmpConn.Data.BezierHandleY = null;\n\t\t\t\t\ttmpConn.Data.OrthoCorner1X = null;\n\t\t\t\t\ttmpConn.Data.OrthoCorner1Y = null;\n\t\t\t\t\ttmpConn.Data.OrthoCorner2X = null;\n\t\t\t\t\ttmpConn.Data.OrthoCorner2Y = null;\n\t\t\t\t\ttmpConn.Data.OrthoMidOffset = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Reset tether handles for panels attached to this node\n\t\tif (this._FlowView._TetherService)\n\t\t{\n\t\t\tthis._FlowView._TetherService.resetHandlesForNode(this._FlowView._FlowData.OpenPanels, pNodeHash);\n\t\t}\n\t}\n\n\t/**\n\t * Reset tether handle positions for a specific panel.\n\t * Called when a panel is dragged.\n\t * @param {string} pPanelHash\n\t */\n\tresetHandlesForPanel(pPanelHash)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tif (this._FlowView._TetherService)\n\t\t{\n\t\t\tthis._FlowView._TetherService.resetHandlePositions(tmpPanel);\n\t\t}\n\t}\n}\n\nmodule.exports = PictServiceFlowConnectionHandleManager;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nclass PictServiceFlowConnectionRenderer extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowConnectionRenderer';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Render a connection as an SVG path with hit area and optional handles.\n\t * @param {Object} pConnection - The connection data\n\t * @param {SVGGElement} pConnectionsLayer - The SVG group to append to\n\t * @param {boolean} pIsSelected - Whether this connection is selected\n\t */\n\trenderConnection(pConnection, pConnectionsLayer, pIsSelected)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpSourcePos = this._FlowView.getPortPosition(pConnection.SourceNodeHash, pConnection.SourcePortHash);\n\t\tlet tmpTargetPos = this._FlowView.getPortPosition(pConnection.TargetNodeHash, pConnection.TargetPortHash);\n\n\t\t// Look up the source port's PortType for connection coloring\n\t\tlet tmpSourcePortType = null;\n\t\tlet tmpSourceNode = this._FlowView.getNode(pConnection.SourceNodeHash);\n\t\tif (tmpSourceNode && tmpSourceNode.Ports)\n\t\t{\n\t\t\tfor (let i = 0; i < tmpSourceNode.Ports.length; i++)\n\t\t\t{\n\t\t\t\tif (tmpSourceNode.Ports[i].Hash === pConnection.SourcePortHash)\n\t\t\t\t{\n\t\t\t\t\ttmpSourcePortType = tmpSourceNode.Ports[i].PortType || null;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!tmpSourcePos || !tmpTargetPos) return;\n\n\t\tlet tmpData = pConnection.Data || {};\n\t\tlet tmpLineMode = tmpData.LineMode || 'bezier';\n\t\tlet tmpPath;\n\n\t\tif (tmpLineMode === 'orthogonal')\n\t\t{\n\t\t\tlet tmpCorners = null;\n\t\t\tif (tmpData.HandleCustomized && tmpData.OrthoCorner1X != null)\n\t\t\t{\n\t\t\t\ttmpCorners =\n\t\t\t\t{\n\t\t\t\t\tcorner1: { x: tmpData.OrthoCorner1X, y: tmpData.OrthoCorner1Y },\n\t\t\t\t\tcorner2: { x: tmpData.OrthoCorner2X, y: tmpData.OrthoCorner2Y }\n\t\t\t\t};\n\t\t\t}\n\t\t\ttmpPath = this._generateOrthogonalPath(tmpSourcePos, tmpTargetPos, tmpCorners, tmpData.OrthoMidOffset || 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpHandles = this._getBezierHandles(tmpData);\n\t\t\tif (tmpHandles.length > 0)\n\t\t\t{\n\t\t\t\ttmpPath = this._generateMultiHandleBezierPath(tmpSourcePos, tmpTargetPos, tmpHandles);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpPath = this._generateDirectionalPath(tmpSourcePos, tmpTargetPos);\n\t\t\t}\n\t\t}\n\n\t\t// Apply theme noise post-processing to the path\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\ttmpPath = this._FlowView._ThemeProvider.processPathString(tmpPath, pConnection.Hash);\n\t\t}\n\n\t\t// Apply stroke-dasharray from theme's ConnectionConfig\n\t\tlet tmpStrokeDashArray = null;\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\tlet tmpActiveTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\t\tif (tmpActiveTheme && tmpActiveTheme.ConnectionConfig && tmpActiveTheme.ConnectionConfig.StrokeDashArray)\n\t\t\t{\n\t\t\t\ttmpStrokeDashArray = tmpActiveTheme.ConnectionConfig.StrokeDashArray;\n\t\t\t}\n\t\t}\n\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\n\t\t// Build the port-type CSS class suffix for connection coloring\n\t\tlet tmpConnTypeClass = tmpSourcePortType ? (' conn-type-' + tmpSourcePortType) : '';\n\n\t\t// Determine the arrowhead marker based on port type\n\t\tlet tmpArrowMarkerId;\n\t\tif (pIsSelected)\n\t\t{\n\t\t\ttmpArrowMarkerId = 'flow-arrowhead-selected-' + tmpViewIdentifier;\n\t\t}\n\t\telse if (tmpSourcePortType)\n\t\t{\n\t\t\ttmpArrowMarkerId = 'flow-arrowhead-' + tmpSourcePortType + '-' + tmpViewIdentifier;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpArrowMarkerId = 'flow-arrowhead-' + tmpViewIdentifier;\n\t\t}\n\n\t\t// Hit area (wider invisible path for easier selection)\n\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\tif (tmpShapeProvider)\n\t\t{\n\t\t\tlet tmpHitArea = tmpShapeProvider.createConnectionHitAreaElement(tmpPath, pConnection.Hash);\n\t\t\tpConnectionsLayer.appendChild(tmpHitArea);\n\n\t\t\tlet tmpPathElement = tmpShapeProvider.createConnectionPathElement(\n\t\t\t\ttmpPath, pConnection.Hash, pIsSelected, tmpViewIdentifier);\n\t\t\tif (tmpConnTypeClass)\n\t\t\t{\n\t\t\t\ttmpPathElement.setAttribute('class',\n\t\t\t\t\t(tmpPathElement.getAttribute('class') || '') + tmpConnTypeClass);\n\t\t\t}\n\t\t\t// Override the default arrowhead with the typed one\n\t\t\ttmpPathElement.setAttribute('marker-end', 'url(#' + tmpArrowMarkerId + ')');\n\t\t\tif (tmpStrokeDashArray)\n\t\t\t{\n\t\t\t\ttmpPathElement.setAttribute('stroke-dasharray', tmpStrokeDashArray);\n\t\t\t}\n\t\t\tpConnectionsLayer.appendChild(tmpPathElement);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpHitArea = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpHitArea.setAttribute('class', 'pict-flow-connection-hitarea');\n\t\t\ttmpHitArea.setAttribute('d', tmpPath);\n\t\t\ttmpHitArea.setAttribute('data-connection-hash', pConnection.Hash);\n\t\t\ttmpHitArea.setAttribute('data-element-type', 'connection-hitarea');\n\t\t\tpConnectionsLayer.appendChild(tmpHitArea);\n\n\t\t\tlet tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpPathElement.setAttribute('class', `pict-flow-connection${tmpConnTypeClass} ${pIsSelected ? 'selected' : ''}`);\n\t\t\ttmpPathElement.setAttribute('d', tmpPath);\n\t\t\ttmpPathElement.setAttribute('data-connection-hash', pConnection.Hash);\n\t\t\ttmpPathElement.setAttribute('data-element-type', 'connection');\n\t\t\ttmpPathElement.setAttribute('marker-end', 'url(#' + tmpArrowMarkerId + ')');\n\n\t\t\tif (tmpStrokeDashArray)\n\t\t\t{\n\t\t\t\ttmpPathElement.setAttribute('stroke-dasharray', tmpStrokeDashArray);\n\t\t\t}\n\n\t\t\tpConnectionsLayer.appendChild(tmpPathElement);\n\t\t}\n\n\t\t// Render drag handles when selected\n\t\tif (pIsSelected)\n\t\t{\n\t\t\tthis._renderHandles(pConnection, pConnectionsLayer, tmpSourcePos, tmpTargetPos);\n\t\t}\n\t}\n\n\t/**\n\t * Compute the departure and approach points plus control points\n\t * for a direction-aware bezier between two ports.\n\t *\n\t * This extracts the intermediate geometry from _generateDirectionalPath\n\t * so it can be reused by _getAutoMidpoint and _generateBezierPathWithHandle.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @returns {{departX, departY, approachX, approachY, cp1X, cp1Y, cp2X, cp2Y, startDir, endDir}}\n\t */\n\t_computeDirectionalGeometry(pStart, pEnd)\n\t{\n\t\treturn this._FlowView._PathGenerator.computeDirectionalGeometry(pStart, pEnd);\n\t}\n\n\t/**\n\t * Generate a direction-aware path between two ports.\n\t *\n\t * The path is composed of three segments:\n\t * 1. A short straight \"departure\" segment leaving the source port\n\t * in its outward direction.\n\t * 2. A cubic bezier curve connecting the departure point to an\n\t * \"approach\" point near the target port.\n\t * 3. A short straight \"approach\" segment arriving at the target\n\t * port aligned with its inward direction.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart - Start port position + side\n\t * @param {{x: number, y: number, side: string}} pEnd - End port position + side\n\t * @returns {string} SVG path d attribute\n\t */\n\t_generateDirectionalPath(pStart, pEnd)\n\t{\n\t\tlet tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);\n\n\t\treturn this._FlowView._PathGenerator.buildBezierPathString(\n\t\t\t{ x: pStart.x, y: pStart.y },\n\t\t\t{ x: tmpGeo.departX, y: tmpGeo.departY },\n\t\t\t{ x: tmpGeo.cp1X, y: tmpGeo.cp1Y },\n\t\t\t{ x: tmpGeo.cp2X, y: tmpGeo.cp2Y },\n\t\t\t{ x: tmpGeo.approachX, y: tmpGeo.approachY },\n\t\t\t{ x: pEnd.x, y: pEnd.y }\n\t\t);\n\t}\n\n\t/**\n\t * Get the bezier handles array from connection data, with backward\n\t * compatibility for the legacy BezierHandleX/Y single-handle format.\n\t *\n\t * @param {Object} pData - Connection.Data\n\t * @returns {Array<{x: number, y: number}>} Ordered handle waypoints (may be empty)\n\t */\n\t_getBezierHandles(pData)\n\t{\n\t\tif (!pData || !pData.HandleCustomized)\n\t\t{\n\t\t\treturn [];\n\t\t}\n\n\t\t// New multi-handle format\n\t\tif (Array.isArray(pData.BezierHandles) && pData.BezierHandles.length > 0)\n\t\t{\n\t\t\treturn pData.BezierHandles;\n\t\t}\n\n\t\t// Legacy single-handle format\n\t\tif (pData.BezierHandleX != null && pData.BezierHandleY != null)\n\t\t{\n\t\t\treturn [{ x: pData.BezierHandleX, y: pData.BezierHandleY }];\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Generate a multi-handle bezier path between two ports.\n\t * Delegates to PathGenerator.buildMultiBezierPathString for the\n\t * actual SVG path assembly with Catmull-Rom tangent continuity.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @param {Array<{x: number, y: number}>} pHandles - Ordered waypoints\n\t * @returns {string} SVG path d attribute\n\t */\n\t_generateMultiHandleBezierPath(pStart, pEnd, pHandles)\n\t{\n\t\tlet tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);\n\n\t\treturn this._FlowView._PathGenerator.buildMultiBezierPathString(\n\t\t\t{ x: pStart.x, y: pStart.y },\n\t\t\t{ x: tmpGeo.departX, y: tmpGeo.departY },\n\t\t\tpHandles,\n\t\t\t{ x: tmpGeo.approachX, y: tmpGeo.approachY },\n\t\t\t{ x: pEnd.x, y: pEnd.y },\n\t\t\ttmpGeo.startDir,\n\t\t\ttmpGeo.endDir\n\t\t);\n\t}\n\n\t/**\n\t * Find which segment of the multi-handle bezier the given click point\n\t * is closest to, returning the index at which a new handle should be\n\t * inserted into the BezierHandles array.\n\t *\n\t * Segments are: depart→handle[0], handle[0]→handle[1], ..., handle[N-1]→approach.\n\t * Returns 0 for before handle[0], 1 for between handle[0] and handle[1], etc.\n\t *\n\t * @param {Array<{x: number, y: number}>} pHandles - Current handles\n\t * @param {{x: number, y: number}} pClickPoint - Where the user right-clicked\n\t * @param {{x: number, y: number, side: string}} pStart - Source port position\n\t * @param {{x: number, y: number, side: string}} pEnd - Target port position\n\t * @returns {number} Insertion index\n\t */\n\tcomputeInsertionIndex(pHandles, pClickPoint, pStart, pEnd)\n\t{\n\t\tlet tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);\n\n\t\t// Build the waypoint chain: depart, handle[0..N-1], approach\n\t\tlet tmpWaypoints = [{ x: tmpGeo.departX, y: tmpGeo.departY }];\n\t\tfor (let i = 0; i < pHandles.length; i++)\n\t\t{\n\t\t\ttmpWaypoints.push(pHandles[i]);\n\t\t}\n\t\ttmpWaypoints.push({ x: tmpGeo.approachX, y: tmpGeo.approachY });\n\n\t\tlet tmpBestDist = Infinity;\n\t\tlet tmpBestIndex = 0;\n\n\t\tfor (let i = 0; i < tmpWaypoints.length - 1; i++)\n\t\t{\n\t\t\tlet tmpDist = this._distanceToSegment(\n\t\t\t\tpClickPoint.x, pClickPoint.y,\n\t\t\t\ttmpWaypoints[i].x, tmpWaypoints[i].y,\n\t\t\t\ttmpWaypoints[i + 1].x, tmpWaypoints[i + 1].y\n\t\t\t);\n\n\t\t\tif (tmpDist < tmpBestDist)\n\t\t\t{\n\t\t\t\ttmpBestDist = tmpDist;\n\t\t\t\ttmpBestIndex = i;\n\t\t\t}\n\t\t}\n\n\t\treturn tmpBestIndex;\n\t}\n\n\t/**\n\t * Distance from point (px,py) to line segment (ax,ay)-(bx,by).\n\t */\n\t_distanceToSegment(pPX, pPY, pAX, pAY, pBX, pBY)\n\t{\n\t\treturn this._FlowView._PathGenerator.distanceToSegment(pPX, pPY, pAX, pAY, pBX, pBY);\n\t}\n\n\t/**\n\t * Get the auto-calculated midpoint of the default bezier curve between two ports.\n\t * Evaluates the cubic bezier at t=0.5.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetAutoMidpoint(pStart, pEnd)\n\t{\n\t\treturn this._FlowView._PathGenerator.getAutoMidpoint(pStart, pEnd);\n\t}\n\n\t/**\n\t * Generate an orthogonal (90-degree angles only) path between two ports.\n\t *\n\t * Path format: M start L depart L corner1 L corner2 L approach L end\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @param {Object|null} pCorners - { corner1: {x,y}, corner2: {x,y} } or null for auto\n\t * @param {number} pMidOffset - Offset for the auto-calculated corridor position\n\t * @returns {string} SVG path d attribute\n\t */\n\t_generateOrthogonalPath(pStart, pEnd, pCorners, pMidOffset)\n\t{\n\t\tlet tmpDA = this._FlowView._PathGenerator.computeDepartApproach(pStart, pEnd, 20);\n\n\t\tlet tmpCorner1, tmpCorner2;\n\n\t\tif (pCorners && pCorners.corner1 && pCorners.corner2)\n\t\t{\n\t\t\ttmpCorner1 = pCorners.corner1;\n\t\t\ttmpCorner2 = pCorners.corner2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpAutoCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(\n\t\t\t\ttmpDA.departX, tmpDA.departY,\n\t\t\t\ttmpDA.approachX, tmpDA.approachY,\n\t\t\t\ttmpDA.fromDir, tmpDA.toDir,\n\t\t\t\tpMidOffset || 0\n\t\t\t);\n\t\t\ttmpCorner1 = tmpAutoCorners.corner1;\n\t\t\ttmpCorner2 = tmpAutoCorners.corner2;\n\t\t}\n\n\t\treturn this._FlowView._PathGenerator.buildOrthogonalPathString(\n\t\t\t{ x: pStart.x, y: pStart.y },\n\t\t\t{ x: tmpDA.departX, y: tmpDA.departY },\n\t\t\ttmpCorner1,\n\t\t\ttmpCorner2,\n\t\t\t{ x: tmpDA.approachX, y: tmpDA.approachY },\n\t\t\t{ x: pEnd.x, y: pEnd.y }\n\t\t);\n\t}\n\n\t/**\n\t * Get the full orthogonal geometry for a connection (for handle positioning).\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @param {Object} pData - Connection.Data\n\t * @returns {{corner1: {x,y}, corner2: {x,y}, midpoint: {x,y}}}\n\t */\n\tgetOrthogonalGeometry(pStart, pEnd, pData)\n\t{\n\t\tlet tmpDA = this._FlowView._PathGenerator.computeDepartApproach(pStart, pEnd, 20);\n\n\t\tif (pData && pData.HandleCustomized && pData.OrthoCorner1X != null)\n\t\t{\n\t\t\tlet tmpCorner1 = { x: pData.OrthoCorner1X, y: pData.OrthoCorner1Y };\n\t\t\tlet tmpCorner2 = { x: pData.OrthoCorner2X, y: pData.OrthoCorner2Y };\n\t\t\tlet tmpMidpoint =\n\t\t\t{\n\t\t\t\tx: (tmpCorner1.x + tmpCorner2.x) / 2,\n\t\t\t\ty: (tmpCorner1.y + tmpCorner2.y) / 2\n\t\t\t};\n\t\t\treturn { corner1: tmpCorner1, corner2: tmpCorner2, midpoint: tmpMidpoint };\n\t\t}\n\n\t\treturn this._FlowView._PathGenerator.computeAutoOrthogonalCorners(\n\t\t\ttmpDA.departX, tmpDA.departY,\n\t\t\ttmpDA.approachX, tmpDA.approachY,\n\t\t\ttmpDA.fromDir, tmpDA.toDir,\n\t\t\t(pData && pData.OrthoMidOffset) || 0\n\t\t);\n\t}\n\n\t/**\n\t * Render drag handles for the selected connection.\n\t *\n\t * @param {Object} pConnection\n\t * @param {SVGGElement} pLayer\n\t * @param {{x, y, side}} pStart - Source port position\n\t * @param {{x, y, side}} pEnd - Target port position\n\t */\n\t_renderHandles(pConnection, pLayer, pStart, pEnd)\n\t{\n\t\tlet tmpData = pConnection.Data || {};\n\t\tlet tmpLineMode = tmpData.LineMode || 'bezier';\n\n\t\tif (tmpLineMode === 'orthogonal')\n\t\t{\n\t\t\tlet tmpGeometry = this.getOrthogonalGeometry(pStart, pEnd, tmpData);\n\n\t\t\t// Corner 1 handle\n\t\t\tthis._createHandle(pLayer, pConnection.Hash, 'ortho-corner1',\n\t\t\t\ttmpGeometry.corner1.x, tmpGeometry.corner1.y, 'pict-flow-connection-handle');\n\n\t\t\t// Midpoint handle (between corners)\n\t\t\tthis._createHandle(pLayer, pConnection.Hash, 'ortho-midpoint',\n\t\t\t\ttmpGeometry.midpoint.x, tmpGeometry.midpoint.y, 'pict-flow-connection-handle-midpoint');\n\n\t\t\t// Corner 2 handle\n\t\t\tthis._createHandle(pLayer, pConnection.Hash, 'ortho-corner2',\n\t\t\t\ttmpGeometry.corner2.x, tmpGeometry.corner2.y, 'pict-flow-connection-handle');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Bezier handles — show one handle per waypoint, or a\n\t\t\t// single auto-midpoint when no custom handles exist.\n\t\t\tlet tmpHandles = this._getBezierHandles(tmpData);\n\n\t\t\tif (tmpHandles.length > 0)\n\t\t\t{\n\t\t\t\tfor (let i = 0; i < tmpHandles.length; i++)\n\t\t\t\t{\n\t\t\t\t\tthis._createHandle(pLayer, pConnection.Hash,\n\t\t\t\t\t\t'bezier-handle-' + i,\n\t\t\t\t\t\ttmpHandles[i].x, tmpHandles[i].y,\n\t\t\t\t\t\t'pict-flow-connection-handle');\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlet tmpMidpoint = this.getAutoMidpoint(pStart, pEnd);\n\t\t\t\tthis._createHandle(pLayer, pConnection.Hash, 'bezier-midpoint',\n\t\t\t\t\ttmpMidpoint.x, tmpMidpoint.y, 'pict-flow-connection-handle');\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a single SVG circle handle element.\n\t *\n\t * @param {SVGGElement} pLayer\n\t * @param {string} pConnectionHash\n\t * @param {string} pHandleType\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {string} pClassName\n\t */\n\t_createHandle(pLayer, pConnectionHash, pHandleType, pX, pY, pClassName)\n\t{\n\t\tif (!this._FlowView._ConnectorShapesProvider) return;\n\n\t\tlet tmpShapeKey = (pClassName === 'pict-flow-connection-handle-midpoint')\n\t\t\t? 'connection-handle-midpoint' : 'connection-handle';\n\n\t\tthis._FlowView._ConnectorShapesProvider.createFullHandle(\n\t\t\tpLayer, pConnectionHash, pHandleType, pX, pY,\n\t\t\ttmpShapeKey, 'connection-handle', 'data-connection-hash');\n\t}\n\n\t/**\n\t * Legacy bezier path for drag connections where we don't have side info.\n\t * @param {{x: number, y: number}} pStart\n\t * @param {{x: number, y: number}} pEnd\n\t * @returns {string}\n\t */\n\t_generateBezierPath(pStart, pEnd)\n\t{\n\t\t// During drag operations we may not have side info; default to right->left\n\t\tlet tmpStart = { x: pStart.x, y: pStart.y, side: pStart.side || 'right' };\n\t\tlet tmpEnd = { x: pEnd.x, y: pEnd.y, side: pEnd.side || 'left' };\n\t\treturn this._generateDirectionalPath(tmpStart, tmpEnd);\n\t}\n\n\t/**\n\t * Render a temporary drag connection line (used during connection creation)\n\t * @param {number} pStartX\n\t * @param {number} pStartY\n\t * @param {number} pEndX\n\t * @param {number} pEndY\n\t * @param {SVGGElement} pLayer - The layer to render into\n\t * @param {string} [pStartSide] - The side the source port is on\n\t * @returns {SVGPathElement} The created path element\n\t */\n\trenderDragConnection(pStartX, pStartY, pEndX, pEndY, pLayer, pStartSide)\n\t{\n\t\tlet tmpPath = this._generateDirectionalPath(\n\t\t\t{ x: pStartX, y: pStartY, side: pStartSide || 'right' },\n\t\t\t{ x: pEndX, y: pEndY, side: 'left' }\n\t\t);\n\n\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\tlet tmpPathElement;\n\n\t\tif (tmpShapeProvider)\n\t\t{\n\t\t\ttmpPathElement = tmpShapeProvider.createDragConnectionElement(tmpPath);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpPathElement.setAttribute('class', 'pict-flow-drag-connection');\n\t\t\ttmpPathElement.setAttribute('d', tmpPath);\n\t\t}\n\n\t\tpLayer.appendChild(tmpPathElement);\n\n\t\treturn tmpPathElement;\n\t}\n\n\t/**\n\t * Update a drag connection path\n\t * @param {SVGPathElement} pPathElement\n\t * @param {number} pStartX\n\t * @param {number} pStartY\n\t * @param {number} pEndX\n\t * @param {number} pEndY\n\t * @param {string} [pStartSide] - The side the source port is on\n\t */\n\tupdateDragConnection(pPathElement, pStartX, pStartY, pEndX, pEndY, pStartSide)\n\t{\n\t\tif (!pPathElement) return;\n\n\t\tlet tmpPath = this._generateDirectionalPath(\n\t\t\t{ x: pStartX, y: pStartY, side: pStartSide || 'right' },\n\t\t\t{ x: pEndX, y: pEndY, side: 'left' }\n\t\t);\n\n\t\tpPathElement.setAttribute('d', tmpPath);\n\t}\n}\n\nmodule.exports = PictServiceFlowConnectionRenderer;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-DataManager\n *\n * Manages flow data lifecycle: serialization, deserialization, and CRUD\n * operations for nodes and connections.\n *\n * Extracted from PictView-Flow.js to keep the view focused on\n * coordination and lifecycle rather than data manipulation.\n */\nclass PictServiceFlowDataManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowDataManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t// ---- Marshaling ----\n\n\t/**\n\t * Marshal data from AppData into the flow view\n\t */\n\tmarshalToView()\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tif (this._FlowView.options.FlowDataAddress)\n\t\t{\n\t\t\tconst tmpAddressSpace =\n\t\t\t{\n\t\t\t\tFable: this._FlowView.fable,\n\t\t\t\tPict: this._FlowView.pict || this._FlowView.fable,\n\t\t\t\tAppData: this._FlowView.pict ? this._FlowView.pict.AppData : this._FlowView.fable.AppData,\n\t\t\t\tBundle: this._FlowView.Bundle,\n\t\t\t\tOptions: this._FlowView.options\n\t\t\t};\n\t\t\tlet tmpData = this._FlowView.fable.manifest.getValueByHash(tmpAddressSpace, this._FlowView.options.FlowDataAddress);\n\t\t\tif (typeof tmpData === 'object' && tmpData !== null)\n\t\t\t{\n\t\t\t\tthis.setFlowData(tmpData);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Marshal data from the flow view back to AppData\n\t */\n\tmarshalFromView()\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tif (this._FlowView.options.FlowDataAddress)\n\t\t{\n\t\t\tconst tmpAddressSpace =\n\t\t\t{\n\t\t\t\tFable: this._FlowView.fable,\n\t\t\t\tPict: this._FlowView.pict || this._FlowView.fable,\n\t\t\t\tAppData: this._FlowView.pict ? this._FlowView.pict.AppData : this._FlowView.fable.AppData,\n\t\t\t\tBundle: this._FlowView.Bundle,\n\t\t\t\tOptions: this._FlowView.options\n\t\t\t};\n\t\t\tthis._FlowView.fable.manifest.setValueByHash(tmpAddressSpace, this._FlowView.options.FlowDataAddress, JSON.parse(JSON.stringify(this._FlowView._FlowData)));\n\t\t}\n\t}\n\n\t// ---- Flow Data Get/Set ----\n\n\t/**\n\t * Get the complete flow data object\n\t * @returns {Object} The flow data including nodes, connections, and view state\n\t */\n\tgetFlowData()\n\t{\n\t\tif (!this._FlowView) return {};\n\t\treturn JSON.parse(JSON.stringify(this._FlowView._FlowData));\n\t}\n\n\t/**\n\t * Set the complete flow data object and re-render\n\t * @param {Object} pFlowData - The flow data to set\n\t */\n\tsetFlowData(pFlowData)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tif (typeof pFlowData !== 'object' || pFlowData === null)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow setFlowData received invalid data');\n\t\t\treturn;\n\t\t}\n\n\t\tthis._FlowView._FlowData = {\n\t\t\tNodes: Array.isArray(pFlowData.Nodes) ? pFlowData.Nodes : [],\n\t\t\tConnections: Array.isArray(pFlowData.Connections) ? pFlowData.Connections : [],\n\t\t\tOpenPanels: Array.isArray(pFlowData.OpenPanels) ? pFlowData.OpenPanels : [],\n\t\t\tSavedLayouts: Array.isArray(pFlowData.SavedLayouts) ? pFlowData.SavedLayouts : [],\n\t\t\tViewState: Object.assign(\n\t\t\t\t{ PanX: 0, PanY: 0, Zoom: 1, SelectedNodeHash: null, SelectedConnectionHash: null, SelectedTetherHash: null },\n\t\t\t\tpFlowData.ViewState || {}\n\t\t\t)\n\t\t};\n\n\t\t// Merge any browser-persisted layouts into the newly loaded data\n\t\tif (this._FlowView._LayoutProvider)\n\t\t{\n\t\t\tthis._FlowView._LayoutProvider.loadPersistedLayouts();\n\t\t}\n\n\t\tif (this._FlowView.initialRenderComplete)\n\t\t{\n\t\t\tthis._FlowView.renderFlow();\n\t\t}\n\t}\n\n\t// ---- Node CRUD ----\n\n\t/**\n\t * Add a new node to the flow\n\t * @param {string} pType - The node type hash\n\t * @param {number} pX - X position\n\t * @param {number} pY - Y position\n\t * @param {string} [pTitle] - Optional title\n\t * @param {Object} [pData] - Optional additional data\n\t * @returns {Object} The created node\n\t */\n\taddNode(pType, pX, pY, pTitle, pData)\n\t{\n\t\tif (!this._FlowView) return null;\n\n\t\tlet tmpType = pType || this._FlowView.options.DefaultNodeType;\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpType);\n\n\t\tlet tmpNodeHash = `node-${this._FlowView.fable.getUUID()}`;\n\t\tlet tmpNode =\n\t\t{\n\t\t\tHash: tmpNodeHash,\n\t\t\tType: tmpType,\n\t\t\tX: pX || 100,\n\t\t\tY: pY || 100,\n\t\t\tWidth: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultWidth) || this._FlowView.options.DefaultNodeWidth,\n\t\t\tHeight: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultHeight) || this._FlowView.options.DefaultNodeHeight,\n\t\t\tTitle: pTitle || (tmpNodeTypeConfig && tmpNodeTypeConfig.Label) || 'New Node',\n\t\t\tPorts: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultPorts)\n\t\t\t\t? JSON.parse(JSON.stringify(tmpNodeTypeConfig.DefaultPorts))\n\t\t\t\t: [\n\t\t\t\t\t{ Hash: `port-in-${this._FlowView.fable.getUUID()}`, Direction: 'input', Side: 'left', Label: 'In' },\n\t\t\t\t\t{ Hash: `port-out-${this._FlowView.fable.getUUID()}`, Direction: 'output', Side: 'right', Label: 'Out' }\n\t\t\t\t],\n\t\t\tData: pData || {}\n\t\t};\n\n\t\t// Ensure each port has a unique hash\n\t\tfor (let i = 0; i < tmpNode.Ports.length; i++)\n\t\t{\n\t\t\tif (!tmpNode.Ports[i].Hash)\n\t\t\t{\n\t\t\t\ttmpNode.Ports[i].Hash = `port-${tmpNode.Ports[i].Direction}-${this._FlowView.fable.getUUID()}`;\n\t\t\t}\n\t\t}\n\n\t\tthis._FlowView._FlowData.Nodes.push(tmpNode);\n\t\tthis._FlowView.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onNodeAdded', tmpNode);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn tmpNode;\n\t}\n\n\t/**\n\t * Remove a node and all its connections\n\t * @param {string} pNodeHash - The hash of the node to remove\n\t * @returns {boolean} Whether the node was removed\n\t */\n\tremoveNode(pNodeHash)\n\t{\n\t\tif (!this._FlowView) return false;\n\n\t\tlet tmpNodeIndex = this._FlowView._FlowData.Nodes.findIndex((pNode) => pNode.Hash === pNodeHash);\n\t\tif (tmpNodeIndex < 0)\n\t\t{\n\t\t\tthis._FlowView.log.warn(`PictSectionFlow removeNode: node ${pNodeHash} not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpRemovedNode = this._FlowView._FlowData.Nodes.splice(tmpNodeIndex, 1)[0];\n\n\t\t// Remove all connections involving this node\n\t\tthis._FlowView._FlowData.Connections = this._FlowView._FlowData.Connections.filter((pConnection) =>\n\t\t{\n\t\t\treturn pConnection.SourceNodeHash !== pNodeHash && pConnection.TargetNodeHash !== pNodeHash;\n\t\t});\n\n\t\t// Close any open panels for this node\n\t\tthis._FlowView.closePanelForNode(pNodeHash);\n\n\t\t// Clear selection if this node was selected\n\t\tif (this._FlowView._FlowData.ViewState.SelectedNodeHash === pNodeHash)\n\t\t{\n\t\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = null;\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onNodeRemoved', tmpRemovedNode);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t// ---- Connection CRUD ----\n\n\t/**\n\t * Add a connection between two ports\n\t * @param {string} pSourceNodeHash\n\t * @param {string} pSourcePortHash\n\t * @param {string} pTargetNodeHash\n\t * @param {string} pTargetPortHash\n\t * @param {Object} [pData] - Optional additional data\n\t * @returns {Object|false} The created connection, or false if invalid\n\t */\n\taddConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData)\n\t{\n\t\tif (!this._FlowView) return false;\n\n\t\t// Validate that both nodes and ports exist\n\t\tlet tmpSourceNode = this._FlowView._FlowData.Nodes.find((pNode) => pNode.Hash === pSourceNodeHash);\n\t\tlet tmpTargetNode = this._FlowView._FlowData.Nodes.find((pNode) => pNode.Hash === pTargetNodeHash);\n\n\t\tif (!tmpSourceNode || !tmpTargetNode)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow addConnection: source or target node not found');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpSourcePort = tmpSourceNode.Ports.find((pPort) => pPort.Hash === pSourcePortHash);\n\t\tlet tmpTargetPort = tmpTargetNode.Ports.find((pPort) => pPort.Hash === pTargetPortHash);\n\n\t\tif (!tmpSourcePort || !tmpTargetPort)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow addConnection: source or target port not found');\n\t\t\treturn false;\n\t\t}\n\n\t\t// Prevent self-connections\n\t\tif (pSourceNodeHash === pTargetNodeHash)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow addConnection: cannot connect a node to itself');\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check for duplicate connections\n\t\tlet tmpDuplicate = this._FlowView._FlowData.Connections.find((pConn) =>\n\t\t{\n\t\t\treturn pConn.SourceNodeHash === pSourceNodeHash\n\t\t\t\t&& pConn.SourcePortHash === pSourcePortHash\n\t\t\t\t&& pConn.TargetNodeHash === pTargetNodeHash\n\t\t\t\t&& pConn.TargetPortHash === pTargetPortHash;\n\t\t});\n\t\tif (tmpDuplicate)\n\t\t{\n\t\t\tthis._FlowView.log.warn('PictSectionFlow addConnection: duplicate connection');\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpConnection =\n\t\t{\n\t\t\tHash: `conn-${this._FlowView.fable.getUUID()}`,\n\t\t\tSourceNodeHash: pSourceNodeHash,\n\t\t\tSourcePortHash: pSourcePortHash,\n\t\t\tTargetNodeHash: pTargetNodeHash,\n\t\t\tTargetPortHash: pTargetPortHash,\n\t\t\tData: pData || {}\n\t\t};\n\n\t\tthis._FlowView._FlowData.Connections.push(tmpConnection);\n\t\tthis._FlowView.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionCreated', tmpConnection);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn tmpConnection;\n\t}\n\n\t/**\n\t * Remove a connection\n\t * @param {string} pConnectionHash - The hash of the connection to remove\n\t * @returns {boolean} Whether the connection was removed\n\t */\n\tremoveConnection(pConnectionHash)\n\t{\n\t\tif (!this._FlowView) return false;\n\n\t\tlet tmpConnectionIndex = this._FlowView._FlowData.Connections.findIndex((pConn) => pConn.Hash === pConnectionHash);\n\t\tif (tmpConnectionIndex < 0)\n\t\t{\n\t\t\tthis._FlowView.log.warn(`PictSectionFlow removeConnection: connection ${pConnectionHash} not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpRemovedConnection = this._FlowView._FlowData.Connections.splice(tmpConnectionIndex, 1)[0];\n\n\t\tif (this._FlowView._FlowData.ViewState.SelectedConnectionHash === pConnectionHash)\n\t\t{\n\t\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = null;\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionRemoved', tmpRemovedConnection);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn true;\n\t}\n}\n\nmodule.exports = PictServiceFlowDataManager;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * Interaction states for the flow diagram\n */\nconst INTERACTION_STATES =\n{\n\tIDLE: 'idle',\n\tDRAGGING_NODE: 'dragging-node',\n\tDRAGGING_PANEL: 'dragging-panel',\n\tDRAGGING_HANDLE: 'dragging-handle',\n\tCONNECTING: 'connecting',\n\tPANNING: 'panning',\n\tRESIZING_PANEL: 'resizing-panel'\n};\n\nclass PictServiceFlowInteractionManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowInteractionManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\tthis._SVGElement = null;\n\t\tthis._ViewportElement = null;\n\n\t\t// Interaction state\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\n\t\t// Drag state\n\t\tthis._DragNodeHash = null;\n\t\tthis._DragStartX = 0;\n\t\tthis._DragStartY = 0;\n\t\tthis._DragNodeStartX = 0;\n\t\tthis._DragNodeStartY = 0;\n\n\t\t// Panel drag state\n\t\tthis._DragPanelHash = null;\n\t\tthis._DragPanelStartX = 0;\n\t\tthis._DragPanelStartY = 0;\n\t\tthis._DragPanelDataStartX = 0;\n\t\tthis._DragPanelDataStartY = 0;\n\n\t\t// Handle drag state\n\t\tthis._DragHandleConnectionHash = null;\n\t\tthis._DragHandlePanelHash = null;\n\t\tthis._DragHandleType = null;\n\t\tthis._DragHandleIsTether = false;\n\n\t\t// Pan state\n\t\tthis._PanStartX = 0;\n\t\tthis._PanStartY = 0;\n\t\tthis._PanStartPanX = 0;\n\t\tthis._PanStartPanY = 0;\n\n\t\t// Connection drag state\n\t\tthis._ConnectSourceNodeHash = null;\n\t\tthis._ConnectSourcePortHash = null;\n\t\tthis._ConnectDragLine = null;\n\n\t\t// Double-click detection\n\t\tthis._LastClickTime = 0;\n\t\tthis._LastClickNodeHash = null;\n\t\tthis._DoubleClickThreshold = 400;\n\n\t\t// Panel resize state\n\t\tthis._ResizePanelHash = null;\n\t\tthis._ResizeStartY = 0;\n\t\tthis._ResizePanelStartHeight = 0;\n\n\t\t// Double-click detection for connections\n\t\tthis._LastConnectionClickTime = 0;\n\t\tthis._LastConnectionClickHash = null;\n\n\t\t// Double-click detection for tethers\n\t\tthis._LastTetherClickTime = 0;\n\t\tthis._LastTetherClickHash = null;\n\n\t\t// Double-click detection for handles\n\t\tthis._LastHandleClickTime = 0;\n\t\tthis._LastHandleClickHash = null;\n\t\tthis._LastHandleClickType = null;\n\n\t\t// Bound event handlers (for removeEventListener)\n\t\tthis._boundOnPointerDown = this._onPointerDown.bind(this);\n\t\tthis._boundOnPointerMove = this._onPointerMove.bind(this);\n\t\tthis._boundOnPointerUp = this._onPointerUp.bind(this);\n\t\tthis._boundOnWheel = this._onWheel.bind(this);\n\t\tthis._boundOnKeyDown = this._onKeyDown.bind(this);\n\t}\n\n\t/**\n\t * Initialize event listeners on the SVG element\n\t * @param {SVGSVGElement} pSVGElement\n\t * @param {SVGGElement} pViewportElement\n\t */\n\tinitialize(pSVGElement, pViewportElement)\n\t{\n\t\tthis._SVGElement = pSVGElement;\n\t\tthis._ViewportElement = pViewportElement;\n\n\t\tif (!this._SVGElement) return;\n\n\t\t// Use pointer events for unified mouse/touch handling\n\t\tthis._SVGElement.addEventListener('pointerdown', this._boundOnPointerDown);\n\t\tthis._SVGElement.addEventListener('pointermove', this._boundOnPointerMove);\n\t\tthis._SVGElement.addEventListener('pointerup', this._boundOnPointerUp);\n\t\tthis._SVGElement.addEventListener('pointerleave', this._boundOnPointerUp);\n\t\tthis._SVGElement.addEventListener('wheel', this._boundOnWheel, { passive: false });\n\n\t\t// Keyboard events for delete\n\t\tdocument.addEventListener('keydown', this._boundOnKeyDown);\n\n\t\t// Handle right-click: add/remove bezier handles on connections\n\t\tthis._SVGElement.addEventListener('contextmenu', (pEvent) =>\n\t\t{\n\t\t\tpEvent.preventDefault();\n\n\t\t\tlet tmpTarget = pEvent.target;\n\t\t\tlet tmpElementType = this._getElementType(tmpTarget);\n\n\t\t\tswitch (tmpElementType)\n\t\t\t{\n\t\t\t\tcase 'connection':\n\t\t\t\tcase 'connection-hitarea':\n\t\t\t\t\tthis._addBezierHandle(tmpTarget, pEvent);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'connection-handle':\n\t\t\t\t\tthis._removeBezierHandle(tmpTarget);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'tether':\n\t\t\t\tcase 'tether-hitarea':\n\t\t\t\t\tthis._addTetherBezierHandle(tmpTarget, pEvent);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'tether-handle':\n\t\t\t\t\tthis._removeTetherBezierHandle(tmpTarget);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Remove all event listeners\n\t */\n\tdestroy()\n\t{\n\t\tif (this._SVGElement)\n\t\t{\n\t\t\tthis._SVGElement.removeEventListener('pointerdown', this._boundOnPointerDown);\n\t\t\tthis._SVGElement.removeEventListener('pointermove', this._boundOnPointerMove);\n\t\t\tthis._SVGElement.removeEventListener('pointerup', this._boundOnPointerUp);\n\t\t\tthis._SVGElement.removeEventListener('pointerleave', this._boundOnPointerUp);\n\t\t\tthis._SVGElement.removeEventListener('wheel', this._boundOnWheel);\n\t\t}\n\n\t\tdocument.removeEventListener('keydown', this._boundOnKeyDown);\n\t}\n\n\t/**\n\t * Handle pointer down event\n\t * @param {PointerEvent} pEvent\n\t */\n\t_onPointerDown(pEvent)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpTarget = pEvent.target;\n\t\tlet tmpElementType = this._getElementType(tmpTarget);\n\n\t\t// Check if click is inside a panel content area — let HTML handle its own events\n\t\tif (tmpTarget.closest && tmpTarget.closest('.pict-flow-panel-content'))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t// Capture pointer for smooth dragging (left-click only — right-click\n\t\t// needs the real target for contextmenu handle add/remove)\n\t\tif (pEvent.button === 0)\n\t\t{\n\t\t\tthis._SVGElement.setPointerCapture(pEvent.pointerId);\n\t\t}\n\n\t\tswitch (tmpElementType)\n\t\t{\n\t\t\tcase 'port':\n\t\t\t\tthis._startConnection(pEvent, tmpTarget);\n\t\t\t\tbreak;\n\n\t\t\tcase 'node':\n\t\t\tcase 'node-body':\n\t\t\tcase 'panel-indicator':\n\t\t\t{\n\t\t\t\tlet tmpNodeHash = this._getNodeHash(tmpTarget);\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on same node\n\t\t\t\tif (tmpNodeHash && tmpNodeHash === this._LastClickNodeHash\n\t\t\t\t\t&& (tmpNow - this._LastClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\t// Double-click: toggle panel\n\t\t\t\t\tthis._LastClickTime = 0;\n\t\t\t\t\tthis._LastClickNodeHash = null;\n\t\t\t\t\tthis._FlowView.togglePanel(tmpNodeHash);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Single click: start node drag\n\t\t\t\t\tthis._LastClickTime = tmpNow;\n\t\t\t\t\tthis._LastClickNodeHash = tmpNodeHash;\n\t\t\t\t\tthis._startNodeDrag(pEvent, tmpTarget);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'panel-titlebar':\n\t\t\t\tthis._startPanelDrag(pEvent, tmpTarget);\n\t\t\t\tbreak;\n\n\t\t\tcase 'panel-resize':\n\t\t\t\tthis._startPanelResize(pEvent, tmpTarget);\n\t\t\t\tbreak;\n\n\t\t\tcase 'panel-close':\n\t\t\t{\n\t\t\t\tlet tmpPanelHash = this._getPanelHash(tmpTarget);\n\t\t\t\tif (tmpPanelHash)\n\t\t\t\t{\n\t\t\t\t\tthis._FlowView.closePanel(tmpPanelHash);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'connection-handle':\n\t\t\t{\n\t\t\t\tlet tmpConnectionHash = this._getConnectionHash(tmpTarget);\n\t\t\t\tlet tmpHandleType = tmpTarget.getAttribute('data-handle-type');\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on handle to toggle mode\n\t\t\t\tif (tmpConnectionHash === this._LastHandleClickHash\n\t\t\t\t\t&& tmpHandleType === this._LastHandleClickType\n\t\t\t\t\t&& (tmpNow - this._LastHandleClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\tthis._toggleConnectionLineMode(tmpConnectionHash);\n\t\t\t\t\tthis._LastHandleClickTime = 0;\n\t\t\t\t\tthis._LastHandleClickHash = null;\n\t\t\t\t\tthis._LastHandleClickType = null;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._LastHandleClickTime = tmpNow;\n\t\t\t\t\tthis._LastHandleClickHash = tmpConnectionHash;\n\t\t\t\t\tthis._LastHandleClickType = tmpHandleType;\n\t\t\t\t\tthis._startHandleDrag(pEvent, tmpConnectionHash, null, tmpHandleType, false);\n\t\t\t\t}\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'tether':\n\t\t\tcase 'tether-hitarea':\n\t\t\t{\n\t\t\t\tlet tmpPanelHash = this._getPanelHash(tmpTarget);\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on same tether to add a handle\n\t\t\t\tif (tmpPanelHash && tmpPanelHash === this._LastTetherClickHash\n\t\t\t\t\t&& (tmpNow - this._LastTetherClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\tthis._LastTetherClickTime = 0;\n\t\t\t\t\tthis._LastTetherClickHash = null;\n\t\t\t\t\tthis._addTetherBezierHandle(tmpTarget, pEvent);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._LastTetherClickTime = tmpNow;\n\t\t\t\t\tthis._LastTetherClickHash = tmpPanelHash;\n\t\t\t\t\tthis._selectTether(tmpTarget);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'tether-handle':\n\t\t\t{\n\t\t\t\tlet tmpPanelHash = this._getPanelHash(tmpTarget);\n\t\t\t\tlet tmpHandleType = tmpTarget.getAttribute('data-handle-type');\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on tether handle to toggle mode\n\t\t\t\tif (tmpPanelHash === this._LastHandleClickHash\n\t\t\t\t\t&& tmpHandleType === this._LastHandleClickType\n\t\t\t\t\t&& (tmpNow - this._LastHandleClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\tthis._toggleTetherLineMode(tmpPanelHash);\n\t\t\t\t\tthis._LastHandleClickTime = 0;\n\t\t\t\t\tthis._LastHandleClickHash = null;\n\t\t\t\t\tthis._LastHandleClickType = null;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._LastHandleClickTime = tmpNow;\n\t\t\t\t\tthis._LastHandleClickHash = tmpPanelHash;\n\t\t\t\t\tthis._LastHandleClickType = tmpHandleType;\n\t\t\t\t\tthis._startHandleDrag(pEvent, null, tmpPanelHash, tmpHandleType, true);\n\t\t\t\t}\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'connection':\n\t\t\tcase 'connection-hitarea':\n\t\t\t{\n\t\t\t\tlet tmpConnectionHash = this._getConnectionHash(tmpTarget);\n\t\t\t\tlet tmpNow = Date.now();\n\n\t\t\t\t// Check for double-click on same connection to add a handle\n\t\t\t\tif (tmpConnectionHash && tmpConnectionHash === this._LastConnectionClickHash\n\t\t\t\t\t&& (tmpNow - this._LastConnectionClickTime) < this._DoubleClickThreshold)\n\t\t\t\t{\n\t\t\t\t\tthis._LastConnectionClickTime = 0;\n\t\t\t\t\tthis._LastConnectionClickHash = null;\n\t\t\t\t\tthis._addBezierHandle(tmpTarget, pEvent);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._LastConnectionClickTime = tmpNow;\n\t\t\t\t\tthis._LastConnectionClickHash = tmpConnectionHash;\n\t\t\t\t\tthis._selectConnection(tmpTarget);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\t// Click on background - start panning or deselect\n\t\t\t\tif (pEvent.button === 0 && this._FlowView.options.EnablePanning)\n\t\t\t\t{\n\t\t\t\t\tthis._startPanning(pEvent);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Handle pointer move event\n\t * @param {PointerEvent} pEvent\n\t */\n\t_onPointerMove(pEvent)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tswitch (this._State)\n\t\t{\n\t\t\tcase INTERACTION_STATES.DRAGGING_NODE:\n\t\t\t\tthis._onNodeDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.DRAGGING_PANEL:\n\t\t\t\tthis._onPanelDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.DRAGGING_HANDLE:\n\t\t\t\tthis._onHandleDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.CONNECTING:\n\t\t\t\tthis._onConnectionDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.RESIZING_PANEL:\n\t\t\t\tthis._onPanelResize(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.PANNING:\n\t\t\t\tthis._onPan(pEvent);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Handle pointer up event\n\t * @param {PointerEvent} pEvent\n\t */\n\t_onPointerUp(pEvent)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\t// Release pointer capture\n\t\tif (this._SVGElement.hasPointerCapture && this._SVGElement.hasPointerCapture(pEvent.pointerId))\n\t\t{\n\t\t\tthis._SVGElement.releasePointerCapture(pEvent.pointerId);\n\t\t}\n\n\t\tswitch (this._State)\n\t\t{\n\t\t\tcase INTERACTION_STATES.DRAGGING_NODE:\n\t\t\t\tthis._endNodeDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.DRAGGING_PANEL:\n\t\t\t\tthis._endPanelDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.DRAGGING_HANDLE:\n\t\t\t\tthis._endHandleDrag(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.RESIZING_PANEL:\n\t\t\t\tthis._endPanelResize(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.CONNECTING:\n\t\t\t\tthis._endConnection(pEvent);\n\t\t\t\tbreak;\n\n\t\t\tcase INTERACTION_STATES.PANNING:\n\t\t\t\tthis._endPanning(pEvent);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Handle mouse wheel for zoom\n\t * @param {WheelEvent} pEvent\n\t */\n\t_onWheel(pEvent)\n\t{\n\t\tif (!this._FlowView || !this._FlowView.options.EnableZooming) return;\n\n\t\tpEvent.preventDefault();\n\n\t\tlet tmpDelta = pEvent.deltaY > 0 ? -this._FlowView.options.ZoomStep : this._FlowView.options.ZoomStep;\n\t\tlet tmpNewZoom = this._FlowView.viewState.Zoom + tmpDelta;\n\n\t\t// Zoom toward mouse position\n\t\tlet tmpRect = this._SVGElement.getBoundingClientRect();\n\t\tlet tmpMouseX = pEvent.clientX - tmpRect.left;\n\t\tlet tmpMouseY = pEvent.clientY - tmpRect.top;\n\n\t\tthis._FlowView.setZoom(tmpNewZoom, tmpMouseX, tmpMouseY);\n\t}\n\n\t/**\n\t * Handle keyboard events\n\t * @param {KeyboardEvent} pEvent\n\t */\n\t_onKeyDown(pEvent)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\t// Only handle events when the flow is focused/visible\n\t\tif (pEvent.key === 'Delete' || pEvent.key === 'Backspace')\n\t\t{\n\t\t\t// Don't delete if user is typing in an input or inside a panel\n\t\t\tif (pEvent.target && (pEvent.target.tagName === 'INPUT' || pEvent.target.tagName === 'TEXTAREA' || pEvent.target.tagName === 'SELECT'))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pEvent.target && pEvent.target.closest && pEvent.target.closest('.pict-flow-panel'))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis._FlowView.deleteSelected();\n\t\t\tpEvent.preventDefault();\n\t\t}\n\t\telse if (pEvent.key === 'Escape')\n\t\t{\n\t\t\tif (this._State === INTERACTION_STATES.CONNECTING)\n\t\t\t{\n\t\t\t\tthis._cancelConnection();\n\t\t\t}\n\n\t\t\t// Exit fullscreen if currently in fullscreen mode\n\t\t\tif (this._FlowView._IsFullscreen)\n\t\t\t{\n\t\t\t\tthis._FlowView.exitFullscreen();\n\t\t\t\t// Update the toolbar button text\n\t\t\t\tif (this._FlowView._ToolbarView)\n\t\t\t\t{\n\t\t\t\t\tlet tmpFlowViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\t\t\t\tlet tmpBtnElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-${tmpFlowViewIdentifier}`);\n\t\t\t\t\tif (tmpBtnElements.length > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBtnElements[0].innerHTML = '&#x26F6; Fullscreen';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpEvent.preventDefault();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis._FlowView.deselectAll();\n\t\t}\n\t}\n\n\t// ---- Node Dragging ----\n\n\t_startNodeDrag(pEvent, pTarget)\n\t{\n\t\tif (!this._FlowView.options.EnableNodeDragging) return;\n\n\t\tlet tmpNodeHash = this._getNodeHash(pTarget);\n\t\tif (!tmpNodeHash) return;\n\n\t\tthis._FlowView.selectNode(tmpNodeHash);\n\n\t\tlet tmpNode = this._FlowView.getNode(tmpNodeHash);\n\t\tif (!tmpNode) return;\n\n\t\tthis._State = INTERACTION_STATES.DRAGGING_NODE;\n\t\tthis._DragNodeHash = tmpNodeHash;\n\t\tthis._DragStartX = pEvent.clientX;\n\t\tthis._DragStartY = pEvent.clientY;\n\t\tthis._DragNodeStartX = tmpNode.X;\n\t\tthis._DragNodeStartY = tmpNode.Y;\n\n\t\tthis._SVGElement.classList.add('panning');\n\n\t\tlet tmpNodeGroup = this._FlowView._NodesLayer.querySelector(`[data-node-hash=\"${tmpNodeHash}\"]`);\n\t\tif (tmpNodeGroup)\n\t\t{\n\t\t\ttmpNodeGroup.classList.add('dragging');\n\t\t}\n\t}\n\n\t_onNodeDrag(pEvent)\n\t{\n\t\tif (!this._DragNodeHash) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\t\tlet tmpDX = (pEvent.clientX - this._DragStartX) / tmpVS.Zoom;\n\t\tlet tmpDY = (pEvent.clientY - this._DragStartY) / tmpVS.Zoom;\n\n\t\tlet tmpNewX = this._DragNodeStartX + tmpDX;\n\t\tlet tmpNewY = this._DragNodeStartY + tmpDY;\n\n\t\tthis._FlowView.updateNodePosition(this._DragNodeHash, tmpNewX, tmpNewY);\n\t}\n\n\t_endNodeDrag(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\n\t\tlet tmpNodeGroup = this._FlowView._NodesLayer.querySelector(`[data-node-hash=\"${this._DragNodeHash}\"]`);\n\t\tif (tmpNodeGroup)\n\t\t{\n\t\t\ttmpNodeGroup.classList.remove('dragging');\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tlet tmpNode = this._FlowView.getNode(this._DragNodeHash);\n\t\tif (tmpNode && this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onNodeMoved', tmpNode);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._DragNodeHash = null;\n\t}\n\n\t// ---- Panel Dragging ----\n\n\t_startPanelDrag(pEvent, pTarget)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (!tmpPanelHash) return;\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === tmpPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tthis._State = INTERACTION_STATES.DRAGGING_PANEL;\n\t\tthis._DragPanelHash = tmpPanelHash;\n\t\tthis._DragPanelStartX = pEvent.clientX;\n\t\tthis._DragPanelStartY = pEvent.clientY;\n\t\tthis._DragPanelDataStartX = tmpPanel.X;\n\t\tthis._DragPanelDataStartY = tmpPanel.Y;\n\n\t\tthis._SVGElement.classList.add('panning');\n\t}\n\n\t_onPanelDrag(pEvent)\n\t{\n\t\tif (!this._DragPanelHash) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\t\tlet tmpDX = (pEvent.clientX - this._DragPanelStartX) / tmpVS.Zoom;\n\t\tlet tmpDY = (pEvent.clientY - this._DragPanelStartY) / tmpVS.Zoom;\n\n\t\tlet tmpNewX = this._DragPanelDataStartX + tmpDX;\n\t\tlet tmpNewY = this._DragPanelDataStartY + tmpDY;\n\n\t\tthis._FlowView.updatePanelPosition(this._DragPanelHash, tmpNewX, tmpNewY);\n\t}\n\n\t_endPanelDrag(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\n\t\tthis._FlowView.marshalFromView();\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === this._DragPanelHash);\n\t\tif (tmpPanel && this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onPanelMoved', tmpPanel);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._DragPanelHash = null;\n\t}\n\n\t// ---- Panel Resizing ----\n\n\t_startPanelResize(pEvent, pTarget)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (!tmpPanelHash) return;\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === tmpPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tthis._State = INTERACTION_STATES.RESIZING_PANEL;\n\t\tthis._ResizePanelHash = tmpPanelHash;\n\t\tthis._ResizeStartY = pEvent.clientY;\n\t\tthis._ResizePanelStartHeight = tmpPanel.Height;\n\n\t\tthis._SVGElement.classList.add('panning');\n\t}\n\n\t_onPanelResize(pEvent)\n\t{\n\t\tif (!this._ResizePanelHash) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\t\tlet tmpDY = (pEvent.clientY - this._ResizeStartY) / tmpVS.Zoom;\n\t\tlet tmpNewHeight = Math.max(120, this._ResizePanelStartHeight + tmpDY);\n\n\t\t// Update the panel data\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === this._ResizePanelHash);\n\t\tif (tmpPanel)\n\t\t{\n\t\t\ttmpPanel.Height = tmpNewHeight;\n\t\t}\n\n\t\t// Update the foreignObject height directly for smooth resizing\n\t\tif (this._FlowView._PanelsLayer)\n\t\t{\n\t\t\tlet tmpFO = this._FlowView._PanelsLayer.querySelector('[data-panel-hash=\"' + this._ResizePanelHash + '\"]');\n\t\t\tif (tmpFO)\n\t\t\t{\n\t\t\t\ttmpFO.setAttribute('height', String(tmpNewHeight));\n\t\t\t}\n\t\t}\n\t}\n\n\t_endPanelResize(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\n\t\t// Re-render to sync tethers\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._ResizePanelHash = null;\n\t}\n\n\t// ---- Handle Dragging ----\n\n\t_startHandleDrag(pEvent, pConnectionHash, pPanelHash, pHandleType, pIsTether)\n\t{\n\t\tthis._State = INTERACTION_STATES.DRAGGING_HANDLE;\n\t\tthis._DragHandleConnectionHash = pConnectionHash;\n\t\tthis._DragHandlePanelHash = pPanelHash;\n\t\tthis._DragHandleType = pHandleType;\n\t\tthis._DragHandleIsTether = pIsTether;\n\t\tthis._DragStartX = pEvent.clientX;\n\t\tthis._DragStartY = pEvent.clientY;\n\t\tthis._SVGElement.classList.add('panning');\n\t}\n\n\t_onHandleDrag(pEvent)\n\t{\n\t\tlet tmpCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);\n\n\t\tif (this._DragHandleIsTether)\n\t\t{\n\t\t\tthis._FlowView.updateTetherHandle(\n\t\t\t\tthis._DragHandlePanelHash,\n\t\t\t\tthis._DragHandleType,\n\t\t\t\ttmpCoords.x,\n\t\t\t\ttmpCoords.y\n\t\t\t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._FlowView.updateConnectionHandle(\n\t\t\t\tthis._DragHandleConnectionHash,\n\t\t\t\tthis._DragHandleType,\n\t\t\t\ttmpCoords.x,\n\t\t\t\ttmpCoords.y\n\t\t\t);\n\t\t}\n\t}\n\n\t_endHandleDrag(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\n\t\t\tif (this._DragHandleIsTether)\n\t\t\t{\n\t\t\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === this._DragHandlePanelHash);\n\t\t\t\tif (tmpPanel)\n\t\t\t\t{\n\t\t\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onTetherHandleMoved', tmpPanel);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlet tmpConnection = this._FlowView.getConnection(this._DragHandleConnectionHash);\n\t\t\t\tif (tmpConnection)\n\t\t\t\t{\n\t\t\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionHandleMoved', tmpConnection);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._DragHandleConnectionHash = null;\n\t\tthis._DragHandlePanelHash = null;\n\t\tthis._DragHandleType = null;\n\t\tthis._DragHandleIsTether = false;\n\t}\n\n\t// ---- Right-Click Handle Add/Remove ----\n\n\t/**\n\t * Add a bezier handle to a connection at the right-click position.\n\t * @param {Element} pTarget - The SVG element that was right-clicked\n\t * @param {MouseEvent} pEvent - The contextmenu event\n\t */\n\t_addBezierHandle(pTarget, pEvent)\n\t{\n\t\tlet tmpConnectionHash = this._getConnectionHash(pTarget);\n\t\tif (!tmpConnectionHash) return;\n\n\t\t// Select the connection so handle circles are rendered after the re-render\n\t\tthis._FlowView.selectConnection(tmpConnectionHash);\n\n\t\tlet tmpCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);\n\t\tthis._FlowView.addConnectionHandle(tmpConnectionHash, tmpCoords.x, tmpCoords.y);\n\t}\n\n\t/**\n\t * Remove a bezier handle from a connection.\n\t * @param {Element} pTarget - The handle SVG element that was right-clicked\n\t */\n\t_removeBezierHandle(pTarget)\n\t{\n\t\tlet tmpConnectionHash = this._getConnectionHash(pTarget);\n\t\tif (!tmpConnectionHash) return;\n\n\t\tlet tmpHandleType = pTarget.getAttribute('data-handle-type');\n\t\tif (!tmpHandleType || !tmpHandleType.startsWith('bezier-handle-')) return;\n\n\t\tlet tmpIndex = parseInt(tmpHandleType.replace('bezier-handle-', ''), 10);\n\t\tif (isNaN(tmpIndex)) return;\n\n\t\tthis._FlowView.removeConnectionHandle(tmpConnectionHash, tmpIndex);\n\t}\n\n\t/**\n\t * Add a bezier handle to a tether.\n\t * @param {Element} pTarget - The tether SVG element that was right-clicked or double-clicked\n\t * @param {Event} pEvent - The mouse event (for coordinate extraction)\n\t */\n\t_addTetherBezierHandle(pTarget, pEvent)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (!tmpPanelHash) return;\n\n\t\t// Select the tether so handles render after re-render\n\t\tthis._FlowView.selectTether(tmpPanelHash);\n\n\t\tlet tmpCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);\n\t\tthis._FlowView.addTetherHandle(tmpPanelHash, tmpCoords.x, tmpCoords.y);\n\t}\n\n\t/**\n\t * Remove a bezier handle from a tether.\n\t * @param {Element} pTarget - The tether handle SVG element that was right-clicked\n\t */\n\t_removeTetherBezierHandle(pTarget)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (!tmpPanelHash) return;\n\n\t\tlet tmpHandleType = pTarget.getAttribute('data-handle-type');\n\t\tif (!tmpHandleType || !tmpHandleType.startsWith('bezier-handle-')) return;\n\n\t\tlet tmpIndex = parseInt(tmpHandleType.replace('bezier-handle-', ''), 10);\n\t\tif (isNaN(tmpIndex)) return;\n\n\t\tthis._FlowView.removeTetherHandle(tmpPanelHash, tmpIndex);\n\t}\n\n\t// ---- Line Mode Toggling ----\n\n\t_toggleConnectionLineMode(pConnectionHash)\n\t{\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection) return;\n\n\t\tif (!tmpConnection.Data) tmpConnection.Data = {};\n\n\t\tlet tmpCurrentMode = tmpConnection.Data.LineMode || 'bezier';\n\t\ttmpConnection.Data.LineMode = (tmpCurrentMode === 'bezier') ? 'orthogonal' : 'bezier';\n\n\t\t// Reset handle positions when switching modes\n\t\ttmpConnection.Data.HandleCustomized = false;\n\t\ttmpConnection.Data.BezierHandles = [];\n\t\ttmpConnection.Data.BezierHandleX = null;\n\t\ttmpConnection.Data.BezierHandleY = null;\n\t\ttmpConnection.Data.OrthoCorner1X = null;\n\t\ttmpConnection.Data.OrthoCorner1Y = null;\n\t\ttmpConnection.Data.OrthoCorner2X = null;\n\t\ttmpConnection.Data.OrthoCorner2Y = null;\n\t\ttmpConnection.Data.OrthoMidOffset = 0;\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionModeChanged', tmpConnection);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\t}\n\n\t_toggleTetherLineMode(pPanelHash)\n\t{\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tif (this._FlowView._TetherService)\n\t\t{\n\t\t\tthis._FlowView._TetherService.toggleLineMode(tmpPanel);\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onTetherModeChanged', tmpPanel);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);\n\t\t}\n\t}\n\n\t// ---- Tether Selection ----\n\n\t_selectTether(pTarget)\n\t{\n\t\tlet tmpPanelHash = this._getPanelHash(pTarget);\n\t\tif (tmpPanelHash)\n\t\t{\n\t\t\tthis._FlowView.selectTether(tmpPanelHash);\n\t\t}\n\t}\n\n\t// ---- Connection Creation ----\n\n\t_startConnection(pEvent, pTarget)\n\t{\n\t\tif (!this._FlowView.options.EnableConnectionCreation) return;\n\n\t\tlet tmpNodeHash = pTarget.getAttribute('data-node-hash');\n\t\tlet tmpPortHash = pTarget.getAttribute('data-port-hash');\n\t\tlet tmpPortDirection = pTarget.getAttribute('data-port-direction');\n\n\t\tif (!tmpNodeHash || !tmpPortHash) return;\n\n\t\tif (tmpPortDirection !== 'output')\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.CONNECTING;\n\t\tthis._ConnectSourceNodeHash = tmpNodeHash;\n\t\tthis._ConnectSourcePortHash = tmpPortHash;\n\n\t\tthis._SVGElement.classList.add('connecting');\n\n\t\tlet tmpPortPos = this._FlowView.getPortPosition(tmpNodeHash, tmpPortHash);\n\t\tif (tmpPortPos)\n\t\t{\n\t\t\tthis._ConnectDragLine = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n\t\t\tthis._ConnectDragLine.setAttribute('class', 'pict-flow-drag-connection');\n\t\t\tthis._ConnectDragLine.setAttribute('d', `M ${tmpPortPos.x} ${tmpPortPos.y} L ${tmpPortPos.x} ${tmpPortPos.y}`);\n\t\t\tthis._FlowView._ViewportElement.appendChild(this._ConnectDragLine);\n\t\t}\n\n\t\tpEvent.stopPropagation();\n\t}\n\n\t_onConnectionDrag(pEvent)\n\t{\n\t\tif (!this._ConnectDragLine) return;\n\n\t\tlet tmpSourcePos = this._FlowView.getPortPosition(this._ConnectSourceNodeHash, this._ConnectSourcePortHash);\n\t\tif (!tmpSourcePos) return;\n\n\t\tlet tmpEndCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);\n\n\t\tlet tmpDX = Math.abs(tmpEndCoords.x - tmpSourcePos.x) * 0.5;\n\t\tlet tmpPath = `M ${tmpSourcePos.x} ${tmpSourcePos.y} C ${tmpSourcePos.x + tmpDX} ${tmpSourcePos.y}, ${tmpEndCoords.x - tmpDX} ${tmpEndCoords.y}, ${tmpEndCoords.x} ${tmpEndCoords.y}`;\n\t\tthis._ConnectDragLine.setAttribute('d', tmpPath);\n\t}\n\n\t_endConnection(pEvent)\n\t{\n\t\tif (this._ConnectDragLine && this._ConnectDragLine.parentNode)\n\t\t{\n\t\t\tthis._ConnectDragLine.parentNode.removeChild(this._ConnectDragLine);\n\t\t}\n\t\tthis._ConnectDragLine = null;\n\n\t\tthis._SVGElement.classList.remove('connecting');\n\n\t\tlet tmpTarget = document.elementFromPoint(pEvent.clientX, pEvent.clientY);\n\t\tif (tmpTarget)\n\t\t{\n\t\t\tlet tmpTargetPortHash = tmpTarget.getAttribute('data-port-hash');\n\t\t\tlet tmpTargetNodeHash = tmpTarget.getAttribute('data-node-hash');\n\t\t\tlet tmpTargetPortDirection = tmpTarget.getAttribute('data-port-direction');\n\n\t\t\tif (tmpTargetPortHash && tmpTargetNodeHash && tmpTargetPortDirection === 'input')\n\t\t\t{\n\t\t\t\tthis._FlowView.addConnection(\n\t\t\t\t\tthis._ConnectSourceNodeHash,\n\t\t\t\t\tthis._ConnectSourcePortHash,\n\t\t\t\t\ttmpTargetNodeHash,\n\t\t\t\t\ttmpTargetPortHash\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._ConnectSourceNodeHash = null;\n\t\tthis._ConnectSourcePortHash = null;\n\t}\n\n\t_cancelConnection()\n\t{\n\t\tif (this._ConnectDragLine && this._ConnectDragLine.parentNode)\n\t\t{\n\t\t\tthis._ConnectDragLine.parentNode.removeChild(this._ConnectDragLine);\n\t\t}\n\t\tthis._ConnectDragLine = null;\n\n\t\tthis._SVGElement.classList.remove('connecting');\n\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t\tthis._ConnectSourceNodeHash = null;\n\t\tthis._ConnectSourcePortHash = null;\n\t}\n\n\t// ---- Panning ----\n\n\t_startPanning(pEvent)\n\t{\n\t\tthis._FlowView.deselectAll();\n\n\t\tthis._State = INTERACTION_STATES.PANNING;\n\t\tthis._PanStartX = pEvent.clientX;\n\t\tthis._PanStartY = pEvent.clientY;\n\t\tthis._PanStartPanX = this._FlowView.viewState.PanX;\n\t\tthis._PanStartPanY = this._FlowView.viewState.PanY;\n\n\t\tthis._SVGElement.classList.add('panning');\n\t}\n\n\t_onPan(pEvent)\n\t{\n\t\tlet tmpDX = pEvent.clientX - this._PanStartX;\n\t\tlet tmpDY = pEvent.clientY - this._PanStartY;\n\n\t\tthis._FlowView.viewState.PanX = this._PanStartPanX + tmpDX;\n\t\tthis._FlowView.viewState.PanY = this._PanStartPanY + tmpDY;\n\n\t\tthis._FlowView.updateViewportTransform();\n\t}\n\n\t_endPanning(pEvent)\n\t{\n\t\tthis._SVGElement.classList.remove('panning');\n\t\tthis._State = INTERACTION_STATES.IDLE;\n\t}\n\n\t// ---- Connection Selection ----\n\n\t_selectConnection(pTarget)\n\t{\n\t\tlet tmpConnectionHash = this._getConnectionHash(pTarget);\n\t\tif (tmpConnectionHash)\n\t\t{\n\t\t\tthis._FlowView.selectConnection(tmpConnectionHash);\n\t\t}\n\t}\n\n\t// ---- Utilities ----\n\n\t_getElementType(pTarget)\n\t{\n\t\tif (!pTarget) return 'background';\n\n\t\tlet tmpType = pTarget.getAttribute ? pTarget.getAttribute('data-element-type') : null;\n\t\tif (tmpType) return tmpType;\n\n\t\tlet tmpParent = pTarget.parentElement;\n\t\tlet tmpDepth = 0;\n\t\twhile (tmpParent && tmpDepth < 5)\n\t\t{\n\t\t\ttmpType = tmpParent.getAttribute ? tmpParent.getAttribute('data-element-type') : null;\n\t\t\tif (tmpType) return tmpType;\n\t\t\ttmpParent = tmpParent.parentElement;\n\t\t\ttmpDepth++;\n\t\t}\n\n\t\treturn 'background';\n\t}\n\n\t_getNodeHash(pTarget)\n\t{\n\t\tif (!pTarget) return null;\n\n\t\tlet tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-node-hash') : null;\n\t\tif (tmpHash) return tmpHash;\n\n\t\tlet tmpParent = pTarget.parentElement;\n\t\tlet tmpDepth = 0;\n\t\twhile (tmpParent && tmpDepth < 5)\n\t\t{\n\t\t\ttmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-node-hash') : null;\n\t\t\tif (tmpHash) return tmpHash;\n\t\t\ttmpParent = tmpParent.parentElement;\n\t\t\ttmpDepth++;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t_getPanelHash(pTarget)\n\t{\n\t\tif (!pTarget) return null;\n\n\t\tlet tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-panel-hash') : null;\n\t\tif (tmpHash) return tmpHash;\n\n\t\tlet tmpParent = pTarget.parentElement;\n\t\tlet tmpDepth = 0;\n\t\twhile (tmpParent && tmpDepth < 5)\n\t\t{\n\t\t\ttmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-panel-hash') : null;\n\t\t\tif (tmpHash) return tmpHash;\n\t\t\ttmpParent = tmpParent.parentElement;\n\t\t\ttmpDepth++;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t_getConnectionHash(pTarget)\n\t{\n\t\tif (!pTarget) return null;\n\n\t\tlet tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-connection-hash') : null;\n\t\tif (tmpHash) return tmpHash;\n\n\t\tlet tmpParent = pTarget.parentElement;\n\t\tlet tmpDepth = 0;\n\t\twhile (tmpParent && tmpDepth < 5)\n\t\t{\n\t\t\ttmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-connection-hash') : null;\n\t\t\tif (tmpHash) return tmpHash;\n\t\t\ttmpParent = tmpParent.parentElement;\n\t\t\ttmpDepth++;\n\t\t}\n\n\t\treturn null;\n\t}\n}\n\nmodule.exports = PictServiceFlowInteractionManager;\nmodule.exports.INTERACTION_STATES = INTERACTION_STATES;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\nclass PictServiceFlowLayout extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowLayout';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\t// Layout configuration\n\t\tthis._HorizontalSpacing = 250;\n\t\tthis._VerticalSpacing = 120;\n\t\tthis._StartX = 100;\n\t\tthis._StartY = 100;\n\t}\n\n\t/**\n\t * Snap a coordinate to the nearest grid point\n\t * @param {number} pValue - The coordinate value\n\t * @param {number} pGridSize - The grid size\n\t * @returns {number}\n\t */\n\tsnapToGrid(pValue, pGridSize)\n\t{\n\t\tif (!pGridSize || pGridSize <= 0) return pValue;\n\t\treturn Math.round(pValue / pGridSize) * pGridSize;\n\t}\n\n\t/**\n\t * Auto-layout nodes using a simple left-to-right topological approach\n\t * @param {Array} pNodes - Array of node data objects\n\t * @param {Array} pConnections - Array of connection data objects\n\t */\n\tautoLayout(pNodes, pConnections)\n\t{\n\t\tif (!pNodes || pNodes.length === 0) return;\n\n\t\t// Build adjacency information\n\t\tlet tmpNodeMap = {};\n\t\tlet tmpInDegree = {};\n\t\tlet tmpOutEdges = {};\n\n\t\tfor (let i = 0; i < pNodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = pNodes[i];\n\t\t\ttmpNodeMap[tmpNode.Hash] = tmpNode;\n\t\t\ttmpInDegree[tmpNode.Hash] = 0;\n\t\t\ttmpOutEdges[tmpNode.Hash] = [];\n\t\t}\n\n\t\tfor (let i = 0; i < pConnections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = pConnections[i];\n\t\t\tif (tmpInDegree.hasOwnProperty(tmpConn.TargetNodeHash))\n\t\t\t{\n\t\t\t\ttmpInDegree[tmpConn.TargetNodeHash]++;\n\t\t\t}\n\t\t\tif (tmpOutEdges.hasOwnProperty(tmpConn.SourceNodeHash))\n\t\t\t{\n\t\t\t\ttmpOutEdges[tmpConn.SourceNodeHash].push(tmpConn.TargetNodeHash);\n\t\t\t}\n\t\t}\n\n\t\t// Topological sort (Kahn's algorithm)\n\t\tlet tmpLayers = [];\n\t\tlet tmpQueue = [];\n\t\tlet tmpAssigned = {};\n\n\t\t// Start with nodes that have no incoming edges\n\t\tfor (let tmpHash in tmpInDegree)\n\t\t{\n\t\t\tif (tmpInDegree[tmpHash] === 0)\n\t\t\t{\n\t\t\t\ttmpQueue.push(tmpHash);\n\t\t\t}\n\t\t}\n\n\t\twhile (tmpQueue.length > 0)\n\t\t{\n\t\t\tlet tmpCurrentLayer = [];\n\n\t\t\tlet tmpNextQueue = [];\n\t\t\tfor (let i = 0; i < tmpQueue.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNodeHash = tmpQueue[i];\n\t\t\t\tif (tmpAssigned[tmpNodeHash]) continue;\n\n\t\t\t\ttmpAssigned[tmpNodeHash] = true;\n\t\t\t\ttmpCurrentLayer.push(tmpNodeHash);\n\n\t\t\t\t// Process outgoing edges\n\t\t\t\tlet tmpEdges = tmpOutEdges[tmpNodeHash] || [];\n\t\t\t\tfor (let j = 0; j < tmpEdges.length; j++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpTargetHash = tmpEdges[j];\n\t\t\t\t\ttmpInDegree[tmpTargetHash]--;\n\t\t\t\t\tif (tmpInDegree[tmpTargetHash] <= 0 && !tmpAssigned[tmpTargetHash])\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpNextQueue.push(tmpTargetHash);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tmpCurrentLayer.length > 0)\n\t\t\t{\n\t\t\t\ttmpLayers.push(tmpCurrentLayer);\n\t\t\t}\n\n\t\t\ttmpQueue = tmpNextQueue;\n\t\t}\n\n\t\t// Handle any remaining unassigned nodes (cycles or disconnected)\n\t\tlet tmpRemainingNodes = [];\n\t\tfor (let i = 0; i < pNodes.length; i++)\n\t\t{\n\t\t\tif (!tmpAssigned[pNodes[i].Hash])\n\t\t\t{\n\t\t\t\ttmpRemainingNodes.push(pNodes[i].Hash);\n\t\t\t}\n\t\t}\n\t\tif (tmpRemainingNodes.length > 0)\n\t\t{\n\t\t\ttmpLayers.push(tmpRemainingNodes);\n\t\t}\n\n\t\t// Assign positions based on layers\n\t\tlet tmpCurrentX = this._StartX;\n\n\t\tfor (let tmpLayerIndex = 0; tmpLayerIndex < tmpLayers.length; tmpLayerIndex++)\n\t\t{\n\t\t\tlet tmpLayer = tmpLayers[tmpLayerIndex];\n\t\t\tlet tmpMaxWidth = 0;\n\n\t\t\t// Calculate the total height for this layer to center vertically\n\t\t\tlet tmpTotalHeight = 0;\n\t\t\tfor (let i = 0; i < tmpLayer.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNode = tmpNodeMap[tmpLayer[i]];\n\t\t\t\tif (tmpNode)\n\t\t\t\t{\n\t\t\t\t\ttmpTotalHeight += tmpNode.Height || 80;\n\t\t\t\t\tif (i < tmpLayer.length - 1)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpTotalHeight += this._VerticalSpacing;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet tmpCurrentY = this._StartY;\n\n\t\t\tfor (let i = 0; i < tmpLayer.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNode = tmpNodeMap[tmpLayer[i]];\n\t\t\t\tif (!tmpNode) continue;\n\n\t\t\t\ttmpNode.X = tmpCurrentX;\n\t\t\t\ttmpNode.Y = tmpCurrentY;\n\n\t\t\t\tlet tmpWidth = tmpNode.Width || 180;\n\t\t\t\tlet tmpHeight = tmpNode.Height || 80;\n\n\t\t\t\ttmpMaxWidth = Math.max(tmpMaxWidth, tmpWidth);\n\t\t\t\ttmpCurrentY += tmpHeight + this._VerticalSpacing;\n\t\t\t}\n\n\t\t\ttmpCurrentX += tmpMaxWidth + this._HorizontalSpacing;\n\t\t}\n\t}\n\n\t/**\n\t * Auto-layout a subset of nodes, positioning them to the right of\n\t * any fixed (already-positioned) nodes.\n\t *\n\t * Uses the same topological sort approach as autoLayout, but only\n\t * repositions the nodes in pNodesToLayout. The pFixedNodes are used\n\t * to compute a bounding box that the new layout avoids.\n\t *\n\t * @param {Array} pNodesToLayout - Nodes that need new positions\n\t * @param {Array} pFixedNodes - Nodes that already have positions (read-only)\n\t * @param {Array} pConnections - All connections in the flow\n\t */\n\tautoLayoutSubset(pNodesToLayout, pFixedNodes, pConnections)\n\t{\n\t\tif (!pNodesToLayout || pNodesToLayout.length === 0) return;\n\n\t\t// Compute the starting X position to the right of all fixed nodes\n\t\tlet tmpStartX = this._StartX;\n\t\tlet tmpStartY = this._StartY;\n\n\t\tif (pFixedNodes && pFixedNodes.length > 0)\n\t\t{\n\t\t\tlet tmpMaxX = -Infinity;\n\n\t\t\tfor (let i = 0; i < pFixedNodes.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpRight = pFixedNodes[i].X + (pFixedNodes[i].Width || 180);\n\t\t\t\tif (tmpRight > tmpMaxX)\n\t\t\t\t{\n\t\t\t\t\ttmpMaxX = tmpRight;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Place unmatched nodes to the right of all fixed nodes\n\t\t\ttmpStartX = tmpMaxX + this._HorizontalSpacing;\n\t\t}\n\n\t\t// Build a set of nodes we are laying out for quick lookup\n\t\tlet tmpNodeSet = {};\n\t\tfor (let i = 0; i < pNodesToLayout.length; i++)\n\t\t{\n\t\t\ttmpNodeSet[pNodesToLayout[i].Hash] = true;\n\t\t}\n\n\t\t// Build adjacency information only for nodes in the subset\n\t\tlet tmpNodeMap = {};\n\t\tlet tmpInDegree = {};\n\t\tlet tmpOutEdges = {};\n\n\t\tfor (let i = 0; i < pNodesToLayout.length; i++)\n\t\t{\n\t\t\tlet tmpNode = pNodesToLayout[i];\n\t\t\ttmpNodeMap[tmpNode.Hash] = tmpNode;\n\t\t\ttmpInDegree[tmpNode.Hash] = 0;\n\t\t\ttmpOutEdges[tmpNode.Hash] = [];\n\t\t}\n\n\t\t// Only count edges between nodes in the subset\n\t\tfor (let i = 0; i < pConnections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = pConnections[i];\n\t\t\tlet tmpSourceInSubset = tmpNodeSet[tmpConn.SourceNodeHash];\n\t\t\tlet tmpTargetInSubset = tmpNodeSet[tmpConn.TargetNodeHash];\n\n\t\t\tif (tmpSourceInSubset && tmpTargetInSubset)\n\t\t\t{\n\t\t\t\ttmpInDegree[tmpConn.TargetNodeHash]++;\n\t\t\t\ttmpOutEdges[tmpConn.SourceNodeHash].push(tmpConn.TargetNodeHash);\n\t\t\t}\n\t\t}\n\n\t\t// Topological sort (Kahn's algorithm)\n\t\tlet tmpLayers = [];\n\t\tlet tmpQueue = [];\n\t\tlet tmpAssigned = {};\n\n\t\tfor (let tmpHash in tmpInDegree)\n\t\t{\n\t\t\tif (tmpInDegree[tmpHash] === 0)\n\t\t\t{\n\t\t\t\ttmpQueue.push(tmpHash);\n\t\t\t}\n\t\t}\n\n\t\twhile (tmpQueue.length > 0)\n\t\t{\n\t\t\tlet tmpCurrentLayer = [];\n\t\t\tlet tmpNextQueue = [];\n\n\t\t\tfor (let i = 0; i < tmpQueue.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNodeHash = tmpQueue[i];\n\t\t\t\tif (tmpAssigned[tmpNodeHash]) continue;\n\n\t\t\t\ttmpAssigned[tmpNodeHash] = true;\n\t\t\t\ttmpCurrentLayer.push(tmpNodeHash);\n\n\t\t\t\tlet tmpEdges = tmpOutEdges[tmpNodeHash] || [];\n\t\t\t\tfor (let j = 0; j < tmpEdges.length; j++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpTargetHash = tmpEdges[j];\n\t\t\t\t\ttmpInDegree[tmpTargetHash]--;\n\t\t\t\t\tif (tmpInDegree[tmpTargetHash] <= 0 && !tmpAssigned[tmpTargetHash])\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpNextQueue.push(tmpTargetHash);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tmpCurrentLayer.length > 0)\n\t\t\t{\n\t\t\t\ttmpLayers.push(tmpCurrentLayer);\n\t\t\t}\n\n\t\t\ttmpQueue = tmpNextQueue;\n\t\t}\n\n\t\t// Handle remaining unassigned nodes (cycles or disconnected)\n\t\tlet tmpRemainingNodes = [];\n\t\tfor (let i = 0; i < pNodesToLayout.length; i++)\n\t\t{\n\t\t\tif (!tmpAssigned[pNodesToLayout[i].Hash])\n\t\t\t{\n\t\t\t\ttmpRemainingNodes.push(pNodesToLayout[i].Hash);\n\t\t\t}\n\t\t}\n\t\tif (tmpRemainingNodes.length > 0)\n\t\t{\n\t\t\ttmpLayers.push(tmpRemainingNodes);\n\t\t}\n\n\t\t// Assign positions based on layers, starting from tmpStartX\n\t\tlet tmpCurrentX = tmpStartX;\n\n\t\tfor (let tmpLayerIndex = 0; tmpLayerIndex < tmpLayers.length; tmpLayerIndex++)\n\t\t{\n\t\t\tlet tmpLayer = tmpLayers[tmpLayerIndex];\n\t\t\tlet tmpMaxWidth = 0;\n\t\t\tlet tmpCurrentY = tmpStartY;\n\n\t\t\tfor (let i = 0; i < tmpLayer.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNode = tmpNodeMap[tmpLayer[i]];\n\t\t\t\tif (!tmpNode) continue;\n\n\t\t\t\ttmpNode.X = tmpCurrentX;\n\t\t\t\ttmpNode.Y = tmpCurrentY;\n\n\t\t\t\tlet tmpWidth = tmpNode.Width || 180;\n\t\t\t\tlet tmpHeight = tmpNode.Height || 80;\n\n\t\t\t\ttmpMaxWidth = Math.max(tmpMaxWidth, tmpWidth);\n\t\t\t\ttmpCurrentY += tmpHeight + this._VerticalSpacing;\n\t\t\t}\n\n\t\t\ttmpCurrentX += tmpMaxWidth + this._HorizontalSpacing;\n\t\t}\n\t}\n\n\t/**\n\t * Center all nodes around a given point\n\t * @param {Array} pNodes\n\t * @param {number} pCenterX\n\t * @param {number} pCenterY\n\t */\n\tcenterNodes(pNodes, pCenterX, pCenterY)\n\t{\n\t\tif (!pNodes || pNodes.length === 0) return;\n\n\t\tlet tmpMinX = Infinity, tmpMinY = Infinity;\n\t\tlet tmpMaxX = -Infinity, tmpMaxY = -Infinity;\n\n\t\tfor (let i = 0; i < pNodes.length; i++)\n\t\t{\n\t\t\ttmpMinX = Math.min(tmpMinX, pNodes[i].X);\n\t\t\ttmpMinY = Math.min(tmpMinY, pNodes[i].Y);\n\t\t\ttmpMaxX = Math.max(tmpMaxX, pNodes[i].X + (pNodes[i].Width || 180));\n\t\t\ttmpMaxY = Math.max(tmpMaxY, pNodes[i].Y + (pNodes[i].Height || 80));\n\t\t}\n\n\t\tlet tmpCurrentCenterX = (tmpMinX + tmpMaxX) / 2;\n\t\tlet tmpCurrentCenterY = (tmpMinY + tmpMaxY) / 2;\n\t\tlet tmpOffsetX = pCenterX - tmpCurrentCenterX;\n\t\tlet tmpOffsetY = pCenterY - tmpCurrentCenterY;\n\n\t\tfor (let i = 0; i < pNodes.length; i++)\n\t\t{\n\t\t\tpNodes[i].X += tmpOffsetX;\n\t\t\tpNodes[i].Y += tmpOffsetY;\n\t\t}\n\t}\n}\n\nmodule.exports = PictServiceFlowLayout;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-PanelManager\n *\n * Manages the lifecycle of properties panels in the flow diagram:\n * opening, closing, toggling, and position updates for panels\n * associated with flow nodes.\n */\nclass PictServiceFlowPanelManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowPanelManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Open a properties panel for a node.\n\t * @param {string} pNodeHash - The hash of the node to open a panel for\n\t * @returns {Object|false} The panel data, or false if the node was not found\n\t */\n\topenPanel(pNodeHash)\n\t{\n\t\tlet tmpNode = this._FlowView.getNode(pNodeHash);\n\t\tif (!tmpNode) return false;\n\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNode.Type);\n\t\tif (!tmpNodeTypeConfig) return false;\n\n\t\t// Check if a panel is already open for this node\n\t\tlet tmpExisting = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.NodeHash === pNodeHash);\n\t\tif (tmpExisting) return tmpExisting;\n\n\t\tlet tmpPanelConfig = tmpNodeTypeConfig.PropertiesPanel;\n\t\tlet tmpPanelHash = `panel-${this.fable.getUUID()}`;\n\t\tlet tmpWidth, tmpHeight, tmpPanelType, tmpTitle;\n\n\t\tif (tmpPanelConfig)\n\t\t{\n\t\t\ttmpWidth = tmpPanelConfig.DefaultWidth || 300;\n\t\t\ttmpHeight = tmpPanelConfig.DefaultHeight || 200;\n\t\t\ttmpPanelType = tmpPanelConfig.PanelType || 'Base';\n\t\t\ttmpTitle = tmpPanelConfig.Title || tmpNodeTypeConfig.Label || 'Properties';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// No PropertiesPanel configured — open an auto-generated info panel\n\t\t\ttmpWidth = 240;\n\t\t\ttmpHeight = 180;\n\t\t\ttmpPanelType = 'Info';\n\t\t\ttmpTitle = tmpNodeTypeConfig.Label || tmpNode.Title || 'Node Info';\n\t\t}\n\n\t\tlet tmpPanelData =\n\t\t{\n\t\t\tHash: tmpPanelHash,\n\t\t\tNodeHash: pNodeHash,\n\t\t\tPanelType: tmpPanelType,\n\t\t\tTitle: tmpTitle,\n\t\t\tX: tmpNode.X + tmpNode.Width + 30,\n\t\t\tY: tmpNode.Y,\n\t\t\tWidth: tmpWidth,\n\t\t\tHeight: tmpHeight\n\t\t};\n\n\t\tthis._FlowView._FlowData.OpenPanels.push(tmpPanelData);\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onPanelOpened', tmpPanelData);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn tmpPanelData;\n\t}\n\n\t/**\n\t * Close a properties panel by panel hash.\n\t * @param {string} pPanelHash\n\t * @returns {boolean}\n\t */\n\tclosePanel(pPanelHash)\n\t{\n\t\tlet tmpIndex = this._FlowView._FlowData.OpenPanels.findIndex((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (tmpIndex < 0) return false;\n\n\t\tlet tmpRemovedPanel = this._FlowView._FlowData.OpenPanels.splice(tmpIndex, 1)[0];\n\n\t\t// Clean up the panel instance\n\t\tif (this._FlowView._PropertiesPanelView)\n\t\t{\n\t\t\tthis._FlowView._PropertiesPanelView.destroyPanel(pPanelHash);\n\t\t}\n\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onPanelClosed', tmpRemovedPanel);\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Close all panels for a given node.\n\t * @param {string} pNodeHash\n\t * @returns {boolean}\n\t */\n\tclosePanelForNode(pNodeHash)\n\t{\n\t\tlet tmpPanelsToClose = this._FlowView._FlowData.OpenPanels.filter((pPanel) => pPanel.NodeHash === pNodeHash);\n\t\tif (tmpPanelsToClose.length === 0) return false;\n\n\t\tfor (let i = 0; i < tmpPanelsToClose.length; i++)\n\t\t{\n\t\t\tlet tmpIndex = this._FlowView._FlowData.OpenPanels.indexOf(tmpPanelsToClose[i]);\n\t\t\tif (tmpIndex >= 0)\n\t\t\t{\n\t\t\t\tthis._FlowView._FlowData.OpenPanels.splice(tmpIndex, 1);\n\t\t\t}\n\t\t\tif (this._FlowView._PropertiesPanelView)\n\t\t\t{\n\t\t\t\tthis._FlowView._PropertiesPanelView.destroyPanel(tmpPanelsToClose[i].Hash);\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Toggle a properties panel for a node (open if closed, close if open).\n\t * @param {string} pNodeHash\n\t * @returns {Object|false}\n\t */\n\ttogglePanel(pNodeHash)\n\t{\n\t\tlet tmpExisting = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.NodeHash === pNodeHash);\n\t\tif (tmpExisting)\n\t\t{\n\t\t\tthis.closePanel(tmpExisting.Hash);\n\t\t\treturn false;\n\t\t}\n\t\treturn this.openPanel(pNodeHash);\n\t}\n\n\t/**\n\t * Update a panel's position (for drag).\n\t * @param {string} pPanelHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdatePanelPosition(pPanelHash, pX, pY)\n\t{\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\ttmpPanel.X = pX;\n\t\ttmpPanel.Y = pY;\n\n\t\t// Reset tether handle positions when panel moves\n\t\tthis._FlowView._resetHandlesForPanel(pPanelHash);\n\n\t\t// Update the foreignObject position directly for smooth dragging\n\t\tif (this._FlowView._PanelsLayer)\n\t\t{\n\t\t\tlet tmpFO = this._FlowView._PanelsLayer.querySelector(`[data-panel-hash=\"${pPanelHash}\"]`);\n\t\t\tif (tmpFO)\n\t\t\t{\n\t\t\t\ttmpFO.setAttribute('x', String(pX));\n\t\t\t\ttmpFO.setAttribute('y', String(pY));\n\t\t\t}\n\t\t}\n\n\t\t// Update the tether for this panel\n\t\tthis._FlowView._renderTethersForNode(tmpPanel.NodeHash);\n\t}\n}\n\nmodule.exports = PictServiceFlowPanelManager;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-PathGenerator\n *\n * Centralizes SVG path generation for the flow diagram.\n * Provides shared building blocks used by both the ConnectionRenderer\n * (port-to-port connections) and the TetherService (panel-to-node tethers).\n *\n * Responsibilities:\n * - Departure/approach point calculation from anchors\n * - Auto orthogonal corner computation for right-angle paths\n * - Cubic bezier evaluation at arbitrary parameter t\n * - SVG path string assembly (bezier, split-bezier, orthogonal)\n */\nclass PictServiceFlowPathGenerator extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowPathGenerator';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t// ---- Departure / Approach Calculation ----\n\n\t/**\n\t * Compute departure and approach points from start/end anchors.\n\t * The departure point extends outward from the start in its side direction,\n\t * and the approach point extends outward from the end in its side direction.\n\t *\n\t * @param {{x: number, y: number, side: string}} pFrom - Start anchor with side\n\t * @param {{x: number, y: number, side: string}} pTo - End anchor with side\n\t * @param {number} pDepartDist - Distance for departure/approach straight segments\n\t * @returns {{departX: number, departY: number, approachX: number, approachY: number, fromDir: {dx: number, dy: number}, toDir: {dx: number, dy: number}}}\n\t */\n\tcomputeDepartApproach(pFrom, pTo, pDepartDist)\n\t{\n\t\tlet tmpGeometry = this._FlowView._GeometryProvider;\n\n\t\tlet tmpFromDir = tmpGeometry.sideDirection(pFrom.side || 'right');\n\t\tlet tmpToDir = tmpGeometry.sideDirection(pTo.side || 'left');\n\n\t\treturn {\n\t\t\tdepartX: pFrom.x + tmpFromDir.dx * pDepartDist,\n\t\t\tdepartY: pFrom.y + tmpFromDir.dy * pDepartDist,\n\t\t\tapproachX: pTo.x + tmpToDir.dx * pDepartDist,\n\t\t\tapproachY: pTo.y + tmpToDir.dy * pDepartDist,\n\t\t\tfromDir: tmpFromDir,\n\t\t\ttoDir: tmpToDir\n\t\t};\n\t}\n\n\t// ---- Orthogonal Corner Calculation ----\n\n\t/**\n\t * Compute auto orthogonal corners for an L-shaped or Z-shaped path.\n\t * Determines corner placement based on departure/approach directions.\n\t *\n\t * Used by both connection and tether renderers for right-angle paths.\n\t *\n\t * @param {number} pDepartX\n\t * @param {number} pDepartY\n\t * @param {number} pApproachX\n\t * @param {number} pApproachY\n\t * @param {{dx: number, dy: number}} pFromDir - Departure direction vector\n\t * @param {{dx: number, dy: number}} pToDir - Approach direction vector\n\t * @param {number} pMidOffset - Offset for the corridor midpoint\n\t * @returns {{corner1: {x: number, y: number}, corner2: {x: number, y: number}, midpoint: {x: number, y: number}}}\n\t */\n\tcomputeAutoOrthogonalCorners(pDepartX, pDepartY, pApproachX, pApproachY, pFromDir, pToDir, pMidOffset)\n\t{\n\t\tlet tmpOffset = pMidOffset || 0;\n\t\tlet tmpFromHoriz = Math.abs(pFromDir.dx) > 0;\n\t\tlet tmpToHoriz = Math.abs(pToDir.dx) > 0;\n\n\t\tlet tmpCorner1, tmpCorner2, tmpMidpoint;\n\n\t\tif (tmpFromHoriz && tmpToHoriz)\n\t\t{\n\t\t\t// Both horizontal departure/approach: corridor is vertical\n\t\t\tlet tmpMidX = (pDepartX + pApproachX) / 2 + tmpOffset;\n\t\t\ttmpCorner1 = { x: tmpMidX, y: pDepartY };\n\t\t\ttmpCorner2 = { x: tmpMidX, y: pApproachY };\n\t\t\ttmpMidpoint = { x: tmpMidX, y: (pDepartY + pApproachY) / 2 };\n\t\t}\n\t\telse if (!tmpFromHoriz && !tmpToHoriz)\n\t\t{\n\t\t\t// Both vertical: corridor is horizontal\n\t\t\tlet tmpMidY = (pDepartY + pApproachY) / 2 + tmpOffset;\n\t\t\ttmpCorner1 = { x: pDepartX, y: tmpMidY };\n\t\t\ttmpCorner2 = { x: pApproachX, y: tmpMidY };\n\t\t\ttmpMidpoint = { x: (pDepartX + pApproachX) / 2, y: tmpMidY };\n\t\t}\n\t\telse if (tmpFromHoriz && !tmpToHoriz)\n\t\t{\n\t\t\t// Horizontal→Vertical: single L-bend\n\t\t\ttmpCorner1 = { x: pApproachX + tmpOffset, y: pDepartY };\n\t\t\ttmpCorner2 = { x: pApproachX + tmpOffset, y: pApproachY };\n\t\t\ttmpMidpoint = { x: pApproachX + tmpOffset, y: (pDepartY + pApproachY) / 2 };\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Vertical→Horizontal: single L-bend\n\t\t\ttmpCorner1 = { x: pDepartX, y: pApproachY + tmpOffset };\n\t\t\ttmpCorner2 = { x: pApproachX, y: pApproachY + tmpOffset };\n\t\t\ttmpMidpoint = { x: (pDepartX + pApproachX) / 2, y: pApproachY + tmpOffset };\n\t\t}\n\n\t\treturn { corner1: tmpCorner1, corner2: tmpCorner2, midpoint: tmpMidpoint };\n\t}\n\n\t// ---- Bezier Evaluation ----\n\n\t/**\n\t * Evaluate a cubic bezier curve at parameter t.\n\t * B(t) = (1-t)³P0 + 3(1-t)²tP1 + 3(1-t)t²P2 + t³P3\n\t *\n\t * @param {{x: number, y: number}} pP0 - Start point\n\t * @param {{x: number, y: number}} pP1 - First control point\n\t * @param {{x: number, y: number}} pP2 - Second control point\n\t * @param {{x: number, y: number}} pP3 - End point\n\t * @param {number} pT - Parameter in range [0, 1]\n\t * @returns {{x: number, y: number}}\n\t */\n\tevaluateCubicBezier(pP0, pP1, pP2, pP3, pT)\n\t{\n\t\tlet tmpOMT = 1 - pT;\n\t\tlet tmpOMT2 = tmpOMT * tmpOMT;\n\t\tlet tmpOMT3 = tmpOMT2 * tmpOMT;\n\t\tlet tmpT2 = pT * pT;\n\t\tlet tmpT3 = tmpT2 * pT;\n\n\t\treturn {\n\t\t\tx: tmpOMT3 * pP0.x + 3 * tmpOMT2 * pT * pP1.x + 3 * tmpOMT * tmpT2 * pP2.x + tmpT3 * pP3.x,\n\t\t\ty: tmpOMT3 * pP0.y + 3 * tmpOMT2 * pT * pP1.y + 3 * tmpOMT * tmpT2 * pP2.y + tmpT3 * pP3.y\n\t\t};\n\t}\n\n\t// ---- SVG Path String Assembly ----\n\n\t/**\n\t * Build an SVG bezier path string.\n\t * Pattern: M start L depart C cp1, cp2, approach L end\n\t *\n\t * @param {{x: number, y: number}} pStart - Start point\n\t * @param {{x: number, y: number}} pDepart - Departure point after straight segment\n\t * @param {{x: number, y: number}} pCP1 - First control point\n\t * @param {{x: number, y: number}} pCP2 - Second control point\n\t * @param {{x: number, y: number}} pApproach - Approach point before final straight segment\n\t * @param {{x: number, y: number}} pEnd - End point\n\t * @returns {string} SVG path d attribute\n\t */\n\tbuildBezierPathString(pStart, pDepart, pCP1, pCP2, pApproach, pEnd)\n\t{\n\t\treturn `M ${pStart.x} ${pStart.y} L ${pDepart.x} ${pDepart.y} C ${pCP1.x} ${pCP1.y}, ${pCP2.x} ${pCP2.y}, ${pApproach.x} ${pApproach.y} L ${pEnd.x} ${pEnd.y}`;\n\t}\n\n\t/**\n\t * Build an SVG split bezier path string (two cubic segments through a handle point).\n\t * Pattern: M start L depart C cp1a, cp1b, handle C cp2a, cp2b, approach L end\n\t *\n\t * @param {{x: number, y: number}} pStart\n\t * @param {{x: number, y: number}} pDepart\n\t * @param {{x: number, y: number}} pCP1a - First segment's first control point\n\t * @param {{x: number, y: number}} pCP1b - First segment's second control point\n\t * @param {{x: number, y: number}} pHandle - Handle point where the two segments meet\n\t * @param {{x: number, y: number}} pCP2a - Second segment's first control point\n\t * @param {{x: number, y: number}} pCP2b - Second segment's second control point\n\t * @param {{x: number, y: number}} pApproach\n\t * @param {{x: number, y: number}} pEnd\n\t * @returns {string} SVG path d attribute\n\t */\n\tbuildSplitBezierPathString(pStart, pDepart, pCP1a, pCP1b, pHandle, pCP2a, pCP2b, pApproach, pEnd)\n\t{\n\t\treturn `M ${pStart.x} ${pStart.y} L ${pDepart.x} ${pDepart.y} C ${pCP1a.x} ${pCP1a.y}, ${pCP1b.x} ${pCP1b.y}, ${pHandle.x} ${pHandle.y} C ${pCP2a.x} ${pCP2a.y}, ${pCP2b.x} ${pCP2b.y}, ${pApproach.x} ${pApproach.y} L ${pEnd.x} ${pEnd.y}`;\n\t}\n\n\t/**\n\t * Build an SVG multi-segment bezier path string.\n\t * Generates N+1 cubic bezier segments through N handle points.\n\t *\n\t * Pattern: M start L depart C cp,cp,handle[0] C cp,cp,handle[1] ... C cp,cp,approach L end\n\t *\n\t * Control points are computed using Catmull-Rom-to-Bezier conversion\n\t * for C1 (smooth tangent) continuity at every handle.\n\t *\n\t * @param {{x: number, y: number}} pStart - Port anchor start\n\t * @param {{x: number, y: number}} pDepart - Departure point after straight segment\n\t * @param {Array<{x: number, y: number}>} pHandles - Ordered handle waypoints\n\t * @param {{x: number, y: number}} pApproach - Approach point before final straight segment\n\t * @param {{x: number, y: number}} pEnd - Port anchor end\n\t * @param {{dx: number, dy: number}} pStartDir - Departure direction unit vector\n\t * @param {{dx: number, dy: number}} pEndDir - Approach direction unit vector\n\t * @returns {string} SVG path d attribute\n\t */\n\tbuildMultiBezierPathString(pStart, pDepart, pHandles, pApproach, pEnd, pStartDir, pEndDir)\n\t{\n\t\t// Build the full list of waypoints: depart, handle[0..N-1], approach\n\t\tlet tmpWaypoints = [pDepart];\n\t\tfor (let i = 0; i < pHandles.length; i++)\n\t\t{\n\t\t\ttmpWaypoints.push(pHandles[i]);\n\t\t}\n\t\ttmpWaypoints.push(pApproach);\n\n\t\tlet tmpPath = `M ${pStart.x} ${pStart.y} L ${pDepart.x} ${pDepart.y}`;\n\n\t\tfor (let i = 0; i < tmpWaypoints.length - 1; i++)\n\t\t{\n\t\t\tlet tmpFrom = tmpWaypoints[i];\n\t\t\tlet tmpTo = tmpWaypoints[i + 1];\n\n\t\t\tlet tmpSegDX = tmpTo.x - tmpFrom.x;\n\t\t\tlet tmpSegDY = tmpTo.y - tmpFrom.y;\n\t\t\tlet tmpSegLen = Math.sqrt(tmpSegDX * tmpSegDX + tmpSegDY * tmpSegDY);\n\t\t\tif (tmpSegLen < 1)\n\t\t\t{\n\t\t\t\ttmpSegLen = 1;\n\t\t\t}\n\t\t\tlet tmpScale = tmpSegLen * 0.35;\n\n\t\t\t// Tangent at tmpFrom\n\t\t\tlet tmpTanFromX, tmpTanFromY;\n\t\t\tif (i === 0)\n\t\t\t{\n\t\t\t\t// First segment: use the port departure direction\n\t\t\t\ttmpTanFromX = pStartDir.dx;\n\t\t\t\ttmpTanFromY = pStartDir.dy;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Interior handle: tangent points from previous toward next waypoint\n\t\t\t\tlet tmpPrev = tmpWaypoints[i - 1];\n\t\t\t\tlet tmpNext = tmpWaypoints[i + 1];\n\t\t\t\ttmpTanFromX = tmpNext.x - tmpPrev.x;\n\t\t\t\ttmpTanFromY = tmpNext.y - tmpPrev.y;\n\t\t\t\tlet tmpTanLen = Math.sqrt(tmpTanFromX * tmpTanFromX + tmpTanFromY * tmpTanFromY);\n\t\t\t\tif (tmpTanLen < 1) tmpTanLen = 1;\n\t\t\t\ttmpTanFromX /= tmpTanLen;\n\t\t\t\ttmpTanFromY /= tmpTanLen;\n\t\t\t}\n\n\t\t\t// Tangent at tmpTo\n\t\t\tlet tmpTanToX, tmpTanToY;\n\t\t\tif (i === tmpWaypoints.length - 2)\n\t\t\t{\n\t\t\t\t// Last segment: use the port approach direction (reversed for incoming)\n\t\t\t\ttmpTanToX = -pEndDir.dx;\n\t\t\t\ttmpTanToY = -pEndDir.dy;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Interior handle: tangent points from previous toward next waypoint\n\t\t\t\tlet tmpPrev = tmpWaypoints[i];\n\t\t\t\tlet tmpNext = tmpWaypoints[i + 2];\n\t\t\t\ttmpTanToX = tmpNext.x - tmpPrev.x;\n\t\t\t\ttmpTanToY = tmpNext.y - tmpPrev.y;\n\t\t\t\tlet tmpTanLen = Math.sqrt(tmpTanToX * tmpTanToX + tmpTanToY * tmpTanToY);\n\t\t\t\tif (tmpTanLen < 1) tmpTanLen = 1;\n\t\t\t\ttmpTanToX /= tmpTanLen;\n\t\t\t\ttmpTanToY /= tmpTanLen;\n\t\t\t}\n\n\t\t\tlet tmpCP1X = tmpFrom.x + tmpTanFromX * tmpScale;\n\t\t\tlet tmpCP1Y = tmpFrom.y + tmpTanFromY * tmpScale;\n\t\t\tlet tmpCP2X = tmpTo.x - tmpTanToX * tmpScale;\n\t\t\tlet tmpCP2Y = tmpTo.y - tmpTanToY * tmpScale;\n\n\t\t\ttmpPath += ` C ${tmpCP1X} ${tmpCP1Y}, ${tmpCP2X} ${tmpCP2Y}, ${tmpTo.x} ${tmpTo.y}`;\n\t\t}\n\n\t\ttmpPath += ` L ${pEnd.x} ${pEnd.y}`;\n\n\t\treturn tmpPath;\n\t}\n\n\t/**\n\t * Build an SVG orthogonal (right-angle) path string.\n\t * Pattern: M start L depart L corner1 L corner2 L approach L end\n\t *\n\t * @param {{x: number, y: number}} pStart\n\t * @param {{x: number, y: number}} pDepart\n\t * @param {{x: number, y: number}} pCorner1\n\t * @param {{x: number, y: number}} pCorner2\n\t * @param {{x: number, y: number}} pApproach\n\t * @param {{x: number, y: number}} pEnd\n\t * @returns {string} SVG path d attribute\n\t */\n\tbuildOrthogonalPathString(pStart, pDepart, pCorner1, pCorner2, pApproach, pEnd)\n\t{\n\t\treturn `M ${pStart.x} ${pStart.y} L ${pDepart.x} ${pDepart.y} L ${pCorner1.x} ${pCorner1.y} L ${pCorner2.x} ${pCorner2.y} L ${pApproach.x} ${pApproach.y} L ${pEnd.x} ${pEnd.y}`;\n\t}\n\n\t// ---- Directional Geometry ----\n\n\t/**\n\t * Compute full directional geometry between two port anchors, including\n\t * departure/approach points and bezier control points.\n\t *\n\t * Uses sophisticated facing detection: when ports face each other the\n\t * curve offset scales with inline distance; when ports are on the same\n\t * axis but not facing, a wider offset prevents the path from collapsing;\n\t * perpendicular exits use a moderate offset.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @returns {{departX: number, departY: number, approachX: number, approachY: number, cp1X: number, cp1Y: number, cp2X: number, cp2Y: number, startDir: {dx: number, dy: number}, endDir: {dx: number, dy: number}}}\n\t */\n\tcomputeDirectionalGeometry(pStart, pEnd)\n\t{\n\t\tlet tmpStartDir = this._FlowView._GeometryProvider.sideDirection(pStart.side || 'right');\n\t\tlet tmpEndDir = this._FlowView._GeometryProvider.sideDirection(pEnd.side || 'left');\n\n\t\tlet tmpStraightLen = 20;\n\n\t\tlet tmpDepartX = pStart.x + tmpStartDir.dx * tmpStraightLen;\n\t\tlet tmpDepartY = pStart.y + tmpStartDir.dy * tmpStraightLen;\n\n\t\tlet tmpApproachX = pEnd.x + tmpEndDir.dx * tmpStraightLen;\n\t\tlet tmpApproachY = pEnd.y + tmpEndDir.dy * tmpStraightLen;\n\n\t\tlet tmpDX = Math.abs(tmpApproachX - tmpDepartX);\n\t\tlet tmpDY = Math.abs(tmpApproachY - tmpDepartY);\n\t\tlet tmpDist = Math.sqrt(tmpDX * tmpDX + tmpDY * tmpDY);\n\n\t\tlet tmpBaseOffset = Math.max(Math.min(tmpDist * 0.4, 180), 30);\n\n\t\tlet tmpSameAxis = (tmpStartDir.dx !== 0 && tmpEndDir.dx !== 0) ||\n\t\t (tmpStartDir.dy !== 0 && tmpEndDir.dy !== 0);\n\n\t\tlet tmpFacingEachOther = false;\n\t\tif (tmpSameAxis)\n\t\t{\n\t\t\tif (tmpStartDir.dx === 1 && tmpEndDir.dx === -1 && pEnd.x >= pStart.x)\n\t\t\t{\n\t\t\t\ttmpFacingEachOther = true;\n\t\t\t}\n\t\t\telse if (tmpStartDir.dx === -1 && tmpEndDir.dx === 1 && pEnd.x <= pStart.x)\n\t\t\t{\n\t\t\t\ttmpFacingEachOther = true;\n\t\t\t}\n\t\t\telse if (tmpStartDir.dy === 1 && tmpEndDir.dy === -1 && pEnd.y >= pStart.y)\n\t\t\t{\n\t\t\t\ttmpFacingEachOther = true;\n\t\t\t}\n\t\t\telse if (tmpStartDir.dy === -1 && tmpEndDir.dy === 1 && pEnd.y <= pStart.y)\n\t\t\t{\n\t\t\t\ttmpFacingEachOther = true;\n\t\t\t}\n\t\t}\n\n\t\tlet tmpCurveOffset;\n\n\t\tif (tmpFacingEachOther)\n\t\t{\n\t\t\tlet tmpInlineDist = (tmpStartDir.dx !== 0) ? tmpDX : tmpDY;\n\t\t\ttmpCurveOffset = Math.max(tmpInlineDist * 0.35, 30);\n\t\t}\n\t\telse if (tmpSameAxis)\n\t\t{\n\t\t\ttmpCurveOffset = Math.max(tmpBaseOffset, 60);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpCurveOffset = Math.max(tmpBaseOffset * 0.8, 40);\n\t\t}\n\n\t\tlet tmpCP1X = tmpDepartX + tmpStartDir.dx * tmpCurveOffset;\n\t\tlet tmpCP1Y = tmpDepartY + tmpStartDir.dy * tmpCurveOffset;\n\t\tlet tmpCP2X = tmpApproachX + tmpEndDir.dx * tmpCurveOffset;\n\t\tlet tmpCP2Y = tmpApproachY + tmpEndDir.dy * tmpCurveOffset;\n\n\t\treturn {\n\t\t\tdepartX: tmpDepartX, departY: tmpDepartY,\n\t\t\tapproachX: tmpApproachX, approachY: tmpApproachY,\n\t\t\tcp1X: tmpCP1X, cp1Y: tmpCP1Y,\n\t\t\tcp2X: tmpCP2X, cp2Y: tmpCP2Y,\n\t\t\tstartDir: tmpStartDir, endDir: tmpEndDir\n\t\t};\n\t}\n\n\t// ---- Distance Utilities ----\n\n\t/**\n\t * Distance from point (pPX, pPY) to line segment (pAX, pAY)-(pBX, pBY).\n\t * Pure math utility, no state.\n\t *\n\t * @param {number} pPX\n\t * @param {number} pPY\n\t * @param {number} pAX\n\t * @param {number} pAY\n\t * @param {number} pBX\n\t * @param {number} pBY\n\t * @returns {number}\n\t */\n\tdistanceToSegment(pPX, pPY, pAX, pAY, pBX, pBY)\n\t{\n\t\tlet tmpDX = pBX - pAX;\n\t\tlet tmpDY = pBY - pAY;\n\t\tlet tmpLenSq = tmpDX * tmpDX + tmpDY * tmpDY;\n\n\t\tif (tmpLenSq < 0.001)\n\t\t{\n\t\t\t// Degenerate segment\n\t\t\tlet tmpDPX = pPX - pAX;\n\t\t\tlet tmpDPY = pPY - pAY;\n\t\t\treturn Math.sqrt(tmpDPX * tmpDPX + tmpDPY * tmpDPY);\n\t\t}\n\n\t\t// Project point onto segment, clamped to [0, 1]\n\t\tlet tmpT = ((pPX - pAX) * tmpDX + (pPY - pAY) * tmpDY) / tmpLenSq;\n\t\tif (tmpT < 0) tmpT = 0;\n\t\tif (tmpT > 1) tmpT = 1;\n\n\t\tlet tmpClosestX = pAX + tmpT * tmpDX;\n\t\tlet tmpClosestY = pAY + tmpT * tmpDY;\n\t\tlet tmpDistX = pPX - tmpClosestX;\n\t\tlet tmpDistY = pPY - tmpClosestY;\n\t\treturn Math.sqrt(tmpDistX * tmpDistX + tmpDistY * tmpDistY);\n\t}\n\n\t// ---- Auto Midpoint Calculation ----\n\n\t/**\n\t * Get the auto-calculated midpoint of the default bezier curve between\n\t * two port anchors, using the full directional geometry (facing detection,\n\t * adaptive curve offsets). Evaluates the cubic bezier at t=0.5.\n\t *\n\t * Used by ConnectionRenderer for connection midpoints.\n\t *\n\t * @param {{x: number, y: number, side: string}} pStart\n\t * @param {{x: number, y: number, side: string}} pEnd\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetAutoMidpoint(pStart, pEnd)\n\t{\n\t\tlet tmpGeo = this.computeDirectionalGeometry(pStart, pEnd);\n\n\t\treturn this.evaluateCubicBezier(\n\t\t\t{ x: tmpGeo.departX, y: tmpGeo.departY },\n\t\t\t{ x: tmpGeo.cp1X, y: tmpGeo.cp1Y },\n\t\t\t{ x: tmpGeo.cp2X, y: tmpGeo.cp2Y },\n\t\t\t{ x: tmpGeo.approachX, y: tmpGeo.approachY },\n\t\t\t0.5\n\t\t);\n\t}\n\n\t/**\n\t * Get the auto-calculated midpoint using simple span-based control points.\n\t * Uses computeDepartApproach for basic geometry, then span * 0.4 for\n\t * control point distance. Evaluates the cubic bezier at t=0.5.\n\t *\n\t * Used by TetherService for tether midpoints.\n\t *\n\t * @param {{x: number, y: number, side: string}} pFrom\n\t * @param {{x: number, y: number, side: string}} pTo\n\t * @param {number} pDepartDist - Departure/approach distance\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetAutoMidpointSimple(pFrom, pTo, pDepartDist)\n\t{\n\t\tlet tmpDA = this.computeDepartApproach(pFrom, pTo, pDepartDist);\n\n\t\tlet tmpSpanX = Math.abs(tmpDA.approachX - tmpDA.departX);\n\t\tlet tmpSpanY = Math.abs(tmpDA.approachY - tmpDA.departY);\n\t\tlet tmpSpan = Math.max(tmpSpanX, tmpSpanY, 40);\n\t\tlet tmpCPDist = tmpSpan * 0.4;\n\n\t\tlet tmpP0 = { x: tmpDA.departX, y: tmpDA.departY };\n\t\tlet tmpP1 = { x: tmpDA.departX + tmpDA.fromDir.dx * tmpCPDist, y: tmpDA.departY + tmpDA.fromDir.dy * tmpCPDist };\n\t\tlet tmpP2 = { x: tmpDA.approachX + tmpDA.toDir.dx * tmpCPDist, y: tmpDA.approachY + tmpDA.toDir.dy * tmpCPDist };\n\t\tlet tmpP3 = { x: tmpDA.approachX, y: tmpDA.approachY };\n\n\t\treturn this.evaluateCubicBezier(tmpP0, tmpP1, tmpP2, tmpP3, 0.5);\n\t}\n}\n\nmodule.exports = PictServiceFlowPathGenerator;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-PortRenderer\n *\n * Renders port circles, labels, and badges for flow diagram nodes.\n *\n * Extracted from PictView-Flow-Node.js to isolate port rendering logic\n * from node body rendering and layout.\n *\n * Dependencies (all accessed via this._FlowView):\n * - _GeometryProvider — for getEdgeFromSide, getPortLocalPosition\n * - _ConnectorShapesProvider — for createPortElement\n * - _SVGHelperProvider — for createSVGElement\n */\nclass PictServiceFlowPortRenderer extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowPortRenderer';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Render ports for a node.\n\t * @param {Object} pNodeData\n\t * @param {SVGGElement} pGroup - The node's SVG group\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @param {Object} [pNodeTypeConfig] - Node type configuration (for label display options)\n\t * @param {number} pNodeTitleBarHeight - Title bar height (for port position offset)\n\t */\n\trenderPorts(pNodeData, pGroup, pWidth, pHeight, pNodeTypeConfig, pNodeTitleBarHeight)\n\t{\n\t\tif (!this._FlowView) return;\n\t\tif (!pNodeData.Ports || !Array.isArray(pNodeData.Ports)) return;\n\n\t\tlet tmpPortLabelsVertical = (pNodeTypeConfig && pNodeTypeConfig.PortLabelsVertical);\n\t\tlet tmpPortLabelPadding = (pNodeTypeConfig && pNodeTypeConfig.PortLabelPadding);\n\t\tlet tmpPortLabelsOutside = (pNodeTypeConfig && pNodeTypeConfig.PortLabelsOutside);\n\t\tlet tmpGeometryProvider = this._FlowView._GeometryProvider;\n\n\t\t// Group ports by their Side value (supports all 12 positions)\n\t\tlet tmpPortsBySide = {};\n\t\tfor (let i = 0; i < pNodeData.Ports.length; i++)\n\t\t{\n\t\t\tlet tmpPort = pNodeData.Ports[i];\n\t\t\tlet tmpSide = tmpPort.Side || (tmpPort.Direction === 'input' ? 'left' : 'right');\n\t\t\tif (!tmpPortsBySide[tmpSide])\n\t\t\t{\n\t\t\t\ttmpPortsBySide[tmpSide] = [];\n\t\t\t}\n\t\t\ttmpPortsBySide[tmpSide].push(tmpPort);\n\t\t}\n\n\t\t// Build port counts map for adaptive zone sizing\n\t\tlet tmpPortCountsBySide = {};\n\t\tfor (let tmpKey in tmpPortsBySide)\n\t\t{\n\t\t\ttmpPortCountsBySide[tmpKey] = tmpPortsBySide[tmpKey].length;\n\t\t}\n\n\t\tfor (let tmpSide in tmpPortsBySide)\n\t\t{\n\t\t\tlet tmpPorts = tmpPortsBySide[tmpSide];\n\t\t\t// Determine the edge for label positioning\n\t\t\tlet tmpEdge = tmpGeometryProvider ? tmpGeometryProvider.getEdgeFromSide(tmpSide) : tmpSide;\n\n\t\t\tfor (let i = 0; i < tmpPorts.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpPorts[i];\n\t\t\t\tlet tmpPosition = this.getPortLocalPosition(tmpSide, i, tmpPorts.length, pWidth, pHeight, pNodeTitleBarHeight, tmpPortCountsBySide);\n\n\t\t\t\t// Port label badge — flush against the node edge with no\n\t\t\t\t// border on the edge side; rendered before the port circle\n\t\t\t\t// so the circle visually sits on top of the badge\n\t\t\t\tlet tmpLabelElement = null;\n\t\t\t\tif (tmpPort.Label)\n\t\t\t\t{\n\t\t\t\t\tlet tmpPortTypeColorMap =\n\t\t\t\t\t{\n\t\t\t\t\t\t'event-in': '#3498db',\n\t\t\t\t\t\t'event-out': '#2ecc71',\n\t\t\t\t\t\t'setting': '#e67e22',\n\t\t\t\t\t\t'value': '#f1c40f',\n\t\t\t\t\t\t'error': '#e74c3c'\n\t\t\t\t\t};\n\t\t\t\t\tlet tmpBorderColor = tmpPort.PortType ? (tmpPortTypeColorMap[tmpPort.PortType] || '#95a5a6') : '#95a5a6';\n\n\t\t\t\t\tlet tmpBadgeHeight = 12;\n\t\t\t\t\tlet tmpBadgePadH = 5;\n\t\t\t\t\tlet tmpBadgeBorderW = 2;\n\t\t\t\t\tlet tmpEdgePad = 1;\n\t\t\t\t\tlet tmpPortRadius = 5;\n\n\t\t\t\t\tlet tmpTextLen = tmpPort.Label.length * 5;\n\t\t\t\t\tlet tmpBadgeX, tmpBadgeY, tmpBadgeWidth;\n\t\t\t\t\tlet tmpTextX, tmpTextAnchor;\n\t\t\t\t\tlet tmpStripeX, tmpStripeY, tmpStripeW, tmpStripeH;\n\t\t\t\t\tlet tmpBorderPath;\n\n\t\t\t\t\tif (tmpEdge === 'left')\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBadgeWidth = tmpPortRadius + tmpBadgePadH + tmpTextLen + tmpBadgePadH + tmpBadgeBorderW;\n\t\t\t\t\t\ttmpBadgeX = tmpEdgePad;\n\t\t\t\t\t\ttmpBadgeY = tmpPosition.y - tmpBadgeHeight / 2;\n\t\t\t\t\t\ttmpTextX = tmpBadgeX + tmpPortRadius + tmpBadgePadH;\n\t\t\t\t\t\ttmpTextAnchor = 'start';\n\t\t\t\t\t\ttmpStripeX = tmpBadgeX + tmpBadgeWidth - tmpBadgeBorderW;\n\t\t\t\t\t\ttmpStripeY = tmpBadgeY;\n\t\t\t\t\t\ttmpStripeW = tmpBadgeBorderW;\n\t\t\t\t\t\ttmpStripeH = tmpBadgeHeight;\n\t\t\t\t\t\ttmpBorderPath = 'M ' + tmpBadgeX + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + (tmpBadgeY + tmpBadgeHeight);\n\t\t\t\t\t}\n\t\t\t\t\telse if (tmpEdge === 'right')\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBadgeWidth = tmpBadgeBorderW + tmpBadgePadH + tmpTextLen + tmpBadgePadH + tmpPortRadius;\n\t\t\t\t\t\ttmpBadgeX = pWidth - tmpBadgeWidth - tmpEdgePad;\n\t\t\t\t\t\ttmpBadgeY = tmpPosition.y - tmpBadgeHeight / 2;\n\t\t\t\t\t\ttmpTextX = tmpBadgeX + tmpBadgeBorderW + tmpBadgePadH;\n\t\t\t\t\t\ttmpTextAnchor = 'start';\n\t\t\t\t\t\ttmpStripeX = tmpBadgeX;\n\t\t\t\t\t\ttmpStripeY = tmpBadgeY;\n\t\t\t\t\t\ttmpStripeW = tmpBadgeBorderW;\n\t\t\t\t\t\ttmpStripeH = tmpBadgeHeight;\n\t\t\t\t\t\ttmpBorderPath = 'M ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + (tmpBadgeY + tmpBadgeHeight);\n\t\t\t\t\t}\n\t\t\t\t\telse if (tmpEdge === 'top')\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBadgeWidth = tmpTextLen + tmpBadgePadH * 2;\n\t\t\t\t\t\ttmpBadgeX = tmpPosition.x - tmpBadgeWidth / 2;\n\t\t\t\t\t\ttmpBadgeY = tmpEdgePad;\n\t\t\t\t\t\ttmpTextX = tmpPosition.x;\n\t\t\t\t\t\ttmpTextAnchor = 'middle';\n\t\t\t\t\t\ttmpStripeX = tmpBadgeX;\n\t\t\t\t\t\ttmpStripeY = tmpBadgeY + tmpBadgeHeight - tmpBadgeBorderW;\n\t\t\t\t\t\ttmpStripeW = tmpBadgeWidth;\n\t\t\t\t\t\ttmpStripeH = tmpBadgeBorderW;\n\t\t\t\t\t\ttmpBorderPath = 'M ' + tmpBadgeX + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + tmpBadgeY;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpBadgeWidth = tmpTextLen + tmpBadgePadH * 2;\n\t\t\t\t\t\ttmpBadgeX = tmpPosition.x - tmpBadgeWidth / 2;\n\t\t\t\t\t\ttmpBadgeY = pHeight - tmpBadgeHeight - tmpEdgePad;\n\t\t\t\t\t\ttmpTextX = tmpPosition.x;\n\t\t\t\t\t\ttmpTextAnchor = 'middle';\n\t\t\t\t\t\ttmpStripeX = tmpBadgeX;\n\t\t\t\t\t\ttmpStripeY = tmpBadgeY;\n\t\t\t\t\t\ttmpStripeW = tmpBadgeWidth;\n\t\t\t\t\t\ttmpStripeH = tmpBadgeBorderW;\n\t\t\t\t\t\ttmpBorderPath = 'M ' + tmpBadgeX + ' ' + (tmpBadgeY + tmpBadgeHeight)\n\t\t\t\t\t\t\t+ ' L ' + tmpBadgeX + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + tmpBadgeY\n\t\t\t\t\t\t\t+ ' L ' + (tmpBadgeX + tmpBadgeWidth) + ' ' + (tmpBadgeY + tmpBadgeHeight);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Background rect (cream, no stroke — border drawn separately)\n\t\t\t\t\tlet tmpBgRect = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\t\t\t\ttmpBgRect.setAttribute('class', 'pict-flow-port-label-bg');\n\t\t\t\t\ttmpBgRect.setAttribute('x', String(tmpBadgeX));\n\t\t\t\t\ttmpBgRect.setAttribute('y', String(tmpBadgeY));\n\t\t\t\t\ttmpBgRect.setAttribute('width', String(tmpBadgeWidth));\n\t\t\t\t\ttmpBgRect.setAttribute('height', String(tmpBadgeHeight));\n\t\t\t\t\ttmpBgRect.setAttribute('fill', 'var(--pf-port-label-bg, rgba(255, 253, 240, 0.5))');\n\t\t\t\t\tpGroup.appendChild(tmpBgRect);\n\n\t\t\t\t\t// 3-sided border path (open on the edge-facing side)\n\t\t\t\t\tlet tmpBorderPathEl = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\t\t\ttmpBorderPathEl.setAttribute('class', 'pict-flow-port-label-bg');\n\t\t\t\t\ttmpBorderPathEl.setAttribute('d', tmpBorderPath);\n\t\t\t\t\ttmpBorderPathEl.setAttribute('fill', 'none');\n\t\t\t\t\ttmpBorderPathEl.setAttribute('stroke', tmpBorderColor);\n\t\t\t\t\ttmpBorderPathEl.setAttribute('stroke-width', '0.75');\n\t\t\t\t\tpGroup.appendChild(tmpBorderPathEl);\n\n\t\t\t\t\t// Colored stripe on the inner side\n\t\t\t\t\tlet tmpStripe = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\t\t\t\ttmpStripe.setAttribute('class', 'pict-flow-port-label-bg');\n\t\t\t\t\ttmpStripe.setAttribute('x', String(tmpStripeX));\n\t\t\t\t\ttmpStripe.setAttribute('y', String(tmpStripeY));\n\t\t\t\t\ttmpStripe.setAttribute('width', String(tmpStripeW));\n\t\t\t\t\ttmpStripe.setAttribute('height', String(tmpStripeH));\n\t\t\t\t\ttmpStripe.setAttribute('fill', tmpBorderColor);\n\t\t\t\t\tpGroup.appendChild(tmpStripe);\n\n\t\t\t\t\t// Text label — appended after circle for z-order\n\t\t\t\t\ttmpLabelElement = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\t\ttmpLabelElement.setAttribute('class', 'pict-flow-port-label');\n\t\t\t\t\ttmpLabelElement.setAttribute('fill', 'var(--pf-port-label-text, #2c3e50)');\n\t\t\t\t\ttmpLabelElement.textContent = tmpPort.Label;\n\t\t\t\t\ttmpLabelElement.setAttribute('x', String(tmpTextX));\n\t\t\t\t\ttmpLabelElement.setAttribute('y', String(tmpBadgeY + tmpBadgeHeight / 2));\n\t\t\t\t\ttmpLabelElement.setAttribute('text-anchor', tmpTextAnchor);\n\t\t\t\t\ttmpLabelElement.setAttribute('dominant-baseline', 'central');\n\t\t\t\t}\n\n\t\t\t\t// Port circle (rendered on top of badge background)\n\t\t\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\t\t\tlet tmpCircle;\n\t\t\t\tif (tmpShapeProvider)\n\t\t\t\t{\n\t\t\t\t\ttmpCircle = tmpShapeProvider.createPortElement(tmpPort, tmpPosition, pNodeData.Hash);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttmpCircle = this._FlowView._SVGHelperProvider.createSVGElement('circle');\n\t\t\t\t\tlet tmpPortClass = `pict-flow-port ${tmpPort.Direction}`;\n\t\t\t\t\tif (tmpPort.PortType)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpPortClass += ` port-type-${tmpPort.PortType}`;\n\t\t\t\t\t}\n\t\t\t\t\ttmpCircle.setAttribute('class', tmpPortClass);\n\t\t\t\t\ttmpCircle.setAttribute('cx', String(tmpPosition.x));\n\t\t\t\t\ttmpCircle.setAttribute('cy', String(tmpPosition.y));\n\t\t\t\t\ttmpCircle.setAttribute('r', '5');\n\t\t\t\t\ttmpCircle.setAttribute('data-port-hash', tmpPort.Hash);\n\t\t\t\t\ttmpCircle.setAttribute('data-node-hash', pNodeData.Hash);\n\t\t\t\t\ttmpCircle.setAttribute('data-port-direction', tmpPort.Direction);\n\t\t\t\t\tif (tmpPort.PortType)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpCircle.setAttribute('data-port-type', tmpPort.PortType);\n\t\t\t\t\t}\n\t\t\t\t\ttmpCircle.setAttribute('data-element-type', 'port');\n\t\t\t\t}\n\t\t\t\tpGroup.appendChild(tmpCircle);\n\n\t\t\t\t// Port label text (on top of everything)\n\t\t\t\tif (tmpLabelElement)\n\t\t\t\t{\n\t\t\t\t\tpGroup.appendChild(tmpLabelElement);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Calculate port position relative to node origin.\n\t *\n\t * Delegates to the geometry provider's getPortLocalPosition method.\n\t *\n\t * @param {string} pSide - 'left', 'right', 'top', 'bottom' (or compound sides)\n\t * @param {number} pIndex - Index of this port on its side\n\t * @param {number} pTotal - Total ports on this side\n\t * @param {number} pWidth - Node width\n\t * @param {number} pHeight - Node height\n\t * @param {number} pNodeTitleBarHeight - Title bar height\n\t * @param {Object} [pPortCountsBySide] - Optional map of Side → count for adaptive zones\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetPortLocalPosition(pSide, pIndex, pTotal, pWidth, pHeight, pNodeTitleBarHeight, pPortCountsBySide)\n\t{\n\t\treturn this._FlowView._GeometryProvider.getPortLocalPosition(pSide, pIndex, pTotal, pWidth, pHeight, pNodeTitleBarHeight, pPortCountsBySide);\n\t}\n}\n\nmodule.exports = PictServiceFlowPortRenderer;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-RenderManager\n *\n * Orchestrates rendering of the flow diagram: nodes, connections,\n * tethers, panels, SVG marker definitions, and node position updates.\n *\n * Extracted from PictView-Flow.js to isolate rendering orchestration\n * from data management and interaction handling.\n */\nclass PictServiceFlowRenderManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowRenderManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Render the complete flow diagram\n\t */\n\trenderFlow()\n\t{\n\t\tif (!this._FlowView) return;\n\t\tif (!this._FlowView._NodesLayer || !this._FlowView._ConnectionsLayer) return;\n\n\t\t// Clear existing SVG content\n\t\twhile (this._FlowView._NodesLayer.firstChild)\n\t\t{\n\t\t\tthis._FlowView._NodesLayer.removeChild(this._FlowView._NodesLayer.firstChild);\n\t\t}\n\t\twhile (this._FlowView._ConnectionsLayer.firstChild)\n\t\t{\n\t\t\tthis._FlowView._ConnectionsLayer.removeChild(this._FlowView._ConnectionsLayer.firstChild);\n\t\t}\n\n\t\t// Render connections first (behind nodes)\n\t\tfor (let i = 0; i < this._FlowView._FlowData.Connections.length; i++)\n\t\t{\n\t\t\tlet tmpConnection = this._FlowView._FlowData.Connections[i];\n\t\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedConnectionHash === tmpConnection.Hash);\n\n\t\t\tthis._FlowView._ConnectionRenderer.renderConnection(\n\t\t\t\ttmpConnection,\n\t\t\t\tthis._FlowView._ConnectionsLayer,\n\t\t\t\ttmpIsSelected\n\t\t\t);\n\t\t}\n\n\t\t// Render nodes\n\t\tfor (let i = 0; i < this._FlowView._FlowData.Nodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = this._FlowView._FlowData.Nodes[i];\n\t\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedNodeHash === tmpNode.Hash);\n\t\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNode.Type);\n\n\t\t\t// Enrich saved port data with metadata from the node type's DefaultPorts.\n\t\t\t// Saved flow data may not include PortType or may have stale Side values,\n\t\t\t// so we match each port to its DefaultPort counterpart by Label and Direction,\n\t\t\t// then copy over PortType and Side from the authoritative node type definition.\n\t\t\tif (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultPorts && tmpNode.Ports)\n\t\t\t{\n\t\t\t\tfor (let p = 0; p < tmpNode.Ports.length; p++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpPort = tmpNode.Ports[p];\n\t\t\t\t\tfor (let d = 0; d < tmpNodeTypeConfig.DefaultPorts.length; d++)\n\t\t\t\t\t{\n\t\t\t\t\t\tlet tmpDefault = tmpNodeTypeConfig.DefaultPorts[d];\n\t\t\t\t\t\tif (tmpDefault.Label === tmpPort.Label && tmpDefault.Direction === tmpPort.Direction)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (tmpDefault.PortType)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttmpPort.PortType = tmpDefault.PortType;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (tmpDefault.Side)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttmpPort.Side = tmpDefault.Side;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._FlowView._NodeView.renderNode(tmpNode, this._FlowView._NodesLayer, tmpIsSelected, tmpNodeTypeConfig);\n\t\t}\n\n\t\t// Render properties panels and tethers\n\t\tif (this._FlowView._PropertiesPanelView && this._FlowView._PanelsLayer && this._FlowView._TethersLayer)\n\t\t{\n\t\t\tthis._FlowView._PropertiesPanelView.renderPanels(\n\t\t\t\tthis._FlowView._FlowData.OpenPanels,\n\t\t\t\tthis._FlowView._PanelsLayer,\n\t\t\t\tthis._FlowView._TethersLayer,\n\t\t\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash\n\t\t\t);\n\t\t}\n\n\t\t// Update viewport transform\n\t\tthis._FlowView.updateViewportTransform();\n\t}\n\n\t/**\n\t * Re-render a single connection (remove and re-add) for smooth drag performance.\n\t * @param {string} pConnectionHash\n\t */\n\trenderSingleConnection(pConnectionHash)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._ConnectionsLayer) return;\n\n\t\t// Remove existing elements for this connection\n\t\tlet tmpExisting = this._FlowView._ConnectionsLayer.querySelectorAll(`[data-connection-hash=\"${pConnectionHash}\"]`);\n\t\tfor (let i = 0; i < tmpExisting.length; i++)\n\t\t{\n\t\t\ttmpExisting[i].remove();\n\t\t}\n\n\t\tlet tmpConnection = this._FlowView.getConnection(pConnectionHash);\n\t\tif (!tmpConnection) return;\n\n\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedConnectionHash === pConnectionHash);\n\t\tthis._FlowView._ConnectionRenderer.renderConnection(tmpConnection, this._FlowView._ConnectionsLayer, tmpIsSelected);\n\t}\n\n\t/**\n\t * Re-render a single tether (remove and re-add) for smooth drag performance.\n\t * @param {string} pPanelHash\n\t */\n\trenderSingleTether(pPanelHash)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._TethersLayer || !this._FlowView._TetherService) return;\n\n\t\t// Remove existing tether elements for this panel\n\t\tlet tmpExisting = this._FlowView._TethersLayer.querySelectorAll(`[data-panel-hash=\"${pPanelHash}\"]`);\n\t\tfor (let i = 0; i < tmpExisting.length; i++)\n\t\t{\n\t\t\ttmpExisting[i].remove();\n\t\t}\n\n\t\tlet tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tlet tmpNodeData = this._FlowView.getNode(tmpPanel.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedTetherHash === pPanelHash);\n\t\tthis._FlowView._TetherService.renderTether(tmpPanel, tmpNodeData, this._FlowView._TethersLayer, tmpIsSelected, this._FlowView.options.ViewIdentifier);\n\t}\n\n\t/**\n\t * Update a single node's position in the SVG without full re-render (for drag performance)\n\t * @param {string} pNodeHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdateNodePosition(pNodeHash, pX, pY)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpNode = this._FlowView.getNode(pNodeHash);\n\t\tif (!tmpNode) return;\n\n\t\tif (this._FlowView.options.EnableGridSnap)\n\t\t{\n\t\t\tpX = this._FlowView._LayoutService.snapToGrid(pX, this._FlowView.options.GridSnapSize);\n\t\t\tpY = this._FlowView._LayoutService.snapToGrid(pY, this._FlowView.options.GridSnapSize);\n\t\t}\n\n\t\ttmpNode.X = pX;\n\t\ttmpNode.Y = pY;\n\n\t\t// Reset customized handle positions for connections/tethers involving this node\n\t\tthis._FlowView._resetHandlesForNode(pNodeHash);\n\n\t\t// Update the node's SVG group transform for smooth dragging\n\t\tlet tmpNodeGroup = this._FlowView._NodesLayer.querySelector(`[data-node-hash=\"${pNodeHash}\"]`);\n\t\tif (tmpNodeGroup)\n\t\t{\n\t\t\ttmpNodeGroup.setAttribute('transform', `translate(${pX}, ${pY})`);\n\t\t}\n\n\t\t// Re-render connections that involve this node\n\t\tthis.renderConnectionsForNode(pNodeHash);\n\n\t\t// Update tethers for any panels attached to this node\n\t\tthis.renderTethersForNode(pNodeHash);\n\t}\n\n\t/**\n\t * Re-render only connections that involve a specific node (for drag performance)\n\t * @param {string} pNodeHash\n\t */\n\trenderConnectionsForNode(pNodeHash)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._ConnectionsLayer) return;\n\n\t\tlet tmpAffectedConnections = this._FlowView._FlowData.Connections.filter((pConn) =>\n\t\t{\n\t\t\treturn pConn.SourceNodeHash === pNodeHash || pConn.TargetNodeHash === pNodeHash;\n\t\t});\n\n\t\tfor (let i = 0; i < tmpAffectedConnections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = tmpAffectedConnections[i];\n\t\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedConnectionHash === tmpConn.Hash);\n\n\t\t\t// Remove existing connection SVG elements\n\t\t\tlet tmpExisting = this._FlowView._ConnectionsLayer.querySelectorAll(`[data-connection-hash=\"${tmpConn.Hash}\"]`);\n\t\t\tfor (let j = 0; j < tmpExisting.length; j++)\n\t\t\t{\n\t\t\t\ttmpExisting[j].remove();\n\t\t\t}\n\n\t\t\t// Re-render this connection\n\t\t\tthis._FlowView._ConnectionRenderer.renderConnection(tmpConn, this._FlowView._ConnectionsLayer, tmpIsSelected);\n\t\t}\n\t}\n\n\t/**\n\t * Re-render tethers for panels attached to a specific node (for drag performance).\n\t * @param {string} pNodeHash\n\t */\n\trenderTethersForNode(pNodeHash)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._TethersLayer || !this._FlowView._TetherService) return;\n\n\t\tlet tmpAffectedPanels = this._FlowView._FlowData.OpenPanels.filter((pPanel) => pPanel.NodeHash === pNodeHash);\n\t\tif (tmpAffectedPanels.length === 0) return;\n\n\t\t// Remove existing tethers for these panels and re-render via TetherService\n\t\tfor (let i = 0; i < tmpAffectedPanels.length; i++)\n\t\t{\n\t\t\tlet tmpExisting = this._FlowView._TethersLayer.querySelectorAll(`[data-panel-hash=\"${tmpAffectedPanels[i].Hash}\"]`);\n\t\t\tfor (let j = 0; j < tmpExisting.length; j++)\n\t\t\t{\n\t\t\t\ttmpExisting[j].remove();\n\t\t\t}\n\n\t\t\tlet tmpNodeData = this._FlowView.getNode(tmpAffectedPanels[i].NodeHash);\n\t\t\tif (!tmpNodeData) continue;\n\n\t\t\tlet tmpIsSelected = (this._FlowView._FlowData.ViewState.SelectedTetherHash === tmpAffectedPanels[i].Hash);\n\t\t\tthis._FlowView._TetherService.renderTether(tmpAffectedPanels[i], tmpNodeData, this._FlowView._TethersLayer, tmpIsSelected, this._FlowView.options.ViewIdentifier);\n\t\t}\n\t}\n\n\t/**\n\t * Re-inject SVG marker definitions (arrowheads).\n\t * Called after a theme switch to update arrowhead colors.\n\t */\n\treinjectMarkerDefs()\n\t{\n\t\tif (!this._FlowView || !this._FlowView._ConnectorShapesProvider || !this._FlowView._SVGElement) return;\n\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\tlet tmpDefs = this._FlowView._SVGElement.querySelector('defs');\n\t\tif (!tmpDefs) return;\n\n\t\t// Remove existing marker elements\n\t\tlet tmpExistingMarkers = tmpDefs.querySelectorAll('marker');\n\t\tfor (let i = 0; i < tmpExistingMarkers.length; i++)\n\t\t{\n\t\t\ttmpExistingMarkers[i].remove();\n\t\t}\n\n\t\t// Re-generate and inject\n\t\tlet tmpMarkerMarkup = this._FlowView._ConnectorShapesProvider.generateMarkerDefs(tmpViewIdentifier);\n\t\tlet tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\t\ttmpTempSVG.innerHTML = tmpMarkerMarkup;\n\t\twhile (tmpTempSVG.firstChild)\n\t\t{\n\t\t\ttmpDefs.appendChild(tmpTempSVG.firstChild);\n\t\t}\n\t}\n}\n\nmodule.exports = PictServiceFlowRenderManager;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-SelectionManager\n *\n * Manages selection state for nodes, connections, and tethers in the flow diagram.\n * Handles selecting, deselecting, and deleting selected elements.\n */\nclass PictServiceFlowSelectionManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowSelectionManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t/**\n\t * Select a node\n\t * @param {string|null} pNodeHash - Hash of the node to select, or null to deselect\n\t */\n\tselectNode(pNodeHash)\n\t{\n\t\tlet tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedNodeHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = pNodeHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash = null;\n\n\t\tthis._FlowView.renderFlow();\n\n\t\tif (this._FlowView._EventHandlerProvider && pNodeHash !== tmpPreviousSelection)\n\t\t{\n\t\t\tlet tmpNode = pNodeHash ? this._FlowView._FlowData.Nodes.find((pNode) => pNode.Hash === pNodeHash) : null;\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onNodeSelected', tmpNode);\n\t\t}\n\t}\n\n\t/**\n\t * Select a connection\n\t * @param {string|null} pConnectionHash - Hash of the connection to select, or null to deselect\n\t */\n\tselectConnection(pConnectionHash)\n\t{\n\t\tlet tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedConnectionHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = pConnectionHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash = null;\n\n\t\tthis._FlowView.renderFlow();\n\n\t\tif (this._FlowView._EventHandlerProvider && pConnectionHash !== tmpPreviousSelection)\n\t\t{\n\t\t\tlet tmpConnection = pConnectionHash ? this._FlowView._FlowData.Connections.find((pConn) => pConn.Hash === pConnectionHash) : null;\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onConnectionSelected', tmpConnection);\n\t\t}\n\t}\n\n\t/**\n\t * Select a tether by its panel hash.\n\t * @param {string|null} pPanelHash - Hash of the panel whose tether to select, or null to deselect\n\t */\n\tselectTether(pPanelHash)\n\t{\n\t\tlet tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedTetherHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash = pPanelHash;\n\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = null;\n\n\t\tthis._FlowView.renderFlow();\n\n\t\tif (this._FlowView._EventHandlerProvider && pPanelHash !== tmpPreviousSelection)\n\t\t{\n\t\t\tlet tmpPanel = pPanelHash ? this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash) : null;\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onTetherSelected', tmpPanel);\n\t\t}\n\t}\n\n\t/**\n\t * Deselect all nodes and connections\n\t */\n\tdeselectAll()\n\t{\n\t\tthis._FlowView._FlowData.ViewState.SelectedNodeHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedConnectionHash = null;\n\t\tthis._FlowView._FlowData.ViewState.SelectedTetherHash = null;\n\t\tthis._FlowView.renderFlow();\n\t}\n\n\t/**\n\t * Delete the currently selected node or connection\n\t * @returns {boolean}\n\t */\n\tdeleteSelected()\n\t{\n\t\tif (this._FlowView._FlowData.ViewState.SelectedNodeHash)\n\t\t{\n\t\t\treturn this._FlowView.removeNode(this._FlowView._FlowData.ViewState.SelectedNodeHash);\n\t\t}\n\t\tif (this._FlowView._FlowData.ViewState.SelectedConnectionHash)\n\t\t{\n\t\t\treturn this._FlowView.removeConnection(this._FlowView._FlowData.ViewState.SelectedConnectionHash);\n\t\t}\n\t\treturn false;\n\t}\n}\n\nmodule.exports = PictServiceFlowSelectionManager;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-Tether\n *\n * Centralizes all tether geometry, path generation, handle state management,\n * and SVG rendering for the lines that connect properties panels to their nodes.\n *\n * Delegates to shared providers for:\n * - SVG element creation (_FlowView._SVGHelperProvider)\n * - Geometry calculations (_FlowView._GeometryProvider)\n * - Path string building (_FlowView._PathGenerator)\n *\n * Responsibilities:\n * - Smart 4-quadrant anchor detection (which edge of the node/panel to connect)\n * - Bezier and orthogonal path generation\n * - Auto-midpoint and auto-corner calculations\n * - Handle position updates during drag\n * - Handle reset when nodes or panels move\n * - Line mode toggling (bezier <-> orthogonal)\n * - SVG element creation for tether lines, hit areas, and drag handles\n */\nclass PictServiceFlowTether extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowTether';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\t}\n\n\t// ---- Anchor Calculation ----\n\n\t/**\n\t * Determine which node edge and panel edge to connect based on 4-quadrant detection.\n\t * Uses the relative position of the panel center to the node center.\n\t *\n\t * @param {Object} pPanelData - Panel data with X, Y, Width, Height\n\t * @param {Object} pNodeData - Node data with X, Y, Width, Height\n\t * @returns {{nodeAnchor: {x,y,side}, panelAnchor: {x,y,side}}}\n\t */\n\tgetSmartAnchors(pPanelData, pNodeData)\n\t{\n\t\tlet tmpNodeCX = pNodeData.X + pNodeData.Width / 2;\n\t\tlet tmpNodeCY = pNodeData.Y + pNodeData.Height / 2;\n\t\tlet tmpPanelCX = pPanelData.X + pPanelData.Width / 2;\n\t\tlet tmpPanelCY = pPanelData.Y + pPanelData.Height / 2;\n\n\t\tlet tmpDX = tmpPanelCX - tmpNodeCX;\n\t\tlet tmpDY = tmpPanelCY - tmpNodeCY;\n\n\t\tlet tmpNodeSide, tmpPanelSide;\n\n\t\tif (Math.abs(tmpDX) >= Math.abs(tmpDY))\n\t\t{\n\t\t\t// Panel is primarily to the left or right\n\t\t\tif (tmpDX >= 0)\n\t\t\t{\n\t\t\t\ttmpNodeSide = 'right';\n\t\t\t\ttmpPanelSide = 'left';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpNodeSide = 'left';\n\t\t\t\ttmpPanelSide = 'right';\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Panel is primarily above or below\n\t\t\tif (tmpDY >= 0)\n\t\t\t{\n\t\t\t\ttmpNodeSide = 'bottom';\n\t\t\t\ttmpPanelSide = 'top';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpNodeSide = 'top';\n\t\t\t\ttmpPanelSide = 'bottom';\n\t\t\t}\n\t\t}\n\n\t\tlet tmpNodeAnchor = this._FlowView._GeometryProvider.getEdgeCenter(pNodeData, tmpNodeSide);\n\t\tlet tmpPanelAnchor = this._FlowView._GeometryProvider.getEdgeCenter(pPanelData, tmpPanelSide);\n\n\t\treturn {\n\t\t\tnodeAnchor: Object.assign(tmpNodeAnchor, { side: tmpNodeSide }),\n\t\t\tpanelAnchor: Object.assign(tmpPanelAnchor, { side: tmpPanelSide })\n\t\t};\n\t}\n\n\t// ---- Path Generation ----\n\n\t/**\n\t * Generate a bezier path between two anchor points with directional departure/approach.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @param {number|null} pHandleX - User-set handle X or null for auto\n\t * @param {number|null} pHandleY - User-set handle Y or null for auto\n\t * @returns {string} SVG path d attribute\n\t */\n\tgenerateBezierPath(pFrom, pTo, pHandleX, pHandleY)\n\t{\n\t\tlet tmpDepartDist = 20;\n\t\tlet tmpFromDir = this._FlowView._GeometryProvider.sideDirection(pFrom.side);\n\t\tlet tmpToDir = this._FlowView._GeometryProvider.sideDirection(pTo.side);\n\n\t\tlet tmpDepartX = pFrom.x + tmpFromDir.dx * tmpDepartDist;\n\t\tlet tmpDepartY = pFrom.y + tmpFromDir.dy * tmpDepartDist;\n\t\tlet tmpApproachX = pTo.x + tmpToDir.dx * tmpDepartDist;\n\t\tlet tmpApproachY = pTo.y + tmpToDir.dy * tmpDepartDist;\n\n\t\tif (pHandleX == null || pHandleY == null)\n\t\t{\n\t\t\t// Auto bezier: simple cubic from depart to approach\n\t\t\tlet tmpSpanX = Math.abs(tmpApproachX - tmpDepartX);\n\t\t\tlet tmpSpanY = Math.abs(tmpApproachY - tmpDepartY);\n\t\t\tlet tmpSpan = Math.max(tmpSpanX, tmpSpanY, 40);\n\t\t\tlet tmpCPDist = tmpSpan * 0.4;\n\n\t\t\tlet tmpCP1X = tmpDepartX + tmpFromDir.dx * tmpCPDist;\n\t\t\tlet tmpCP1Y = tmpDepartY + tmpFromDir.dy * tmpCPDist;\n\t\t\tlet tmpCP2X = tmpApproachX + tmpToDir.dx * tmpCPDist;\n\t\t\tlet tmpCP2Y = tmpApproachY + tmpToDir.dy * tmpCPDist;\n\n\t\t\treturn this._FlowView._PathGenerator.buildBezierPathString(\n\t\t\t\t{ x: pFrom.x, y: pFrom.y },\n\t\t\t\t{ x: tmpDepartX, y: tmpDepartY },\n\t\t\t\t{ x: tmpCP1X, y: tmpCP1Y },\n\t\t\t\t{ x: tmpCP2X, y: tmpCP2Y },\n\t\t\t\t{ x: tmpApproachX, y: tmpApproachY },\n\t\t\t\t{ x: pTo.x, y: pTo.y }\n\t\t\t);\n\t\t}\n\n\t\t// User-set handle: split bezier into two segments through handle\n\t\tlet tmpCP1aDist = 30;\n\t\tlet tmpCP1aX = tmpDepartX + tmpFromDir.dx * tmpCP1aDist;\n\t\tlet tmpCP1aY = tmpDepartY + tmpFromDir.dy * tmpCP1aDist;\n\n\t\tlet tmpCP2aDist = 30;\n\t\tlet tmpCP2aX = tmpApproachX + tmpToDir.dx * tmpCP2aDist;\n\t\tlet tmpCP2aY = tmpApproachY + tmpToDir.dy * tmpCP2aDist;\n\n\t\t// Tangent at the handle — direction from first segment end to second segment start\n\t\tlet tmpTangentX = tmpApproachX - tmpDepartX;\n\t\tlet tmpTangentY = tmpApproachY - tmpDepartY;\n\t\tlet tmpTangentLen = Math.sqrt(tmpTangentX * tmpTangentX + tmpTangentY * tmpTangentY) || 1;\n\t\ttmpTangentX /= tmpTangentLen;\n\t\ttmpTangentY /= tmpTangentLen;\n\t\tlet tmpTangentDist = 25;\n\n\t\tlet tmpCP1bX = pHandleX - tmpTangentX * tmpTangentDist;\n\t\tlet tmpCP1bY = pHandleY - tmpTangentY * tmpTangentDist;\n\t\tlet tmpCP2bX = pHandleX + tmpTangentX * tmpTangentDist;\n\t\tlet tmpCP2bY = pHandleY + tmpTangentY * tmpTangentDist;\n\n\t\treturn this._FlowView._PathGenerator.buildSplitBezierPathString(\n\t\t\t{ x: pFrom.x, y: pFrom.y },\n\t\t\t{ x: tmpDepartX, y: tmpDepartY },\n\t\t\t{ x: tmpCP1aX, y: tmpCP1aY },\n\t\t\t{ x: tmpCP1bX, y: tmpCP1bY },\n\t\t\t{ x: pHandleX, y: pHandleY },\n\t\t\t{ x: tmpCP2bX, y: tmpCP2bY },\n\t\t\t{ x: tmpCP2aX, y: tmpCP2aY },\n\t\t\t{ x: tmpApproachX, y: tmpApproachY },\n\t\t\t{ x: pTo.x, y: pTo.y }\n\t\t);\n\t}\n\n\t/**\n\t * Generate an orthogonal (90-degree) path between two anchor points.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @param {Object|null} pCorners - {corner1: {x,y}, corner2: {x,y}} or null for auto\n\t * @param {number} pMidOffset - Offset for the corridor midpoint\n\t * @returns {string} SVG path d attribute\n\t */\n\tgenerateOrthogonalPath(pFrom, pTo, pCorners, pMidOffset)\n\t{\n\t\tlet tmpDepartDist = 20;\n\t\tlet tmpFromDir = this._FlowView._GeometryProvider.sideDirection(pFrom.side);\n\t\tlet tmpToDir = this._FlowView._GeometryProvider.sideDirection(pTo.side);\n\n\t\tlet tmpDepartX = pFrom.x + tmpFromDir.dx * tmpDepartDist;\n\t\tlet tmpDepartY = pFrom.y + tmpFromDir.dy * tmpDepartDist;\n\t\tlet tmpApproachX = pTo.x + tmpToDir.dx * tmpDepartDist;\n\t\tlet tmpApproachY = pTo.y + tmpToDir.dy * tmpDepartDist;\n\n\t\tlet tmpCorner1, tmpCorner2;\n\n\t\tif (pCorners && pCorners.corner1 && pCorners.corner2)\n\t\t{\n\t\t\ttmpCorner1 = pCorners.corner1;\n\t\t\ttmpCorner2 = pCorners.corner2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Auto-calculate corners based on direction\n\t\t\tlet tmpAutoCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(tmpDepartX, tmpDepartY, tmpApproachX, tmpApproachY, tmpFromDir, tmpToDir, pMidOffset);\n\t\t\ttmpCorner1 = tmpAutoCorners.corner1;\n\t\t\ttmpCorner2 = tmpAutoCorners.corner2;\n\t\t}\n\n\t\treturn this._FlowView._PathGenerator.buildOrthogonalPathString(\n\t\t\t{ x: pFrom.x, y: pFrom.y },\n\t\t\t{ x: tmpDepartX, y: tmpDepartY },\n\t\t\t{ x: tmpCorner1.x, y: tmpCorner1.y },\n\t\t\t{ x: tmpCorner2.x, y: tmpCorner2.y },\n\t\t\t{ x: tmpApproachX, y: tmpApproachY },\n\t\t\t{ x: pTo.x, y: pTo.y }\n\t\t);\n\t}\n\n\t// ---- Handle Position Computation ----\n\n\t/**\n\t * Get auto-calculated bezier midpoint for a tether at t=0.5 on the curve.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @returns {{x: number, y: number}}\n\t */\n\tgetAutoMidpoint(pFrom, pTo)\n\t{\n\t\treturn this._FlowView._PathGenerator.getAutoMidpointSimple(pFrom, pTo, 20);\n\t}\n\n\t/**\n\t * Get full orthogonal geometry including corners and midpoint for handle rendering.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @param {Object} pPanelData - Panel data with tether handle properties\n\t * @returns {{corner1: {x,y}, corner2: {x,y}, midpoint: {x,y}}}\n\t */\n\tgetOrthoGeometry(pFrom, pTo, pPanelData)\n\t{\n\t\tlet tmpDepartDist = 20;\n\t\tlet tmpFromDir = this._FlowView._GeometryProvider.sideDirection(pFrom.side);\n\t\tlet tmpToDir = this._FlowView._GeometryProvider.sideDirection(pTo.side);\n\n\t\tlet tmpDepartX = pFrom.x + tmpFromDir.dx * tmpDepartDist;\n\t\tlet tmpDepartY = pFrom.y + tmpFromDir.dy * tmpDepartDist;\n\t\tlet tmpApproachX = pTo.x + tmpToDir.dx * tmpDepartDist;\n\t\tlet tmpApproachY = pTo.y + tmpToDir.dy * tmpDepartDist;\n\n\t\tlet tmpCorners;\n\t\tif (pPanelData.TetherHandleCustomized && pPanelData.TetherOrthoCorner1X != null)\n\t\t{\n\t\t\ttmpCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(tmpDepartX, tmpDepartY, tmpApproachX, tmpApproachY, tmpFromDir, tmpToDir, pPanelData.TetherOrthoMidOffset || 0);\n\t\t\ttmpCorners.corner1 = { x: pPanelData.TetherOrthoCorner1X, y: pPanelData.TetherOrthoCorner1Y };\n\t\t\ttmpCorners.corner2 = { x: pPanelData.TetherOrthoCorner2X, y: pPanelData.TetherOrthoCorner2Y };\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(tmpDepartX, tmpDepartY, tmpApproachX, tmpApproachY, tmpFromDir, tmpToDir, pPanelData.TetherOrthoMidOffset || 0);\n\t\t}\n\n\t\tlet tmpMidpoint =\n\t\t{\n\t\t\tx: (tmpCorners.corner1.x + tmpCorners.corner2.x) / 2,\n\t\t\ty: (tmpCorners.corner1.y + tmpCorners.corner2.y) / 2\n\t\t};\n\n\t\treturn {\n\t\t\tcorner1: tmpCorners.corner1,\n\t\t\tcorner2: tmpCorners.corner2,\n\t\t\tmidpoint: tmpMidpoint\n\t\t};\n\t}\n\n\t// ---- Path Generation (high-level) ----\n\n\t/**\n\t * Generate the SVG path string for a tether based on its panel data and anchors.\n\t * @param {Object} pPanelData - Panel data with tether handle properties\n\t * @param {Object} pFrom - {x, y, side} panel anchor\n\t * @param {Object} pTo - {x, y, side} node anchor\n\t * @returns {string} SVG path d attribute\n\t */\n\tgeneratePath(pPanelData, pFrom, pTo)\n\t{\n\t\tlet tmpLineMode = pPanelData.TetherLineMode || 'bezier';\n\n\t\tif (tmpLineMode === 'orthogonal')\n\t\t{\n\t\t\tlet tmpCorners = null;\n\t\t\tif (pPanelData.TetherHandleCustomized && pPanelData.TetherOrthoCorner1X != null)\n\t\t\t{\n\t\t\t\ttmpCorners =\n\t\t\t\t{\n\t\t\t\t\tcorner1: { x: pPanelData.TetherOrthoCorner1X, y: pPanelData.TetherOrthoCorner1Y },\n\t\t\t\t\tcorner2: { x: pPanelData.TetherOrthoCorner2X, y: pPanelData.TetherOrthoCorner2Y }\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn this.generateOrthogonalPath(pFrom, pTo, tmpCorners, pPanelData.TetherOrthoMidOffset || 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Check for multi-handle array first\n\t\t\tlet tmpHandles = this._getTetherBezierHandles(pPanelData);\n\t\t\tif (tmpHandles.length > 0)\n\t\t\t{\n\t\t\t\treturn this.generateMultiBezierPath(pFrom, pTo, tmpHandles);\n\t\t\t}\n\n\t\t\t// Single-handle legacy path\n\t\t\tlet tmpHandleX = (pPanelData.TetherHandleCustomized && pPanelData.TetherBezierHandleX != null) ? pPanelData.TetherBezierHandleX : null;\n\t\t\tlet tmpHandleY = (pPanelData.TetherHandleCustomized && pPanelData.TetherBezierHandleY != null) ? pPanelData.TetherBezierHandleY : null;\n\t\t\treturn this.generateBezierPath(pFrom, pTo, tmpHandleX, tmpHandleY);\n\t\t}\n\t}\n\n\t/**\n\t * Get the bezier handles array for a tether, respecting the customized flag.\n\t * @param {Object} pPanelData\n\t * @returns {Array<{x: number, y: number}>}\n\t */\n\t_getTetherBezierHandles(pPanelData)\n\t{\n\t\tif (!pPanelData || !pPanelData.TetherHandleCustomized)\n\t\t{\n\t\t\treturn [];\n\t\t}\n\n\t\t// Multi-handle format\n\t\tif (Array.isArray(pPanelData.TetherBezierHandles) && pPanelData.TetherBezierHandles.length > 0)\n\t\t{\n\t\t\treturn pPanelData.TetherBezierHandles;\n\t\t}\n\n\t\t// Legacy single-handle format\n\t\tif (pPanelData.TetherBezierHandleX != null && pPanelData.TetherBezierHandleY != null)\n\t\t{\n\t\t\treturn [{ x: pPanelData.TetherBezierHandleX, y: pPanelData.TetherBezierHandleY }];\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Generate a multi-handle bezier path for a tether.\n\t * Delegates to PathGenerator.buildMultiBezierPathString.\n\t * @param {Object} pFrom - {x, y, side}\n\t * @param {Object} pTo - {x, y, side}\n\t * @param {Array<{x: number, y: number}>} pHandles\n\t * @returns {string} SVG path d attribute\n\t */\n\tgenerateMultiBezierPath(pFrom, pTo, pHandles)\n\t{\n\t\tlet tmpDepartDist = 20;\n\t\tlet tmpFromDir = this._FlowView._GeometryProvider.sideDirection(pFrom.side);\n\t\tlet tmpToDir = this._FlowView._GeometryProvider.sideDirection(pTo.side);\n\n\t\tlet tmpDepart = {\n\t\t\tx: pFrom.x + tmpFromDir.dx * tmpDepartDist,\n\t\t\ty: pFrom.y + tmpFromDir.dy * tmpDepartDist\n\t\t};\n\t\tlet tmpApproach = {\n\t\t\tx: pTo.x + tmpToDir.dx * tmpDepartDist,\n\t\t\ty: pTo.y + tmpToDir.dy * tmpDepartDist\n\t\t};\n\n\t\treturn this._FlowView._PathGenerator.buildMultiBezierPathString(\n\t\t\tpFrom, tmpDepart, pFrom.side, tmpApproach, pTo.side, pTo, pHandles);\n\t}\n\n\t// ---- Handle State Management ----\n\n\t/**\n\t * Update a tether handle position during drag.\n\t * @param {Object} pPanelData - Panel data to update\n\t * @param {string} pHandleType - 'bezier-midpoint', 'ortho-corner1', 'ortho-corner2', 'ortho-midpoint'\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdateHandlePosition(pPanelData, pHandleType, pX, pY)\n\t{\n\t\tpPanelData.TetherHandleCustomized = true;\n\n\t\t// Multi-handle bezier: handle type is 'bezier-handle-N'\n\t\tif (pHandleType && pHandleType.startsWith('bezier-handle-'))\n\t\t{\n\t\t\tlet tmpIndex = parseInt(pHandleType.replace('bezier-handle-', ''), 10);\n\t\t\tif (!isNaN(tmpIndex) && Array.isArray(pPanelData.TetherBezierHandles)\n\t\t\t\t&& tmpIndex < pPanelData.TetherBezierHandles.length)\n\t\t\t{\n\t\t\t\tpPanelData.TetherBezierHandles[tmpIndex].x = pX;\n\t\t\t\tpPanelData.TetherBezierHandles[tmpIndex].y = pY;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (pHandleType)\n\t\t{\n\t\t\tcase 'bezier-midpoint':\n\t\t\t\t// Migrate to multi-handle array\n\t\t\t\tif (!Array.isArray(pPanelData.TetherBezierHandles)\n\t\t\t\t\t|| pPanelData.TetherBezierHandles.length === 0)\n\t\t\t\t{\n\t\t\t\t\tpPanelData.TetherBezierHandles = [{ x: pX, y: pY }];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpPanelData.TetherBezierHandles[0].x = pX;\n\t\t\t\t\tpPanelData.TetherBezierHandles[0].y = pY;\n\t\t\t\t}\n\t\t\t\t// Keep legacy fields in sync\n\t\t\t\tpPanelData.TetherBezierHandleX = pX;\n\t\t\t\tpPanelData.TetherBezierHandleY = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-corner1':\n\t\t\t\tpPanelData.TetherOrthoCorner1X = pX;\n\t\t\t\tpPanelData.TetherOrthoCorner1Y = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-corner2':\n\t\t\t\tpPanelData.TetherOrthoCorner2X = pX;\n\t\t\t\tpPanelData.TetherOrthoCorner2Y = pY;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ortho-midpoint':\n\t\t\t\t// Store the desired position for offset computation\n\t\t\t\tpPanelData.TetherOrthoMidOffset = (pPanelData.TetherOrthoMidOffset || 0);\n\t\t\t\tpPanelData._TetherMidDragX = pX;\n\t\t\t\tpPanelData._TetherMidDragY = pY;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Reset tether handle positions to auto for a single panel.\n\t * Preserves TetherLineMode but clears all handle coordinates.\n\t * @param {Object} pPanelData - Panel data to reset\n\t */\n\tresetHandlePositions(pPanelData)\n\t{\n\t\tif (pPanelData.TetherHandleCustomized)\n\t\t{\n\t\t\tpPanelData.TetherHandleCustomized = false;\n\t\t\tpPanelData.TetherBezierHandles = [];\n\t\t\tpPanelData.TetherBezierHandleX = null;\n\t\t\tpPanelData.TetherBezierHandleY = null;\n\t\t\tpPanelData.TetherOrthoCorner1X = null;\n\t\t\tpPanelData.TetherOrthoCorner1Y = null;\n\t\t\tpPanelData.TetherOrthoCorner2X = null;\n\t\t\tpPanelData.TetherOrthoCorner2Y = null;\n\t\t\tpPanelData.TetherOrthoMidOffset = 0;\n\t\t}\n\t}\n\n\t/**\n\t * Reset tether handle positions for all panels attached to a node.\n\t * Called when a node moves.\n\t * @param {Array} pOpenPanels - Array of all open panel data objects\n\t * @param {string} pNodeHash - The node hash whose panels should be reset\n\t */\n\tresetHandlesForNode(pOpenPanels, pNodeHash)\n\t{\n\t\tfor (let i = 0; i < pOpenPanels.length; i++)\n\t\t{\n\t\t\tlet tmpPanel = pOpenPanels[i];\n\t\t\tif (tmpPanel.NodeHash === pNodeHash)\n\t\t\t{\n\t\t\t\tthis.resetHandlePositions(tmpPanel);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Add a bezier handle to a tether at the specified position.\n\t * @param {Object} pPanelData - Panel data\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {Object} pFrom - Panel anchor {x, y, side}\n\t * @param {Object} pTo - Node anchor {x, y, side}\n\t */\n\taddHandle(pPanelData, pX, pY, pFrom, pTo)\n\t{\n\t\t// Ensure bezier mode and multi-handle array\n\t\tpPanelData.TetherLineMode = 'bezier';\n\n\t\tif (!Array.isArray(pPanelData.TetherBezierHandles))\n\t\t{\n\t\t\tpPanelData.TetherBezierHandles = [];\n\t\t\t// Migrate legacy single-handle if present\n\t\t\tif (pPanelData.TetherBezierHandleX != null && pPanelData.TetherBezierHandleY != null)\n\t\t\t{\n\t\t\t\tpPanelData.TetherBezierHandles.push({\n\t\t\t\t\tx: pPanelData.TetherBezierHandleX,\n\t\t\t\t\ty: pPanelData.TetherBezierHandleY\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Compute insertion index\n\t\tlet tmpInsertIndex = 0;\n\t\tif (this._FlowView._ConnectionRenderer && pFrom && pTo)\n\t\t{\n\t\t\ttmpInsertIndex = this._FlowView._ConnectionRenderer.computeInsertionIndex(\n\t\t\t\tpPanelData.TetherBezierHandles,\n\t\t\t\t{ x: pX, y: pY },\n\t\t\t\tpFrom,\n\t\t\t\tpTo\n\t\t\t);\n\t\t}\n\n\t\tpPanelData.TetherBezierHandles.splice(tmpInsertIndex, 0, { x: pX, y: pY });\n\t\tpPanelData.TetherHandleCustomized = true;\n\t}\n\n\t/**\n\t * Remove a bezier handle from a tether by index.\n\t * @param {Object} pPanelData - Panel data\n\t * @param {number} pIndex - Index in TetherBezierHandles array\n\t */\n\tremoveHandle(pPanelData, pIndex)\n\t{\n\t\tif (!Array.isArray(pPanelData.TetherBezierHandles)) return;\n\t\tif (pIndex < 0 || pIndex >= pPanelData.TetherBezierHandles.length) return;\n\n\t\tpPanelData.TetherBezierHandles.splice(pIndex, 1);\n\n\t\tif (pPanelData.TetherBezierHandles.length === 0)\n\t\t{\n\t\t\tpPanelData.TetherHandleCustomized = false;\n\t\t\tpPanelData.TetherBezierHandleX = null;\n\t\t\tpPanelData.TetherBezierHandleY = null;\n\t\t}\n\t}\n\n\t/**\n\t * Toggle tether line mode between bezier and orthogonal.\n\t * Resets handle positions on toggle.\n\t * @param {Object} pPanelData - Panel data to toggle\n\t * @returns {string} The new line mode ('bezier' or 'orthogonal')\n\t */\n\ttoggleLineMode(pPanelData)\n\t{\n\t\tlet tmpCurrentMode = pPanelData.TetherLineMode || 'bezier';\n\t\tpPanelData.TetherLineMode = (tmpCurrentMode === 'bezier') ? 'orthogonal' : 'bezier';\n\n\t\tpPanelData.TetherHandleCustomized = false;\n\t\tpPanelData.TetherBezierHandles = [];\n\t\tpPanelData.TetherBezierHandleX = null;\n\t\tpPanelData.TetherBezierHandleY = null;\n\t\tpPanelData.TetherOrthoCorner1X = null;\n\t\tpPanelData.TetherOrthoCorner1Y = null;\n\t\tpPanelData.TetherOrthoCorner2X = null;\n\t\tpPanelData.TetherOrthoCorner2Y = null;\n\t\tpPanelData.TetherOrthoMidOffset = 0;\n\n\t\treturn pPanelData.TetherLineMode;\n\t}\n\n\t// ---- SVG Rendering ----\n\n\t/**\n\t * Render a tether from a panel to its node.\n\t * Creates SVG path elements for the line and hit area, plus drag handles when selected.\n\t *\n\t * @param {Object} pPanelData - Panel data with position and tether properties\n\t * @param {Object} pNodeData - Node data with position\n\t * @param {SVGGElement} pTethersLayer - SVG group to append elements to\n\t * @param {boolean} pIsSelected - Whether this tether is currently selected\n\t * @param {string} pViewIdentifier - The flow view identifier (for marker URL)\n\t */\n\trenderTether(pPanelData, pNodeData, pTethersLayer, pIsSelected, pViewIdentifier)\n\t{\n\t\tif (!pNodeData) return;\n\n\t\tlet tmpAnchors = this.getSmartAnchors(pPanelData, pNodeData);\n\t\tlet tmpFrom = tmpAnchors.panelAnchor;\n\t\tlet tmpTo = tmpAnchors.nodeAnchor;\n\n\t\tlet tmpPath = this.generatePath(pPanelData, tmpFrom, tmpTo);\n\n\t\t// Hit area and visible tether path\n\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\tif (tmpShapeProvider)\n\t\t{\n\t\t\tlet tmpHitArea = tmpShapeProvider.createTetherHitAreaElement(tmpPath, pPanelData.Hash);\n\t\t\tpTethersLayer.appendChild(tmpHitArea);\n\n\t\t\tlet tmpPathElement = tmpShapeProvider.createTetherPathElement(\n\t\t\t\ttmpPath, pPanelData.Hash, pIsSelected, pViewIdentifier);\n\t\t\tpTethersLayer.appendChild(tmpPathElement);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpHitArea = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpHitArea.setAttribute('class', 'pict-flow-tether-hitarea');\n\t\t\ttmpHitArea.setAttribute('d', tmpPath);\n\t\t\ttmpHitArea.setAttribute('data-element-type', 'tether-hitarea');\n\t\t\ttmpHitArea.setAttribute('data-panel-hash', pPanelData.Hash);\n\t\t\tpTethersLayer.appendChild(tmpHitArea);\n\n\t\t\tlet tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\t\ttmpPathElement.setAttribute('class', `pict-flow-tether-line${pIsSelected ? ' selected' : ''}`);\n\t\t\ttmpPathElement.setAttribute('d', tmpPath);\n\t\t\ttmpPathElement.setAttribute('marker-end', `url(#flow-tether-arrowhead-${pViewIdentifier})`);\n\t\t\ttmpPathElement.setAttribute('data-element-type', 'tether');\n\t\t\ttmpPathElement.setAttribute('data-panel-hash', pPanelData.Hash);\n\t\t\tpTethersLayer.appendChild(tmpPathElement);\n\t\t}\n\n\t\t// Render drag handles when selected\n\t\tif (pIsSelected)\n\t\t{\n\t\t\tthis._renderHandles(pPanelData, pTethersLayer, tmpFrom, tmpTo);\n\t\t}\n\t}\n\n\t/**\n\t * Render drag handles for a selected tether.\n\t * @param {Object} pPanelData\n\t * @param {SVGGElement} pTethersLayer\n\t * @param {Object} pFrom - Panel anchor {x, y, side}\n\t * @param {Object} pTo - Node anchor {x, y, side}\n\t */\n\t_renderHandles(pPanelData, pTethersLayer, pFrom, pTo)\n\t{\n\t\tlet tmpLineMode = pPanelData.TetherLineMode || 'bezier';\n\n\t\tif (tmpLineMode === 'orthogonal')\n\t\t{\n\t\t\tlet tmpGeom = this.getOrthoGeometry(pFrom, pTo, pPanelData);\n\n\t\t\t// Corner 1 handle\n\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'ortho-corner1',\n\t\t\t\ttmpGeom.corner1.x, tmpGeom.corner1.y, 'pict-flow-tether-handle');\n\n\t\t\t// Midpoint handle\n\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'ortho-midpoint',\n\t\t\t\ttmpGeom.midpoint.x, tmpGeom.midpoint.y, 'pict-flow-tether-handle-midpoint');\n\n\t\t\t// Corner 2 handle\n\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'ortho-corner2',\n\t\t\t\ttmpGeom.corner2.x, tmpGeom.corner2.y, 'pict-flow-tether-handle');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Bezier handles\n\t\t\tlet tmpHandles = this._getTetherBezierHandles(pPanelData);\n\n\t\t\tif (tmpHandles.length > 0)\n\t\t\t{\n\t\t\t\t// Multi-handle: render each handle as a draggable circle\n\t\t\t\tfor (let i = 0; i < tmpHandles.length; i++)\n\t\t\t\t{\n\t\t\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'bezier-handle-' + i,\n\t\t\t\t\t\ttmpHandles[i].x, tmpHandles[i].y, 'pict-flow-tether-handle');\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// No custom handles: show auto-computed midpoint\n\t\t\t\tlet tmpMid = this.getAutoMidpoint(pFrom, pTo);\n\t\t\t\tthis._createHandle(pTethersLayer, pPanelData.Hash, 'bezier-midpoint',\n\t\t\t\t\ttmpMid.x, tmpMid.y, 'pict-flow-tether-handle-midpoint');\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a single tether drag handle circle.\n\t * @param {SVGGElement} pLayer\n\t * @param {string} pPanelHash\n\t * @param {string} pHandleType\n\t * @param {number} pX\n\t * @param {number} pY\n\t * @param {string} pClassName\n\t */\n\t_createHandle(pLayer, pPanelHash, pHandleType, pX, pY, pClassName)\n\t{\n\t\tif (!this._FlowView._ConnectorShapesProvider) return;\n\n\t\tlet tmpShapeKey = (pClassName === 'pict-flow-tether-handle-midpoint')\n\t\t\t? 'tether-handle-midpoint' : 'tether-handle';\n\n\t\tthis._FlowView._ConnectorShapesProvider.createFullHandle(\n\t\t\tpLayer, pPanelHash, pHandleType, pX, pY,\n\t\t\ttmpShapeKey, 'tether-handle', 'data-panel-hash');\n\t}\n}\n\nmodule.exports = PictServiceFlowTether;\n","const libFableServiceProviderBase = require('fable-serviceproviderbase');\n\n/**\n * PictService-Flow-ViewportManager\n *\n * Manages viewport transforms (pan/zoom), coordinate conversion between\n * screen and SVG space, zoom-to-fit calculations, and fullscreen toggling\n * for the flow diagram.\n */\nclass PictServiceFlowViewportManager extends libFableServiceProviderBase\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictServiceFlowViewportManager';\n\n\t\tthis._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;\n\n\t\tthis._IsFullscreen = false;\n\t}\n\n\t/**\n\t * Update the viewport transform (pan and zoom)\n\t */\n\tupdateViewportTransform()\n\t{\n\t\tif (!this._FlowView._ViewportElement) return;\n\t\tlet tmpVS = this._FlowView._FlowData.ViewState;\n\t\tthis._FlowView._ViewportElement.setAttribute('transform',\n\t\t\t`translate(${tmpVS.PanX}, ${tmpVS.PanY}) scale(${tmpVS.Zoom})`\n\t\t);\n\t}\n\n\t/**\n\t * Set zoom level\n\t * @param {number} pZoom - The zoom level\n\t * @param {number} [pFocusX] - X coordinate to zoom toward (SVG space)\n\t * @param {number} [pFocusY] - Y coordinate to zoom toward (SVG space)\n\t */\n\tsetZoom(pZoom, pFocusX, pFocusY)\n\t{\n\t\tlet tmpNewZoom = Math.max(this._FlowView.options.MinZoom, Math.min(this._FlowView.options.MaxZoom, pZoom));\n\t\tlet tmpOldZoom = this._FlowView._FlowData.ViewState.Zoom;\n\n\t\tif (typeof pFocusX === 'number' && typeof pFocusY === 'number')\n\t\t{\n\t\t\t// Zoom toward focus point\n\t\t\tlet tmpVS = this._FlowView._FlowData.ViewState;\n\t\t\ttmpVS.PanX = pFocusX - (pFocusX - tmpVS.PanX) * (tmpNewZoom / tmpOldZoom);\n\t\t\ttmpVS.PanY = pFocusY - (pFocusY - tmpVS.PanY) * (tmpNewZoom / tmpOldZoom);\n\t\t}\n\n\t\tthis._FlowView._FlowData.ViewState.Zoom = tmpNewZoom;\n\t\tthis.updateViewportTransform();\n\t}\n\n\t/**\n\t * Zoom to fit all nodes in the viewport\n\t */\n\tzoomToFit()\n\t{\n\t\tif (this._FlowView._FlowData.Nodes.length === 0) return;\n\t\tif (!this._FlowView._SVGElement) return;\n\n\t\tlet tmpMinX = Infinity, tmpMinY = Infinity;\n\t\tlet tmpMaxX = -Infinity, tmpMaxY = -Infinity;\n\n\t\tfor (let i = 0; i < this._FlowView._FlowData.Nodes.length; i++)\n\t\t{\n\t\t\tlet tmpNode = this._FlowView._FlowData.Nodes[i];\n\t\t\ttmpMinX = Math.min(tmpMinX, tmpNode.X);\n\t\t\ttmpMinY = Math.min(tmpMinY, tmpNode.Y);\n\t\t\ttmpMaxX = Math.max(tmpMaxX, tmpNode.X + tmpNode.Width);\n\t\t\ttmpMaxY = Math.max(tmpMaxY, tmpNode.Y + tmpNode.Height);\n\t\t}\n\n\t\tlet tmpPadding = 50;\n\t\tlet tmpFlowWidth = tmpMaxX - tmpMinX + tmpPadding * 2;\n\t\tlet tmpFlowHeight = tmpMaxY - tmpMinY + tmpPadding * 2;\n\n\t\tlet tmpSVGRect = this._FlowView._SVGElement.getBoundingClientRect();\n\t\tlet tmpScaleX = tmpSVGRect.width / tmpFlowWidth;\n\t\tlet tmpScaleY = tmpSVGRect.height / tmpFlowHeight;\n\t\tlet tmpZoom = Math.min(tmpScaleX, tmpScaleY, 1.0); // Don't zoom in past 1.0\n\t\ttmpZoom = Math.max(this._FlowView.options.MinZoom, Math.min(this._FlowView.options.MaxZoom, tmpZoom));\n\n\t\tlet tmpCenterX = (tmpMinX + tmpMaxX) / 2;\n\t\tlet tmpCenterY = (tmpMinY + tmpMaxY) / 2;\n\n\t\tthis._FlowView._FlowData.ViewState.Zoom = tmpZoom;\n\t\tthis._FlowView._FlowData.ViewState.PanX = (tmpSVGRect.width / 2) - (tmpCenterX * tmpZoom);\n\t\tthis._FlowView._FlowData.ViewState.PanY = (tmpSVGRect.height / 2) - (tmpCenterY * tmpZoom);\n\n\t\tthis.updateViewportTransform();\n\t}\n\n\t/**\n\t * Convert screen coordinates to SVG viewport coordinates\n\t * @param {number} pScreenX\n\t * @param {number} pScreenY\n\t * @returns {{x: number, y: number}}\n\t */\n\tscreenToSVGCoords(pScreenX, pScreenY)\n\t{\n\t\tif (!this._FlowView._SVGElement)\n\t\t{\n\t\t\treturn { x: pScreenX, y: pScreenY };\n\t\t}\n\n\t\tlet tmpPoint = this._FlowView._SVGElement.createSVGPoint();\n\t\ttmpPoint.x = pScreenX;\n\t\ttmpPoint.y = pScreenY;\n\n\t\tlet tmpCTM = this._FlowView._SVGElement.getScreenCTM();\n\t\tif (tmpCTM)\n\t\t{\n\t\t\tlet tmpInverse = tmpCTM.inverse();\n\t\t\tlet tmpTransformed = tmpPoint.matrixTransform(tmpInverse);\n\t\t\t// Account for viewport pan/zoom\n\t\t\tlet tmpVS = this._FlowView._FlowData.ViewState;\n\t\t\treturn {\n\t\t\t\tx: (tmpTransformed.x - tmpVS.PanX) / tmpVS.Zoom,\n\t\t\t\ty: (tmpTransformed.y - tmpVS.PanY) / tmpVS.Zoom\n\t\t\t};\n\t\t}\n\n\t\treturn { x: pScreenX, y: pScreenY };\n\t}\n\n\t/**\n\t * Toggle fullscreen mode on the flow editor container.\n\t * Uses a CSS fixed-position overlay instead of the Fullscreen API.\n\t * @returns {boolean} The new fullscreen state\n\t */\n\ttoggleFullscreen()\n\t{\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\tlet tmpContainerElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Wrapper-${tmpViewIdentifier}`);\n\t\tif (tmpContainerElements.length < 1) return this._IsFullscreen;\n\n\t\tlet tmpContainer = tmpContainerElements[0];\n\n\t\tthis._IsFullscreen = !this._IsFullscreen;\n\n\t\tif (this._IsFullscreen)\n\t\t{\n\t\t\ttmpContainer.classList.add('pict-flow-fullscreen');\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpContainer.classList.remove('pict-flow-fullscreen');\n\t\t}\n\n\t\treturn this._IsFullscreen;\n\t}\n\n\t/**\n\t * Exit fullscreen mode if currently active.\n\t */\n\texitFullscreen()\n\t{\n\t\tif (!this._IsFullscreen) return;\n\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\tlet tmpContainerElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Wrapper-${tmpViewIdentifier}`);\n\t\tif (tmpContainerElements.length > 0)\n\t\t{\n\t\t\ttmpContainerElements[0].classList.remove('pict-flow-fullscreen');\n\t\t}\n\n\t\tthis._IsFullscreen = false;\n\t}\n}\n\nmodule.exports = PictServiceFlowViewportManager;\n","const libPictView = require('pict-view');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Flow-FloatingToolbar',\n\n\tDefaultRenderable: 'Flow-FloatingToolbar-Content',\n\tDefaultDestinationAddress: '#Flow-FloatingToolbar-Container',\n\n\tAutoRender: false,\n\n\tFlowViewIdentifier: 'Pict-Flow',\n\n\tCSS: false,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'Flow-FloatingToolbar-Template',\n\t\t\tTemplate: /*html*/`\n<div class=\"pict-flow-floating-toolbar\" id=\"Flow-FloatingToolbar-{~D:Record.FlowViewIdentifier~}\">\n\t<div class=\"pict-flow-floating-grip\" id=\"Flow-FloatingGrip-{~D:Record.FlowViewIdentifier~}\" title=\"Drag to move · Double-click to collapse\">\n\t\t<span id=\"Flow-FloatingIcon-grip-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</div>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"add-node\" title=\"Add Node\">\n\t\t<span id=\"Flow-FloatingIcon-plus-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"cards-popup\" title=\"Cards\">\n\t\t<span id=\"Flow-FloatingIcon-cards-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"delete-selected\" title=\"Delete Selected\">\n\t\t<span id=\"Flow-FloatingIcon-trash-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<div class=\"pict-flow-floating-separator\"></div>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"zoom-in\" title=\"Zoom In\">\n\t\t<span id=\"Flow-FloatingIcon-zoom-in-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"zoom-out\" title=\"Zoom Out\">\n\t\t<span id=\"Flow-FloatingIcon-zoom-out-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"zoom-fit\" title=\"Fit to View\">\n\t\t<span id=\"Flow-FloatingIcon-zoom-fit-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<div class=\"pict-flow-floating-separator\"></div>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"auto-layout\" title=\"Auto Layout\">\n\t\t<span id=\"Flow-FloatingIcon-auto-layout-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"layout-popup\" title=\"Layout\">\n\t\t<span id=\"Flow-FloatingIcon-layout-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"fullscreen\" title=\"Toggle Fullscreen\">\n\t\t<span id=\"Flow-FloatingIcon-fullscreen-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n\t<div class=\"pict-flow-floating-separator\"></div>\n\t<button class=\"pict-flow-floating-btn\" data-flow-action=\"dock-toolbar\" title=\"Dock Toolbar\">\n\t\t<span id=\"Flow-FloatingIcon-dock-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: 'Flow-FloatingToolbar-Content',\n\t\t\tTemplateHash: 'Flow-FloatingToolbar-Template',\n\t\t\tDestinationAddress: '#Flow-FloatingToolbar-Container',\n\t\t\tRenderMethod: 'replace'\n\t\t}\n\t]\n};\n\nclass PictViewFlowFloatingToolbar extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictViewFlowFloatingToolbar';\n\n\t\tthis._ToolbarView = null;\n\t\tthis._FlowView = null;\n\n\t\tthis._IsCollapsed = false;\n\n\t\tthis._IsDragging = false;\n\t\tthis._DragStartX = 0;\n\t\tthis._DragStartY = 0;\n\t\tthis._DragStartLeft = 0;\n\t\tthis._DragStartTop = 0;\n\n\t\tthis._BoundMouseMove = null;\n\t\tthis._BoundMouseUp = null;\n\t}\n\n\trender(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\treturn super.render(pRenderableHash, pRenderDestinationAddress, this.options);\n\t}\n\n\tonAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\t// Bind click delegation for action buttons\n\t\tlet tmpFloatingToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpFloatingToolbar.length > 0)\n\t\t{\n\t\t\ttmpFloatingToolbar[0].addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\tlet tmpTarget = pEvent.target;\n\t\t\t\tif (!tmpTarget) return;\n\n\t\t\t\tlet tmpButton = tmpTarget.closest('[data-flow-action]');\n\t\t\t\tif (!tmpButton) return;\n\n\t\t\t\tlet tmpAction = tmpButton.getAttribute('data-flow-action');\n\t\t\t\tif (tmpAction === 'dock-toolbar')\n\t\t\t\t{\n\t\t\t\t\tif (this._ToolbarView)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._ToolbarView._setToolbarMode('docked');\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Delegate all other actions to the docked toolbar\n\t\t\t\tif (this._ToolbarView)\n\t\t\t\t{\n\t\t\t\t\tthis._ToolbarView._handleToolbarAction(tmpAction);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Bind drag behavior on the grip\n\t\tlet tmpGrip = this.pict.ContentAssignment.getElement(`#Flow-FloatingGrip-${tmpFlowViewIdentifier}`);\n\t\tif (tmpGrip.length > 0)\n\t\t{\n\t\t\ttmpGrip[0].addEventListener('mousedown', (pEvent) =>\n\t\t\t{\n\t\t\t\tthis._startDrag(pEvent);\n\t\t\t});\n\n\t\t\t// Double-click grip to toggle collapsed state\n\t\t\ttmpGrip[0].addEventListener('dblclick', (pEvent) =>\n\t\t\t{\n\t\t\t\tpEvent.preventDefault();\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tthis._toggleCollapse();\n\t\t\t});\n\t\t}\n\n\t\t// Populate icons\n\t\tthis._populateIcons();\n\n\t\t// Remove buttons from DOM based on options\n\t\tif (tmpFloatingToolbar.length > 0)\n\t\t{\n\t\t\tif (this.options.EnableAddNode === false)\n\t\t\t{\n\t\t\t\tlet tmpAddNodeBtn = tmpFloatingToolbar[0].querySelector('[data-flow-action=\"add-node\"]');\n\t\t\t\tif (tmpAddNodeBtn)\n\t\t\t\t{\n\t\t\t\t\ttmpAddNodeBtn.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.options.EnableCardPalette === false)\n\t\t\t{\n\t\t\t\tlet tmpCardsBtn = tmpFloatingToolbar[0].querySelector('[data-flow-action=\"cards-popup\"]');\n\t\t\t\tif (tmpCardsBtn)\n\t\t\t\t{\n\t\t\t\t\ttmpCardsBtn.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);\n\t}\n\n\t/**\n\t * Populate SVG icons into all floating toolbar button spans.\n\t */\n\t_populateIcons()\n\t{\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\t\tif (!tmpIconProvider) return;\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\tlet tmpIconMap =\n\t\t{\n\t\t\t'grip': 'grip',\n\t\t\t'plus': 'plus',\n\t\t\t'trash': 'trash',\n\t\t\t'zoom-in': 'zoom-in',\n\t\t\t'zoom-out': 'zoom-out',\n\t\t\t'zoom-fit': 'zoom-fit',\n\t\t\t'auto-layout': 'auto-layout',\n\t\t\t'cards': 'cards',\n\t\t\t'layout': 'layout',\n\t\t\t'fullscreen': 'fullscreen',\n\t\t\t'dock': 'dock'\n\t\t};\n\n\t\tlet tmpKeys = Object.keys(tmpIconMap);\n\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t{\n\t\t\tlet tmpElementId = `Flow-FloatingIcon-${tmpKeys[i]}-${tmpFlowViewIdentifier}`;\n\t\t\tlet tmpElements = this.pict.ContentAssignment.getElement(`#${tmpElementId}`);\n\t\t\tif (tmpElements.length > 0)\n\t\t\t{\n\t\t\t\ttmpElements[0].innerHTML = tmpIconProvider.getIconSVGMarkup(tmpIconMap[tmpKeys[i]], 16);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Toggle the floating toolbar between expanded and collapsed (grip-only square) states.\n\t */\n\t_toggleCollapse()\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbar.length < 1) return;\n\n\t\tthis._IsCollapsed = !this._IsCollapsed;\n\n\t\tif (this._IsCollapsed)\n\t\t{\n\t\t\ttmpToolbar[0].classList.add('collapsed');\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpToolbar[0].classList.remove('collapsed');\n\t\t}\n\t}\n\n\t/**\n\t * Start dragging the floating toolbar.\n\t * @param {MouseEvent} pEvent\n\t */\n\t_startDrag(pEvent)\n\t{\n\t\tpEvent.preventDefault();\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbar.length < 1) return;\n\n\t\tlet tmpEl = tmpToolbar[0];\n\t\tthis._IsDragging = true;\n\t\tthis._DragStartX = pEvent.clientX;\n\t\tthis._DragStartY = pEvent.clientY;\n\t\tthis._DragStartLeft = tmpEl.offsetLeft;\n\t\tthis._DragStartTop = tmpEl.offsetTop;\n\n\t\tthis._BoundMouseMove = (pMoveEvent) => { this._onDragMove(pMoveEvent); };\n\t\tthis._BoundMouseUp = () => { this._onDragEnd(); };\n\n\t\tdocument.addEventListener('mousemove', this._BoundMouseMove);\n\t\tdocument.addEventListener('mouseup', this._BoundMouseUp);\n\t}\n\n\t/**\n\t * Handle drag movement.\n\t * @param {MouseEvent} pEvent\n\t */\n\t_onDragMove(pEvent)\n\t{\n\t\tif (!this._IsDragging) return;\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbar.length < 1) return;\n\n\t\tlet tmpEl = tmpToolbar[0];\n\t\tlet tmpDeltaX = pEvent.clientX - this._DragStartX;\n\t\tlet tmpDeltaY = pEvent.clientY - this._DragStartY;\n\n\t\tlet tmpNewLeft = this._DragStartLeft + tmpDeltaX;\n\t\tlet tmpNewTop = this._DragStartTop + tmpDeltaY;\n\n\t\t// Clamp to parent bounds\n\t\tlet tmpParent = tmpEl.parentElement;\n\t\tif (tmpParent)\n\t\t{\n\t\t\tlet tmpMaxLeft = tmpParent.clientWidth - tmpEl.offsetWidth;\n\t\t\tlet tmpMaxTop = tmpParent.clientHeight - tmpEl.offsetHeight;\n\t\t\ttmpNewLeft = Math.max(0, Math.min(tmpNewLeft, tmpMaxLeft));\n\t\t\ttmpNewTop = Math.max(0, Math.min(tmpNewTop, tmpMaxTop));\n\t\t}\n\n\t\ttmpEl.style.left = tmpNewLeft + 'px';\n\t\ttmpEl.style.top = tmpNewTop + 'px';\n\t}\n\n\t/**\n\t * End dragging.\n\t */\n\t_onDragEnd()\n\t{\n\t\tthis._IsDragging = false;\n\n\t\tif (this._BoundMouseMove)\n\t\t{\n\t\t\tdocument.removeEventListener('mousemove', this._BoundMouseMove);\n\t\t\tthis._BoundMouseMove = null;\n\t\t}\n\t\tif (this._BoundMouseUp)\n\t\t{\n\t\t\tdocument.removeEventListener('mouseup', this._BoundMouseUp);\n\t\t\tthis._BoundMouseUp = null;\n\t\t}\n\n\t\t// Save position\n\t\tif (this._ToolbarView)\n\t\t{\n\t\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\t\tif (tmpToolbar.length > 0)\n\t\t\t{\n\t\t\t\tthis._ToolbarView._FloatingPosition.X = tmpToolbar[0].offsetLeft;\n\t\t\t\tthis._ToolbarView._FloatingPosition.Y = tmpToolbar[0].offsetTop;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Show the floating toolbar at the saved position.\n\t */\n\tshow()\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpContainer = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-Container-${tmpFlowViewIdentifier}`);\n\t\tif (tmpContainer.length > 0)\n\t\t{\n\t\t\ttmpContainer[0].style.display = 'block';\n\t\t}\n\n\t\tlet tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbar.length > 0 && this._ToolbarView)\n\t\t{\n\t\t\ttmpToolbar[0].style.left = this._ToolbarView._FloatingPosition.X + 'px';\n\t\t\ttmpToolbar[0].style.top = this._ToolbarView._FloatingPosition.Y + 'px';\n\t\t}\n\n\t\t// Restore expanded state when showing\n\t\tif (this._IsCollapsed && tmpToolbar.length > 0)\n\t\t{\n\t\t\tthis._IsCollapsed = false;\n\t\t\ttmpToolbar[0].classList.remove('collapsed');\n\t\t}\n\t}\n\n\t/**\n\t * Hide the floating toolbar.\n\t */\n\thide()\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpContainer = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-Container-${tmpFlowViewIdentifier}`);\n\t\tif (tmpContainer.length > 0)\n\t\t{\n\t\t\ttmpContainer[0].style.display = 'none';\n\t\t}\n\t}\n}\n\nmodule.exports = PictViewFlowFloatingToolbar;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n","const libPictView = require('pict-view');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Flow-NodeRenderer',\n\n\tAutoRender: false,\n\n\t// Title bar height for nodes\n\tNodeTitleBarHeight: 22\n};\n\nclass PictViewFlowNode extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictViewFlowNode';\n\n\t\tthis._FlowView = null;\n\t}\n\n\t/**\n\t * Render a node into the nodes SVG layer\n\t * @param {Object} pNodeData - The node data object\n\t * @param {SVGGElement} pNodesLayer - The SVG <g> element to append to\n\t * @param {boolean} pIsSelected - Whether this node is selected\n\t * @param {Object} pNodeTypeConfig - The node type configuration\n\t */\n\trenderNode(pNodeData, pNodesLayer, pIsSelected, pNodeTypeConfig)\n\t{\n\t\tlet tmpGroup = this._FlowView._SVGHelperProvider.createSVGElement('g');\n\n\t\t// Build CSS class list with optional per-type modifier classes\n\t\tlet tmpClassList = `pict-flow-node ${pIsSelected ? 'selected' : ''} pict-flow-node-${pNodeData.Type || 'default'}`;\n\t\tif (pNodeTypeConfig)\n\t\t{\n\t\t\tif (pNodeTypeConfig.PortLabelsOnHover) tmpClassList += ' pict-flow-node-port-labels-hover';\n\t\t\tif (pNodeTypeConfig.PortLabelsVertical) tmpClassList += ' pict-flow-node-port-labels-vertical';\n\t\t}\n\t\ttmpGroup.setAttribute('class', tmpClassList);\n\t\ttmpGroup.setAttribute('transform', `translate(${pNodeData.X}, ${pNodeData.Y})`);\n\t\ttmpGroup.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpGroup.setAttribute('data-element-type', 'node');\n\n\t\tlet tmpWidth = pNodeData.Width || 180;\n\t\tlet tmpHeight = pNodeData.Height || 80;\n\t\tlet tmpTitleBarHeight = this.options.NodeTitleBarHeight;\n\n\t\t// Ensure node is tall enough for all ports in their zones\n\t\tlet tmpGeomProvider = this._FlowView._GeometryProvider;\n\t\tif (tmpGeomProvider && pNodeData.Ports && pNodeData.Ports.length > 0)\n\t\t{\n\t\t\tlet tmpMinHeight = tmpGeomProvider.computeMinimumNodeHeight(pNodeData.Ports, tmpTitleBarHeight);\n\t\t\tif (tmpMinHeight > tmpHeight)\n\t\t\t{\n\t\t\t\ttmpHeight = tmpMinHeight;\n\t\t\t}\n\t\t}\n\n\t\t// Write the adjusted dimensions back to the node data so that\n\t\t// connection rendering (which reads pNodeData.Width/Height to\n\t\t// compute port positions) uses the same values we render with.\n\t\tpNodeData.Width = tmpWidth;\n\t\tpNodeData.Height = tmpHeight;\n\n\t\t// Determine node body mode from theme (bracket vs rect)\n\t\tlet tmpNodeBodyMode = 'rect';\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\tlet tmpActiveTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\t\tif (tmpActiveTheme && tmpActiveTheme.NodeBodyMode)\n\t\t\t{\n\t\t\t\ttmpNodeBodyMode = tmpActiveTheme.NodeBodyMode;\n\t\t\t}\n\t\t}\n\n\t\tif (tmpNodeBodyMode === 'bracket')\n\t\t{\n\t\t\tthis._renderBracketNodeBody(tmpGroup, pNodeData, tmpWidth, tmpHeight, tmpTitleBarHeight, pNodeTypeConfig);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis._renderRectNodeBody(tmpGroup, pNodeData, tmpWidth, tmpHeight, tmpTitleBarHeight, pNodeTypeConfig);\n\t\t}\n\n\t\t// Determine if this node has a title-bar icon (FlowCard with CardMetadata)\n\t\tlet tmpHasTitleIcon = false;\n\t\tlet tmpTitleIconSize = 12;\n\t\tlet tmpTitleIconMarginLeft = 8;\n\t\tlet tmpTitleIconGap = 4;\n\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.CardMetadata)\n\t\t{\n\t\t\tlet tmpMeta = pNodeTypeConfig.CardMetadata;\n\t\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\t\tif (tmpMeta.Icon || tmpIconProvider)\n\t\t\t{\n\t\t\t\ttmpHasTitleIcon = true;\n\t\t\t}\n\t\t}\n\n\t\t// Title text (position adjusts when a title-bar icon is present)\n\t\tlet tmpTitle = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\ttmpTitle.setAttribute('class', 'pict-flow-node-title');\n\t\tif (tmpHasTitleIcon)\n\t\t{\n\t\t\ttmpTitle.setAttribute('x', String(tmpTitleIconMarginLeft + tmpTitleIconSize + tmpTitleIconGap));\n\t\t\ttmpTitle.setAttribute('text-anchor', 'start');\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpTitle.setAttribute('x', String(tmpWidth / 2));\n\t\t\ttmpTitle.setAttribute('text-anchor', 'middle');\n\t\t}\n\t\ttmpTitle.setAttribute('y', String(tmpTitleBarHeight / 2 + 1));\n\t\ttmpTitle.setAttribute('dominant-baseline', 'central');\n\t\ttmpTitle.textContent = pNodeData.Title || 'Untitled';\n\t\ttmpGroup.appendChild(tmpTitle);\n\n\t\t// Determine whether labels should be rendered\n\t\tlet tmpShowTypeLabel = (!pNodeTypeConfig || pNodeTypeConfig.ShowTypeLabel !== false);\n\t\tlet tmpLabelsInFront = (!pNodeTypeConfig || pNodeTypeConfig.LabelsInFront !== false);\n\n\t\t// Helper: render type label + code badge + tooltip (the \"middle labels\")\n\t\tlet tmpRenderTypeLabels = () =>\n\t\t{\n\t\t\t// Type label (below title bar — hover-only for FlowCard nodes via CSS)\n\t\t\tif (tmpShowTypeLabel && pNodeTypeConfig && pNodeTypeConfig.Label && pNodeTypeConfig.Label !== pNodeData.Title)\n\t\t\t{\n\t\t\t\tlet tmpTypeLabel = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\ttmpTypeLabel.setAttribute('class', 'pict-flow-node-type-label');\n\t\t\t\ttmpTypeLabel.setAttribute('x', String(tmpWidth / 2));\n\t\t\t\ttmpTypeLabel.setAttribute('y', String(tmpTitleBarHeight + 16));\n\t\t\t\ttmpTypeLabel.setAttribute('text-anchor', 'middle');\n\t\t\t\ttmpTypeLabel.setAttribute('dominant-baseline', 'central');\n\t\t\t\ttmpTypeLabel.textContent = pNodeTypeConfig.Label;\n\t\t\t\ttmpGroup.appendChild(tmpTypeLabel);\n\t\t\t}\n\n\t\t\t// FlowCard metadata: icon in title bar, code badge in body (hover-only via CSS)\n\t\t\tif (pNodeTypeConfig && pNodeTypeConfig.CardMetadata)\n\t\t\t{\n\t\t\t\tlet tmpMeta = pNodeTypeConfig.CardMetadata;\n\t\t\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\t\t\tlet tmpTitleIconRendered = false;\n\n\t\t\t\t// Icon position in title bar (vertically centered)\n\t\t\t\tlet tmpIconX = tmpTitleIconMarginLeft;\n\t\t\t\tlet tmpIconY = (tmpTitleBarHeight - tmpTitleIconSize) / 2;\n\n\t\t\t\tif (tmpMeta.Icon && tmpIconProvider && !tmpIconProvider.isEmojiIcon(tmpMeta.Icon))\n\t\t\t\t{\n\t\t\t\t\t// SVG icon via the icon provider — rendered into title bar\n\t\t\t\t\tlet tmpResolvedKey = tmpIconProvider.resolveIconKey(tmpMeta);\n\t\t\t\t\tlet tmpIconGroup = tmpIconProvider.renderIconIntoSVGGroup(\n\t\t\t\t\t\ttmpResolvedKey, tmpGroup,\n\t\t\t\t\t\ttmpIconX, tmpIconY,\n\t\t\t\t\t\ttmpTitleIconSize);\n\t\t\t\t\tif (tmpIconGroup)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpIconGroup.setAttribute('class',\n\t\t\t\t\t\t\t(tmpIconGroup.getAttribute('class') || '') + ' pict-flow-node-title-icon');\n\t\t\t\t\t}\n\t\t\t\t\ttmpTitleIconRendered = true;\n\t\t\t\t}\n\t\t\t\telse if (tmpMeta.Icon && tmpIconProvider && tmpIconProvider.isEmojiIcon(tmpMeta.Icon))\n\t\t\t\t{\n\t\t\t\t\t// Emoji icon in title bar\n\t\t\t\t\tlet tmpIconText = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\t\ttmpIconText.setAttribute('class', 'pict-flow-node-card-icon pict-flow-node-title-icon-emoji');\n\t\t\t\t\ttmpIconText.setAttribute('font-size', String(tmpTitleIconSize));\n\t\t\t\t\ttmpIconText.setAttribute('text-anchor', 'middle');\n\t\t\t\t\ttmpIconText.setAttribute('dominant-baseline', 'central');\n\t\t\t\t\ttmpIconText.setAttribute('pointer-events', 'none');\n\t\t\t\t\ttmpIconText.setAttribute('x', String(tmpIconX + tmpTitleIconSize / 2));\n\t\t\t\t\ttmpIconText.setAttribute('y', String(tmpTitleBarHeight / 2));\n\t\t\t\t\ttmpIconText.textContent = tmpMeta.Icon;\n\t\t\t\t\ttmpGroup.appendChild(tmpIconText);\n\t\t\t\t\ttmpTitleIconRendered = true;\n\t\t\t\t}\n\t\t\t\telse if (tmpMeta.Icon)\n\t\t\t\t{\n\t\t\t\t\t// No icon provider — text fallback in title bar\n\t\t\t\t\tlet tmpIconText = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\t\ttmpIconText.setAttribute('class', 'pict-flow-node-card-icon pict-flow-node-title-icon-emoji');\n\t\t\t\t\ttmpIconText.setAttribute('font-size', String(tmpTitleIconSize));\n\t\t\t\t\ttmpIconText.setAttribute('text-anchor', 'middle');\n\t\t\t\t\ttmpIconText.setAttribute('dominant-baseline', 'central');\n\t\t\t\t\ttmpIconText.setAttribute('pointer-events', 'none');\n\t\t\t\t\ttmpIconText.setAttribute('x', String(tmpIconX + tmpTitleIconSize / 2));\n\t\t\t\t\ttmpIconText.setAttribute('y', String(tmpTitleBarHeight / 2));\n\t\t\t\t\ttmpIconText.textContent = tmpMeta.Icon;\n\t\t\t\t\ttmpGroup.appendChild(tmpIconText);\n\t\t\t\t\ttmpTitleIconRendered = true;\n\t\t\t\t}\n\n\t\t\t\t// Default fallback icon in title bar\n\t\t\t\tif (!tmpTitleIconRendered && tmpIconProvider)\n\t\t\t\t{\n\t\t\t\t\tlet tmpIconGroup = tmpIconProvider.renderIconIntoSVGGroup(\n\t\t\t\t\t\t'default', tmpGroup,\n\t\t\t\t\t\ttmpIconX, tmpIconY,\n\t\t\t\t\t\ttmpTitleIconSize);\n\t\t\t\t\tif (tmpIconGroup)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpIconGroup.setAttribute('class',\n\t\t\t\t\t\t\t(tmpIconGroup.getAttribute('class') || '') + ' pict-flow-node-title-icon');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Code badge in body (hover-only via CSS, skipped when ShowTypeLabel is false)\n\t\t\t\tlet tmpBodyCenterY = tmpTitleBarHeight + (tmpHeight - tmpTitleBarHeight) / 2;\n\t\t\t\tif (tmpShowTypeLabel && tmpMeta.Code)\n\t\t\t\t{\n\t\t\t\t\tlet tmpCodeText = this._FlowView._SVGHelperProvider.createSVGElement('text');\n\t\t\t\t\ttmpCodeText.setAttribute('class', 'pict-flow-node-card-code');\n\t\t\t\t\ttmpCodeText.setAttribute('font-size', '10');\n\t\t\t\t\ttmpCodeText.setAttribute('font-family', 'monospace');\n\t\t\t\t\ttmpCodeText.setAttribute('fill', '#7f8c8d');\n\t\t\t\t\ttmpCodeText.setAttribute('text-anchor', 'middle');\n\t\t\t\t\ttmpCodeText.setAttribute('dominant-baseline', 'central');\n\t\t\t\t\ttmpCodeText.setAttribute('pointer-events', 'none');\n\t\t\t\t\ttmpCodeText.setAttribute('x', String(tmpWidth / 2));\n\t\t\t\t\ttmpCodeText.setAttribute('y', String(tmpBodyCenterY));\n\t\t\t\t\ttmpCodeText.textContent = tmpMeta.Code;\n\t\t\t\t\ttmpGroup.appendChild(tmpCodeText);\n\t\t\t\t}\n\n\t\t\t\t// Tooltip via SVG <title> element\n\t\t\t\tif (tmpMeta.Tooltip || tmpMeta.Description)\n\t\t\t\t{\n\t\t\t\t\tlet tmpSVGTitle = this._FlowView._SVGHelperProvider.createSVGElement('title');\n\t\t\t\t\ttmpSVGTitle.textContent = tmpMeta.Tooltip || tmpMeta.Description;\n\t\t\t\t\ttmpGroup.appendChild(tmpSVGTitle);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Render order depends on LabelsInFront:\n\t\t// true (default): body content first, then labels + ports (labels on top)\n\t\t// false: labels + ports first, then body content (content on top)\n\t\tif (tmpLabelsInFront)\n\t\t{\n\t\t\tthis._renderBodyContent(pNodeData, tmpGroup, tmpWidth, tmpHeight, pNodeTypeConfig);\n\t\t\ttmpRenderTypeLabels();\n\t\t\tthis._renderPorts(pNodeData, tmpGroup, tmpWidth, tmpHeight, pNodeTypeConfig);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRenderTypeLabels();\n\t\t\tthis._renderPorts(pNodeData, tmpGroup, tmpWidth, tmpHeight, pNodeTypeConfig);\n\t\t\tthis._renderBodyContent(pNodeData, tmpGroup, tmpWidth, tmpHeight, pNodeTypeConfig);\n\t\t}\n\n\t\t// Panel indicator icon (small rect in bottom-right corner)\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.PropertiesPanel)\n\t\t{\n\t\t\tlet tmpIndicatorSize = 10;\n\t\t\tlet tmpIndicatorMargin = 4;\n\t\t\tlet tmpIndicatorX = tmpWidth - tmpIndicatorSize - tmpIndicatorMargin;\n\t\t\tlet tmpIndicatorY = tmpHeight - tmpIndicatorSize - tmpIndicatorMargin;\n\t\t\tlet tmpShapeProvider = this._FlowView._ConnectorShapesProvider;\n\t\t\tlet tmpIndicator;\n\n\t\t\tif (tmpShapeProvider)\n\t\t\t{\n\t\t\t\ttmpIndicator = tmpShapeProvider.createPanelIndicatorElement(\n\t\t\t\t\tpNodeData.Hash, tmpIndicatorX, tmpIndicatorY,\n\t\t\t\t\ttmpIndicatorSize, tmpIndicatorSize);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpIndicator = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\t\t\ttmpIndicator.setAttribute('class', 'pict-flow-node-panel-indicator');\n\t\t\t\ttmpIndicator.setAttribute('x', String(tmpIndicatorX));\n\t\t\t\ttmpIndicator.setAttribute('y', String(tmpIndicatorY));\n\t\t\t\ttmpIndicator.setAttribute('width', String(tmpIndicatorSize));\n\t\t\t\ttmpIndicator.setAttribute('height', String(tmpIndicatorSize));\n\t\t\t\ttmpIndicator.setAttribute('rx', '2');\n\t\t\t\ttmpIndicator.setAttribute('ry', '2');\n\t\t\t\ttmpIndicator.setAttribute('data-node-hash', pNodeData.Hash);\n\t\t\t\ttmpIndicator.setAttribute('data-element-type', 'panel-indicator');\n\t\t\t}\n\n\t\t\tlet tmpIndicatorTitle = this._FlowView._SVGHelperProvider.createSVGElement('title');\n\t\t\ttmpIndicatorTitle.textContent = 'Double-click to open properties';\n\t\t\ttmpIndicator.appendChild(tmpIndicatorTitle);\n\n\t\t\ttmpGroup.appendChild(tmpIndicator);\n\t\t}\n\n\t\tpNodesLayer.appendChild(tmpGroup);\n\t}\n\n\t/**\n\t * Render ports for a node — delegates to the PortRenderer service.\n\t * @param {Object} pNodeData\n\t * @param {SVGGElement} pGroup - The node's SVG group\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @param {Object} [pNodeTypeConfig] - Node type configuration (for label display options)\n\t */\n\t_renderPorts(pNodeData, pGroup, pWidth, pHeight, pNodeTypeConfig)\n\t{\n\t\tthis._FlowView._PortRenderer.renderPorts(pNodeData, pGroup, pWidth, pHeight, pNodeTypeConfig, this.options.NodeTitleBarHeight);\n\t}\n\n\t/**\n\t * Render custom body content for a node (svg, html, or canvas).\n\t *\n\t * Checks for a BodyContent configuration on the node type and renders\n\t * the appropriate content type into the node's SVG group.\n\t *\n\t * @param {Object} pNodeData - The node data object\n\t * @param {SVGGElement} pGroup - The node's SVG group\n\t * @param {number} pWidth - Node width\n\t * @param {number} pHeight - Node height\n\t * @param {Object} pNodeTypeConfig - The node type configuration\n\t */\n\t_renderBodyContent(pNodeData, pGroup, pWidth, pHeight, pNodeTypeConfig)\n\t{\n\t\tif (!pNodeTypeConfig || !pNodeTypeConfig.BodyContent) return;\n\n\t\tlet tmpBodyContent = pNodeTypeConfig.BodyContent;\n\t\tlet tmpContentType = tmpBodyContent.ContentType;\n\t\tif (!tmpContentType) return;\n\n\t\tlet tmpTitleBarHeight = this.options.NodeTitleBarHeight;\n\t\tlet tmpPadding = (typeof tmpBodyContent.Padding === 'number') ? tmpBodyContent.Padding : 2;\n\t\tlet tmpBodyBounds =\n\t\t{\n\t\t\tx: tmpPadding,\n\t\t\ty: tmpTitleBarHeight + tmpPadding,\n\t\t\twidth: pWidth - (tmpPadding * 2),\n\t\t\theight: pHeight - tmpTitleBarHeight - (tmpPadding * 2)\n\t\t};\n\n\t\tlet tmpPict = this._FlowView.pict || this.pict;\n\n\t\t// Register any templates defined in the BodyContent config (once)\n\t\tif (tmpBodyContent.Templates && Array.isArray(tmpBodyContent.Templates))\n\t\t{\n\t\t\tif (!this._registeredBodyTemplates)\n\t\t\t{\n\t\t\t\tthis._registeredBodyTemplates = new Set();\n\t\t\t}\n\t\t\tfor (let i = 0; i < tmpBodyContent.Templates.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpTpl = tmpBodyContent.Templates[i];\n\t\t\t\tif (tmpTpl.Hash && tmpTpl.Template && !this._registeredBodyTemplates.has(tmpTpl.Hash))\n\t\t\t\t{\n\t\t\t\t\ttmpPict.TemplateProvider.addTemplate(tmpTpl.Hash, tmpTpl.Template, 'PictViewFlowNode-BodyContent');\n\t\t\t\t\tthis._registeredBodyTemplates.add(tmpTpl.Hash);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch (tmpContentType)\n\t\t{\n\t\t\tcase 'svg':\n\t\t\t\tthis._renderBodyContentSVG(pNodeData, pGroup, tmpBodyContent, tmpBodyBounds, pNodeTypeConfig, tmpPict);\n\t\t\t\tbreak;\n\t\t\tcase 'html':\n\t\t\t\tthis._renderBodyContentHTML(pNodeData, pGroup, tmpBodyContent, tmpBodyBounds, pNodeTypeConfig, tmpPict);\n\t\t\t\tbreak;\n\t\t\tcase 'canvas':\n\t\t\t\tthis._renderBodyContentCanvas(pNodeData, pGroup, tmpBodyContent, tmpBodyBounds, pNodeTypeConfig);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthis.log.warn('PictViewFlowNode _renderBodyContent: unknown ContentType [' + tmpContentType + ']');\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Render SVG body content into a <g> group.\n\t */\n\t_renderBodyContentSVG(pNodeData, pGroup, pBodyContent, pBounds, pNodeTypeConfig, pPict)\n\t{\n\t\tlet tmpContentGroup = this._FlowView._SVGHelperProvider.createSVGElement('g');\n\t\ttmpContentGroup.setAttribute('class', 'pict-flow-node-body-content');\n\t\ttmpContentGroup.setAttribute('transform', `translate(${pBounds.x}, ${pBounds.y})`);\n\n\t\t// Render template content\n\t\tlet tmpRenderedContent = this._resolveBodyTemplate(pBodyContent, pNodeData, pPict);\n\t\tif (tmpRenderedContent)\n\t\t{\n\t\t\t// Parse SVG markup into the group via a temporary SVG element\n\t\t\tlet tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\t\t\ttmpTempSVG.innerHTML = tmpRenderedContent;\n\t\t\twhile (tmpTempSVG.firstChild)\n\t\t\t{\n\t\t\t\ttmpContentGroup.appendChild(tmpTempSVG.firstChild);\n\t\t\t}\n\t\t}\n\n\t\t// Invoke render callback if provided\n\t\tif (typeof pBodyContent.RenderCallback === 'function')\n\t\t{\n\t\t\tpBodyContent.RenderCallback(tmpContentGroup, pNodeData, pNodeTypeConfig, pBounds);\n\t\t}\n\n\t\tpGroup.appendChild(tmpContentGroup);\n\t}\n\n\t/**\n\t * Render HTML body content into a foreignObject.\n\t */\n\t_renderBodyContentHTML(pNodeData, pGroup, pBodyContent, pBounds, pNodeTypeConfig, pPict)\n\t{\n\t\tlet tmpFO = this._FlowView._SVGHelperProvider.createSVGElement('foreignObject');\n\t\ttmpFO.setAttribute('class', 'pict-flow-node-body-content-fo');\n\t\ttmpFO.setAttribute('x', String(pBounds.x));\n\t\ttmpFO.setAttribute('y', String(pBounds.y));\n\t\ttmpFO.setAttribute('width', String(pBounds.width));\n\t\ttmpFO.setAttribute('height', String(pBounds.height));\n\n\t\tlet tmpDiv = document.createElement('div');\n\t\ttmpDiv.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');\n\t\ttmpDiv.setAttribute('class', 'pict-flow-node-body-content-html');\n\n\t\t// Pointer event isolation — prevent node drag/canvas pan\n\t\ttmpDiv.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\ttmpDiv.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });\n\n\t\t// Render template content\n\t\tlet tmpRenderedContent = this._resolveBodyTemplate(pBodyContent, pNodeData, pPict);\n\t\tif (tmpRenderedContent)\n\t\t{\n\t\t\ttmpDiv.innerHTML = tmpRenderedContent;\n\t\t}\n\n\t\t// Invoke render callback if provided\n\t\tif (typeof pBodyContent.RenderCallback === 'function')\n\t\t{\n\t\t\tpBodyContent.RenderCallback(tmpDiv, pNodeData, pNodeTypeConfig, pBounds);\n\t\t}\n\n\t\ttmpFO.appendChild(tmpDiv);\n\t\tpGroup.appendChild(tmpFO);\n\t}\n\n\t/**\n\t * Render canvas body content into a foreignObject.\n\t */\n\t_renderBodyContentCanvas(pNodeData, pGroup, pBodyContent, pBounds, pNodeTypeConfig)\n\t{\n\t\tlet tmpFO = this._FlowView._SVGHelperProvider.createSVGElement('foreignObject');\n\t\ttmpFO.setAttribute('class', 'pict-flow-node-body-content-fo');\n\t\ttmpFO.setAttribute('x', String(pBounds.x));\n\t\ttmpFO.setAttribute('y', String(pBounds.y));\n\t\ttmpFO.setAttribute('width', String(pBounds.width));\n\t\ttmpFO.setAttribute('height', String(pBounds.height));\n\n\t\tlet tmpCanvas = document.createElement('canvas');\n\t\ttmpCanvas.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');\n\t\ttmpCanvas.setAttribute('class', 'pict-flow-node-body-content-canvas');\n\t\ttmpCanvas.width = Math.floor(pBounds.width);\n\t\ttmpCanvas.height = Math.floor(pBounds.height);\n\t\ttmpCanvas.style.width = '100%';\n\t\ttmpCanvas.style.height = '100%';\n\n\t\t// Pointer event isolation\n\t\ttmpCanvas.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\ttmpCanvas.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });\n\n\t\t// Invoke render callback (the primary rendering path for canvas)\n\t\tif (typeof pBodyContent.RenderCallback === 'function')\n\t\t{\n\t\t\tpBodyContent.RenderCallback(tmpCanvas, pNodeData, pNodeTypeConfig, pBounds);\n\t\t}\n\n\t\ttmpFO.appendChild(tmpCanvas);\n\t\tpGroup.appendChild(tmpFO);\n\t}\n\n\t/**\n\t * Resolve and render a body content template string.\n\t * @param {Object} pBodyContent - The BodyContent config\n\t * @param {Object} pNodeData - The node data (template record)\n\t * @param {Object} pPict - The Pict instance\n\t * @returns {string|null} Rendered template content, or null\n\t */\n\t_resolveBodyTemplate(pBodyContent, pNodeData, pPict)\n\t{\n\t\tif (pBodyContent.TemplateHash)\n\t\t{\n\t\t\treturn pPict.parseTemplateByHash(pBodyContent.TemplateHash, pNodeData);\n\t\t}\n\t\tif (pBodyContent.Template)\n\t\t{\n\t\t\treturn pPict.parseTemplate(pBodyContent.Template, pNodeData, null, [pNodeData]);\n\t\t}\n\t\treturn null;\n\t}\n\n\t// ── Node Body Renderers ──────────────────────────────────────────────\n\n\t/**\n\t * Render the standard rect-based node body (default mode).\n\t * @param {SVGGElement} pGroup\n\t * @param {Object} pNodeData\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @param {number} pTitleBarHeight\n\t * @param {Object} pNodeTypeConfig\n\t */\n\t_renderRectNodeBody(pGroup, pNodeData, pWidth, pHeight, pTitleBarHeight, pNodeTypeConfig)\n\t{\n\t\t// Node body (main rectangle)\n\t\tlet tmpBody = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpBody.setAttribute('class', 'pict-flow-node-body');\n\t\ttmpBody.setAttribute('x', '0');\n\t\ttmpBody.setAttribute('y', '0');\n\t\ttmpBody.setAttribute('width', String(pWidth));\n\t\ttmpBody.setAttribute('height', String(pHeight));\n\t\ttmpBody.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpBody.setAttribute('data-element-type', 'node-body');\n\n\t\t// Apply custom styles from node type\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.BodyStyle)\n\t\t{\n\t\t\tfor (let tmpStyleKey in pNodeTypeConfig.BodyStyle)\n\t\t\t{\n\t\t\t\ttmpBody.setAttribute(tmpStyleKey, pNodeTypeConfig.BodyStyle[tmpStyleKey]);\n\t\t\t}\n\t\t}\n\n\t\t// Apply per-instance style overrides (for node-specific editing)\n\t\t// These must be applied as inline styles so they override CSS rules\n\t\t// (CSS declarations take precedence over SVG presentation attributes).\n\t\tif (pNodeData.Style)\n\t\t{\n\t\t\tlet tmpInlineStyles = [];\n\t\t\tif (pNodeData.Style.BodyFill) tmpInlineStyles.push('fill:' + pNodeData.Style.BodyFill);\n\t\t\tif (pNodeData.Style.BodyStroke) tmpInlineStyles.push('stroke:' + pNodeData.Style.BodyStroke);\n\t\t\tif (pNodeData.Style.BodyStrokeWidth) tmpInlineStyles.push('stroke-width:' + pNodeData.Style.BodyStrokeWidth);\n\t\t\tif (tmpInlineStyles.length > 0)\n\t\t\t{\n\t\t\t\ttmpBody.setAttribute('style', tmpInlineStyles.join(';'));\n\t\t\t}\n\t\t}\n\n\t\tpGroup.appendChild(tmpBody);\n\n\t\t// Title bar background (top portion)\n\t\tlet tmpTitleBar = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpTitleBar.setAttribute('class', 'pict-flow-node-title-bar');\n\t\ttmpTitleBar.setAttribute('x', '0');\n\t\ttmpTitleBar.setAttribute('y', '0');\n\t\ttmpTitleBar.setAttribute('width', String(pWidth));\n\t\ttmpTitleBar.setAttribute('height', String(pTitleBarHeight));\n\t\ttmpTitleBar.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpTitleBar.setAttribute('data-element-type', 'node-body');\n\n\t\t// Apply custom title bar color\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleBar.setAttribute('fill', pNodeTypeConfig.TitleBarColor);\n\t\t}\n\n\t\tpGroup.appendChild(tmpTitleBar);\n\n\t\t// Title bar bottom fill (to square off the rounded corners at the bottom of the title bar)\n\t\tlet tmpTitleBarBottom = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpTitleBarBottom.setAttribute('class', 'pict-flow-node-title-bar-bottom');\n\t\ttmpTitleBarBottom.setAttribute('x', '0');\n\t\ttmpTitleBarBottom.setAttribute('y', String(pTitleBarHeight - 8));\n\t\ttmpTitleBarBottom.setAttribute('width', String(pWidth));\n\t\ttmpTitleBarBottom.setAttribute('height', '8');\n\t\ttmpTitleBarBottom.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpTitleBarBottom.setAttribute('data-element-type', 'node-body');\n\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleBarBottom.setAttribute('fill', pNodeTypeConfig.TitleBarColor);\n\t\t}\n\n\t\t// Per-instance title bar color override\n\t\t// Applied as inline style to override CSS rules.\n\t\tif (pNodeData.Style && pNodeData.Style.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleBar.setAttribute('style', 'fill:' + pNodeData.Style.TitleBarColor);\n\t\t\ttmpTitleBarBottom.setAttribute('style', 'fill:' + pNodeData.Style.TitleBarColor);\n\t\t}\n\n\t\tpGroup.appendChild(tmpTitleBarBottom);\n\t}\n\n\t/**\n\t * Render a bracket-style node body (used by sketch/blueprint themes).\n\t *\n\t * The bracket body consists of:\n\t * 1. A fill rect for the body background (no stroke)\n\t * 2. A fill rect for the title bar background (no stroke)\n\t * 3. A bracket path drawn via the noise provider (outline + title divider)\n\t *\n\t * @param {SVGGElement} pGroup\n\t * @param {Object} pNodeData\n\t * @param {number} pWidth\n\t * @param {number} pHeight\n\t * @param {number} pTitleBarHeight\n\t * @param {Object} pNodeTypeConfig\n\t */\n\t_renderBracketNodeBody(pGroup, pNodeData, pWidth, pHeight, pTitleBarHeight, pNodeTypeConfig)\n\t{\n\t\t// 1. Body fill rect (background only, no stroke)\n\t\tlet tmpBodyFill = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpBodyFill.setAttribute('class', 'pict-flow-node-body pict-flow-node-bracket-fill');\n\t\ttmpBodyFill.setAttribute('x', '0');\n\t\ttmpBodyFill.setAttribute('y', '0');\n\t\ttmpBodyFill.setAttribute('width', String(pWidth));\n\t\ttmpBodyFill.setAttribute('height', String(pHeight));\n\t\ttmpBodyFill.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpBodyFill.setAttribute('data-element-type', 'node-body');\n\n\t\t// Per-instance style overrides\n\t\tif (pNodeData.Style)\n\t\t{\n\t\t\tlet tmpInlineStyles = [];\n\t\t\tif (pNodeData.Style.BodyFill) tmpInlineStyles.push('fill:' + pNodeData.Style.BodyFill);\n\t\t\tif (tmpInlineStyles.length > 0)\n\t\t\t{\n\t\t\t\ttmpBodyFill.setAttribute('style', tmpInlineStyles.join(';'));\n\t\t\t}\n\t\t}\n\n\t\tpGroup.appendChild(tmpBodyFill);\n\n\t\t// 2. Title bar fill rect (background only, no stroke)\n\t\tlet tmpTitleFill = this._FlowView._SVGHelperProvider.createSVGElement('rect');\n\t\ttmpTitleFill.setAttribute('class', 'pict-flow-node-title-bar pict-flow-node-bracket-title-fill');\n\t\ttmpTitleFill.setAttribute('x', '0');\n\t\ttmpTitleFill.setAttribute('y', '0');\n\t\ttmpTitleFill.setAttribute('width', String(pWidth));\n\t\ttmpTitleFill.setAttribute('height', String(pTitleBarHeight));\n\t\ttmpTitleFill.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpTitleFill.setAttribute('data-element-type', 'node-body');\n\n\t\tif (pNodeTypeConfig && pNodeTypeConfig.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleFill.setAttribute('style', 'fill:' + pNodeTypeConfig.TitleBarColor);\n\t\t}\n\t\tif (pNodeData.Style && pNodeData.Style.TitleBarColor)\n\t\t{\n\t\t\ttmpTitleFill.setAttribute('style', 'fill:' + pNodeData.Style.TitleBarColor);\n\t\t}\n\n\t\tpGroup.appendChild(tmpTitleFill);\n\n\t\t// 3. Bracket path (outline + title divider with optional noise)\n\t\tlet tmpBracketConfig = { SerifLength: 6, TitleSeparator: true };\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\tlet tmpActiveTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\t\tif (tmpActiveTheme && tmpActiveTheme.BracketConfig)\n\t\t\t{\n\t\t\t\ttmpBracketConfig = Object.assign(tmpBracketConfig, tmpActiveTheme.BracketConfig);\n\t\t\t}\n\t\t}\n\n\t\tlet tmpAmplitude = 0;\n\t\tif (this._FlowView._ThemeProvider)\n\t\t{\n\t\t\ttmpAmplitude = this._FlowView._ThemeProvider.getNodeNoiseAmplitude();\n\t\t}\n\n\t\tlet tmpBracketD = '';\n\t\tif (this._FlowView._NoiseProvider)\n\t\t{\n\t\t\ttmpBracketD = this._FlowView._NoiseProvider.generateBracketPath(\n\t\t\t\tpWidth, pHeight,\n\t\t\t\ttmpBracketConfig.SerifLength,\n\t\t\t\ttmpBracketConfig.TitleSeparator ? pTitleBarHeight : 0,\n\t\t\t\ttmpAmplitude,\n\t\t\t\tpNodeData.Hash\n\t\t\t);\n\t\t}\n\n\t\tlet tmpBracketPath = this._FlowView._SVGHelperProvider.createSVGElement('path');\n\t\ttmpBracketPath.setAttribute('class', 'pict-flow-node-bracket');\n\t\ttmpBracketPath.setAttribute('d', tmpBracketD);\n\t\ttmpBracketPath.setAttribute('data-node-hash', pNodeData.Hash);\n\t\ttmpBracketPath.setAttribute('data-element-type', 'node-body');\n\n\t\t// Per-instance stroke overrides\n\t\tif (pNodeData.Style)\n\t\t{\n\t\t\tlet tmpInlineStyles = [];\n\t\t\tif (pNodeData.Style.BodyStroke) tmpInlineStyles.push('stroke:' + pNodeData.Style.BodyStroke);\n\t\t\tif (pNodeData.Style.BodyStrokeWidth) tmpInlineStyles.push('stroke-width:' + pNodeData.Style.BodyStrokeWidth);\n\t\t\tif (tmpInlineStyles.length > 0)\n\t\t\t{\n\t\t\t\ttmpBracketPath.setAttribute('style', tmpInlineStyles.join(';'));\n\t\t\t}\n\t\t}\n\n\t\tpGroup.appendChild(tmpBracketPath);\n\t}\n}\n\nmodule.exports = PictViewFlowNode;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n","const libPictView = require('pict-view');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Flow-PropertiesPanel',\n\n\tAutoRender: false,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Wrapper',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel\">{~D:Record.PanelContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Header-Icon',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-header with-icon\">{~D:Record.Icon~} {~D:Record.Label~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Header',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-header\">{~D:Record.Label~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Description',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-description\">{~D:Record.Description~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Badges',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-badges\">{~D:Record.BadgesContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Badge-Category',\n\t\t\tTemplate: '<span class=\"pict-flow-info-panel-badge category\">{~D:Record.Category~}</span>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Badge-Code',\n\t\t\tTemplate: '<span class=\"pict-flow-info-panel-badge code\">{~D:Record.Code~}</span>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Section-Inputs',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-section\"><div class=\"pict-flow-info-panel-section-title\">Inputs</div>{~D:Record.PortsContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Section-Outputs',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-section\"><div class=\"pict-flow-info-panel-section-title\">Outputs</div>{~D:Record.PortsContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Input',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-port input\">{~D:Record.Label~}{~D:Record.Constraint~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Output',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-port output\">{~D:Record.Label~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Constraint',\n\t\t\tTemplate: ' <span class=\"pict-flow-info-panel-port-constraint\">{~D:Record.ConstraintText~}</span>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Section-Generic',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-section\"><div class=\"pict-flow-info-panel-section-title\">{~D:Record.SectionTitle~}</div>{~D:Record.PortsContent~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Event',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-port event\">{~D:Record.Label~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-Value',\n\t\t\tTemplate: '<div class=\"pict-flow-info-panel-port value\">{~D:Record.Label~}{~D:Record.DataType~}</div>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-InfoPanel-Port-DataType',\n\t\t\tTemplate: ' <span class=\"pict-flow-info-panel-port-constraint\">{~D:Record.DataTypeText~}</span>'\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-NodeProps-Editor',\n\t\t\tTemplate: '<div class=\"pict-flow-node-props-fields\"><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Title</label><input type=\"text\" class=\"pict-flow-node-props-input\" data-prop=\"Title\" value=\"{~D:Record.Title~}\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Width</label><input type=\"number\" class=\"pict-flow-node-props-input\" data-prop=\"Width\" value=\"{~D:Record.Width~}\" min=\"60\" step=\"10\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Height</label><input type=\"number\" class=\"pict-flow-node-props-input\" data-prop=\"Height\" value=\"{~D:Record.Height~}\" min=\"40\" step=\"10\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Body Fill</label><input type=\"color\" class=\"pict-flow-node-props-input pict-flow-node-props-color\" data-prop=\"Style.BodyFill\" value=\"{~D:Record.BodyFillValue~}\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Body Stroke</label><input type=\"color\" class=\"pict-flow-node-props-input pict-flow-node-props-color\" data-prop=\"Style.BodyStroke\" value=\"{~D:Record.BodyStrokeValue~}\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Stroke Width</label><input type=\"number\" class=\"pict-flow-node-props-input\" data-prop=\"Style.BodyStrokeWidth\" value=\"{~D:Record.BodyStrokeWidthValue~}\" min=\"0\" max=\"10\" step=\"0.5\" /></div><div class=\"pict-flow-node-props-field\"><label class=\"pict-flow-node-props-label\">Title Bar</label><input type=\"color\" class=\"pict-flow-node-props-input pict-flow-node-props-color\" data-prop=\"Style.TitleBarColor\" value=\"{~D:Record.TitleBarColorValue~}\" /></div></div>'\n\t\t}\n\t]\n};\n\n/**\n * PictView-Flow-PropertiesPanel\n *\n * Renders and manages all open properties panels on the flow graph.\n * Panels are SVG foreignObject elements containing HTML, placed inside\n * the viewport group so they zoom/pan with the graph.\n *\n * Responsibilities:\n * - Reconcile DOM (add new panels, remove closed ones, update positions)\n * - Render tether lines from each panel to its node\n * - Manage panel instance cache (PictFlowCardPropertiesPanel subclasses)\n * - Isolate HTML events from SVG interactions\n */\nclass PictViewFlowPropertiesPanel extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictViewFlowPropertiesPanel';\n\n\t\tthis._FlowView = null;\n\n\t\t// Cache of active panel instances: Map<panelHash, PictFlowCardPropertiesPanel>\n\t\tthis._PanelInstances = {};\n\t}\n\n\t/**\n\t * Render all open panels and their tethers.\n\t *\n\t * Uses DOM reconciliation for panels (to preserve live HTML state)\n\t * and clear-and-rebuild for tethers (trivial SVG lines).\n\t *\n\t * @param {Array} pOpenPanels - Array of panel data objects from _FlowData.OpenPanels\n\t * @param {SVGGElement} pPanelsLayer - The SVG <g> for panel foreignObjects\n\t * @param {SVGGElement} pTethersLayer - The SVG <g> for tether lines\n\t * @param {string|null} pSelectedTetherHash - Hash of the selected tether's panel, or null\n\t */\n\trenderPanels(pOpenPanels, pPanelsLayer, pTethersLayer, pSelectedTetherHash)\n\t{\n\t\tif (!pPanelsLayer || !pTethersLayer) return;\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpOpenPanels = Array.isArray(pOpenPanels) ? pOpenPanels : [];\n\n\t\t// --- Reconcile panels layer (add new, remove closed, update positions) ---\n\t\tlet tmpExistingPanelHashes = new Set();\n\t\tlet tmpExistingForeignObjects = pPanelsLayer.querySelectorAll('.pict-flow-panel-foreign-object');\n\t\tfor (let i = 0; i < tmpExistingForeignObjects.length; i++)\n\t\t{\n\t\t\ttmpExistingPanelHashes.add(tmpExistingForeignObjects[i].getAttribute('data-panel-hash'));\n\t\t}\n\n\t\tlet tmpDesiredPanelHashes = new Set();\n\t\tfor (let i = 0; i < tmpOpenPanels.length; i++)\n\t\t{\n\t\t\ttmpDesiredPanelHashes.add(tmpOpenPanels[i].Hash);\n\t\t}\n\n\t\t// Remove panels that are no longer open\n\t\tfor (let i = 0; i < tmpExistingForeignObjects.length; i++)\n\t\t{\n\t\t\tlet tmpHash = tmpExistingForeignObjects[i].getAttribute('data-panel-hash');\n\t\t\tif (!tmpDesiredPanelHashes.has(tmpHash))\n\t\t\t{\n\t\t\t\ttmpExistingForeignObjects[i].remove();\n\t\t\t\t// Destroy cached instance\n\t\t\t\tif (this._PanelInstances[tmpHash])\n\t\t\t\t{\n\t\t\t\t\tthis._PanelInstances[tmpHash].destroy();\n\t\t\t\t\tdelete this._PanelInstances[tmpHash];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add or update panels\n\t\tfor (let i = 0; i < tmpOpenPanels.length; i++)\n\t\t{\n\t\t\tlet tmpPanelData = tmpOpenPanels[i];\n\n\t\t\tif (tmpExistingPanelHashes.has(tmpPanelData.Hash))\n\t\t\t{\n\t\t\t\t// Update position of existing panel\n\t\t\t\tlet tmpFO = pPanelsLayer.querySelector(`[data-panel-hash=\"${tmpPanelData.Hash}\"]`);\n\t\t\t\tif (tmpFO)\n\t\t\t\t{\n\t\t\t\t\ttmpFO.setAttribute('x', String(tmpPanelData.X));\n\t\t\t\t\ttmpFO.setAttribute('y', String(tmpPanelData.Y));\n\t\t\t\t\ttmpFO.setAttribute('width', String(tmpPanelData.Width));\n\t\t\t\t\ttmpFO.setAttribute('height', String(tmpPanelData.Height));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Create new panel\n\t\t\t\tthis._createPanelForeignObject(tmpPanelData, pPanelsLayer);\n\t\t\t}\n\t\t}\n\n\t\t// --- Clear and rebuild tethers ---\n\t\twhile (pTethersLayer.firstChild)\n\t\t{\n\t\t\tpTethersLayer.removeChild(pTethersLayer.firstChild);\n\t\t}\n\n\t\tfor (let i = 0; i < tmpOpenPanels.length; i++)\n\t\t{\n\t\t\tlet tmpIsSelected = (pSelectedTetherHash === tmpOpenPanels[i].Hash);\n\t\t\tthis._renderTether(tmpOpenPanels[i], pTethersLayer, tmpIsSelected);\n\t\t}\n\t}\n\n\t/**\n\t * Create a foreignObject containing the panel chrome and content.\n\t * Delegates to the PanelChrome provider for template-based chrome creation,\n\t * then renders panel content into the body container.\n\t *\n\t * @param {Object} pPanelData - Panel data from OpenPanels\n\t * @param {SVGGElement} pPanelsLayer\n\t */\n\t_createPanelForeignObject(pPanelData, pPanelsLayer)\n\t{\n\t\tlet tmpPanelChromeProvider = this._FlowView._PanelChromeProvider;\n\t\tif (!tmpPanelChromeProvider) return;\n\n\t\tlet tmpBody = tmpPanelChromeProvider.createPanelForeignObject(pPanelData, pPanelsLayer);\n\n\t\t// Render the panel content into the Properties tab pane\n\t\tif (tmpBody)\n\t\t{\n\t\t\tthis._renderPanelContent(pPanelData, tmpBody);\n\t\t}\n\n\t\tlet tmpFO = pPanelsLayer.querySelector(`[data-panel-hash=\"${pPanelData.Hash}\"]`);\n\t\tif (tmpFO)\n\t\t{\n\t\t\t// Render appearance and help tabs\n\t\t\tthis._renderAppearanceTab(pPanelData, tmpFO);\n\t\t\tthis._renderHelpTab(pPanelData, tmpFO);\n\n\t\t\t// Wire up tab switching\n\t\t\tthis._wireTabSwitching(tmpFO);\n\t\t}\n\t}\n\n\t/**\n\t * Instantiate (or reuse) the panel type implementation and render into the body container.\n\t *\n\t * @param {Object} pPanelData\n\t * @param {HTMLDivElement} pBodyContainer\n\t */\n\t_renderPanelContent(pPanelData, pBodyContainer)\n\t{\n\t\tlet tmpNodeData = this._FlowView.getNode(pPanelData.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNodeData.Type);\n\t\tif (!tmpNodeTypeConfig) return;\n\n\t\t// If no PropertiesPanel is configured, render the auto-generated info panel\n\t\tif (!tmpNodeTypeConfig.PropertiesPanel)\n\t\t{\n\t\t\tthis._renderInfoPanelContent(pBodyContainer, tmpNodeData, tmpNodeTypeConfig);\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpPanelConfig = tmpNodeTypeConfig.PropertiesPanel;\n\t\tlet tmpPanelType = tmpPanelConfig.PanelType || 'Base';\n\n\t\t// Try to get a registered panel type service\n\t\tlet tmpServiceName = `PictFlowCardPropertiesPanel-${tmpPanelType}`;\n\t\tlet tmpInstance = null;\n\n\t\tif (this._PanelInstances[pPanelData.Hash])\n\t\t{\n\t\t\t// Reuse existing instance\n\t\t\ttmpInstance = this._PanelInstances[pPanelData.Hash];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Create a new instance\n\t\t\tif (this.fable.servicesMap.hasOwnProperty(tmpServiceName))\n\t\t\t{\n\t\t\t\ttmpInstance = this.fable.instantiateServiceProviderWithoutRegistration(tmpServiceName, tmpPanelConfig);\n\t\t\t}\n\t\t\telse if (this.fable.servicesMap.hasOwnProperty('PictFlowCardPropertiesPanel'))\n\t\t\t{\n\t\t\t\t// Fall back to base class\n\t\t\t\ttmpInstance = this.fable.instantiateServiceProviderWithoutRegistration('PictFlowCardPropertiesPanel', tmpPanelConfig);\n\t\t\t}\n\n\t\t\tif (tmpInstance)\n\t\t\t{\n\t\t\t\ttmpInstance._FlowView = this._FlowView;\n\t\t\t\tthis._PanelInstances[pPanelData.Hash] = tmpInstance;\n\t\t\t}\n\t\t}\n\n\t\tif (tmpInstance)\n\t\t{\n\t\t\ttmpInstance.render(pBodyContainer, tmpNodeData);\n\t\t}\n\n\t\t// After the form renders, append port summary sections showing\n\t\t// event inputs, event outputs, and state outputs.\n\t\t// SettingsInputs are already visible as form fields above.\n\t\tthis._renderPortSummary(pBodyContainer, tmpNodeTypeConfig);\n\t}\n\n\t/**\n\t * Render an auto-generated info panel for nodes without a configured PropertiesPanel.\n\t * Shows the node type, description, and a summary of input/output ports with\n\t * their connection constraints.\n\t *\n\t * Uses configuration-based templates from _DefaultConfiguration.Templates\n\t * rendered via pict.parseTemplateByHash().\n\t *\n\t * @param {HTMLDivElement} pContainer\n\t * @param {Object} pNodeData\n\t * @param {Object} pNodeTypeConfig\n\t */\n\t_renderInfoPanelContent(pContainer, pNodeData, pNodeTypeConfig)\n\t{\n\t\tlet tmpMeta = pNodeTypeConfig.CardMetadata || {};\n\t\tlet tmpPorts = pNodeTypeConfig.DefaultPorts || [];\n\n\t\tlet tmpInputs = tmpPorts.filter((pPort) => pPort.Direction === 'input');\n\t\tlet tmpOutputs = tmpPorts.filter((pPort) => pPort.Direction === 'output');\n\n\t\tlet tmpLabel = pNodeTypeConfig.Label || pNodeData.Type;\n\n\t\t// Build content by rendering configuration-based templates\n\t\tlet tmpContentParts = [];\n\n\t\t// Header\n\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\tif (tmpMeta.Icon && tmpIconProvider && !tmpIconProvider.isEmojiIcon(tmpMeta.Icon))\n\t\t{\n\t\t\t// SVG icon markup for the header\n\t\t\tlet tmpResolvedKey = tmpIconProvider.resolveIconKey(tmpMeta);\n\t\t\tlet tmpIconMarkup = tmpIconProvider.getIconSVGMarkup(tmpResolvedKey, 18);\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Header-Icon', { Icon: tmpIconMarkup, Label: tmpLabel }));\n\t\t}\n\t\telse if (tmpMeta.Icon)\n\t\t{\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Header-Icon', { Icon: tmpMeta.Icon, Label: tmpLabel }));\n\t\t}\n\t\telse if (tmpIconProvider)\n\t\t{\n\t\t\t// No icon specified — render default fallback\n\t\t\tlet tmpDefaultMarkup = tmpIconProvider.getIconSVGMarkup('default', 18);\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Header-Icon', { Icon: tmpDefaultMarkup, Label: tmpLabel }));\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Header', { Label: tmpLabel }));\n\t\t}\n\n\t\t// Description\n\t\tif (tmpMeta.Description)\n\t\t{\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Description', { Description: tmpMeta.Description }));\n\t\t}\n\n\t\t// Category + Code badges\n\t\tif (tmpMeta.Category || tmpMeta.Code)\n\t\t{\n\t\t\tlet tmpBadgesContent = '';\n\t\t\tif (tmpMeta.Category)\n\t\t\t{\n\t\t\t\ttmpBadgesContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Badge-Category', { Category: tmpMeta.Category });\n\t\t\t}\n\t\t\tif (tmpMeta.Code)\n\t\t\t{\n\t\t\t\ttmpBadgesContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Badge-Code', { Code: tmpMeta.Code });\n\t\t\t}\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Badges', { BadgesContent: tmpBadgesContent }));\n\t\t}\n\n\t\t// Inputs\n\t\tif (tmpInputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpInputs.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpInputs[i];\n\t\t\t\tlet tmpConstraint = this._getPortConstraintHTML(tmpPort);\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Input', { Label: tmpPort.Label || 'In', Constraint: tmpConstraint });\n\t\t\t}\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Inputs', { PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\t// Outputs\n\t\tif (tmpOutputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpOutputs.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpOutputs[i];\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Output', { Label: tmpPort.Label || 'Out' });\n\t\t\t}\n\t\t\ttmpContentParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Outputs', { PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\tpContainer.innerHTML = this.pict.parseTemplateByHash('Flow-InfoPanel-Wrapper', { PanelContent: tmpContentParts.join('') });\n\t}\n\n\t/**\n\t * Render port summary sections below the form panel content.\n\t * Shows event inputs, event outputs, and state outputs — the ports\n\t * that the form does not cover (since the form only shows SettingsInputs).\n\t *\n\t * @param {HTMLDivElement} pContainer\n\t * @param {Object} pNodeTypeConfig\n\t */\n\t_renderPortSummary(pContainer, pNodeTypeConfig)\n\t{\n\t\tlet tmpPorts = pNodeTypeConfig.DefaultPorts || [];\n\t\tif (tmpPorts.length === 0) return;\n\n\t\t// Categorize ports by type (settings are already shown as form fields)\n\t\tlet tmpEventInputs = [];\n\t\tlet tmpEventOutputs = [];\n\t\tlet tmpStateOutputs = [];\n\n\t\tfor (let i = 0; i < tmpPorts.length; i++)\n\t\t{\n\t\t\tlet tmpPort = tmpPorts[i];\n\t\t\tlet tmpPortType = tmpPort.PortType || '';\n\n\t\t\tif (tmpPortType === 'event-in')\n\t\t\t{\n\t\t\t\ttmpEventInputs.push(tmpPort);\n\t\t\t}\n\t\t\telse if (tmpPortType === 'event-out' || tmpPortType === 'error')\n\t\t\t{\n\t\t\t\ttmpEventOutputs.push(tmpPort);\n\t\t\t}\n\t\t\telse if (tmpPortType === 'value')\n\t\t\t{\n\t\t\t\ttmpStateOutputs.push(tmpPort);\n\t\t\t}\n\t\t}\n\n\t\t// Only render if there are non-settings ports to show\n\t\tif (tmpEventInputs.length === 0 && tmpEventOutputs.length === 0 && tmpStateOutputs.length === 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpSummaryParts = [];\n\n\t\t// Event Inputs\n\t\tif (tmpEventInputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpEventInputs.length; i++)\n\t\t\t{\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Event', { Label: tmpEventInputs[i].Label || tmpEventInputs[i].Name || 'Event In' });\n\t\t\t}\n\t\t\ttmpSummaryParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Generic', { SectionTitle: 'Event Inputs', PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\t// Event Outputs\n\t\tif (tmpEventOutputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpEventOutputs.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpEventOutputs[i];\n\t\t\t\tlet tmpLabel = tmpPort.Label || tmpPort.Name || 'Event Out';\n\t\t\t\tif (tmpPort.PortType === 'error')\n\t\t\t\t{\n\t\t\t\t\ttmpLabel += ' ⚠';\n\t\t\t\t}\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Event', { Label: tmpLabel });\n\t\t\t}\n\t\t\ttmpSummaryParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Generic', { SectionTitle: 'Event Outputs', PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\t// State Outputs\n\t\tif (tmpStateOutputs.length > 0)\n\t\t{\n\t\t\tlet tmpPortsContent = '';\n\t\t\tfor (let i = 0; i < tmpStateOutputs.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpPort = tmpStateOutputs[i];\n\t\t\t\tlet tmpLabel = tmpPort.Label || tmpPort.Name || 'Output';\n\t\t\t\tlet tmpDataType = '';\n\t\t\t\tif (tmpPort.DataType)\n\t\t\t\t{\n\t\t\t\t\ttmpDataType = this.pict.parseTemplateByHash('Flow-InfoPanel-Port-DataType', { DataTypeText: tmpPort.DataType });\n\t\t\t\t}\n\t\t\t\ttmpPortsContent += this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Value', { Label: tmpLabel, DataType: tmpDataType });\n\t\t\t}\n\t\t\ttmpSummaryParts.push(this.pict.parseTemplateByHash('Flow-InfoPanel-Section-Generic', { SectionTitle: 'State Outputs', PortsContent: tmpPortsContent }));\n\t\t}\n\n\t\tif (tmpSummaryParts.length > 0)\n\t\t{\n\t\t\t// Create a wrapper div for the port summary and append it to the container\n\t\t\tlet tmpSummaryDiv = document.createElement('div');\n\t\t\ttmpSummaryDiv.className = 'pict-flow-info-panel pict-flow-port-summary';\n\t\t\ttmpSummaryDiv.innerHTML = tmpSummaryParts.join('');\n\t\t\tpContainer.appendChild(tmpSummaryDiv);\n\t\t}\n\t}\n\n\t/**\n\t * Build the constraint markup for a port using configuration templates.\n\t *\n\t * @param {Object} pPort\n\t * @returns {string} Rendered constraint HTML or empty string\n\t */\n\t_getPortConstraintHTML(pPort)\n\t{\n\t\tlet tmpMin = (typeof pPort.MinimumInputCount === 'number') ? pPort.MinimumInputCount : 0;\n\t\tlet tmpMax = (typeof pPort.MaximumInputCount === 'number') ? pPort.MaximumInputCount : -1;\n\n\t\tif (tmpMin > 0 || tmpMax > 0)\n\t\t{\n\t\t\tlet tmpConstraintText = '';\n\t\t\tif (tmpMax < 0)\n\t\t\t{\n\t\t\t\ttmpConstraintText = `(min ${tmpMin})`;\n\t\t\t}\n\t\t\telse if (tmpMin === tmpMax)\n\t\t\t{\n\t\t\t\ttmpConstraintText = `(exactly ${tmpMin})`;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpConstraintText = `(${tmpMin}\\u2013${tmpMax})`;\n\t\t\t}\n\t\t\treturn this.pict.parseTemplateByHash('Flow-InfoPanel-Port-Constraint', { ConstraintText: tmpConstraintText });\n\t\t}\n\t\treturn '';\n\t}\n\n\t/**\n\t * Render the Appearance tab content with node property editor fields.\n\t *\n\t * @param {Object} pPanelData - Panel data from OpenPanels\n\t * @param {Element} pForeignObject - The panel's SVG foreignObject element\n\t */\n\t_renderAppearanceTab(pPanelData, pForeignObject)\n\t{\n\t\tlet tmpNodeData = this._FlowView.getNode(pPanelData.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpAppearancePane = pForeignObject.querySelector('.pict-flow-panel-tab-pane[data-tab=\"appearance\"]');\n\t\tif (!tmpAppearancePane) return;\n\n\t\t// Build the template record with safe defaults for Style values\n\t\tlet tmpStyle = tmpNodeData.Style || {};\n\n\t\t// Resolve default colors from the node type config or CSS token defaults\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNodeData.Type);\n\t\tlet tmpDefaultTitleBarColor = '#2c3e50';\n\t\tlet tmpDefaultBodyFill = '#ffffff';\n\t\tlet tmpDefaultBodyStroke = '#d0d4d8';\n\t\tif (tmpNodeTypeConfig)\n\t\t{\n\t\t\tif (tmpNodeTypeConfig.TitleBarColor) tmpDefaultTitleBarColor = tmpNodeTypeConfig.TitleBarColor;\n\t\t\tif (tmpNodeTypeConfig.BodyStyle)\n\t\t\t{\n\t\t\t\tif (tmpNodeTypeConfig.BodyStyle.fill) tmpDefaultBodyFill = tmpNodeTypeConfig.BodyStyle.fill;\n\t\t\t\tif (tmpNodeTypeConfig.BodyStyle.stroke) tmpDefaultBodyStroke = tmpNodeTypeConfig.BodyStyle.stroke;\n\t\t\t}\n\t\t}\n\n\t\tlet tmpRecord =\n\t\t{\n\t\t\tTitle: tmpNodeData.Title || '',\n\t\t\tWidth: tmpNodeData.Width || 180,\n\t\t\tHeight: tmpNodeData.Height || 80,\n\t\t\tBodyFillValue: tmpStyle.BodyFill || tmpDefaultBodyFill,\n\t\t\tBodyStrokeValue: tmpStyle.BodyStroke || tmpDefaultBodyStroke,\n\t\t\tBodyStrokeWidthValue: tmpStyle.BodyStrokeWidth || 1,\n\t\t\tTitleBarColorValue: tmpStyle.TitleBarColor || tmpDefaultTitleBarColor\n\t\t};\n\n\t\ttmpAppearancePane.innerHTML = this.pict.parseTemplateByHash('Flow-NodeProps-Editor', tmpRecord);\n\n\t\t// Wire up live change handlers on all input fields\n\t\tlet tmpInputs = tmpAppearancePane.querySelectorAll('.pict-flow-node-props-input');\n\t\tfor (let i = 0; i < tmpInputs.length; i++)\n\t\t{\n\t\t\tlet tmpInput = tmpInputs[i];\n\t\t\tlet tmpProp = tmpInput.getAttribute('data-prop');\n\n\t\t\ttmpInput.addEventListener('input', (pEvent) =>\n\t\t\t{\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tthis._applyNodePropChange(pPanelData.NodeHash, tmpProp, tmpInput.value, tmpInput.type);\n\t\t\t});\n\n\t\t\t// Prevent pointer events from propagating to SVG drag handler\n\t\t\ttmpInput.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\t\t}\n\t}\n\n\t/**\n\t * Render the Help tab content if help text is defined on the node type.\n\t * Shows the Help tab button only when help content is available.\n\t *\n\t * @param {Object} pPanelData - Panel data from OpenPanels\n\t * @param {Element} pForeignObject - The panel's SVG foreignObject element\n\t */\n\t_renderHelpTab(pPanelData, pForeignObject)\n\t{\n\t\tlet tmpNodeData = this._FlowView.getNode(pPanelData.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNodeData.Type);\n\t\tif (!tmpNodeTypeConfig) return;\n\n\t\tlet tmpHelpText = (tmpNodeTypeConfig.CardMetadata && tmpNodeTypeConfig.CardMetadata.Help)\n\t\t\t? tmpNodeTypeConfig.CardMetadata.Help\n\t\t\t: null;\n\n\t\tif (!tmpHelpText) return;\n\n\t\t// Show the Help tab button\n\t\tlet tmpHelpTabButton = pForeignObject.querySelector('.pict-flow-panel-tab[data-tab-target=\"help\"]');\n\t\tif (tmpHelpTabButton)\n\t\t{\n\t\t\ttmpHelpTabButton.style.display = '';\n\t\t}\n\n\t\t// Render help content into the help pane\n\t\tlet tmpHelpPane = pForeignObject.querySelector('.pict-flow-panel-tab-pane[data-tab=\"help\"]');\n\t\tif (tmpHelpPane)\n\t\t{\n\t\t\ttmpHelpPane.innerHTML = '<div class=\"pict-flow-panel-help-content\">' + tmpHelpText + '</div>';\n\t\t}\n\t}\n\n\t/**\n\t * Wire up tab switching on all tab buttons within a panel foreignObject.\n\t *\n\t * @param {Element} pForeignObject - The panel's SVG foreignObject element\n\t */\n\t_wireTabSwitching(pForeignObject)\n\t{\n\t\tlet tmpTabs = pForeignObject.querySelectorAll('.pict-flow-panel-tab');\n\t\tlet tmpPanes = pForeignObject.querySelectorAll('.pict-flow-panel-tab-pane');\n\n\t\tfor (let i = 0; i < tmpTabs.length; i++)\n\t\t{\n\t\t\ttmpTabs[i].addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tlet tmpTarget = pEvent.currentTarget.getAttribute('data-tab-target');\n\n\t\t\t\t// Deactivate all tabs and panes\n\t\t\t\tfor (let j = 0; j < tmpTabs.length; j++)\n\t\t\t\t{\n\t\t\t\t\ttmpTabs[j].classList.remove('active');\n\t\t\t\t}\n\t\t\t\tfor (let j = 0; j < tmpPanes.length; j++)\n\t\t\t\t{\n\t\t\t\t\ttmpPanes[j].classList.remove('active');\n\t\t\t\t\ttmpPanes[j].style.display = 'none';\n\t\t\t\t}\n\n\t\t\t\t// Activate the selected tab and pane\n\t\t\t\tpEvent.currentTarget.classList.add('active');\n\t\t\t\tlet tmpTargetPane = pForeignObject.querySelector('.pict-flow-panel-tab-pane[data-tab=\"' + tmpTarget + '\"]');\n\t\t\t\tif (tmpTargetPane)\n\t\t\t\t{\n\t\t\t\t\ttmpTargetPane.classList.add('active');\n\t\t\t\t\ttmpTargetPane.style.display = 'block';\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Apply a node property change from the properties editor and re-render.\n\t *\n\t * @param {string} pNodeHash - Hash of the node to update\n\t * @param {string} pPropPath - Property path (e.g. 'Title', 'Width', 'Style.BodyFill')\n\t * @param {string} pValue - The new value from the input\n\t * @param {string} pInputType - The input element type ('text', 'number', 'color')\n\t */\n\t_applyNodePropChange(pNodeHash, pPropPath, pValue, pInputType)\n\t{\n\t\tlet tmpNodeData = this._FlowView.getNode(pNodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\t// Parse numeric values\n\t\tlet tmpValue = pValue;\n\t\tif (pInputType === 'number')\n\t\t{\n\t\t\ttmpValue = parseFloat(pValue);\n\t\t\tif (isNaN(tmpValue)) return;\n\t\t}\n\n\t\t// Apply the value based on the property path\n\t\tif (pPropPath === 'Title')\n\t\t{\n\t\t\ttmpNodeData.Title = tmpValue;\n\t\t}\n\t\telse if (pPropPath === 'Width')\n\t\t{\n\t\t\ttmpNodeData.Width = tmpValue;\n\t\t}\n\t\telse if (pPropPath === 'Height')\n\t\t{\n\t\t\ttmpNodeData.Height = tmpValue;\n\t\t}\n\t\telse if (pPropPath.startsWith('Style.'))\n\t\t{\n\t\t\tif (!tmpNodeData.Style) tmpNodeData.Style = {};\n\t\t\tlet tmpStyleKey = pPropPath.substring(6); // Remove 'Style.' prefix\n\t\t\ttmpNodeData.Style[tmpStyleKey] = tmpValue;\n\t\t}\n\n\t\t// Re-render the flow to reflect changes\n\t\tthis._FlowView.renderFlow();\n\t\tthis._FlowView.marshalFromView();\n\n\t\t// Fire change event\n\t\tif (this._FlowView._EventHandlerProvider)\n\t\t{\n\t\t\tthis._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Render a tether from a panel to its node.\n\t * Delegates to the TetherService for geometry, path generation, and SVG element creation.\n\t *\n\t * @param {Object} pPanelData\n\t * @param {SVGGElement} pTethersLayer\n\t * @param {boolean} pIsSelected\n\t */\n\t_renderTether(pPanelData, pTethersLayer, pIsSelected)\n\t{\n\t\tlet tmpTetherService = this._FlowView._TetherService;\n\t\tif (!tmpTetherService) return;\n\n\t\tlet tmpNodeData = this._FlowView.getNode(pPanelData.NodeHash);\n\t\tif (!tmpNodeData) return;\n\n\t\tlet tmpViewIdentifier = this._FlowView.options.ViewIdentifier;\n\t\ttmpTetherService.renderTether(pPanelData, tmpNodeData, pTethersLayer, pIsSelected, tmpViewIdentifier);\n\t}\n\n\t/**\n\t * Marshal data from all open panels back into their node Data objects.\n\t */\n\tmarshalAllFromPanels()\n\t{\n\t\tfor (let tmpPanelHash in this._PanelInstances)\n\t\t{\n\t\t\tlet tmpInstance = this._PanelInstances[tmpPanelHash];\n\t\t\tif (tmpInstance && tmpInstance._NodeData)\n\t\t\t{\n\t\t\t\ttmpInstance.marshalFromPanel(tmpInstance._NodeData);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Destroy a specific panel instance and clean up.\n\t *\n\t * @param {string} pPanelHash\n\t */\n\tdestroyPanel(pPanelHash)\n\t{\n\t\tif (this._PanelInstances[pPanelHash])\n\t\t{\n\t\t\tthis._PanelInstances[pPanelHash].destroy();\n\t\t\tdelete this._PanelInstances[pPanelHash];\n\t\t}\n\t}\n\n\t/**\n\t * Destroy all panel instances.\n\t */\n\tdestroyAllPanels()\n\t{\n\t\tfor (let tmpPanelHash in this._PanelInstances)\n\t\t{\n\t\t\tthis._PanelInstances[tmpPanelHash].destroy();\n\t\t}\n\t\tthis._PanelInstances = {};\n\t}\n}\n\nmodule.exports = PictViewFlowPropertiesPanel;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n","const libPictView = require('pict-view');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Flow-Toolbar',\n\n\tDefaultRenderable: 'Flow-Toolbar-Content',\n\tDefaultDestinationAddress: '#Flow-Toolbar-Container',\n\n\tAutoRender: false,\n\n\tFlowViewIdentifier: 'Pict-Flow',\n\n\tEnablePalette: true,\n\tEnableAddNode: true,\n\tEnableCardPalette: true,\n\n\tCSS: false,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'Flow-Toolbar-Template',\n\t\t\tTemplate: /*html*/`\n<div class=\"pict-flow-toolbar\" id=\"Flow-Toolbar-Bar-{~D:Record.FlowViewIdentifier~}\">\n\t<div class=\"pict-flow-toolbar-group\">\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"add-node\" id=\"Flow-Toolbar-AddNode-{~D:Record.FlowViewIdentifier~}\" title=\"Add Node\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-plus-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-text\">Node</span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"cards-popup\" id=\"Flow-Toolbar-Cards-{~D:Record.FlowViewIdentifier~}\" title=\"Card Palette\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-cards-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-text\">Cards</span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-chevron\" id=\"Flow-Toolbar-CardsChevron-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"delete-selected\" title=\"Delete Node\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-trash-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t</div>\n\t<div class=\"pict-flow-toolbar-group\">\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"layout-popup\" id=\"Flow-Toolbar-Layout-{~D:Record.FlowViewIdentifier~}\" title=\"Manage Layouts\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-layout-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-text\">Layout</span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-chevron\" id=\"Flow-Toolbar-LayoutChevron-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"auto-layout\" title=\"Auto Layout\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-auto-layout-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t\t<span class=\"pict-flow-toolbar-btn-text\">Auto Layout</span>\n\t\t</button>\n\t</div>\n\t<div class=\"pict-flow-toolbar-group\">\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"zoom-in\" title=\"Zoom In\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-zoom-in-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"zoom-out\" title=\"Zoom Out\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-zoom-out-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"zoom-fit\" title=\"Fit to View\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-zoom-fit-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t</div>\n\t<div class=\"pict-flow-toolbar-group pict-flow-toolbar-right\">\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"settings-popup\" id=\"Flow-Toolbar-Settings-{~D:Record.FlowViewIdentifier~}\" title=\"Theme Settings\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-settings-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"fullscreen\" id=\"Flow-Toolbar-Fullscreen-{~D:Record.FlowViewIdentifier~}\" title=\"Toggle Fullscreen\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Fullscreen-Icon-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"toggle-floating\" title=\"Float\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-grip-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t\t<button class=\"pict-flow-toolbar-btn\" data-flow-action=\"collapse-toolbar\" title=\"Collapse Toolbar\">\n\t\t\t<span class=\"pict-flow-toolbar-btn-icon\" id=\"Flow-Toolbar-Icon-collapse-{~D:Record.FlowViewIdentifier~}\"></span>\n\t\t</button>\n\t</div>\n</div>\n<div class=\"pict-flow-toolbar-collapsed\" id=\"Flow-Toolbar-Collapsed-{~D:Record.FlowViewIdentifier~}\">\n\t<button class=\"pict-flow-toolbar-expand-btn\" data-flow-action=\"expand-toolbar\" title=\"Expand Toolbar\" id=\"Flow-Toolbar-ExpandBtn-{~D:Record.FlowViewIdentifier~}\">\n\t\t<span id=\"Flow-Toolbar-Icon-expand-{~D:Record.FlowViewIdentifier~}\"></span>\n\t</button>\n</div>\n<div class=\"pict-flow-toolbar-popup-anchor\" id=\"Flow-Toolbar-PopupAnchor-{~D:Record.FlowViewIdentifier~}\">\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: 'Flow-Toolbar-Content',\n\t\t\tTemplateHash: 'Flow-Toolbar-Template',\n\t\t\tDestinationAddress: '#Flow-Toolbar-Container',\n\t\t\tRenderMethod: 'replace'\n\t\t}\n\t]\n};\n\nclass PictViewFlowToolbar extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictViewFlowToolbar';\n\n\t\tthis._FlowView = null;\n\n\t\t// Toolbar mode state\n\t\tthis._ToolbarMode = 'docked'; // 'docked' | 'floating' | 'collapsed'\n\t\tthis._ActivePopup = null; // 'add-node' | 'cards' | 'layout' | null\n\t\tthis._FloatingPosition = { X: 80, Y: 80 };\n\t\tthis._DocumentClickHandler = null;\n\t\tthis._FloatingToolbarView = null;\n\t}\n\n\trender(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\t// Pass this.options as the template record so {~D:Record.FlowViewIdentifier~}\n\t\t// resolves correctly in the toolbar template.\n\t\treturn super.render(pRenderableHash, pRenderDestinationAddress, this.options);\n\t}\n\n\tonAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\t// Bind toolbar button events via event delegation\n\t\tlet tmpToolbarBar = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Bar-${tmpFlowViewIdentifier}`);\n\t\tif (tmpToolbarBar.length > 0)\n\t\t{\n\t\t\ttmpToolbarBar[0].addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\tlet tmpTarget = pEvent.target;\n\t\t\t\tif (!tmpTarget) return;\n\n\t\t\t\t// Walk up to find the button with the action\n\t\t\t\tlet tmpButton = tmpTarget.closest('[data-flow-action]');\n\t\t\t\tif (!tmpButton) return;\n\n\t\t\t\tlet tmpAction = tmpButton.getAttribute('data-flow-action');\n\t\t\t\tthis._handleToolbarAction(tmpAction);\n\t\t\t});\n\t\t}\n\n\t\t// Bind expand button click (it's outside the main toolbar bar)\n\t\tlet tmpExpandBtn = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-ExpandBtn-${tmpFlowViewIdentifier}`);\n\t\tif (tmpExpandBtn.length > 0)\n\t\t{\n\t\t\ttmpExpandBtn[0].addEventListener('click', () =>\n\t\t\t{\n\t\t\t\tthis._setToolbarMode('docked');\n\t\t\t});\n\t\t}\n\n\t\t// Populate SVG icons for toolbar buttons\n\t\tthis._populateToolbarIcons();\n\n\t\t// Remove buttons from DOM based on options\n\t\tif (this.options.EnableAddNode === false)\n\t\t{\n\t\t\tlet tmpAddNodeBtn = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-AddNode-${tmpFlowViewIdentifier}`);\n\t\t\tif (tmpAddNodeBtn.length > 0)\n\t\t\t{\n\t\t\t\ttmpAddNodeBtn[0].remove();\n\t\t\t}\n\t\t}\n\t\tif (this.options.EnableCardPalette === false)\n\t\t{\n\t\t\tlet tmpCardsBtn = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Cards-${tmpFlowViewIdentifier}`);\n\t\t\tif (tmpCardsBtn.length > 0)\n\t\t\t{\n\t\t\t\ttmpCardsBtn[0].remove();\n\t\t\t}\n\t\t}\n\n\t\treturn super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);\n\t}\n\n\t// ── Icon Population ───────────────────────────────────────────────────\n\n\t/**\n\t * Populate SVG icons for all toolbar buttons.\n\t */\n\t_populateToolbarIcons()\n\t{\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\t\tif (!tmpIconProvider) return;\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\t// Map of element ID suffix → icon key\n\t\tlet tmpIconMap =\n\t\t{\n\t\t\t'plus': 'plus',\n\t\t\t'trash': 'trash',\n\t\t\t'zoom-in': 'zoom-in',\n\t\t\t'zoom-out': 'zoom-out',\n\t\t\t'zoom-fit': 'zoom-fit',\n\t\t\t'auto-layout': 'auto-layout',\n\t\t\t'cards': 'cards',\n\t\t\t'layout': 'layout',\n\t\t\t'settings': 'settings',\n\t\t\t'grip': 'grip',\n\t\t\t'collapse': 'collapse',\n\t\t\t'expand': 'expand'\n\t\t};\n\n\t\tlet tmpKeys = Object.keys(tmpIconMap);\n\t\tfor (let i = 0; i < tmpKeys.length; i++)\n\t\t{\n\t\t\tlet tmpElementId = `Flow-Toolbar-Icon-${tmpKeys[i]}-${tmpFlowViewIdentifier}`;\n\t\t\tlet tmpElements = this.pict.ContentAssignment.getElement(`#${tmpElementId}`);\n\t\t\tif (tmpElements.length > 0)\n\t\t\t{\n\t\t\t\ttmpElements[0].innerHTML = tmpIconProvider.getIconSVGMarkup(tmpIconMap[tmpKeys[i]], 14);\n\t\t\t}\n\t\t}\n\n\t\t// Fullscreen icon\n\t\tlet tmpFullscreenIcon = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-Icon-${tmpFlowViewIdentifier}`);\n\t\tif (tmpFullscreenIcon.length > 0)\n\t\t{\n\t\t\ttmpFullscreenIcon[0].innerHTML = tmpIconProvider.getIconSVGMarkup('fullscreen', 14);\n\t\t}\n\n\t\t// Chevrons (smaller)\n\t\tlet tmpCardsChevron = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-CardsChevron-${tmpFlowViewIdentifier}`);\n\t\tif (tmpCardsChevron.length > 0)\n\t\t{\n\t\t\ttmpCardsChevron[0].innerHTML = tmpIconProvider.getIconSVGMarkup('chevron-down', 8);\n\t\t}\n\n\t\tlet tmpLayoutChevron = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-LayoutChevron-${tmpFlowViewIdentifier}`);\n\t\tif (tmpLayoutChevron.length > 0)\n\t\t{\n\t\t\ttmpLayoutChevron[0].innerHTML = tmpIconProvider.getIconSVGMarkup('chevron-down', 8);\n\t\t}\n\t}\n\n\t// ── Popup Management ──────────────────────────────────────────────────\n\n\t/**\n\t * Open a popup below a trigger button.\n\t * @param {string} pType - 'add-node' | 'cards' | 'layout'\n\t */\n\t_openPopup(pType)\n\t{\n\t\t// Toggle off if already open\n\t\tif (this._ActivePopup === pType)\n\t\t{\n\t\t\tthis._closePopup();\n\t\t\treturn;\n\t\t}\n\n\t\t// Close any existing popup first\n\t\tthis._closePopup();\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpAnchor = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-PopupAnchor-${tmpFlowViewIdentifier}`);\n\t\tif (tmpAnchor.length < 1) return;\n\n\t\t// Create popup div\n\t\tlet tmpPopup = document.createElement('div');\n\t\ttmpPopup.className = 'pict-flow-toolbar-popup';\n\t\ttmpPopup.setAttribute('id', `Flow-Toolbar-Popup-${tmpFlowViewIdentifier}`);\n\n\t\t// Build popup content\n\t\tswitch (pType)\n\t\t{\n\t\t\tcase 'add-node':\n\t\t\t\tthis._buildAddNodePopup(tmpPopup);\n\t\t\t\tbreak;\n\t\t\tcase 'cards':\n\t\t\t\tthis._buildCardsPopup(tmpPopup);\n\t\t\t\tbreak;\n\t\t\tcase 'layout':\n\t\t\t\tthis._buildLayoutPopup(tmpPopup);\n\t\t\t\tbreak;\n\t\t\tcase 'settings':\n\t\t\t\tthis._buildSettingsPopup(tmpPopup);\n\t\t\t\tbreak;\n\t\t}\n\n\t\ttmpAnchor[0].appendChild(tmpPopup);\n\t\tthis._ActivePopup = pType;\n\n\t\t// Position the popup below the trigger button\n\t\tthis._positionPopup(tmpPopup, pType);\n\n\t\t// Click-outside-to-close handler (delayed to avoid catching the opening click)\n\t\tsetTimeout(() =>\n\t\t{\n\t\t\tthis._DocumentClickHandler = (pEvent) =>\n\t\t\t{\n\t\t\t\tif (!tmpPopup.contains(pEvent.target))\n\t\t\t\t{\n\t\t\t\t\t// Check if click was on the trigger button itself (toggle behavior)\n\t\t\t\t\tlet tmpButton = pEvent.target.closest('[data-flow-action]');\n\t\t\t\t\tif (tmpButton)\n\t\t\t\t\t{\n\t\t\t\t\t\tlet tmpAction = tmpButton.getAttribute('data-flow-action');\n\t\t\t\t\t\tif (tmpAction === pType || tmpAction === pType.replace('-popup', '') + '-popup')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn; // Let the toggle handle it\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis._closePopup();\n\t\t\t\t}\n\t\t\t};\n\t\t\tdocument.addEventListener('click', this._DocumentClickHandler, true);\n\t\t}, 0);\n\n\t\t// Focus search input if Add Node popup\n\t\tif (pType === 'add-node')\n\t\t{\n\t\t\tlet tmpSearch = tmpPopup.querySelector('.pict-flow-popup-search');\n\t\t\tif (tmpSearch)\n\t\t\t{\n\t\t\t\tsetTimeout(() => { tmpSearch.focus(); }, 50);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Close the active popup and clean up.\n\t */\n\t_closePopup()\n\t{\n\t\tif (this._DocumentClickHandler)\n\t\t{\n\t\t\tdocument.removeEventListener('click', this._DocumentClickHandler, true);\n\t\t\tthis._DocumentClickHandler = null;\n\t\t}\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpPopup = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Popup-${tmpFlowViewIdentifier}`);\n\t\tif (tmpPopup.length > 0)\n\t\t{\n\t\t\ttmpPopup[0].parentNode.removeChild(tmpPopup[0]);\n\t\t}\n\n\t\tthis._ActivePopup = null;\n\t}\n\n\t/**\n\t * Position a popup below its trigger button.\n\t * @param {HTMLElement} pPopupDiv\n\t * @param {string} pType\n\t */\n\t_positionPopup(pPopupDiv, pType)\n\t{\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\t// Determine which button triggered the popup\n\t\tlet tmpTriggerSelector;\n\t\tswitch (pType)\n\t\t{\n\t\t\tcase 'add-node':\n\t\t\t\ttmpTriggerSelector = `#Flow-Toolbar-AddNode-${tmpFlowViewIdentifier}`;\n\t\t\t\tbreak;\n\t\t\tcase 'cards':\n\t\t\t\ttmpTriggerSelector = `#Flow-Toolbar-Cards-${tmpFlowViewIdentifier}`;\n\t\t\t\tbreak;\n\t\t\tcase 'layout':\n\t\t\t\ttmpTriggerSelector = `#Flow-Toolbar-Layout-${tmpFlowViewIdentifier}`;\n\t\t\t\tbreak;\n\t\t\tcase 'settings':\n\t\t\t\ttmpTriggerSelector = `#Flow-Toolbar-Settings-${tmpFlowViewIdentifier}`;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\n\t\tlet tmpTriggerElements = this.pict.ContentAssignment.getElement(tmpTriggerSelector);\n\t\tif (tmpTriggerElements.length < 1) return;\n\n\t\tlet tmpAnchor = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-PopupAnchor-${tmpFlowViewIdentifier}`);\n\t\tif (tmpAnchor.length < 1) return;\n\n\t\tlet tmpTriggerRect = tmpTriggerElements[0].getBoundingClientRect();\n\t\tlet tmpAnchorRect = tmpAnchor[0].getBoundingClientRect();\n\n\t\tlet tmpLeft = tmpTriggerRect.left - tmpAnchorRect.left;\n\t\tpPopupDiv.style.left = tmpLeft + 'px';\n\t\tpPopupDiv.style.top = '0px';\n\t}\n\n\t// ── Add Node Popup ────────────────────────────────────────────────────\n\n\t/**\n\t * Build the searchable Add Node popup content.\n\t * @param {HTMLElement} pContainer\n\t */\n\t_buildAddNodePopup(pContainer)\n\t{\n\t\t// Search wrapper\n\t\tlet tmpSearchWrapper = document.createElement('div');\n\t\ttmpSearchWrapper.className = 'pict-flow-popup-search-wrapper';\n\n\t\tlet tmpSearchIcon = document.createElement('span');\n\t\ttmpSearchIcon.className = 'pict-flow-popup-search-icon';\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\t\tif (tmpIconProvider)\n\t\t{\n\t\t\ttmpSearchIcon.innerHTML = tmpIconProvider.getIconSVGMarkup('search', 12);\n\t\t}\n\t\ttmpSearchWrapper.appendChild(tmpSearchIcon);\n\n\t\tlet tmpSearchInput = document.createElement('input');\n\t\ttmpSearchInput.className = 'pict-flow-popup-search';\n\t\ttmpSearchInput.setAttribute('type', 'text');\n\t\ttmpSearchInput.setAttribute('placeholder', 'Search node types...');\n\t\ttmpSearchWrapper.appendChild(tmpSearchInput);\n\t\tpContainer.appendChild(tmpSearchWrapper);\n\n\t\t// Node list\n\t\tlet tmpListDiv = document.createElement('div');\n\t\ttmpListDiv.className = 'pict-flow-popup-node-list';\n\t\tpContainer.appendChild(tmpListDiv);\n\n\t\t// Initial population\n\t\tthis._populateNodeList(tmpListDiv, '');\n\n\t\t// Filter on input\n\t\ttmpSearchInput.addEventListener('input', () =>\n\t\t{\n\t\t\tthis._populateNodeList(tmpListDiv, tmpSearchInput.value);\n\t\t});\n\t}\n\n\t/**\n\t * Populate the node list in the Add Node popup, filtered by search text.\n\t * @param {HTMLElement} pListDiv\n\t * @param {string} pFilter\n\t */\n\t_populateNodeList(pListDiv, pFilter)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._NodeTypeProvider) return;\n\n\t\t// Clear\n\t\twhile (pListDiv.firstChild)\n\t\t{\n\t\t\tpListDiv.removeChild(pListDiv.firstChild);\n\t\t}\n\n\t\tlet tmpTypes = this._FlowView._NodeTypeProvider.getNodeTypes();\n\t\tlet tmpTypeKeys = Object.keys(tmpTypes);\n\t\tlet tmpFilter = (pFilter || '').toLowerCase().trim();\n\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\tlet tmpMatchCount = 0;\n\n\t\tfor (let i = 0; i < tmpTypeKeys.length; i++)\n\t\t{\n\t\t\tlet tmpTypeConfig = tmpTypes[tmpTypeKeys[i]];\n\t\t\tlet tmpMeta = tmpTypeConfig.CardMetadata || {};\n\n\t\t\t// Skip disabled cards\n\t\t\tif (tmpMeta.Enabled === false) continue;\n\n\t\t\t// Filter match: label, code, or category\n\t\t\tif (tmpFilter)\n\t\t\t{\n\t\t\t\tlet tmpLabel = (tmpTypeConfig.Label || '').toLowerCase();\n\t\t\t\tlet tmpCode = (tmpMeta.Code || '').toLowerCase();\n\t\t\t\tlet tmpCategory = (tmpMeta.Category || '').toLowerCase();\n\t\t\t\tif (tmpLabel.indexOf(tmpFilter) < 0 &&\n\t\t\t\t\ttmpCode.indexOf(tmpFilter) < 0 &&\n\t\t\t\t\ttmpCategory.indexOf(tmpFilter) < 0)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttmpMatchCount++;\n\n\t\t\tlet tmpRow = document.createElement('div');\n\t\t\ttmpRow.className = 'pict-flow-popup-list-item';\n\t\t\ttmpRow.setAttribute('data-node-type', tmpTypeKeys[i]);\n\n\t\t\t// Icon\n\t\t\tlet tmpIconSpan = document.createElement('span');\n\t\t\ttmpIconSpan.className = 'pict-flow-popup-list-item-icon';\n\t\t\tif (tmpIconProvider)\n\t\t\t{\n\t\t\t\tlet tmpResolvedKey = tmpIconProvider.resolveIconKey(tmpMeta);\n\t\t\t\ttmpIconSpan.innerHTML = tmpIconProvider.getIconSVGMarkup(tmpResolvedKey, 16);\n\t\t\t}\n\t\t\ttmpRow.appendChild(tmpIconSpan);\n\n\t\t\t// Label\n\t\t\tlet tmpLabelSpan = document.createElement('span');\n\t\t\ttmpLabelSpan.className = 'pict-flow-popup-list-item-label';\n\t\t\ttmpLabelSpan.textContent = tmpTypeConfig.Label;\n\t\t\ttmpRow.appendChild(tmpLabelSpan);\n\n\t\t\t// Code badge\n\t\t\tif (tmpMeta.Code)\n\t\t\t{\n\t\t\t\tlet tmpCodeSpan = document.createElement('span');\n\t\t\t\ttmpCodeSpan.className = 'pict-flow-popup-list-item-code';\n\t\t\t\ttmpCodeSpan.textContent = tmpMeta.Code;\n\t\t\t\ttmpRow.appendChild(tmpCodeSpan);\n\t\t\t}\n\n\t\t\t// Click handler\n\t\t\ttmpRow.addEventListener('click', () =>\n\t\t\t{\n\t\t\t\tthis._addNodeAtCenter(tmpTypeKeys[i]);\n\t\t\t\tthis._closePopup();\n\t\t\t});\n\n\t\t\tpListDiv.appendChild(tmpRow);\n\t\t}\n\n\t\tif (tmpMatchCount === 0)\n\t\t{\n\t\t\tlet tmpEmpty = document.createElement('div');\n\t\t\ttmpEmpty.className = 'pict-flow-popup-list-empty';\n\t\t\ttmpEmpty.textContent = 'No matching node types';\n\t\t\tpListDiv.appendChild(tmpEmpty);\n\t\t}\n\t}\n\n\t// ── Cards Popup ───────────────────────────────────────────────────────\n\n\t/**\n\t * Build the Cards popup content with search and categorized palette.\n\t * @param {HTMLElement} pContainer\n\t */\n\t_buildCardsPopup(pContainer)\n\t{\n\t\t// Search wrapper\n\t\tlet tmpSearchWrapper = document.createElement('div');\n\t\ttmpSearchWrapper.className = 'pict-flow-popup-search-wrapper';\n\n\t\tlet tmpSearchIcon = document.createElement('span');\n\t\ttmpSearchIcon.className = 'pict-flow-popup-search-icon';\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\t\tif (tmpIconProvider)\n\t\t{\n\t\t\ttmpSearchIcon.innerHTML = tmpIconProvider.getIconSVGMarkup('search', 12);\n\t\t}\n\t\ttmpSearchWrapper.appendChild(tmpSearchIcon);\n\n\t\tlet tmpSearchInput = document.createElement('input');\n\t\ttmpSearchInput.className = 'pict-flow-popup-search';\n\t\ttmpSearchInput.setAttribute('type', 'text');\n\t\ttmpSearchInput.setAttribute('placeholder', 'Search cards...');\n\t\ttmpSearchWrapper.appendChild(tmpSearchInput);\n\t\tpContainer.appendChild(tmpSearchWrapper);\n\n\t\t// Palette list container\n\t\tlet tmpListDiv = document.createElement('div');\n\t\ttmpListDiv.className = 'pict-flow-popup-node-list';\n\t\tpContainer.appendChild(tmpListDiv);\n\n\t\t// Initial population\n\t\tthis._renderPalette(tmpListDiv, '');\n\n\t\t// Filter on input\n\t\ttmpSearchInput.addEventListener('input', () =>\n\t\t{\n\t\t\tthis._renderPalette(tmpListDiv, tmpSearchInput.value);\n\t\t});\n\n\t\t// Focus search input\n\t\tsetTimeout(() => { tmpSearchInput.focus(); }, 50);\n\t}\n\n\t/**\n\t * Render the card palette with categories and card chips into a container.\n\t * @param {HTMLElement} pContainer - The target container element\n\t * @param {string} [pFilter] - Optional search filter text\n\t */\n\t_renderPalette(pContainer, pFilter)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._NodeTypeProvider) return;\n\n\t\t// Clear existing content\n\t\twhile (pContainer.firstChild)\n\t\t{\n\t\t\tpContainer.removeChild(pContainer.firstChild);\n\t\t}\n\n\t\tlet tmpCategories = this._FlowView._NodeTypeProvider.getCardsByCategory();\n\t\tlet tmpCategoryKeys = Object.keys(tmpCategories);\n\t\tlet tmpFilter = (pFilter || '').toLowerCase().trim();\n\t\tlet tmpTotalMatchCount = 0;\n\n\t\tfor (let i = 0; i < tmpCategoryKeys.length; i++)\n\t\t{\n\t\t\tlet tmpCategoryName = tmpCategoryKeys[i];\n\t\t\tlet tmpCards = tmpCategories[tmpCategoryName];\n\t\t\tlet tmpMatchingCards = [];\n\n\t\t\t// Filter cards within this category\n\t\t\tfor (let j = 0; j < tmpCards.length; j++)\n\t\t\t{\n\t\t\t\tlet tmpCardConfig = tmpCards[j];\n\t\t\t\tlet tmpMeta = tmpCardConfig.CardMetadata || {};\n\n\t\t\t\tif (tmpFilter)\n\t\t\t\t{\n\t\t\t\t\tlet tmpLabel = (tmpCardConfig.Label || '').toLowerCase();\n\t\t\t\t\tlet tmpCode = (tmpMeta.Code || '').toLowerCase();\n\t\t\t\t\tlet tmpCategory = tmpCategoryName.toLowerCase();\n\t\t\t\t\tif (tmpLabel.indexOf(tmpFilter) < 0 &&\n\t\t\t\t\t\ttmpCode.indexOf(tmpFilter) < 0 &&\n\t\t\t\t\t\ttmpCategory.indexOf(tmpFilter) < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttmpMatchingCards.push(tmpCardConfig);\n\t\t\t}\n\n\t\t\tif (tmpMatchingCards.length === 0) continue;\n\n\t\t\ttmpTotalMatchCount += tmpMatchingCards.length;\n\n\t\t\tlet tmpCategoryDiv = document.createElement('div');\n\t\t\ttmpCategoryDiv.className = 'pict-flow-palette-category';\n\t\t\ttmpCategoryDiv.style.padding = '0.35em 0.5em';\n\n\t\t\tlet tmpCategoryLabel = document.createElement('div');\n\t\t\ttmpCategoryLabel.className = 'pict-flow-palette-category-label';\n\t\t\ttmpCategoryLabel.textContent = tmpCategoryName;\n\t\t\ttmpCategoryDiv.appendChild(tmpCategoryLabel);\n\n\t\t\tlet tmpCardsDiv = document.createElement('div');\n\t\t\ttmpCardsDiv.className = 'pict-flow-palette-cards';\n\n\t\t\tfor (let j = 0; j < tmpMatchingCards.length; j++)\n\t\t\t{\n\t\t\t\tlet tmpCardConfig = tmpMatchingCards[j];\n\t\t\t\tlet tmpMeta = tmpCardConfig.CardMetadata || {};\n\n\t\t\t\tlet tmpCardEl = document.createElement('div');\n\t\t\t\ttmpCardEl.className = 'pict-flow-palette-card';\n\t\t\t\tif (tmpMeta.Enabled === false)\n\t\t\t\t{\n\t\t\t\t\ttmpCardEl.classList.add('disabled');\n\t\t\t\t}\n\t\t\t\ttmpCardEl.setAttribute('data-card-type', tmpCardConfig.Hash);\n\n\t\t\t\tif (tmpMeta.Tooltip)\n\t\t\t\t{\n\t\t\t\t\ttmpCardEl.setAttribute('title', tmpMeta.Tooltip);\n\t\t\t\t}\n\t\t\t\telse if (tmpMeta.Description)\n\t\t\t\t{\n\t\t\t\t\ttmpCardEl.setAttribute('title', tmpMeta.Description);\n\t\t\t\t}\n\n\t\t\t\t// Icon or color swatch\n\t\t\t\tif (tmpMeta.Icon)\n\t\t\t\t{\n\t\t\t\t\tlet tmpIconSpan = document.createElement('span');\n\t\t\t\t\ttmpIconSpan.className = 'pict-flow-palette-card-icon';\n\t\t\t\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\t\t\t\tif (tmpIconProvider && !tmpIconProvider.isEmojiIcon(tmpMeta.Icon))\n\t\t\t\t\t{\n\t\t\t\t\t\tlet tmpResolvedKey = tmpIconProvider.resolveIconKey(tmpMeta);\n\t\t\t\t\t\ttmpIconSpan.innerHTML = tmpIconProvider.getIconSVGMarkup(tmpResolvedKey, 14);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpIconSpan.textContent = tmpMeta.Icon;\n\t\t\t\t\t}\n\t\t\t\t\ttmpCardEl.appendChild(tmpIconSpan);\n\t\t\t\t}\n\t\t\t\telse if (this._FlowView._IconProvider)\n\t\t\t\t{\n\t\t\t\t\tlet tmpIconSpan = document.createElement('span');\n\t\t\t\t\ttmpIconSpan.className = 'pict-flow-palette-card-icon';\n\t\t\t\t\ttmpIconSpan.innerHTML = this._FlowView._IconProvider.getIconSVGMarkup('default', 14);\n\t\t\t\t\ttmpCardEl.appendChild(tmpIconSpan);\n\t\t\t\t}\n\t\t\t\telse if (tmpCardConfig.TitleBarColor)\n\t\t\t\t{\n\t\t\t\t\tlet tmpSwatch = document.createElement('span');\n\t\t\t\t\ttmpSwatch.className = 'pict-flow-palette-card-swatch';\n\t\t\t\t\ttmpSwatch.style.backgroundColor = tmpCardConfig.TitleBarColor;\n\t\t\t\t\ttmpCardEl.appendChild(tmpSwatch);\n\t\t\t\t}\n\n\t\t\t\t// Title\n\t\t\t\tlet tmpTitleSpan = document.createElement('span');\n\t\t\t\ttmpTitleSpan.className = 'pict-flow-palette-card-title';\n\t\t\t\ttmpTitleSpan.textContent = tmpCardConfig.Label;\n\t\t\t\ttmpCardEl.appendChild(tmpTitleSpan);\n\n\t\t\t\t// Code badge\n\t\t\t\tif (tmpMeta.Code)\n\t\t\t\t{\n\t\t\t\t\tlet tmpCodeSpan = document.createElement('span');\n\t\t\t\t\ttmpCodeSpan.className = 'pict-flow-palette-card-code';\n\t\t\t\t\ttmpCodeSpan.textContent = tmpMeta.Code;\n\t\t\t\t\ttmpCardEl.appendChild(tmpCodeSpan);\n\t\t\t\t}\n\n\t\t\t\t// Click handler\n\t\t\t\ttmpCardEl.addEventListener('click', () =>\n\t\t\t\t{\n\t\t\t\t\tthis._addCardFromPalette(tmpCardConfig.Hash);\n\t\t\t\t\tthis._closePopup();\n\t\t\t\t});\n\n\t\t\t\ttmpCardsDiv.appendChild(tmpCardEl);\n\t\t\t}\n\n\t\t\ttmpCategoryDiv.appendChild(tmpCardsDiv);\n\t\t\tpContainer.appendChild(tmpCategoryDiv);\n\t\t}\n\n\t\tif (tmpTotalMatchCount === 0)\n\t\t{\n\t\t\tlet tmpEmpty = document.createElement('div');\n\t\t\ttmpEmpty.className = 'pict-flow-popup-list-empty';\n\t\t\ttmpEmpty.textContent = tmpFilter ? 'No matching cards' : 'No card types available';\n\t\t\tpContainer.appendChild(tmpEmpty);\n\t\t}\n\t}\n\n\t// ── Layout Popup ──────────────────────────────────────────────────────\n\n\t/**\n\t * Build the Layout popup content.\n\t * @param {HTMLElement} pContainer\n\t */\n\t_buildLayoutPopup(pContainer)\n\t{\n\t\tlet tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;\n\n\t\t// Save Layout section at top\n\t\tlet tmpSaveSection = document.createElement('div');\n\t\ttmpSaveSection.className = 'pict-flow-popup-layout-save-section';\n\n\t\t// Save input row (hidden initially)\n\t\tlet tmpSaveInputRow = document.createElement('div');\n\t\ttmpSaveInputRow.className = 'pict-flow-popup-layout-save-input-row';\n\t\ttmpSaveInputRow.style.display = 'none';\n\n\t\tlet tmpSaveInput = document.createElement('input');\n\t\ttmpSaveInput.className = 'pict-flow-popup-layout-save-input';\n\t\ttmpSaveInput.setAttribute('type', 'text');\n\t\ttmpSaveInput.setAttribute('placeholder', 'Layout name...');\n\t\ttmpSaveInputRow.appendChild(tmpSaveInput);\n\n\t\tlet tmpSaveConfirmBtn = document.createElement('button');\n\t\ttmpSaveConfirmBtn.className = 'pict-flow-popup-layout-save-confirm';\n\t\ttmpSaveConfirmBtn.title = 'Save';\n\t\tif (tmpIconProvider)\n\t\t{\n\t\t\ttmpSaveConfirmBtn.innerHTML = tmpIconProvider.getIconSVGMarkup('save', 14);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpSaveConfirmBtn.textContent = '✓';\n\t\t}\n\t\ttmpSaveInputRow.appendChild(tmpSaveConfirmBtn);\n\n\t\t// \"Save Current Layout\" clickable row\n\t\tlet tmpSaveRow = document.createElement('div');\n\t\ttmpSaveRow.className = 'pict-flow-popup-layout-save';\n\n\t\tlet tmpSaveIcon = document.createElement('span');\n\t\ttmpSaveIcon.className = 'pict-flow-popup-layout-save-icon';\n\t\tif (tmpIconProvider)\n\t\t{\n\t\t\ttmpSaveIcon.innerHTML = tmpIconProvider.getIconSVGMarkup('save', 14);\n\t\t}\n\t\ttmpSaveRow.appendChild(tmpSaveIcon);\n\n\t\tlet tmpSaveText = document.createElement('span');\n\t\ttmpSaveText.textContent = 'Save Current Layout';\n\t\ttmpSaveRow.appendChild(tmpSaveText);\n\n\t\t// Click \"Save Current Layout\" to reveal the input row\n\t\ttmpSaveRow.addEventListener('click', () =>\n\t\t{\n\t\t\ttmpSaveRow.style.display = 'none';\n\t\t\ttmpSaveInputRow.style.display = '';\n\t\t\ttmpSaveInput.value = '';\n\t\t\tsetTimeout(() => { tmpSaveInput.focus(); }, 50);\n\t\t});\n\n\t\t// Confirm save via button click\n\t\tlet tmpDoSave = () =>\n\t\t{\n\t\t\tlet tmpName = tmpSaveInput.value.trim();\n\t\t\tif (tmpName === '') return;\n\t\t\tthis._FlowView._LayoutProvider.saveLayout(tmpName);\n\t\t\t// Refresh the popup content\n\t\t\twhile (pContainer.firstChild)\n\t\t\t{\n\t\t\t\tpContainer.removeChild(pContainer.firstChild);\n\t\t\t}\n\t\t\tthis._buildLayoutPopup(pContainer);\n\t\t};\n\n\t\ttmpSaveConfirmBtn.addEventListener('click', tmpDoSave);\n\n\t\t// Confirm save via Enter key\n\t\ttmpSaveInput.addEventListener('keydown', (pEvent) =>\n\t\t{\n\t\t\tif (pEvent.key === 'Enter')\n\t\t\t{\n\t\t\t\tpEvent.preventDefault();\n\t\t\t\ttmpDoSave();\n\t\t\t}\n\t\t\telse if (pEvent.key === 'Escape')\n\t\t\t{\n\t\t\t\t// Cancel — hide input, show the save row again\n\t\t\t\ttmpSaveInputRow.style.display = 'none';\n\t\t\t\ttmpSaveRow.style.display = '';\n\t\t\t}\n\t\t});\n\n\t\t// Prevent clicks inside the input from closing the popup\n\t\ttmpSaveInput.addEventListener('click', (pEvent) =>\n\t\t{\n\t\t\tpEvent.stopPropagation();\n\t\t});\n\n\t\ttmpSaveSection.appendChild(tmpSaveRow);\n\t\ttmpSaveSection.appendChild(tmpSaveInputRow);\n\t\tpContainer.appendChild(tmpSaveSection);\n\n\t\t// Divider\n\t\tlet tmpDivider = document.createElement('div');\n\t\ttmpDivider.className = 'pict-flow-popup-divider';\n\t\tpContainer.appendChild(tmpDivider);\n\n\t\t// Layout rows\n\t\tif (!this._FlowView || !this._FlowView._LayoutProvider)\n\t\t{\n\t\t\tlet tmpEmpty = document.createElement('div');\n\t\t\ttmpEmpty.className = 'pict-flow-popup-list-empty';\n\t\t\ttmpEmpty.textContent = 'No saved layouts';\n\t\t\tpContainer.appendChild(tmpEmpty);\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpLayouts = this._FlowView._LayoutProvider.getLayouts();\n\n\t\tif (tmpLayouts.length === 0)\n\t\t{\n\t\t\tlet tmpEmpty = document.createElement('div');\n\t\t\ttmpEmpty.className = 'pict-flow-popup-list-empty';\n\t\t\ttmpEmpty.textContent = 'No saved layouts';\n\t\t\tpContainer.appendChild(tmpEmpty);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (let i = 0; i < tmpLayouts.length; i++)\n\t\t{\n\t\t\tlet tmpLayout = tmpLayouts[i];\n\n\t\t\tlet tmpRow = document.createElement('div');\n\t\t\ttmpRow.className = 'pict-flow-popup-layout-row';\n\n\t\t\tlet tmpNameSpan = document.createElement('span');\n\t\t\ttmpNameSpan.className = 'pict-flow-popup-layout-name';\n\t\t\ttmpNameSpan.textContent = tmpLayout.Name;\n\t\t\ttmpRow.appendChild(tmpNameSpan);\n\n\t\t\t// Delete button (visible on hover via CSS)\n\t\t\tlet tmpDeleteBtn = document.createElement('button');\n\t\t\ttmpDeleteBtn.className = 'pict-flow-popup-layout-delete';\n\t\t\ttmpDeleteBtn.title = 'Delete layout';\n\t\t\tif (tmpIconProvider)\n\t\t\t{\n\t\t\t\ttmpDeleteBtn.innerHTML = tmpIconProvider.getIconSVGMarkup('trash', 12);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmpDeleteBtn.textContent = '×';\n\t\t\t}\n\t\t\ttmpRow.appendChild(tmpDeleteBtn);\n\n\t\t\t// Click row → restore layout\n\t\t\ttmpRow.addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\t// Don't restore if they clicked the delete button\n\t\t\t\tif (pEvent.target.closest('.pict-flow-popup-layout-delete'))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis._FlowView._LayoutProvider.restoreLayout(tmpLayout.Hash);\n\t\t\t\tthis._closePopup();\n\t\t\t});\n\n\t\t\t// Click delete → delete layout and refresh popup\n\t\t\ttmpDeleteBtn.addEventListener('click', (pEvent) =>\n\t\t\t{\n\t\t\t\tpEvent.stopPropagation();\n\t\t\t\tthis._FlowView._LayoutProvider.deleteLayout(tmpLayout.Hash);\n\t\t\t\t// Refresh the popup content\n\t\t\t\twhile (pContainer.firstChild)\n\t\t\t\t{\n\t\t\t\t\tpContainer.removeChild(pContainer.firstChild);\n\t\t\t\t}\n\t\t\t\tthis._buildLayoutPopup(pContainer);\n\t\t\t});\n\n\t\t\tpContainer.appendChild(tmpRow);\n\t\t}\n\t}\n\n\t// ── Settings Popup ───────────────────────────────────────────────────\n\n\t/**\n\t * Build the Settings popup content (theme dropdown + noise slider).\n\t * @param {HTMLElement} pContainer\n\t */\n\t_buildSettingsPopup(pContainer)\n\t{\n\t\tif (!this._FlowView || !this._FlowView._ThemeProvider) return;\n\n\t\tlet tmpThemeProvider = this._FlowView._ThemeProvider;\n\n\t\t// Theme selector section\n\t\tlet tmpThemeSection = document.createElement('div');\n\t\ttmpThemeSection.className = 'pict-flow-popup-settings-section';\n\n\t\tlet tmpThemeLabel = document.createElement('label');\n\t\ttmpThemeLabel.className = 'pict-flow-popup-settings-label';\n\t\ttmpThemeLabel.textContent = 'Theme';\n\t\ttmpThemeSection.appendChild(tmpThemeLabel);\n\n\t\tlet tmpThemeSelect = document.createElement('select');\n\t\ttmpThemeSelect.className = 'pict-flow-popup-settings-select';\n\n\t\tlet tmpThemeKeys = tmpThemeProvider.getThemeKeys();\n\t\tlet tmpActiveKey = tmpThemeProvider.getActiveThemeKey();\n\n\t\tfor (let i = 0; i < tmpThemeKeys.length; i++)\n\t\t{\n\t\t\tlet tmpOption = document.createElement('option');\n\t\t\ttmpOption.value = tmpThemeKeys[i];\n\n\t\t\tlet tmpTheme = tmpThemeProvider._Themes[tmpThemeKeys[i]];\n\t\t\ttmpOption.textContent = tmpTheme.Label || tmpThemeKeys[i];\n\n\t\t\tif (tmpThemeKeys[i] === tmpActiveKey)\n\t\t\t{\n\t\t\t\ttmpOption.selected = true;\n\t\t\t}\n\t\t\ttmpThemeSelect.appendChild(tmpOption);\n\t\t}\n\n\t\ttmpThemeSelect.addEventListener('change', () =>\n\t\t{\n\t\t\tthis._FlowView.setTheme(tmpThemeSelect.value);\n\t\t\t// Refresh the noise slider visibility\n\t\t\tthis._refreshNoiseSlider(pContainer);\n\t\t});\n\n\t\t// Prevent popup close on select interaction\n\t\ttmpThemeSelect.addEventListener('click', (pEvent) => { pEvent.stopPropagation(); });\n\n\t\ttmpThemeSection.appendChild(tmpThemeSelect);\n\t\tpContainer.appendChild(tmpThemeSection);\n\n\t\t// Divider\n\t\tlet tmpDivider = document.createElement('div');\n\t\ttmpDivider.className = 'pict-flow-popup-divider';\n\t\tpContainer.appendChild(tmpDivider);\n\n\t\t// Noise level section\n\t\tlet tmpNoiseSection = document.createElement('div');\n\t\ttmpNoiseSection.className = 'pict-flow-popup-settings-section pict-flow-popup-settings-noise';\n\t\ttmpNoiseSection.setAttribute('data-settings-type', 'noise');\n\n\t\tlet tmpNoiseLabel = document.createElement('label');\n\t\ttmpNoiseLabel.className = 'pict-flow-popup-settings-label';\n\t\ttmpNoiseLabel.textContent = 'Noise';\n\t\ttmpNoiseSection.appendChild(tmpNoiseLabel);\n\n\t\tlet tmpNoiseRow = document.createElement('div');\n\t\ttmpNoiseRow.className = 'pict-flow-popup-settings-slider-row';\n\n\t\tlet tmpNoiseSlider = document.createElement('input');\n\t\ttmpNoiseSlider.type = 'range';\n\t\ttmpNoiseSlider.className = 'pict-flow-popup-settings-slider';\n\t\ttmpNoiseSlider.min = '0';\n\t\ttmpNoiseSlider.max = '100';\n\t\ttmpNoiseSlider.value = String(Math.round(tmpThemeProvider.getNoiseLevel() * 100));\n\n\t\tlet tmpNoiseValue = document.createElement('span');\n\t\ttmpNoiseValue.className = 'pict-flow-popup-settings-slider-value';\n\t\ttmpNoiseValue.textContent = tmpNoiseSlider.value + '%';\n\n\t\ttmpNoiseSlider.addEventListener('input', () =>\n\t\t{\n\t\t\tlet tmpLevel = parseInt(tmpNoiseSlider.value, 10) / 100;\n\t\t\ttmpNoiseValue.textContent = tmpNoiseSlider.value + '%';\n\t\t\tthis._FlowView.setNoiseLevel(tmpLevel);\n\t\t});\n\n\t\t// Prevent popup close on slider interaction\n\t\ttmpNoiseSlider.addEventListener('click', (pEvent) => { pEvent.stopPropagation(); });\n\t\ttmpNoiseSlider.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });\n\n\t\ttmpNoiseRow.appendChild(tmpNoiseSlider);\n\t\ttmpNoiseRow.appendChild(tmpNoiseValue);\n\t\ttmpNoiseSection.appendChild(tmpNoiseRow);\n\t\tpContainer.appendChild(tmpNoiseSection);\n\n\t\t// Show/hide noise slider based on active theme\n\t\tthis._refreshNoiseSlider(pContainer);\n\t}\n\n\t/**\n\t * Show or hide the noise slider based on whether the active theme supports noise.\n\t * @param {HTMLElement} pContainer - The settings popup container\n\t */\n\t_refreshNoiseSlider(pContainer)\n\t{\n\t\tlet tmpNoiseSection = pContainer.querySelector('[data-settings-type=\"noise\"]');\n\t\tif (!tmpNoiseSection) return;\n\n\t\tlet tmpTheme = this._FlowView._ThemeProvider.getActiveTheme();\n\t\tif (tmpTheme && tmpTheme.NoiseConfig && tmpTheme.NoiseConfig.Enabled)\n\t\t{\n\t\t\ttmpNoiseSection.style.display = '';\n\t\t\t// Update slider value to reflect theme default\n\t\t\tlet tmpSlider = tmpNoiseSection.querySelector('.pict-flow-popup-settings-slider');\n\t\t\tlet tmpValueLabel = tmpNoiseSection.querySelector('.pict-flow-popup-settings-slider-value');\n\t\t\tif (tmpSlider)\n\t\t\t{\n\t\t\t\tlet tmpLevel = Math.round(this._FlowView._ThemeProvider.getNoiseLevel() * 100);\n\t\t\t\ttmpSlider.value = String(tmpLevel);\n\t\t\t\tif (tmpValueLabel) tmpValueLabel.textContent = tmpLevel + '%';\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpNoiseSection.style.display = 'none';\n\t\t}\n\t}\n\n\t// ── Toolbar Mode Switching ────────────────────────────────────────────\n\n\t/**\n\t * Switch between docked, floating, and collapsed modes.\n\t * @param {string} pMode - 'docked' | 'floating' | 'collapsed'\n\t */\n\t_setToolbarMode(pMode)\n\t{\n\t\t// Close any active popup first\n\t\tthis._closePopup();\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\tlet tmpBar = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Bar-${tmpFlowViewIdentifier}`);\n\t\tlet tmpCollapsed = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Collapsed-${tmpFlowViewIdentifier}`);\n\n\t\tswitch (pMode)\n\t\t{\n\t\t\tcase 'docked':\n\t\t\t\t// Show toolbar bar\n\t\t\t\tif (tmpBar.length > 0) tmpBar[0].style.display = '';\n\t\t\t\t// Hide collapsed button\n\t\t\t\tif (tmpCollapsed.length > 0) tmpCollapsed[0].classList.remove('visible');\n\t\t\t\t// Hide floating toolbar\n\t\t\t\tif (this._FloatingToolbarView) this._FloatingToolbarView.hide();\n\t\t\t\tbreak;\n\n\t\t\tcase 'floating':\n\t\t\t\t// Hide toolbar bar\n\t\t\t\tif (tmpBar.length > 0) tmpBar[0].style.display = 'none';\n\t\t\t\t// Hide collapsed button\n\t\t\t\tif (tmpCollapsed.length > 0) tmpCollapsed[0].classList.remove('visible');\n\t\t\t\t// Show floating toolbar\n\t\t\t\tthis._showFloatingToolbar();\n\t\t\t\tbreak;\n\n\t\t\tcase 'collapsed':\n\t\t\t\t// Hide toolbar bar\n\t\t\t\tif (tmpBar.length > 0) tmpBar[0].style.display = 'none';\n\t\t\t\t// Show collapsed button\n\t\t\t\tif (tmpCollapsed.length > 0) tmpCollapsed[0].classList.add('visible');\n\t\t\t\t// Hide floating toolbar\n\t\t\t\tif (this._FloatingToolbarView) this._FloatingToolbarView.hide();\n\t\t\t\tbreak;\n\t\t}\n\n\t\tthis._ToolbarMode = pMode;\n\t}\n\n\t/**\n\t * Lazily create and show the floating toolbar.\n\t */\n\t_showFloatingToolbar()\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tif (!this._FloatingToolbarView)\n\t\t{\n\t\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\t\t\tthis._FloatingToolbarView = this.fable.instantiateServiceProviderWithoutRegistration(\n\t\t\t\t'PictViewFlowFloatingToolbar',\n\t\t\t\t{\n\t\t\t\t\tFlowViewIdentifier: tmpFlowViewIdentifier,\n\t\t\t\t\tDefaultDestinationAddress: `#Flow-FloatingToolbar-Container-${tmpFlowViewIdentifier}`,\n\t\t\t\t\tEnableAddNode: this.options.EnableAddNode,\n\t\t\t\t\tEnableCardPalette: this.options.EnableCardPalette\n\t\t\t\t}\n\t\t\t);\n\t\t\tthis._FloatingToolbarView._ToolbarView = this;\n\t\t\tthis._FloatingToolbarView._FlowView = this._FlowView;\n\t\t\tthis._FloatingToolbarView.render();\n\t\t}\n\n\t\tthis._FloatingToolbarView.show();\n\t}\n\n\t// ── Node Placement Helpers ────────────────────────────────────────────\n\n\t/**\n\t * Add a node at the center of the visible viewport.\n\t * @param {string} pNodeType - The node type hash\n\t */\n\t_addNodeAtCenter(pNodeType)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\n\t\t// Calculate the center of the visible SVG area\n\t\tlet tmpSVGContainer = this._FlowView._SVGElement;\n\t\tlet tmpWidth = tmpSVGContainer ? tmpSVGContainer.clientWidth : 600;\n\t\tlet tmpHeight = tmpSVGContainer ? tmpSVGContainer.clientHeight : 400;\n\n\t\tlet tmpCenterX = (-tmpVS.PanX + tmpWidth / 2) / tmpVS.Zoom;\n\t\tlet tmpCenterY = (-tmpVS.PanY + tmpHeight / 2) / tmpVS.Zoom;\n\n\t\t// Slight offset to avoid stacking\n\t\tlet tmpNodeCount = this._FlowView.flowData.Nodes.length;\n\t\ttmpCenterX += (tmpNodeCount % 5) * 30;\n\t\ttmpCenterY += (tmpNodeCount % 5) * 30;\n\n\t\tthis._FlowView.addNode(pNodeType, tmpCenterX, tmpCenterY);\n\t}\n\n\t/**\n\t * Add a node from a palette card click.\n\t * @param {string} pCardType - The card type hash\n\t */\n\t_addCardFromPalette(pCardType)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpVS = this._FlowView.viewState;\n\t\tlet tmpX = (-tmpVS.PanX + 200) / tmpVS.Zoom;\n\t\tlet tmpY = (-tmpVS.PanY + 200) / tmpVS.Zoom;\n\n\t\t// Offset to avoid overlap\n\t\tlet tmpNodeCount = this._FlowView.flowData.Nodes.length;\n\t\ttmpX += (tmpNodeCount % 5) * 40;\n\t\ttmpY += (tmpNodeCount % 5) * 40;\n\n\t\tthis._FlowView.addNode(pCardType, tmpX, tmpY);\n\t}\n\n\t// ── Action Handler ────────────────────────────────────────────────────\n\n\t/**\n\t * Handle a toolbar action\n\t * @param {string} pAction\n\t */\n\t_handleToolbarAction(pAction)\n\t{\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpFlowViewIdentifier = this.options.FlowViewIdentifier;\n\n\t\tswitch (pAction)\n\t\t{\n\t\t\tcase 'add-node':\n\t\t\t\tthis._openPopup('add-node');\n\t\t\t\tbreak;\n\n\t\t\tcase 'delete-selected':\n\t\t\t\tthis._FlowView.deleteSelected();\n\t\t\t\tbreak;\n\n\t\t\tcase 'zoom-in':\n\t\t\t\tthis._FlowView.setZoom(this._FlowView.viewState.Zoom + this._FlowView.options.ZoomStep);\n\t\t\t\tbreak;\n\n\t\t\tcase 'zoom-out':\n\t\t\t\tthis._FlowView.setZoom(this._FlowView.viewState.Zoom - this._FlowView.options.ZoomStep);\n\t\t\t\tbreak;\n\n\t\t\tcase 'zoom-fit':\n\t\t\t\tthis._FlowView.zoomToFit();\n\t\t\t\tbreak;\n\n\t\t\tcase 'auto-layout':\n\t\t\t\tthis._FlowView.autoLayout();\n\t\t\t\tbreak;\n\n\t\t\tcase 'cards-popup':\n\t\t\t\tthis._openPopup('cards');\n\t\t\t\tbreak;\n\n\t\t\tcase 'layout-popup':\n\t\t\t\tthis._openPopup('layout');\n\t\t\t\tbreak;\n\n\t\t\tcase 'settings-popup':\n\t\t\t\tthis._openPopup('settings');\n\t\t\t\tbreak;\n\n\t\t\tcase 'toggle-floating':\n\t\t\t\tif (this._ToolbarMode === 'floating')\n\t\t\t\t{\n\t\t\t\t\tthis._setToolbarMode('docked');\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis._setToolbarMode('floating');\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'collapse-toolbar':\n\t\t\t\tthis._setToolbarMode('collapsed');\n\t\t\t\tbreak;\n\n\t\t\tcase 'expand-toolbar':\n\t\t\t\tthis._setToolbarMode('docked');\n\t\t\t\tbreak;\n\n\t\t\tcase 'fullscreen':\n\t\t\t\t{\n\t\t\t\t\tlet tmpIsFullscreen = this._FlowView.toggleFullscreen();\n\t\t\t\t\tlet tmpIconProvider = this._FlowView._IconProvider;\n\t\t\t\t\tlet tmpIconElements = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-Icon-${tmpFlowViewIdentifier}`);\n\t\t\t\t\tif (tmpIconElements.length > 0 && tmpIconProvider)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpIconElements[0].innerHTML = tmpIconProvider.getIconSVGMarkup(\n\t\t\t\t\t\t\ttmpIsFullscreen ? 'exit-fullscreen' : 'fullscreen', 14);\n\t\t\t\t\t}\n\t\t\t\t\tlet tmpFullscreenBtn = this.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-${tmpFlowViewIdentifier}`);\n\t\t\t\t\tif (tmpFullscreenBtn.length > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpFullscreenBtn[0].setAttribute('title', tmpIsFullscreen ? 'Exit Fullscreen' : 'Toggle Fullscreen');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthis.log.warn(`PictViewFlowToolbar: unknown action '${pAction}'`);\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nmodule.exports = PictViewFlowToolbar;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n","const libPictView = require('pict-view');\n\nconst libPictServiceFlowInteractionManager = require('../services/PictService-Flow-InteractionManager.js');\nconst libPictServiceFlowConnectionRenderer = require('../services/PictService-Flow-ConnectionRenderer.js');\nconst libPictServiceFlowTether = require('../services/PictService-Flow-Tether.js');\nconst libPictServiceFlowLayout = require('../services/PictService-Flow-Layout.js');\nconst libPictServiceFlowPathGenerator = require('../services/PictService-Flow-PathGenerator.js');\nconst libPictServiceFlowViewportManager = require('../services/PictService-Flow-ViewportManager.js');\nconst libPictServiceFlowSelectionManager = require('../services/PictService-Flow-SelectionManager.js');\nconst libPictServiceFlowPanelManager = require('../services/PictService-Flow-PanelManager.js');\nconst libPictServiceFlowDataManager = require('../services/PictService-Flow-DataManager.js');\nconst libPictServiceFlowConnectionHandleManager = require('../services/PictService-Flow-ConnectionHandleManager.js');\nconst libPictServiceFlowRenderManager = require('../services/PictService-Flow-RenderManager.js');\nconst libPictServiceFlowPortRenderer = require('../services/PictService-Flow-PortRenderer.js');\n\nconst libPictProviderFlowNodeTypes = require('../providers/PictProvider-Flow-NodeTypes.js');\nconst libPictProviderFlowEventHandler = require('../providers/PictProvider-Flow-EventHandler.js');\nconst libPictProviderFlowLayouts = require('../providers/PictProvider-Flow-Layouts.js');\nconst libPictProviderFlowSVGHelpers = require('../providers/PictProvider-Flow-SVGHelpers.js');\nconst libPictProviderFlowGeometry = require('../providers/PictProvider-Flow-Geometry.js');\nconst libPictProviderFlowPanelChrome = require('../providers/PictProvider-Flow-PanelChrome.js');\nconst libPictProviderFlowCSS = require('../providers/PictProvider-Flow-CSS.js');\nconst libPictProviderFlowIcons = require('../providers/PictProvider-Flow-Icons.js');\nconst libPictProviderFlowConnectorShapes = require('../providers/PictProvider-Flow-ConnectorShapes.js');\nconst libPictProviderFlowTheme = require('../providers/PictProvider-Flow-Theme.js');\nconst libPictProviderFlowNoise = require('../providers/PictProvider-Flow-Noise.js');\n\nconst libPictViewFlowNode = require('./PictView-Flow-Node.js');\nconst libPictViewFlowToolbar = require('./PictView-Flow-Toolbar.js');\nconst libPictViewFlowFloatingToolbar = require('./PictView-Flow-FloatingToolbar.js');\nconst libPictViewFlowPropertiesPanel = require('./PictView-Flow-PropertiesPanel.js');\n\nconst libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');\nconst libPanelTemplate = require('../panels/FlowCardPropertiesPanel-Template.js');\nconst libPanelMarkdown = require('../panels/FlowCardPropertiesPanel-Markdown.js');\nconst libPanelForm = require('../panels/FlowCardPropertiesPanel-Form.js');\nconst libPanelView = require('../panels/FlowCardPropertiesPanel-View.js');\n\nconst _DefaultConfiguration =\n{\n\tViewIdentifier: 'Pict-Flow',\n\n\tDefaultRenderable: 'Flow-Container',\n\tDefaultDestinationAddress: '#Flow-Container',\n\n\tAutoRender: false,\n\n\tFlowDataAddress: false,\n\n\tTargetElementAddress: '#Flow-SVG-Container',\n\n\tEnableToolbar: true,\n\tEnableAddNode: true,\n\tEnableCardPalette: true,\n\tIncludeDefaultNodeTypes: true,\n\tEnablePanning: true,\n\tEnableZooming: true,\n\tEnableNodeDragging: true,\n\tEnableConnectionCreation: true,\n\tEnableGridSnap: false,\n\tGridSnapSize: 20,\n\n\tMinZoom: 0.1,\n\tMaxZoom: 5.0,\n\tZoomStep: 0.1,\n\n\tDefaultNodeType: 'default',\n\tDefaultNodeWidth: 180,\n\tDefaultNodeHeight: 80,\n\n\tCSS: false,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: 'Flow-PanelChrome-Template',\n\t\t\tTemplate: /*html*/`<div class=\"pict-flow-panel\" xmlns=\"http://www.w3.org/1999/xhtml\"><div class=\"pict-flow-panel-titlebar\" data-element-type=\"panel-titlebar\" data-panel-hash=\"{~D:Record.Hash~}\"><span class=\"pict-flow-panel-title-text\">{~D:Record.Title~}</span><span class=\"pict-flow-panel-close-btn\" data-element-type=\"panel-close\" data-panel-hash=\"{~D:Record.Hash~}\"><span class=\"pict-flow-panel-close-icon\"></span></span></div><div class=\"pict-flow-panel-content\" data-panel-hash=\"{~D:Record.Hash~}\"><div class=\"pict-flow-panel-tab-pane active\" data-tab=\"properties\" data-panel-hash=\"{~D:Record.Hash~}\"></div><div class=\"pict-flow-panel-tab-pane\" data-tab=\"help\" data-panel-hash=\"{~D:Record.Hash~}\" style=\"display:none;\"></div><div class=\"pict-flow-panel-tab-pane\" data-tab=\"appearance\" data-panel-hash=\"{~D:Record.Hash~}\" style=\"display:none;\"></div></div><div class=\"pict-flow-panel-resize-handle\" data-element-type=\"panel-resize\" data-panel-hash=\"{~D:Record.Hash~}\"></div><div class=\"pict-flow-panel-tabbar\" data-panel-hash=\"{~D:Record.Hash~}\"><div class=\"pict-flow-panel-tab active\" data-tab-target=\"properties\" data-panel-hash=\"{~D:Record.Hash~}\">Properties</div><div class=\"pict-flow-panel-tab\" data-tab-target=\"help\" data-panel-hash=\"{~D:Record.Hash~}\" style=\"display:none;\">Help</div><div class=\"pict-flow-panel-tab\" data-tab-target=\"appearance\" data-panel-hash=\"{~D:Record.Hash~}\">Appearance</div></div></div>`\n\t\t},\n\t\t{\n\t\t\tHash: 'Flow-Container-Template',\n\t\t\tTemplate: /*html*/`\n<div class=\"pict-flow-container\" id=\"Flow-Wrapper-{~D:Record.ViewIdentifier~}\">\n\t<div id=\"Flow-Toolbar-{~D:Record.ViewIdentifier~}\"></div>\n\t<div id=\"Flow-FloatingToolbar-Container-{~D:Record.ViewIdentifier~}\" style=\"display:none;position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:100;\"></div>\n\t<div class=\"pict-flow-svg-container\" id=\"Flow-SVG-Container-{~D:Record.ViewIdentifier~}\">\n\t\t<svg class=\"pict-flow-svg\"\n\t\t\tid=\"Flow-SVG-{~D:Record.ViewIdentifier~}\"\n\t\t\txmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t<defs>\n\t\t\t\t<pattern id=\"flow-grid-{~D:Record.ViewIdentifier~}\"\n\t\t\t\t\twidth=\"20\" height=\"20\" patternUnits=\"userSpaceOnUse\">\n\t\t\t\t\t<line x1=\"20\" y1=\"0\" x2=\"20\" y2=\"20\" class=\"pict-flow-grid-pattern\" />\n\t\t\t\t\t<line x1=\"0\" y1=\"20\" x2=\"20\" y2=\"20\" class=\"pict-flow-grid-pattern\" />\n\t\t\t\t</pattern>\n\t\t\t</defs>\n\t\t\t<rect width=\"10000\" height=\"10000\" x=\"-5000\" y=\"-5000\"\n\t\t\t\tfill=\"url(#flow-grid-{~D:Record.ViewIdentifier~})\"\n\t\t\t\tclass=\"pict-flow-grid-background\" />\n\t\t\t<g class=\"pict-flow-viewport\" id=\"Flow-Viewport-{~D:Record.ViewIdentifier~}\">\n\t\t\t\t<g class=\"pict-flow-connections-layer\" id=\"Flow-Connections-{~D:Record.ViewIdentifier~}\"></g>\n\t\t\t\t<g class=\"pict-flow-nodes-layer\" id=\"Flow-Nodes-{~D:Record.ViewIdentifier~}\"></g>\n\t\t\t\t<g class=\"pict-flow-tethers-layer\" id=\"Flow-Tethers-{~D:Record.ViewIdentifier~}\"></g>\n\t\t\t\t<g class=\"pict-flow-panels-layer\" id=\"Flow-Panels-{~D:Record.ViewIdentifier~}\"></g>\n\t\t\t</g>\n\t\t</svg>\n\t</div>\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: 'Flow-Container',\n\t\t\tTemplateHash: 'Flow-Container-Template',\n\t\t\tDestinationAddress: '#Flow-Container',\n\t\t\tRenderMethod: 'replace'\n\t\t}\n\t]\n};\n\nclass PictViewFlow extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'PictSectionFlow';\n\n\t\t// ---- Declarative service registry ----\n\t\t// Each entry defines a service to register, instantiate, and guard.\n\t\t// Optional flags:\n\t\t// NoFlowView — instantiate without { FlowView: this }\n\t\t// PostInit — method name to call on the instance after creation\n\t\t// RegisterOnly — only register the type; do not bulk-instantiate\n\t\tthis._ServiceRegistry =\n\t\t[\n\t\t\t// Providers (stateless or config-only — no FlowView needed)\n\t\t\t{ ServiceType: 'PictProviderFlowSVGHelpers', Library: libPictProviderFlowSVGHelpers, Property: '_SVGHelperProvider', NoFlowView: true },\n\t\t\t{ ServiceType: 'PictProviderFlowGeometry', Library: libPictProviderFlowGeometry, Property: '_GeometryProvider', NoFlowView: true },\n\t\t\t{ ServiceType: 'PictProviderFlowNoise', Library: libPictProviderFlowNoise, Property: '_NoiseProvider', NoFlowView: true },\n\n\t\t\t// Providers (need FlowView)\n\t\t\t{ ServiceType: 'PictProviderFlowTheme', Library: libPictProviderFlowTheme, Property: '_ThemeProvider' },\n\t\t\t{ ServiceType: 'PictProviderFlowCSS', Library: libPictProviderFlowCSS, Property: '_CSSProvider', PostInit: 'registerCSS' },\n\t\t\t{ ServiceType: 'PictProviderFlowIcons', Library: libPictProviderFlowIcons, Property: '_IconProvider', PostInit: 'registerIconTemplates' },\n\t\t\t{ ServiceType: 'PictProviderFlowConnectorShapes', Library: libPictProviderFlowConnectorShapes, Property: '_ConnectorShapesProvider' },\n\t\t\t{ ServiceType: 'PictProviderFlowPanelChrome', Library: libPictProviderFlowPanelChrome, Property: '_PanelChromeProvider' },\n\t\t\t{ ServiceType: 'PictProviderFlowNodeTypes', Library: libPictProviderFlowNodeTypes, Property: '_NodeTypeProvider', ExtraOptions: () => ({ AdditionalNodeTypes: this.options.NodeTypes, IncludeDefaultNodeTypes: this.options.IncludeDefaultNodeTypes }) },\n\t\t\t{ ServiceType: 'PictProviderFlowEventHandler', Library: libPictProviderFlowEventHandler, Property: '_EventHandlerProvider' },\n\t\t\t{ ServiceType: 'PictProviderFlowLayouts', Library: libPictProviderFlowLayouts, Property: '_LayoutProvider', PostInit: 'loadPersistedLayouts' },\n\n\t\t\t// Services\n\t\t\t{ ServiceType: 'PictServiceFlowPathGenerator', Library: libPictServiceFlowPathGenerator, Property: '_PathGenerator' },\n\t\t\t{ ServiceType: 'PictServiceFlowDataManager', Library: libPictServiceFlowDataManager, Property: '_DataManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowConnectionHandleManager', Library: libPictServiceFlowConnectionHandleManager, Property: '_ConnectionHandleManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowRenderManager', Library: libPictServiceFlowRenderManager, Property: '_RenderManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowPortRenderer', Library: libPictServiceFlowPortRenderer, Property: '_PortRenderer' },\n\t\t\t{ ServiceType: 'PictServiceFlowInteractionManager', Library: libPictServiceFlowInteractionManager, Property: '_InteractionManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowConnectionRenderer', Library: libPictServiceFlowConnectionRenderer, Property: '_ConnectionRenderer' },\n\t\t\t{ ServiceType: 'PictServiceFlowTether', Library: libPictServiceFlowTether, Property: '_TetherService' },\n\t\t\t{ ServiceType: 'PictServiceFlowLayout', Library: libPictServiceFlowLayout, Property: '_LayoutService' },\n\t\t\t{ ServiceType: 'PictServiceFlowViewportManager', Library: libPictServiceFlowViewportManager, Property: '_ViewportManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowSelectionManager', Library: libPictServiceFlowSelectionManager, Property: '_SelectionManager' },\n\t\t\t{ ServiceType: 'PictServiceFlowPanelManager', Library: libPictServiceFlowPanelManager, Property: '_PanelManager' },\n\n\t\t\t// View types (register only — instantiated with custom config in onAfterInitialRender)\n\t\t\t{ ServiceType: 'PictViewFlowNode', Library: libPictViewFlowNode, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictViewFlowToolbar', Library: libPictViewFlowToolbar, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictViewFlowFloatingToolbar', Library: libPictViewFlowFloatingToolbar, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictViewFlowPropertiesPanel', Library: libPictViewFlowPropertiesPanel, RegisterOnly: true },\n\n\t\t\t// Panel types (register only)\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel', Library: libPictFlowCardPropertiesPanel, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel-Template', Library: libPanelTemplate, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel-Markdown', Library: libPanelMarkdown, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel-Form', Library: libPanelForm, RegisterOnly: true },\n\t\t\t{ ServiceType: 'PictFlowCardPropertiesPanel-View', Library: libPanelView, RegisterOnly: true }\n\t\t];\n\n\t\tthis._registerServiceTypes();\n\n\t\t// Internal state\n\t\tthis._FlowData = {\n\t\t\tNodes: [],\n\t\t\tConnections: [],\n\t\t\tOpenPanels: [],\n\t\t\tSavedLayouts: [],\n\t\t\tViewState: {\n\t\t\t\tPanX: 0,\n\t\t\t\tPanY: 0,\n\t\t\t\tZoom: 1,\n\t\t\t\tSelectedNodeHash: null,\n\t\t\t\tSelectedConnectionHash: null,\n\t\t\t\tSelectedTetherHash: null\n\t\t\t}\n\t\t};\n\n\t\tthis._SVGElement = null;\n\t\tthis._ViewportElement = null;\n\t\tthis._NodesLayer = null;\n\t\tthis._ConnectionsLayer = null;\n\t\tthis._TethersLayer = null;\n\t\tthis._PanelsLayer = null;\n\n\t\tthis._DataManager = null;\n\t\tthis._ConnectionHandleManager = null;\n\t\tthis._RenderManager = null;\n\t\tthis._PortRenderer = null;\n\n\t\tthis._InteractionManager = null;\n\t\tthis._ConnectionRenderer = null;\n\t\tthis._TetherService = null;\n\t\tthis._LayoutService = null;\n\t\tthis._PathGenerator = null;\n\t\tthis._ViewportManager = null;\n\t\tthis._SelectionManager = null;\n\t\tthis._PanelManager = null;\n\t\tthis._CSSProvider = null;\n\t\tthis._IconProvider = null;\n\t\tthis._ConnectorShapesProvider = null;\n\t\tthis._ThemeProvider = null;\n\t\tthis._NoiseProvider = null;\n\t\tthis._SVGHelperProvider = null;\n\t\tthis._GeometryProvider = null;\n\t\tthis._PanelChromeProvider = null;\n\t\tthis._NodeTypeProvider = null;\n\t\tthis._LayoutProvider = null;\n\t\tthis._EventHandlerProvider = null;\n\t\tthis._NodeView = null;\n\t\tthis._ToolbarView = null;\n\t\tthis._PropertiesPanelView = null;\n\n\t\tthis.initialRenderComplete = false;\n\t}\n\n\t_registerServiceTypes()\n\t{\n\t\tfor (let i = 0; i < this._ServiceRegistry.length; i++)\n\t\t{\n\t\t\tlet tmpEntry = this._ServiceRegistry[i];\n\t\t\tif (!this.fable.servicesMap.hasOwnProperty(tmpEntry.ServiceType))\n\t\t\t{\n\t\t\t\tthis.fable.addServiceType(tmpEntry.ServiceType, tmpEntry.Library);\n\t\t\t}\n\t\t}\n\t}\n\n\t_instantiateServices()\n\t{\n\t\tfor (let i = 0; i < this._ServiceRegistry.length; i++)\n\t\t{\n\t\t\tlet tmpEntry = this._ServiceRegistry[i];\n\t\t\tif (tmpEntry.RegisterOnly) continue;\n\t\t\tif (this[tmpEntry.Property]) continue;\n\n\t\t\tlet tmpOptions = tmpEntry.NoFlowView ? {} : { FlowView: this };\n\t\t\tif (typeof tmpEntry.ExtraOptions === 'function')\n\t\t\t{\n\t\t\t\tObject.assign(tmpOptions, tmpEntry.ExtraOptions());\n\t\t\t}\n\n\t\t\tthis[tmpEntry.Property] = this.fable.instantiateServiceProviderWithoutRegistration(tmpEntry.ServiceType, tmpOptions);\n\n\t\t\tif (tmpEntry.PostInit && typeof this[tmpEntry.Property][tmpEntry.PostInit] === 'function')\n\t\t\t{\n\t\t\t\tthis[tmpEntry.Property][tmpEntry.PostInit]();\n\t\t\t}\n\t\t}\n\t}\n\n\tget flowData()\n\t{\n\t\treturn this._FlowData;\n\t}\n\n\tget viewState()\n\t{\n\t\treturn this._FlowData.ViewState;\n\t}\n\n\t// Backward-compatible getter for InteractionManager direct access\n\tget _IsFullscreen()\n\t{\n\t\treturn this._ViewportManager ? this._ViewportManager._IsFullscreen : false;\n\t}\n\n\t/**\n\t * Override render to pass view options as the template record,\n\t * so template expressions like {~D:Record.ViewIdentifier~} resolve correctly.\n\t */\n\trender(pRenderableHash, pRenderDestinationAddress)\n\t{\n\t\treturn super.render(pRenderableHash, pRenderDestinationAddress, this.options);\n\t}\n\n\trenderAsync(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback)\n\t{\n\t\t// If no record address is explicitly provided, use this.options as the record\n\t\tif (typeof pTemplateRecordAddress === 'function' || typeof pTemplateRecordAddress === 'undefined')\n\t\t{\n\t\t\treturn super.renderAsync(pRenderableHash, pRenderDestinationAddress, this.options, pRootRenderable, pTemplateRecordAddress || fCallback);\n\t\t}\n\t\treturn super.renderAsync(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback);\n\t}\n\n\tonBeforeInitialize()\n\t{\n\t\tsuper.onBeforeInitialize();\n\n\t\t// Theme + Noise must be created first (CSS PostInit depends on theme state)\n\t\tthis._ThemeProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowTheme', { FlowView: this });\n\t\tthis._NoiseProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowNoise');\n\n\t\t// Apply initial theme from options\n\t\tif (this.options.Theme)\n\t\t{\n\t\t\tthis._ThemeProvider.setTheme(this.options.Theme);\n\t\t}\n\t\tif (typeof this.options.NoiseLevel === 'number')\n\t\t{\n\t\t\tthis._ThemeProvider.setNoiseLevel(this.options.NoiseLevel);\n\t\t}\n\n\t\t// Instantiate all remaining services (skips Theme + Noise since already set)\n\t\tthis._instantiateServices();\n\n\t\treturn super.onBeforeInitialize();\n\t}\n\n\tonAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)\n\t{\n\t\tif (!this.initialRenderComplete)\n\t\t{\n\t\t\tthis.onAfterInitialRender();\n\t\t\tthis.initialRenderComplete = true;\n\t\t}\n\t\treturn super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);\n\t}\n\n\tonAfterInitialRender()\n\t{\n\t\tlet tmpViewIdentifier = this.options.ViewIdentifier;\n\n\t\t// Grab SVG DOM elements\n\t\tlet tmpSVGElements = this.pict.ContentAssignment.getElement(`#Flow-SVG-${tmpViewIdentifier}`);\n\t\tif (tmpSVGElements.length < 1)\n\t\t{\n\t\t\tthis.log.error(`PictSectionFlow could not find SVG element #Flow-SVG-${tmpViewIdentifier}`);\n\t\t\treturn false;\n\t\t}\n\t\tthis._SVGElement = tmpSVGElements[0];\n\n\t\tlet tmpViewportElements = this.pict.ContentAssignment.getElement(`#Flow-Viewport-${tmpViewIdentifier}`);\n\t\tif (tmpViewportElements.length > 0)\n\t\t{\n\t\t\tthis._ViewportElement = tmpViewportElements[0];\n\t\t}\n\n\t\tlet tmpNodesElements = this.pict.ContentAssignment.getElement(`#Flow-Nodes-${tmpViewIdentifier}`);\n\t\tif (tmpNodesElements.length > 0)\n\t\t{\n\t\t\tthis._NodesLayer = tmpNodesElements[0];\n\t\t}\n\n\t\tlet tmpConnectionsElements = this.pict.ContentAssignment.getElement(`#Flow-Connections-${tmpViewIdentifier}`);\n\t\tif (tmpConnectionsElements.length > 0)\n\t\t{\n\t\t\tthis._ConnectionsLayer = tmpConnectionsElements[0];\n\t\t}\n\n\t\tlet tmpTethersElements = this.pict.ContentAssignment.getElement(`#Flow-Tethers-${tmpViewIdentifier}`);\n\t\tif (tmpTethersElements.length > 0)\n\t\t{\n\t\t\tthis._TethersLayer = tmpTethersElements[0];\n\t\t}\n\n\t\tlet tmpPanelsElements = this.pict.ContentAssignment.getElement(`#Flow-Panels-${tmpViewIdentifier}`);\n\t\tif (tmpPanelsElements.length > 0)\n\t\t{\n\t\t\tthis._PanelsLayer = tmpPanelsElements[0];\n\t\t}\n\n\t\t// Ensure all services are initialized (fallback if onBeforeInitialize was skipped)\n\t\tthis._instantiateServices();\n\n\t\t// Inject marker defs via the connector shapes provider\n\t\t// Note: insertAdjacentHTML does not work on SVG elements (wrong namespace),\n\t\t// so we parse via a temporary <svg> element to ensure SVG namespace.\n\t\tif (this._ConnectorShapesProvider && this._SVGElement)\n\t\t{\n\t\t\tlet tmpDefs = this._SVGElement.querySelector('defs');\n\t\t\tif (tmpDefs)\n\t\t\t{\n\t\t\t\tlet tmpMarkerMarkup = this._ConnectorShapesProvider.generateMarkerDefs(tmpViewIdentifier);\n\t\t\t\tlet tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\t\t\t\ttmpTempSVG.innerHTML = tmpMarkerMarkup;\n\t\t\t\twhile (tmpTempSVG.firstChild)\n\t\t\t\t{\n\t\t\t\t\ttmpDefs.appendChild(tmpTempSVG.firstChild);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Setup the toolbar if enabled\n\t\tif (this.options.EnableToolbar)\n\t\t{\n\t\t\tthis._ToolbarView = this.fable.instantiateServiceProviderWithoutRegistration('PictViewFlowToolbar',\n\t\t\t\tObject.assign({},\n\t\t\t\t\tlibPictViewFlowToolbar.default_configuration,\n\t\t\t\t\t{\n\t\t\t\t\t\tViewIdentifier: `Flow-Toolbar-${tmpViewIdentifier}`,\n\t\t\t\t\t\tDefaultDestinationAddress: `#Flow-Toolbar-${tmpViewIdentifier}`,\n\t\t\t\t\t\tFlowViewIdentifier: tmpViewIdentifier,\n\t\t\t\t\t\tEnableAddNode: this.options.EnableAddNode,\n\t\t\t\t\t\tEnableCardPalette: this.options.EnableCardPalette\n\t\t\t\t\t}\n\t\t\t\t));\n\t\t\t// Use the toolbar's render method after it's set up\n\t\t\tif (this._ToolbarView && typeof this._ToolbarView.render === 'function')\n\t\t\t{\n\t\t\t\tthis._ToolbarView._FlowView = this;\n\t\t\t\tthis._ToolbarView.render();\n\t\t\t}\n\t\t}\n\n\t\t// Setup the node renderer\n\t\tthis._NodeView = this.fable.instantiateServiceProviderWithoutRegistration('PictViewFlowNode',\n\t\t\tObject.assign({},\n\t\t\t\tlibPictViewFlowNode.default_configuration,\n\t\t\t\t{\n\t\t\t\t\tViewIdentifier: `Flow-NodeRenderer-${tmpViewIdentifier}`,\n\t\t\t\t\tAutoRender: false\n\t\t\t\t}\n\t\t\t));\n\t\tthis._NodeView._FlowView = this;\n\n\t\t// Setup the properties panel renderer\n\t\tthis._PropertiesPanelView = this.fable.instantiateServiceProviderWithoutRegistration('PictViewFlowPropertiesPanel',\n\t\t\tObject.assign({},\n\t\t\t\tlibPictViewFlowPropertiesPanel.default_configuration,\n\t\t\t\t{\n\t\t\t\t\tViewIdentifier: `Flow-PropertiesPanel-${tmpViewIdentifier}`,\n\t\t\t\t\tAutoRender: false\n\t\t\t\t}\n\t\t\t));\n\t\tthis._PropertiesPanelView._FlowView = this;\n\n\t\t// Bind interaction events\n\t\tthis._InteractionManager.initialize(this._SVGElement, this._ViewportElement);\n\n\t\t// Load initial flow data if an address is configured\n\t\tif (this.options.FlowDataAddress)\n\t\t{\n\t\t\tthis.marshalToView();\n\t\t}\n\n\t\t// Render the initial flow\n\t\tthis.renderFlow();\n\t}\n\n\t// ---- Data Manager Delegations ----\n\n\tmarshalToView() { return this._DataManager.marshalToView(); }\n\tmarshalFromView() { return this._DataManager.marshalFromView(); }\n\tgetFlowData() { return this._DataManager.getFlowData(); }\n\tsetFlowData(pFlowData) { return this._DataManager.setFlowData(pFlowData); }\n\taddNode(pType, pX, pY, pTitle, pData) { return this._DataManager.addNode(pType, pX, pY, pTitle, pData); }\n\tremoveNode(pNodeHash) { return this._DataManager.removeNode(pNodeHash); }\n\taddConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData) { return this._DataManager.addConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData); }\n\tremoveConnection(pConnectionHash) { return this._DataManager.removeConnection(pConnectionHash); }\n\n\t/**\n\t * Select a node\n\t * @param {string|null} pNodeHash - Hash of the node to select, or null to deselect\n\t */\n\tselectNode(pNodeHash)\n\t{\n\t\treturn this._SelectionManager.selectNode(pNodeHash);\n\t}\n\n\t/**\n\t * Select a connection\n\t * @param {string|null} pConnectionHash - Hash of the connection to select, or null to deselect\n\t */\n\tselectConnection(pConnectionHash)\n\t{\n\t\treturn this._SelectionManager.selectConnection(pConnectionHash);\n\t}\n\n\t/**\n\t * Deselect all nodes and connections\n\t */\n\tdeselectAll()\n\t{\n\t\treturn this._SelectionManager.deselectAll();\n\t}\n\n\t/**\n\t * Delete the currently selected node or connection\n\t * @returns {boolean}\n\t */\n\tdeleteSelected()\n\t{\n\t\treturn this._SelectionManager.deleteSelected();\n\t}\n\n\t/**\n\t * Update the viewport transform (pan and zoom)\n\t */\n\tupdateViewportTransform()\n\t{\n\t\treturn this._ViewportManager.updateViewportTransform();\n\t}\n\n\t/**\n\t * Set zoom level\n\t * @param {number} pZoom - The zoom level\n\t * @param {number} [pFocusX] - X coordinate to zoom toward (SVG space)\n\t * @param {number} [pFocusY] - Y coordinate to zoom toward (SVG space)\n\t */\n\tsetZoom(pZoom, pFocusX, pFocusY)\n\t{\n\t\treturn this._ViewportManager.setZoom(pZoom, pFocusX, pFocusY);\n\t}\n\n\t/**\n\t * Zoom to fit all nodes in the viewport\n\t */\n\tzoomToFit()\n\t{\n\t\treturn this._ViewportManager.zoomToFit();\n\t}\n\n\t/**\n\t * Apply auto-layout to all nodes\n\t */\n\tautoLayout()\n\t{\n\t\tif (this._LayoutService)\n\t\t{\n\t\t\tthis._LayoutService.autoLayout(this._FlowData.Nodes, this._FlowData.Connections);\n\t\t\tthis.renderFlow();\n\t\t\tthis.marshalFromView();\n\n\t\t\tif (this._EventHandlerProvider)\n\t\t\t{\n\t\t\t\tthis._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Toggle fullscreen mode on the flow editor container.\n\t * Uses a CSS fixed-position overlay instead of the Fullscreen API.\n\t * @returns {boolean} The new fullscreen state\n\t */\n\ttoggleFullscreen()\n\t{\n\t\treturn this._ViewportManager.toggleFullscreen();\n\t}\n\n\t/**\n\t * Exit fullscreen mode if currently active.\n\t */\n\texitFullscreen()\n\t{\n\t\treturn this._ViewportManager.exitFullscreen();\n\t}\n\n\t// ── Theme API ────────────────────────────────────────────────────────\n\n\t/**\n\t * Switch the active theme and re-render.\n\t * @param {string} pThemeKey - Theme key (e.g. 'default', 'sketch', 'blueprint', 'mono', 'retro-80s', 'retro-90s')\n\t */\n\tsetTheme(pThemeKey)\n\t{\n\t\tif (!this._ThemeProvider)\n\t\t{\n\t\t\tthis.log.warn('PictSectionFlow setTheme: ThemeProvider not available');\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpApplied = this._ThemeProvider.setTheme(pThemeKey);\n\t\tif (!tmpApplied) return;\n\n\t\t// Re-register CSS with the new theme overrides\n\t\tif (this._CSSProvider)\n\t\t{\n\t\t\tthis._CSSProvider.registerCSS();\n\t\t}\n\n\t\t// Re-inject marker defs (arrowhead colors may have changed)\n\t\tthis._reinjectMarkerDefs();\n\n\t\t// Full re-render\n\t\tif (this.initialRenderComplete)\n\t\t{\n\t\t\tthis.renderFlow();\n\t\t}\n\n\t\tif (this._EventHandlerProvider)\n\t\t{\n\t\t\tthis._EventHandlerProvider.fireEvent('onThemeChanged', pThemeKey);\n\t\t}\n\t}\n\n\t/**\n\t * Set the noise level (0 to 1) and re-render.\n\t * @param {number} pLevel - 0 = precise, 1 = maximum wobble\n\t */\n\tsetNoiseLevel(pLevel)\n\t{\n\t\tif (!this._ThemeProvider)\n\t\t{\n\t\t\tthis.log.warn('PictSectionFlow setNoiseLevel: ThemeProvider not available');\n\t\t\treturn;\n\t\t}\n\n\t\tthis._ThemeProvider.setNoiseLevel(pLevel);\n\n\t\t// Full re-render to apply new noise\n\t\tif (this.initialRenderComplete)\n\t\t{\n\t\t\tthis.renderFlow();\n\t\t}\n\t}\n\n\t/**\n\t * Get the current noise level (0 to 1).\n\t * @returns {number}\n\t */\n\tgetNoiseLevel()\n\t{\n\t\tif (this._ThemeProvider)\n\t\t{\n\t\t\treturn this._ThemeProvider.getNoiseLevel();\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get the active theme key.\n\t * @returns {string}\n\t */\n\tgetThemeKey()\n\t{\n\t\tif (this._ThemeProvider)\n\t\t{\n\t\t\treturn this._ThemeProvider.getActiveThemeKey();\n\t\t}\n\t\treturn 'default';\n\t}\n\n\t_reinjectMarkerDefs() { return this._RenderManager.reinjectMarkerDefs(); }\n\n\t/**\n\t * Get a node by hash\n\t * @param {string} pNodeHash\n\t * @returns {Object|null}\n\t */\n\tgetNode(pNodeHash)\n\t{\n\t\treturn this._FlowData.Nodes.find((pNode) => pNode.Hash === pNodeHash) || null;\n\t}\n\n\t/**\n\t * Get a connection by hash\n\t * @param {string} pConnectionHash\n\t * @returns {Object|null}\n\t */\n\tgetConnection(pConnectionHash)\n\t{\n\t\treturn this._FlowData.Connections.find((pConn) => pConn.Hash === pConnectionHash) || null;\n\t}\n\n\t/**\n\t * Select a tether by its panel hash.\n\t * @param {string|null} pPanelHash - Hash of the panel whose tether to select, or null to deselect\n\t */\n\tselectTether(pPanelHash)\n\t{\n\t\treturn this._SelectionManager.selectTether(pPanelHash);\n\t}\n\n\t// ---- Connection Handle Manager Delegations ----\n\n\tupdateConnectionHandle(pConnectionHash, pHandleType, pX, pY) { return this._ConnectionHandleManager.updateConnectionHandle(pConnectionHash, pHandleType, pX, pY); }\n\taddConnectionHandle(pConnectionHash, pX, pY) { return this._ConnectionHandleManager.addConnectionHandle(pConnectionHash, pX, pY); }\n\tremoveConnectionHandle(pConnectionHash, pIndex) { return this._ConnectionHandleManager.removeConnectionHandle(pConnectionHash, pIndex); }\n\t_resetHandlesForNode(pNodeHash) { return this._ConnectionHandleManager.resetHandlesForNode(pNodeHash); }\n\t_resetHandlesForPanel(pPanelHash) { return this._ConnectionHandleManager.resetHandlesForPanel(pPanelHash); }\n\n\t/**\n\t * Add a bezier handle to a tether at the specified SVG position.\n\t * @param {string} pPanelHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\taddTetherHandle(pPanelHash, pX, pY)\n\t{\n\t\tlet tmpPanel = this._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel || !this._TetherService) return;\n\n\t\tlet tmpNode = this.getNode(tmpPanel.NodeHash);\n\t\tif (!tmpNode) return;\n\n\t\tlet tmpAnchors = this._TetherService.getSmartAnchors(tmpPanel, tmpNode);\n\n\t\tthis._TetherService.addHandle(tmpPanel, pX, pY, tmpAnchors.panelAnchor, tmpAnchors.nodeAnchor);\n\n\t\tthis.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._EventHandlerProvider)\n\t\t{\n\t\t\tthis._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Remove a bezier handle from a tether by index.\n\t * @param {string} pPanelHash\n\t * @param {number} pIndex\n\t */\n\tremoveTetherHandle(pPanelHash, pIndex)\n\t{\n\t\tlet tmpPanel = this._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel || !this._TetherService) return;\n\n\t\tthis._TetherService.removeHandle(tmpPanel, pIndex);\n\n\t\tthis.renderFlow();\n\t\tthis.marshalFromView();\n\n\t\tif (this._EventHandlerProvider)\n\t\t{\n\t\t\tthis._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);\n\t\t}\n\t}\n\n\t/**\n\t * Update a tether handle position during drag (for real-time feedback).\n\t * Delegates state update to the TetherService.\n\t * @param {string} pPanelHash\n\t * @param {string} pHandleType\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdateTetherHandle(pPanelHash, pHandleType, pX, pY)\n\t{\n\t\tlet tmpPanel = this._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);\n\t\tif (!tmpPanel) return;\n\n\t\tif (this._TetherService)\n\t\t{\n\t\t\tthis._TetherService.updateHandlePosition(tmpPanel, pHandleType, pX, pY);\n\t\t}\n\n\t\tthis._renderSingleTether(pPanelHash);\n\t}\n\n\t/**\n\t * Get a port's absolute position in SVG coordinates.\n\t *\n\t * For left and right side ports, positioning is offset below the title bar\n\t * so that connection endpoints match the rendered port circles.\n\t *\n\t * @param {string} pNodeHash\n\t * @param {string} pPortHash\n\t * @returns {{x: number, y: number, side: string}|null}\n\t */\n\tgetPortPosition(pNodeHash, pPortHash)\n\t{\n\t\tlet tmpNode = this.getNode(pNodeHash);\n\t\tif (!tmpNode) return null;\n\n\t\tlet tmpPort = tmpNode.Ports.find((p) => p.Hash === pPortHash);\n\t\tif (!tmpPort) return null;\n\n\t\t// Count ports on the same side (matching both direction and side)\n\t\tlet tmpSameSidePorts = tmpNode.Ports.filter((p) => p.Side === tmpPort.Side);\n\t\tlet tmpPortIndex = tmpSameSidePorts.indexOf(tmpPort);\n\t\tlet tmpPortCount = tmpSameSidePorts.length;\n\n\t\tlet tmpTitleBarHeight = (this._NodeView && this._NodeView.options.NodeTitleBarHeight) || 28;\n\n\t\t// Use the adjusted node height that accounts for minimum port\n\t\t// spacing. Connections render before nodes, so the node renderer\n\t\t// may not have written back its adjusted height yet.\n\t\tlet tmpHeight = tmpNode.Height || 80;\n\t\tif (this._GeometryProvider && tmpNode.Ports && tmpNode.Ports.length > 0)\n\t\t{\n\t\t\tlet tmpMinHeight = this._GeometryProvider.computeMinimumNodeHeight(tmpNode.Ports, tmpTitleBarHeight);\n\t\t\tif (tmpMinHeight > tmpHeight)\n\t\t\t{\n\t\t\t\ttmpHeight = tmpMinHeight;\n\t\t\t}\n\t\t}\n\n\t\t// Build port counts map for adaptive zone sizing\n\t\tlet tmpPortCountsBySide = this._GeometryProvider.buildPortCountsBySide(tmpNode.Ports);\n\n\t\tlet tmpLocal = this._GeometryProvider.getPortLocalPosition(tmpPort.Side, tmpPortIndex, tmpPortCount, tmpNode.Width, tmpHeight, tmpTitleBarHeight, tmpPortCountsBySide);\n\n\t\treturn { x: tmpNode.X + tmpLocal.x, y: tmpNode.Y + tmpLocal.y, side: tmpPort.Side || 'right' };\n\t}\n\n\t/**\n\t * Convert screen coordinates to SVG viewport coordinates\n\t * @param {number} pScreenX\n\t * @param {number} pScreenY\n\t * @returns {{x: number, y: number}}\n\t */\n\tscreenToSVGCoords(pScreenX, pScreenY)\n\t{\n\t\treturn this._ViewportManager.screenToSVGCoords(pScreenX, pScreenY);\n\t}\n\n\t// ---- Render Manager Delegations ----\n\n\trenderFlow() { return this._RenderManager.renderFlow(); }\n\t_renderSingleConnection(pConnectionHash) { return this._RenderManager.renderSingleConnection(pConnectionHash); }\n\t_renderSingleTether(pPanelHash) { return this._RenderManager.renderSingleTether(pPanelHash); }\n\tupdateNodePosition(pNodeHash, pX, pY) { return this._RenderManager.updateNodePosition(pNodeHash, pX, pY); }\n\t_renderConnectionsForNode(pNodeHash) { return this._RenderManager.renderConnectionsForNode(pNodeHash); }\n\t_renderTethersForNode(pNodeHash) { return this._RenderManager.renderTethersForNode(pNodeHash); }\n\n\t// ---- Properties Panel Management ----\n\n\t/**\n\t * Open a properties panel for a node.\n\t * @param {string} pNodeHash - The hash of the node to open a panel for\n\t * @returns {Object|false} The panel data, or false if the node has no PropertiesPanel config\n\t */\n\topenPanel(pNodeHash)\n\t{\n\t\treturn this._PanelManager.openPanel(pNodeHash);\n\t}\n\n\t/**\n\t * Close a properties panel by panel hash.\n\t * @param {string} pPanelHash\n\t * @returns {boolean}\n\t */\n\tclosePanel(pPanelHash)\n\t{\n\t\treturn this._PanelManager.closePanel(pPanelHash);\n\t}\n\n\t/**\n\t * Close all panels for a given node.\n\t * @param {string} pNodeHash\n\t * @returns {boolean}\n\t */\n\tclosePanelForNode(pNodeHash)\n\t{\n\t\treturn this._PanelManager.closePanelForNode(pNodeHash);\n\t}\n\n\t/**\n\t * Toggle a properties panel for a node (open if closed, close if open).\n\t * @param {string} pNodeHash\n\t * @returns {Object|false}\n\t */\n\ttogglePanel(pNodeHash)\n\t{\n\t\treturn this._PanelManager.togglePanel(pNodeHash);\n\t}\n\n\t/**\n\t * Update a panel's position (for drag).\n\t * @param {string} pPanelHash\n\t * @param {number} pX\n\t * @param {number} pY\n\t */\n\tupdatePanelPosition(pPanelHash, pX, pY)\n\t{\n\t\treturn this._PanelManager.updatePanelPosition(pPanelHash, pX, pY);\n\t}\n}\n\nmodule.exports = PictViewFlow;\n\nmodule.exports.default_configuration = _DefaultConfiguration;\n","module.exports={\n \"name\": \"pict-view\",\n \"version\": \"1.0.67\",\n \"description\": \"Pict View Base Class\",\n \"main\": \"source/Pict-View.js\",\n \"scripts\": {\n \"test\": \"npx quack test\",\n \"tests\": \"npx quack test -g\",\n \"start\": \"node source/Pict-View.js\",\n \"coverage\": \"npx quack coverage\",\n \"build\": \"npx quack build\",\n \"docker-dev-build\": \"docker build ./ -f Dockerfile_LUXURYCode -t pict-view-image:local\",\n \"docker-dev-run\": \"docker run -it -d --name pict-view-dev -p 30001:8080 -p 38086:8086 -v \\\"$PWD/.config:/home/coder/.config\\\" -v \\\"$PWD:/home/coder/pict-view\\\" -u \\\"$(id -u):$(id -g)\\\" -e \\\"DOCKER_USER=$USER\\\" pict-view-image:local\",\n \"docker-dev-shell\": \"docker exec -it pict-view-dev /bin/bash\",\n \"types\": \"tsc -p .\",\n \"lint\": \"eslint source/**\"\n },\n \"types\": \"types/source/Pict-View.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/stevenvelozo/pict-view.git\"\n },\n \"author\": \"steven velozo <steven@velozo.com>\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/stevenvelozo/pict-view/issues\"\n },\n \"homepage\": \"https://github.com/stevenvelozo/pict-view#readme\",\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.1\",\n \"browser-env\": \"^3.3.0\",\n \"eslint\": \"^9.39.1\",\n \"pict\": \"^1.0.348\",\n \"quackage\": \"^1.0.58\",\n \"typescript\": \"^5.9.3\"\n },\n \"mocha\": {\n \"diff\": true,\n \"extension\": [\n \"js\"\n ],\n \"package\": \"./package.json\",\n \"reporter\": \"spec\",\n \"slow\": \"75\",\n \"timeout\": \"5000\",\n \"ui\": \"tdd\",\n \"watch-files\": [\n \"source/**/*.js\",\n \"test/**/*.js\"\n ],\n \"watch-ignore\": [\n \"lib/vendor\"\n ]\n },\n \"dependencies\": {\n \"fable\": \"^3.1.63\",\n \"fable-serviceproviderbase\": \"^3.0.19\"\n }\n}\n","\nconst libFableServiceBase = require('fable-serviceproviderbase');\n\nconst libPackage = require('../package.json');\n\nconst defaultPictViewSettings = (\n\t{\n\t\tDefaultRenderable: false,\n\t\tDefaultDestinationAddress: false,\n\t\tDefaultTemplateRecordAddress: false,\n\n\t\tViewIdentifier: false,\n\n\t\t// If this is set to true, when the App initializes this will.\n\t\t// After the App initializes, initialize will be called as soon as it's added.\n\t\tAutoInitialize: true,\n\t\tAutoInitializeOrdinal: 0,\n\n\t\t// If this is set to true, when the App autorenders (on load) this will.\n\t\t// After the App initializes, render will be called as soon as it's added.\n\t\tAutoRender: true,\n\t\tAutoRenderOrdinal: 0,\n\n\t\tAutoSolveWithApp: true,\n\t\tAutoSolveOrdinal: 0,\n\n\t\tCSSHash: false,\n\t\tCSS: false,\n\t\tCSSProvider: false,\n\t\tCSSPriority: 500,\n\n\t\tTemplates: [],\n\n\t\tDefaultTemplates: [],\n\n\t\tRenderables: [],\n\n\t\tManifests: {}\n\t});\n\n/** @typedef {(error?: Error) => void} ErrorCallback */\n/** @typedef {number | boolean} PictTimestamp */\n\n/**\n * @typedef {'replace' | 'append' | 'prepend' | 'append_once' | 'virtual-assignment'} RenderMethod\n */\n/**\n * @typedef {Object} Renderable\n *\n * @property {string} RenderableHash - A unique hash for the renderable.\n * @property {string} TemplateHash - The hash of the template to use for rendering this renderable.\n * @property {string} [DefaultTemplateRecordAddress] - The default address for resolving the data record for this renderable.\n * @property {string} [ContentDestinationAddress] - The default address (DOM CSS selector) for rendering the content of this renderable.\n * @property {RenderMethod} [RenderMethod=replace] - The method to use when projecting the renderable to the DOM ('replace', 'append', 'prepend', 'append_once', 'virtual-assignment').\n * @property {string} [TestAddress] - The address to use for testing the renderable.\n * @property {string} [TransactionHash] - The transaction hash for the root renderable.\n * @property {string} [RootRenderableViewHash] - The hash of the root renderable.\n * @property {string} [Content] - The rendered content for this renderable, if applicable.\n */\n\n/**\n * Represents a view in the Pict ecosystem.\n */\nclass PictView extends libFableServiceBase\n{\n\t/**\n\t * @param {any} pFable - The Fable object that this service is attached to.\n\t * @param {any} [pOptions] - (optional) The options for this service.\n\t * @param {string} [pServiceHash] - (optional) The hash of the service.\n\t */\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\t// Intersect default options, parent constructor, service information\n\t\tlet tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(defaultPictViewSettings)), pOptions);\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\t\t//FIXME: add types to fable and ancillaries\n\t\t/** @type {any} */\n\t\tthis.fable;\n\t\t/** @type {any} */\n\t\tthis.options;\n\t\t/** @type {String} */\n\t\tthis.UUID;\n\t\t/** @type {String} */\n\t\tthis.Hash;\n\t\t/** @type {any} */\n\t\tthis.log;\n\n\t\tconst tmpHashIsUUID = this.Hash === this.UUID;\n\t\t//NOTE: since many places are using the view UUID as the HTML element ID, we prefix it to avoid starting with a number\n\t\tthis.UUID = `V-${this.UUID}`;\n\t\tif (tmpHashIsUUID)\n\t\t{\n\t\t\tthis.Hash = this.UUID;\n\t\t}\n\n\t\tif (!this.options.ViewIdentifier)\n\t\t{\n\t\t\tthis.options.ViewIdentifier = `AutoViewID-${this.fable.getUUID()}`;\n\t\t}\n\t\tthis.serviceType = 'PictView';\n\t\t/** @type {Record<string, any>} */\n\t\tthis._Package = libPackage;\n\t\t// Convenience and consistency naming\n\t\t/** @type {import('pict') & { log: any, instantiateServiceProviderWithoutRegistration: (hash: String) => any, instantiateServiceProviderIfNotExists: (hash: string) => any, TransactionTracking: import('pict/types/source/services/Fable-Service-TransactionTracking') }} */\n\t\tthis.pict = this.fable;\n\t\t// Wire in the essential Pict application state\n\t\tthis.AppData = this.pict.AppData;\n\t\tthis.Bundle = this.pict.Bundle;\n\n\t\t/** @type {PictTimestamp} */\n\t\tthis.initializeTimestamp = false;\n\t\t/** @type {PictTimestamp} */\n\t\tthis.lastSolvedTimestamp = false;\n\t\t/** @type {PictTimestamp} */\n\t\tthis.lastRenderedTimestamp = false;\n\t\t/** @type {PictTimestamp} */\n\t\tthis.lastMarshalFromViewTimestamp = false;\n\t\t/** @type {PictTimestamp} */\n\t\tthis.lastMarshalToViewTimestamp = false;\n\n\t\tthis.pict.instantiateServiceProviderIfNotExists('TransactionTracking');\n\n\t\t// Load all templates from the array in the options\n\t\t// Templates are in the form of {Hash:'Some-Template-Hash',Template:'Template content',Source:'TemplateSource'}\n\t\tfor (let i = 0; i < this.options.Templates.length; i++)\n\t\t{\n\t\t\tlet tmpTemplate = this.options.Templates[i];\n\n\t\t\tif (!('Hash' in tmpTemplate) || !('Template' in tmpTemplate))\n\t\t\t{\n\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not load Template ${i} in the options array.`, tmpTemplate);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!tmpTemplate.Source)\n\t\t\t\t{\n\t\t\t\t\ttmpTemplate.Source = `PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} options object.`;\n\t\t\t\t}\n\t\t\t\tthis.pict.TemplateProvider.addTemplate(tmpTemplate.Hash, tmpTemplate.Template, tmpTemplate.Source);\n\t\t\t}\n\t\t}\n\n\t\t// Load all default templates from the array in the options\n\t\t// Templates are in the form of {Prefix:'',Postfix:'-List-Row',Template:'Template content',Source:'TemplateSourceString'}\n\t\tfor (let i = 0; i < this.options.DefaultTemplates.length; i++)\n\t\t{\n\t\t\tlet tmpDefaultTemplate = this.options.DefaultTemplates[i];\n\n\t\t\tif (!('Postfix' in tmpDefaultTemplate) || !('Template' in tmpDefaultTemplate))\n\t\t\t{\n\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not load Default Template ${i} in the options array.`, tmpDefaultTemplate);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!tmpDefaultTemplate.Source)\n\t\t\t\t{\n\t\t\t\t\ttmpDefaultTemplate.Source = `PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} options object.`;\n\t\t\t\t}\n\t\t\t\tthis.pict.TemplateProvider.addDefaultTemplate(tmpDefaultTemplate.Prefix, tmpDefaultTemplate.Postfix, tmpDefaultTemplate.Template, tmpDefaultTemplate.Source);\n\t\t\t}\n\t\t}\n\n\t\t// Load the CSS if it's available\n\t\tif (this.options.CSS)\n\t\t{\n\t\t\tlet tmpCSSHash = this.options.CSSHash ? this.options.CSSHash : `View-${this.options.ViewIdentifier}`;\n\t\t\tlet tmpCSSProvider = this.options.CSSProvider ? this.options.CSSProvider : tmpCSSHash;\n\t\t\tthis.pict.CSSMap.addCSS(tmpCSSHash, this.options.CSS, tmpCSSProvider, this.options.CSSPriority);\n\t\t}\n\n\t\t// Load all renderables\n\t\t// Renderables are launchable renderable instructions with templates\n\t\t// They look as such: {Identifier:'ContentEntry', TemplateHash:'Content-Entry-Section-Main', ContentDestinationAddress:'#ContentSection', RecordAddress:'AppData.Content.DefaultText', ManifestTransformation:'ManyfestHash', ManifestDestinationAddress:'AppData.Content.DataToTransformContent'}\n\t\t// The only parts that are necessary are Identifier and Template\n\t\t// A developer can then do render('ContentEntry') and it just kinda works. Or they can override the ContentDestinationAddress\n\t\t/** @type {Record<String, Renderable>} */\n\t\tthis.renderables = {};\n\t\tfor (let i = 0; i < this.options.Renderables.length; i++)\n\t\t{\n\t\t\t/** @type {Renderable} */\n\t\t\tlet tmpRenderable = this.options.Renderables[i];\n\t\t\tthis.addRenderable(tmpRenderable);\n\t\t}\n\t}\n\n\t/**\n\t * Adds a renderable to the view.\n\t *\n\t * @param {string | Renderable} pRenderableHash - The hash of the renderable, or a renderable object.\n\t * @param {string} [pTemplateHash] - (optional) The hash of the template for the renderable.\n\t * @param {string} [pDefaultTemplateRecordAddress] - (optional) The default data address for the template.\n\t * @param {string} [pDefaultDestinationAddress] - (optional) The default destination address for the renderable.\n\t * @param {RenderMethod} [pRenderMethod=replace] - (optional) The method to use when rendering the renderable (ex. 'replace').\n\t */\n\taddRenderable(pRenderableHash, pTemplateHash, pDefaultTemplateRecordAddress, pDefaultDestinationAddress, pRenderMethod)\n\t{\n\t\t/** @type {Renderable} */\n\t\tlet tmpRenderable;\n\n\t\tif (typeof(pRenderableHash) == 'object')\n\t\t{\n\t\t\t// The developer passed in the renderable as an object.\n\t\t\t// Use theirs instead!\n\t\t\ttmpRenderable = pRenderableHash;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/** @type {RenderMethod} */\n\t\t\tlet tmpRenderMethod = (typeof(pRenderMethod) !== 'string') ? pRenderMethod : 'replace';\n\t\t\ttmpRenderable = (\n\t\t\t\t{\n\t\t\t\t\tRenderableHash: pRenderableHash,\n\t\t\t\t\tTemplateHash: pTemplateHash,\n\t\t\t\t\tDefaultTemplateRecordAddress: pDefaultTemplateRecordAddress,\n\t\t\t\t\tContentDestinationAddress: pDefaultDestinationAddress,\n\t\t\t\t\tRenderMethod: tmpRenderMethod\n\t\t\t\t});\n\t\t}\n\n\t\tif ((typeof(tmpRenderable.RenderableHash) != 'string') || (typeof(tmpRenderable.TemplateHash) != 'string'))\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not load Renderable; RenderableHash or TemplateHash are invalid.`, tmpRenderable);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t{\n\t\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} adding renderable [${tmpRenderable.RenderableHash}] pointed to template ${tmpRenderable.TemplateHash}.`);\n\t\t\t}\n\n\t\t\tthis.renderables[tmpRenderable.RenderableHash] = tmpRenderable;\n\t\t}\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Initialization */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before the view is initialized.\n\t */\n\tonBeforeInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is initialized (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonBeforeInitializeAsync(fCallback)\n\t{\n\t\tthis.onBeforeInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when the view is initialized.\n\t */\n\tonInitialize()\n\t{\n\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when the view is initialized (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonInitializeAsync(fCallback)\n\t{\n\t\tthis.onInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Performs view initialization.\n\t */\n\tinitialize()\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow VIEW [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initialize:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tthis.onBeforeInitialize();\n\t\t\tthis.onInitialize();\n\t\t\tthis.onAfterInitialize();\n\t\t\tthis.initializeTimestamp = this.pict.log.getTimeStamp();\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initialize called but initialization is already completed. Aborting.`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Performs view initialization (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tinitializeAsync(fCallback)\n\t{\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow VIEW [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initializeAsync:`);\n\t\t}\n\n\t\tif (!this.initializeTimestamp)\n\t\t{\n\t\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t{\n\t\t\t\tthis.log.info(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} beginning initialization...`);\n\t\t\t}\n\n\t\t\ttmpAnticipate.anticipate(this.onBeforeInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onInitializeAsync.bind(this));\n\t\t\ttmpAnticipate.anticipate(this.onAfterInitializeAsync.bind(this));\n\n\t\t\ttmpAnticipate.wait(\n\t\t\t\t/** @param {Error} pError */\n\t\t\t\t(pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initialization failed: ${pError.message || pError}`, { stack: pError.stack });\n\t\t\t\t\t}\n\t\t\t\t\tthis.initializeTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\t\tif (this.pict.LogNoisiness > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.info(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} initialization complete.`);\n\t\t\t\t\t}\n\t\t\t\t\treturn fCallback();\n\t\t\t\t});\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} async initialize called but initialization is already completed. Aborting.`);\n\t\t\t// TODO: Should this be an error?\n\t\t\treturn fCallback();\n\t\t}\n\t}\n\n\tonAfterInitialize()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterInitialize:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is initialized (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonAfterInitializeAsync(fCallback)\n\t{\n\t\tthis.onAfterInitialize();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Render */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before the view is rendered.\n\t *\n\t * @param {Renderable} pRenderable - The renderable that will be rendered.\n\t */\n\tonBeforeRender(pRenderable)\n\t{\n\t\t// Overload this to mess with stuff before the content gets generated from the template\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeRender:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is rendered (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that will be rendered.\n\t */\n\tonBeforeRenderAsync(fCallback, pRenderable)\n\t{\n\t\tthis.onBeforeRender(pRenderable);\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is projected into the DOM.\n\t *\n\t * @param {Renderable} pRenderable - The renderable that will be projected.\n\t */\n\tonBeforeProject(pRenderable)\n\t{\n\t\t// Overload this to mess with stuff before the content gets generated from the template\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeProject:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is projected into the DOM (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that will be projected.\n\t */\n\tonBeforeProjectAsync(fCallback, pRenderable)\n\t{\n\t\tthis.onBeforeProject(pRenderable);\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Builds the render options for a renderable.\n\t *\n\t * For DRY purposes on the three flavors of render.\n\t *\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object|ErrorCallback} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t */\n\tbuildRenderOptions(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\tlet tmpRenderOptions = {Valid: true};\n\t\ttmpRenderOptions.RenderableHash = (typeof (pRenderableHash) === 'string') ? pRenderableHash :\n\t\t\t\t\t\t\t\t(typeof (this.options.DefaultRenderable) == 'string') ?\n\t\t\t\t\t\t\t\tthis.options.DefaultRenderable : false;\n\t\tif (!tmpRenderOptions.RenderableHash)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not find a suitable RenderableHash ${tmpRenderOptions.RenderableHash} (param ${pRenderableHash}because it is not a valid renderable.`);\n\t\t\ttmpRenderOptions.Valid = false;\n\t\t}\n\n\t\ttmpRenderOptions.Renderable = this.renderables[tmpRenderOptions.RenderableHash];\n\t\tif (!tmpRenderOptions.Renderable)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderOptions.RenderableHash} (param ${pRenderableHash}) because it does not exist.`);\n\t\t\ttmpRenderOptions.Valid = false;\n\t\t}\n\n\t\ttmpRenderOptions.DestinationAddress = (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress :\n\t\t\t(typeof (tmpRenderOptions.Renderable.ContentDestinationAddress) === 'string') ? tmpRenderOptions.Renderable.ContentDestinationAddress :\n\t\t\t\t(typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : false;\n\t\tif (!tmpRenderOptions.DestinationAddress)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderOptions.RenderableHash} (param ${pRenderableHash}) because it does not have a valid destination address (param ${pRenderDestinationAddress}).`);\n\t\t\ttmpRenderOptions.Valid = false;\n\t\t}\n\n\t\tif (typeof(pTemplateRecordAddress) === 'object')\n\t\t{\n\t\t\ttmpRenderOptions.RecordAddress = 'Passed in as object';\n\t\t\ttmpRenderOptions.Record = pTemplateRecordAddress;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRenderOptions.RecordAddress = (typeof (pTemplateRecordAddress) === 'string') ? pTemplateRecordAddress :\n\t\t\t\t(typeof (tmpRenderOptions.Renderable.DefaultTemplateRecordAddress) === 'string') ? tmpRenderOptions.Renderable.DefaultTemplateRecordAddress :\n\t\t\t\t(typeof (this.options.DefaultTemplateRecordAddress) === 'string') ? this.options.DefaultTemplateRecordAddress : false;\n\t\t\ttmpRenderOptions.Record = (typeof (tmpRenderOptions.RecordAddress) === 'string') ? this.pict.DataProvider.getDataByAddress(tmpRenderOptions.RecordAddress) : undefined;\n\t\t}\n\n\t\treturn tmpRenderOptions;\n\t}\n\n\t/**\n\t * Assigns the content to the destination address.\n\t *\n\t * For DRY purposes on the three flavors of render.\n\t *\n\t * @param {Renderable} pRenderable - The renderable to render.\n\t * @param {string} pRenderDestinationAddress - The address where the renderable will be rendered.\n\t * @param {string} pContent - The content to render.\n\t * @returns {boolean} - Returns true if the content was assigned successfully.\n\t * @memberof PictView\n\t */\n\tassignRenderContent(pRenderable, pRenderDestinationAddress, pContent)\n\t{\n\t\treturn this.pict.ContentAssignment.projectContent(pRenderable.RenderMethod, pRenderDestinationAddress, pContent, pRenderable.TestAddress);\n\t}\n\n\t/**\n\t * Render a renderable from this view.\n\t *\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object} [pTemplateRecordAddress] - The address where the data for the template is stored.\n\t * @param {Renderable} [pRootRenderable] - The root renderable for the render operation, if applicable.\n\t * @return {boolean}\n\t */\n\trender(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable)\n\t{\n\t\treturn this.renderWithScope(this, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable);\n\t}\n\n\t/**\n\t * Render a renderable from this view, providing a specifici scope for the template.\n\t *\n\t * @param {any} pScope - The scope to use for the template rendering.\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object} [pTemplateRecordAddress] - The address where the data for the template is stored.\n\t * @param {Renderable} [pRootRenderable] - The root renderable for the render operation, if applicable.\n\t * @return {boolean}\n\t */\n\trenderWithScope(pScope, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable)\n\t{\n\t\tlet tmpRenderableHash = (typeof (pRenderableHash) === 'string') ? pRenderableHash :\n\t\t\t(typeof (this.options.DefaultRenderable) == 'string') ? this.options.DefaultRenderable : false;\n\t\tif (!tmpRenderableHash)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it is not a valid renderable.`);\n\t\t\treturn false;\n\t\t}\n\n\t\t/** @type {Renderable} */\n\t\tlet tmpRenderable;\n\t\tif (tmpRenderableHash == '__Virtual')\n\t\t{\n\t\t\ttmpRenderable = {\n\t\t\t\tRenderableHash: '__Virtual',\n\t\t\t\tTemplateHash: this.renderables[this.options.DefaultRenderable].TemplateHash,\n\t\t\t\tContentDestinationAddress: (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress :\n\t\t\t\t\t(typeof (tmpRenderable.ContentDestinationAddress) === 'string') ? tmpRenderable.ContentDestinationAddress :\n\t\t\t\t\t(typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : null,\n\t\t\t\tRenderMethod: 'virtual-assignment',\n\t\t\t\tTransactionHash: pRootRenderable && pRootRenderable.TransactionHash,\n\t\t\t\tRootRenderableViewHash: pRootRenderable && pRootRenderable.RootRenderableViewHash,\n\t\t\t};\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRenderable = Object.assign({}, this.renderables[tmpRenderableHash]);\n\t\t\ttmpRenderable.ContentDestinationAddress = (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress :\n\t\t\t\t(typeof (tmpRenderable.ContentDestinationAddress) === 'string') ? tmpRenderable.ContentDestinationAddress :\n\t\t\t\t(typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : null;\n\t\t}\n\n\t\tif (!tmpRenderable.TransactionHash)\n\t\t{\n\t\t\ttmpRenderable.TransactionHash = `ViewRender-V-${this.options.ViewIdentifier}-R-${tmpRenderableHash}-U-${this.pict.getUUID()}`;\n\t\t\ttmpRenderable.RootRenderableViewHash = this.Hash;\n\t\t\tthis.pict.TransactionTracking.registerTransaction(tmpRenderable.TransactionHash);\n\t\t}\n\n\t\tif (!tmpRenderable)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not exist.`);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!tmpRenderable.ContentDestinationAddress)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not have a valid destination address.`);\n\t\t\treturn false;\n\t\t}\n\n\t\tlet tmpRecordAddress;\n\t\tlet tmpRecord;\n\n\t\tif (typeof(pTemplateRecordAddress) === 'object')\n\t\t{\n\t\t\ttmpRecord = pTemplateRecordAddress;\n\t\t\ttmpRecordAddress = 'Passed in as object';\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRecordAddress = (typeof (pTemplateRecordAddress) === 'string') ? pTemplateRecordAddress :\n\t\t\t\t(typeof (tmpRenderable.DefaultTemplateRecordAddress) === 'string') ? tmpRenderable.DefaultTemplateRecordAddress :\n\t\t\t\t\t(typeof (this.options.DefaultTemplateRecordAddress) === 'string') ? this.options.DefaultTemplateRecordAddress : false;\n\n\t\t\ttmpRecord = (typeof (tmpRecordAddress) === 'string') ? this.pict.DataProvider.getDataByAddress(tmpRecordAddress) : undefined;\n\t\t}\n\n\t\t// Execute the developer-overridable pre-render behavior\n\t\tthis.onBeforeRender(tmpRenderable);\n\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow VIEW [${this.UUID}]::[${this.Hash}] Renderable[${tmpRenderableHash}] Destination[${tmpRenderable.ContentDestinationAddress}] TemplateRecordAddress[${tmpRecordAddress}] render:`);\n\t\t}\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} Beginning Render of Renderable[${tmpRenderableHash}] to Destination [${tmpRenderable.ContentDestinationAddress}]...`);\n\t\t}\n\t\t// Generate the content output from the template and data\n\t\ttmpRenderable.Content = this.pict.parseTemplateByHash(tmpRenderable.TemplateHash, tmpRecord, null, [this], pScope, { RootRenderable: typeof pRootRenderable === 'object' ? pRootRenderable : tmpRenderable });\n\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} Assigning Renderable[${tmpRenderableHash}] content length ${tmpRenderable.Content.length} to Destination [${tmpRenderable.ContentDestinationAddress}] using render method [${tmpRenderable.RenderMethod}].`);\n\t\t}\n\n\t\tthis.onBeforeProject(tmpRenderable);\n\t\tthis.onProject(tmpRenderable);\n\n\t\tif (tmpRenderable.RenderMethod !== 'virtual-assignment')\n\t\t{\n\t\t\tthis.onAfterProject(tmpRenderable);\n\n\t\t\t// Execute the developer-overridable post-render behavior\n\t\t\tthis.onAfterRender(tmpRenderable);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Render a renderable from this view.\n\t *\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object|ErrorCallback} [pTemplateRecordAddress] - The address where the data for the template is stored.\n\t * @param {Renderable|ErrorCallback} [pRootRenderable] - The root renderable for the render operation, if applicable.\n\t * @param {ErrorCallback} [fCallback] - The callback to call when the async operation is complete.\n\t *\n\t * @return {void}\n\t */\n\trenderAsync(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback)\n\t{\n\t\treturn this.renderWithScopeAsync(this, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback);\n\t}\n\n\t/**\n\t * Render a renderable from this view.\n\t *\n\t * @param {any} pScope - The scope to use for the template rendering.\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object|ErrorCallback} [pTemplateRecordAddress] - The address where the data for the template is stored.\n\t * @param {Renderable|ErrorCallback} [pRootRenderable] - The root renderable for the render operation, if applicable.\n\t * @param {ErrorCallback} [fCallback] - The callback to call when the async operation is complete.\n\t *\n\t * @return {void}\n\t */\n\trenderWithScopeAsync(pScope, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, pRootRenderable, fCallback)\n\t{\n\t\tlet tmpRenderableHash = (typeof (pRenderableHash) === 'string') ? pRenderableHash :\n\t\t\t(typeof (this.options.DefaultRenderable) == 'string') ? this.options.DefaultRenderable : false;\n\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback :\n\t\t\t\t\t\t\t(typeof(pTemplateRecordAddress) === 'function') ? pTemplateRecordAddress :\n\t\t\t\t\t\t\t(typeof(pRenderDestinationAddress) === 'function') ? pRenderDestinationAddress :\n\t\t\t\t\t\t\t(typeof(pRenderableHash) === 'function') ? pRenderableHash :\n\t\t\t\t\t\t\t(typeof(pRootRenderable) === 'function') ? pRootRenderable :\n\t\t\t\t\t\t\tnull;\n\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} renderAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tif (!tmpRenderableHash)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not asynchronously render ${tmpRenderableHash} (param ${pRenderableHash}because it is not a valid renderable.`);\n\t\t\treturn tmpCallback(new Error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not asynchronously render ${tmpRenderableHash} (param ${pRenderableHash}because it is not a valid renderable.`));\n\t\t}\n\n\t\t/** @type {Renderable} */\n\t\tlet tmpRenderable;\n\t\tif (tmpRenderableHash == '__Virtual')\n\t\t{\n\t\t\ttmpRenderable = {\n\t\t\t\tRenderableHash: '__Virtual',\n\t\t\t\tTemplateHash: this.renderables[this.options.DefaultRenderable].TemplateHash,\n\t\t\t\tContentDestinationAddress: (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress : (typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : null,\n\t\t\t\tRenderMethod: 'virtual-assignment',\n\t\t\t\tTransactionHash: pRootRenderable && typeof pRootRenderable !== 'function' && pRootRenderable.TransactionHash,\n\t\t\t\tRootRenderableViewHash: pRootRenderable && typeof pRootRenderable !== 'function' && pRootRenderable.RootRenderableViewHash,\n\t\t\t};\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRenderable = Object.assign({}, this.renderables[tmpRenderableHash]);\n\t\t\ttmpRenderable.ContentDestinationAddress = (typeof (pRenderDestinationAddress) === 'string') ? pRenderDestinationAddress :\n\t\t\t\t(typeof (tmpRenderable.ContentDestinationAddress) === 'string') ? tmpRenderable.ContentDestinationAddress :\n\t\t\t\t(typeof (this.options.DefaultDestinationAddress) === 'string') ? this.options.DefaultDestinationAddress : null;\n\t\t}\n\n\t\tif (!tmpRenderable.TransactionHash)\n\t\t{\n\t\t\ttmpRenderable.TransactionHash = `ViewRender-V-${this.options.ViewIdentifier}-R-${tmpRenderableHash}-U-${this.pict.getUUID()}`;\n\t\t\ttmpRenderable.RootRenderableViewHash = this.Hash;\n\t\t\tthis.pict.TransactionTracking.registerTransaction(tmpRenderable.TransactionHash);\n\t\t}\n\n\t\tif (!tmpRenderable)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not exist.`);\n\t\t\treturn tmpCallback(new Error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not exist.`));\n\t\t}\n\n\t\tif (!tmpRenderable.ContentDestinationAddress)\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render ${tmpRenderableHash} (param ${pRenderableHash}) because it does not have a valid destination address.`);\n\t\t\treturn tmpCallback(new Error(`Could not render ${tmpRenderableHash}`));\n\t\t}\n\n\t\tlet tmpRecordAddress;\n\t\tlet tmpRecord;\n\n\t\tif (typeof(pTemplateRecordAddress) === 'object')\n\t\t{\n\t\t\ttmpRecord = pTemplateRecordAddress;\n\t\t\ttmpRecordAddress = 'Passed in as object';\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpRecordAddress = (typeof (pTemplateRecordAddress) === 'string') ? pTemplateRecordAddress :\n\t\t\t\t(typeof (tmpRenderable.DefaultTemplateRecordAddress) === 'string') ? tmpRenderable.DefaultTemplateRecordAddress :\n\t\t\t\t\t(typeof (this.options.DefaultTemplateRecordAddress) === 'string') ? this.options.DefaultTemplateRecordAddress : false;\n\n\t\t\ttmpRecord = (typeof (tmpRecordAddress) === 'string') ? this.pict.DataProvider.getDataByAddress(tmpRecordAddress) : undefined;\n\t\t}\n\n\t\tif (this.pict.LogControlFlow)\n\t\t{\n\t\t\tthis.log.trace(`PICT-ControlFlow VIEW [${this.UUID}]::[${this.Hash}] Renderable[${tmpRenderableHash}] Destination[${tmpRenderable.ContentDestinationAddress}] TemplateRecordAddress[${tmpRecordAddress}] renderAsync:`);\n\t\t}\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} Beginning Asynchronous Render (callback-style)...`);\n\t\t}\n\n\t\tlet tmpAnticipate = this.fable.newAnticipate();\n\n\t\ttmpAnticipate.anticipate(\n\t\t\t(fOnBeforeRenderCallback) =>\n\t\t\t{\n\t\t\t\tthis.onBeforeRenderAsync(fOnBeforeRenderCallback, tmpRenderable);\n\t\t\t});\n\n\t\ttmpAnticipate.anticipate(\n\t\t\t(fAsyncTemplateCallback) =>\n\t\t\t{\n\t\t\t\t// Render the template (asynchronously)\n\t\t\t\tthis.pict.parseTemplateByHash(tmpRenderable.TemplateHash, tmpRecord,\n\t\t\t\t\t(pError, pContent) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pError)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render (asynchronously) ${tmpRenderableHash} (param ${pRenderableHash}) because it did not parse the template.`, pError);\n\t\t\t\t\t\t\treturn fAsyncTemplateCallback(pError);\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttmpRenderable.Content = pContent;\n\n\t\t\t\t\t\treturn fAsyncTemplateCallback();\n\t\t\t\t\t}, [this], pScope, { RootRenderable: typeof pRootRenderable === 'object' ? pRootRenderable : tmpRenderable });\n\t\t\t});\n\n\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t{\n\t\t\tthis.onBeforeProjectAsync(fNext, tmpRenderable);\n\t\t});\n\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t{\n\t\t\tthis.onProjectAsync(fNext, tmpRenderable);\n\t\t});\n\n\t\tif (tmpRenderable.RenderMethod !== 'virtual-assignment')\n\t\t{\n\t\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t\t{\n\t\t\t\tthis.onAfterProjectAsync(fNext, tmpRenderable);\n\t\t\t});\n\n\t\t\t// Execute the developer-overridable post-render behavior\n\t\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t\t{\n\t\t\t\tthis.onAfterRenderAsync(fNext, tmpRenderable);\n\t\t\t});\n\t\t}\n\n\t\ttmpAnticipate.wait(tmpCallback);\n\t}\n\n\t/**\n\t * Renders the default renderable.\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\trenderDefaultAsync(fCallback)\n\t{\n\t\t// Render the default renderable\n\t\tthis.renderAsync(fCallback);\n\t}\n\n\t/**\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t */\n\tbasicRender(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\treturn this.basicRenderWithScope(this, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress);\n\t}\n\n\t/**\n\t * @param {any} pScope - The scope to use for the template rendering.\n\t * @param {string} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|object} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t */\n\tbasicRenderWithScope(pScope, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)\n\t{\n\t\tlet tmpRenderOptions = this.buildRenderOptions(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress);\n\t\tif (tmpRenderOptions.Valid)\n\t\t{\n\t\t\tthis.assignRenderContent(tmpRenderOptions.Renderable, tmpRenderOptions.DestinationAddress, this.pict.parseTemplateByHash(tmpRenderOptions.Renderable.TemplateHash, tmpRenderOptions.Record, null, [this], pScope, { RootRenderable: tmpRenderOptions.Renderable }));\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not perform a basic render of ${tmpRenderOptions.RenderableHash} because it is not valid.`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|Object|ErrorCallback} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t * @param {ErrorCallback} [fCallback] - The callback to call when the async operation is complete.\n\t */\n\tbasicRenderAsync(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, fCallback)\n\t{\n\t\treturn this.basicRenderWithScopeAsync(this, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, fCallback);\n\t}\n\n\t/**\n\t * @param {any} pScope - The scope to use for the template rendering.\n\t * @param {string|ErrorCallback} [pRenderableHash] - The hash of the renderable to render.\n\t * @param {string|ErrorCallback} [pRenderDestinationAddress] - The address where the renderable will be rendered.\n\t * @param {string|Object|ErrorCallback} [pTemplateRecordAddress] - The address of (or actual obejct) where the data for the template is stored.\n\t * @param {ErrorCallback} [fCallback] - The callback to call when the async operation is complete.\n\t */\n\tbasicRenderWithScopeAsync(pScope, pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress, fCallback)\n\t{\n\t\t// Allow the callback to be passed in as the last parameter no matter what\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback :\n\t\t\t\t\t\t\t(typeof(pTemplateRecordAddress) === 'function') ? pTemplateRecordAddress :\n\t\t\t\t\t\t\t(typeof(pRenderDestinationAddress) === 'function') ? pRenderDestinationAddress :\n\t\t\t\t\t\t\t(typeof(pRenderableHash) === 'function') ? pRenderableHash :\n\t\t\t\t\t\t\tnull;\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} basicRenderAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} basicRenderAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\tconst tmpRenderOptions = this.buildRenderOptions(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress);\n\t\tif (tmpRenderOptions.Valid)\n\t\t{\n\t\t\tthis.pict.parseTemplateByHash(tmpRenderOptions.Renderable.TemplateHash, tmpRenderOptions.Record,\n\t\t\t\t/**\n\t\t\t\t * @param {Error} [pError] - The error that occurred during template parsing.\n\t\t\t\t * @param {string} [pContent] - The content that was rendered from the template.\n\t\t\t\t */\n\t\t\t\t(pError, pContent) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not render (asynchronously) ${tmpRenderOptions.RenderableHash} because it did not parse the template.`, pError);\n\t\t\t\t\t\treturn tmpCallback(pError);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.assignRenderContent(tmpRenderOptions.Renderable, tmpRenderOptions.DestinationAddress, pContent);\n\t\t\t\t\treturn tmpCallback();\n\t\t\t\t}, [this], pScope, { RootRenderable: tmpRenderOptions.Renderable });\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlet tmpErrorMessage = `PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} could not perform a basic render of ${tmpRenderOptions.RenderableHash} because it is not valid.`;\n\t\t\tthis.log.error(tmpErrorMessage);\n\t\t\treturn tmpCallback(new Error(tmpErrorMessage));\n\t\t}\n\t}\n\n\t/**\n\t * @param {Renderable} pRenderable - The renderable that was rendered.\n\t */\n\tonProject(pRenderable)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onProject:`);\n\t\t}\n\t\tif (pRenderable.RenderMethod === 'virtual-assignment')\n\t\t{\n\t\t\tthis.pict.TransactionTracking.pushToTransactionQueue(pRenderable.TransactionHash, { ViewHash: this.Hash, Renderable: pRenderable }, 'Deferred-Post-Content-Assignment');\n\t\t}\n\n\t\tif (this.pict.LogNoisiness > 0)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} Assigning Renderable[${pRenderable.RenderableHash}] content length ${pRenderable.Content.length} to Destination [${pRenderable.ContentDestinationAddress}] using Async render method ${pRenderable.RenderMethod}.`);\n\t\t}\n\n\t\t// Assign the content to the destination address\n\t\tthis.pict.ContentAssignment.projectContent(pRenderable.RenderMethod, pRenderable.ContentDestinationAddress, pRenderable.Content, pRenderable.TestAddress);\n\n\t\tthis.lastRenderedTimestamp = this.pict.log.getTimeStamp();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is projected into the DOM (async flow).\n\t *\n\t * @param {(error?: Error, content?: string) => void} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that is being projected.\n\t */\n\tonProjectAsync(fCallback, pRenderable)\n\t{\n\t\tthis.onProject(pRenderable);\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is rendered.\n\t *\n\t * @param {Renderable} pRenderable - The renderable that was rendered.\n\t */\n\tonAfterRender(pRenderable)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterRender:`);\n\t\t}\n\t\tif (pRenderable && pRenderable.RootRenderableViewHash === this.Hash)\n\t\t{\n\t\t\tconst tmpTransactionQueue = this.pict.TransactionTracking.clearTransactionQueue(pRenderable.TransactionHash) || [];\n\t\t\tfor (const tmpEvent of tmpTransactionQueue)\n\t\t\t{\n\t\t\t\tconst tmpView = this.pict.views[tmpEvent.Data.ViewHash];\n\t\t\t\tif (!tmpView)\n\t\t\t\t{\n\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterRender: Could not find view for transaction hash ${pRenderable.TransactionHash} and ViewHash ${tmpEvent.Data.ViewHash}.`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttmpView.onAfterProject();\n\n\t\t\t\t// Execute the developer-overridable post-render behavior\n\t\t\t\ttmpView.onAfterRender(tmpEvent.Data.Renderable);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is rendered (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that was rendered.\n\t */\n\tonAfterRenderAsync(fCallback, pRenderable)\n\t{\n\t\tthis.onAfterRender(pRenderable);\n\t\tconst tmpAnticipate = this.fable.newAnticipate();\n\t\tif (pRenderable && pRenderable.RootRenderableViewHash === this.Hash)\n\t\t{\n\t\t\tconst queue = this.pict.TransactionTracking.clearTransactionQueue(pRenderable.TransactionHash) || [];\n\t\t\tfor (const event of queue)\n\t\t\t{\n\t\t\t\t/** @type {PictView} */\n\t\t\t\tconst tmpView = this.pict.views[event.Data.ViewHash];\n\t\t\t\tif (!tmpView)\n\t\t\t\t{\n\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterRenderAsync: Could not find view for transaction hash ${pRenderable.TransactionHash} and ViewHash ${event.Data.ViewHash}.`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttmpAnticipate.anticipate(tmpView.onAfterProjectAsync.bind(tmpView));\n\t\t\t\ttmpAnticipate.anticipate((fNext) =>\n\t\t\t\t{\n\t\t\t\t\ttmpView.onAfterRenderAsync(fNext, event.Data.Renderable);\n\t\t\t\t});\n\n\t\t\t\t// Execute the developer-overridable post-render behavior\n\t\t\t}\n\t\t}\n\t\treturn tmpAnticipate.wait(fCallback);\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is projected into the DOM.\n\t *\n\t * @param {Renderable} pRenderable - The renderable that was projected.\n\t */\n\tonAfterProject(pRenderable)\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterProject:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is projected into the DOM (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t * @param {Renderable} pRenderable - The renderable that was projected.\n\t */\n\tonAfterProjectAsync(fCallback, pRenderable)\n\t{\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Solver */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before the view is solved.\n\t */\n\tonBeforeSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before the view is solved (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonBeforeSolveAsync(fCallback)\n\t{\n\t\tthis.onBeforeSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when the view is solved.\n\t */\n\tonSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when the view is solved (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonSolveAsync(fCallback)\n\t{\n\t\tthis.onSolve();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Performs view solving and triggers lifecycle hooks.\n\t *\n\t * @return {boolean} - True if the view was solved successfully, false otherwise.\n\t */\n\tsolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} executing solve() function...`);\n\t\t}\n\t\tthis.onBeforeSolve();\n\t\tthis.onSolve();\n\t\tthis.onAfterSolve();\n\t\tthis.lastSolvedTimestamp = this.pict.log.getTimeStamp();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Performs view solving and triggers lifecycle hooks (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tsolveAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : null;\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} solveAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeSolveAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onSolveAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterSolveAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} solveAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastSolvedTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is solved.\n\t */\n\tonAfterSolve()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterSolve:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after the view is solved (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonAfterSolveAsync(fCallback)\n\t{\n\t\tthis.onAfterSolve();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Marshal From View */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before data is marshaled from the view.\n\t *\n\t * @return {boolean} - True if the operation was successful, false otherwise.\n\t */\n\tonBeforeMarshalFromView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeMarshalFromView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before data is marshaled from the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonBeforeMarshalFromViewAsync(fCallback)\n\t{\n\t\tthis.onBeforeMarshalFromView();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when data is marshaled from the view.\n\t */\n\tonMarshalFromView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onMarshalFromView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when data is marshaled from the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonMarshalFromViewAsync(fCallback)\n\t{\n\n\t\tthis.onMarshalFromView();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Marshals data from the view.\n\t *\n\t * @return {boolean} - True if the operation was successful, false otherwise.\n\t */\n\tmarshalFromView()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} executing solve() function...`);\n\t\t}\n\t\tthis.onBeforeMarshalFromView();\n\t\tthis.onMarshalFromView();\n\t\tthis.onAfterMarshalFromView();\n\t\tthis.lastMarshalFromViewTimestamp = this.pict.log.getTimeStamp();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Marshals data from the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tmarshalFromViewAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : null;\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalFromViewAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\n\t\ttmpAnticipate.anticipate(this.onBeforeMarshalFromViewAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onMarshalFromViewAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterMarshalFromViewAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} marshalFromViewAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastMarshalFromViewTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after data is marshaled from the view.\n\t */\n\tonAfterMarshalFromView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterMarshalFromView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after data is marshaled from the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonAfterMarshalFromViewAsync(fCallback)\n\t{\n\t\tthis.onAfterMarshalFromView();\n\t\treturn fCallback();\n\t}\n\n\t/* -------------------------------------------------------------------------- */\n\t/* Code Section: Marshal To View */\n\t/* -------------------------------------------------------------------------- */\n\t/**\n\t * Lifecycle hook that triggers before data is marshaled into the view.\n\t */\n\tonBeforeMarshalToView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onBeforeMarshalToView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers before data is marshaled into the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonBeforeMarshalToViewAsync(fCallback)\n\t{\n\t\tthis.onBeforeMarshalToView();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when data is marshaled into the view.\n\t */\n\tonMarshalToView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onMarshalToView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers when data is marshaled into the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonMarshalToViewAsync(fCallback)\n\t{\n\t\tthis.onMarshalToView();\n\t\treturn fCallback();\n\t}\n\n\t/**\n\t * Marshals data into the view.\n\t *\n\t * @return {boolean} - True if the operation was successful, false otherwise.\n\t */\n\tmarshalToView()\n\t{\n\t\tif (this.pict.LogNoisiness > 2)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} executing solve() function...`);\n\t\t}\n\t\tthis.onBeforeMarshalToView();\n\t\tthis.onMarshalToView();\n\t\tthis.onAfterMarshalToView();\n\t\tthis.lastMarshalToViewTimestamp = this.pict.log.getTimeStamp();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Marshals data into the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tmarshalToViewAsync(fCallback)\n\t{\n\t\tlet tmpAnticipate = this.pict.instantiateServiceProviderWithoutRegistration('Anticipate');\n\n\n\t\t/** @type {ErrorCallback} */\n\t\tlet tmpCallback = (typeof(fCallback) === 'function') ? fCallback : null;\n\t\tif (!tmpCallback)\n\t\t{\n\t\t\tthis.log.warn(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewAsync was called without a valid callback. A callback will be generated but this could lead to race conditions.`);\n\t\t\ttmpCallback = (pError) =>\n\t\t\t\t{\n\t\t\t\t\tif (pError)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.log.error(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.Name} marshalToViewAsync Auto Callback Error: ${pError}`, pError);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t}\n\t\ttmpAnticipate.anticipate(this.onBeforeMarshalToViewAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onMarshalToViewAsync.bind(this));\n\t\ttmpAnticipate.anticipate(this.onAfterMarshalToViewAsync.bind(this));\n\n\t\ttmpAnticipate.wait(\n\t\t\t(pError) =>\n\t\t\t{\n\t\t\t\tif (this.pict.LogNoisiness > 2)\n\t\t\t\t{\n\t\t\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} marshalToViewAsync() complete.`);\n\t\t\t\t}\n\t\t\t\tthis.lastMarshalToViewTimestamp = this.pict.log.getTimeStamp();\n\t\t\t\treturn tmpCallback(pError);\n\t\t\t});\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after data is marshaled into the view.\n\t */\n\tonAfterMarshalToView()\n\t{\n\t\tif (this.pict.LogNoisiness > 3)\n\t\t{\n\t\t\tthis.log.trace(`PictView [${this.UUID}]::[${this.Hash}] ${this.options.ViewIdentifier} onAfterMarshalToView:`);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Lifecycle hook that triggers after data is marshaled into the view (async flow).\n\t *\n\t * @param {ErrorCallback} fCallback - The callback to call when the async operation is complete.\n\t */\n\tonAfterMarshalToViewAsync(fCallback)\n\t{\n\t\tthis.onAfterMarshalToView();\n\t\treturn fCallback();\n\t}\n\n\t/** @return {boolean} - True if the object is a PictView. */\n\tget isPictView()\n\t{\n\t\treturn true;\n\t}\n}\n\nmodule.exports = PictView;\n","const MICRODDL_TYPE_MAP =\n{\n\t'@': { DataType: 'AutoIdentity', Label: 'Auto ID', HasSize: false },\n\t'%': { DataType: 'GUID', Label: 'GUID', HasSize: false },\n\t'$': { DataType: 'String', Label: 'String', HasSize: true },\n\t'*': { DataType: 'Text', Label: 'Text', HasSize: false },\n\t'#': { DataType: 'Numeric', Label: 'Numeric', HasSize: false },\n\t'.': { DataType: 'Decimal', Label: 'Decimal', HasSize: true },\n\t'&': { DataType: 'DateTime', Label: 'Date/Time', HasSize: false },\n\t'^': { DataType: 'Boolean', Label: 'Boolean', HasSize: false }\n};\n\nconst DATATYPE_TO_SYMBOL = {};\nfor (let tmpSymbol in MICRODDL_TYPE_MAP)\n{\n\tDATATYPE_TO_SYMBOL[MICRODDL_TYPE_MAP[tmpSymbol].DataType] = tmpSymbol;\n}\n\nfunction columnsToMicroDDL(pColumns, pTableName)\n{\n\tlet tmpTableName = (pTableName || 'Untitled').replace(/[^a-zA-Z0-9_]/g, '');\n\tlet tmpLines = ['!' + tmpTableName];\n\n\tfor (let i = 0; i < pColumns.length; i++)\n\t{\n\t\tlet tmpCol = pColumns[i];\n\t\tlet tmpSymbol = DATATYPE_TO_SYMBOL[tmpCol.DataType] || '$';\n\t\tlet tmpLine = tmpSymbol + (tmpCol.Name || 'Column' + i);\n\n\t\tif (MICRODDL_TYPE_MAP[tmpSymbol].HasSize && tmpCol.Size)\n\t\t{\n\t\t\ttmpLine += ' ' + tmpCol.Size;\n\t\t}\n\n\t\ttmpLines.push(tmpLine);\n\t}\n\n\treturn tmpLines.join('\\n');\n}\n\nfunction microDDLToColumns(pDDL)\n{\n\tlet tmpLines = pDDL.split('\\n');\n\tlet tmpColumns = [];\n\n\tfor (let i = 0; i < tmpLines.length; i++)\n\t{\n\t\tlet tmpLine = tmpLines[i].trim();\n\t\tif (!tmpLine || tmpLine.startsWith('!') || tmpLine.startsWith('//') || tmpLine.startsWith('--') || tmpLine.startsWith('->'))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet tmpSymbol = tmpLine.charAt(0);\n\t\tif (MICRODDL_TYPE_MAP.hasOwnProperty(tmpSymbol))\n\t\t{\n\t\t\tlet tmpRest = tmpLine.substring(1).trim();\n\t\t\tlet tmpParts = tmpRest.split(/\\s+/);\n\t\t\ttmpColumns.push(\n\t\t\t{\n\t\t\t\tName: tmpParts[0] || '',\n\t\t\t\tDataType: MICRODDL_TYPE_MAP[tmpSymbol].DataType,\n\t\t\t\tSize: tmpParts[1] || ''\n\t\t\t});\n\t\t}\n\t}\n\n\treturn tmpColumns;\n}\n\nmodule.exports = { MICRODDL_TYPE_MAP, DATATYPE_TO_SYMBOL, columnsToMicroDDL, microDDLToColumns };\n","const libPictView = require('pict-view');\n\nconst libSchemaUtils = require('./MappingEditor-SchemaUtils.js');\n\nconst _ViewConfiguration =\n{\n\tViewIdentifier: \"MeadowMappingEditor\",\n\n\tDefaultRenderable: \"MeadowMappingEditor-Content\",\n\tDefaultDestinationAddress: \"#MeadowMap-Editor-Container\",\n\n\tAutoRender: false,\n\n\tCSS: /*css*/`\n\t\t/* Meadow Mapping Editor */\n\t\t.meadow-mapping-editor {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.meadow-mapping-editor.active {\n\t\t\tdisplay: block;\n\t\t}\n\t\t.meadow-mapping-header {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 1em;\n\t\t\tmargin-bottom: 1em;\n\t\t}\n\t\t.meadow-mapping-header h3 {\n\t\t\tmargin: 0;\n\t\t\tflex: 1;\n\t\t}\n\t\t.meadow-mapping-list-table {\n\t\t\twidth: 100%;\n\t\t\tborder-collapse: collapse;\n\t\t\tmargin-bottom: 1em;\n\t\t}\n\t\t.meadow-mapping-list-table th {\n\t\t\ttext-align: left;\n\t\t\tfont-size: 0.72em;\n\t\t\tfont-weight: 600;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.5px;\n\t\t\tcolor: var(--facto-text-tertiary, #a09070);\n\t\t\tpadding: 0.5em 0.4em;\n\t\t\tborder-bottom: 1px solid var(--facto-border, #d6c8ae);\n\t\t}\n\t\t.meadow-mapping-list-table td {\n\t\t\tpadding: 0.35em 0.4em;\n\t\t\tborder-bottom: 1px solid var(--facto-border-subtle, #e8ddc8);\n\t\t\tvertical-align: middle;\n\t\t}\n\t\t.meadow-flow-container {\n\t\t\twidth: 100%;\n\t\t\theight: 500px;\n\t\t\tborder: 1px solid var(--facto-border, #d6c8ae);\n\t\t\tborder-radius: 6px;\n\t\t\tbackground: var(--facto-bg-surface, #fcf8f0);\n\t\t\tmargin-bottom: 0.75em;\n\t\t}\n\t\t.meadow-mapping-json-editor {\n\t\t\twidth: 100%;\n\t\t\tmin-height: 300px;\n\t\t\tfont-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;\n\t\t\tfont-size: 0.85em;\n\t\t\tpadding: 0.75em;\n\t\t\tborder: 1px solid var(--facto-border, #d6c8ae);\n\t\t\tborder-radius: 6px;\n\t\t\tbackground: var(--facto-bg-input, #fcf8f0);\n\t\t\tcolor: var(--facto-text, #3a3020);\n\t\t\tresize: vertical;\n\t\t\ttab-size: 4;\n\t\t}\n\t\t.meadow-mapping-store-checklist {\n\t\t\tdisplay: flex;\n\t\t\tflex-wrap: wrap;\n\t\t\tgap: 0.5em;\n\t\t\tmargin-top: 0.25em;\n\t\t}\n\t\t.meadow-mapping-store-checklist label {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 0.35em;\n\t\t\tfont-size: 0.82em;\n\t\t\tcursor: pointer;\n\t\t\tpadding: 0.3em 0.5em;\n\t\t\tborder: 1px solid var(--facto-border-subtle, #e8ddc8);\n\t\t\tborder-radius: 4px;\n\t\t\tbackground: var(--facto-bg-input, #fcf8f0);\n\t\t}\n\t\t.meadow-mapping-store-checklist label:has(input:checked) {\n\t\t\tborder-color: var(--facto-brand, #18a5a0);\n\t\t\tbackground: var(--facto-brand-a12, rgba(24,165,160,0.12));\n\t\t}\n\t\t.meadow-mapping-btn {\n\t\t\tdisplay: inline-flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tpadding: 0.35em 0.9em;\n\t\t\tfont-size: 0.82em;\n\t\t\tfont-weight: 500;\n\t\t\tborder-radius: 4px;\n\t\t\tborder: 1px solid transparent;\n\t\t\tcursor: pointer;\n\t\t\ttext-decoration: none;\n\t\t\tline-height: 1.4;\n\t\t}\n\t\t.meadow-mapping-btn-primary {\n\t\t\tbackground: var(--facto-brand, #18a5a0);\n\t\t\tcolor: #fff;\n\t\t\tborder-color: var(--facto-brand, #18a5a0);\n\t\t}\n\t\t.meadow-mapping-btn-primary:hover {\n\t\t\topacity: 0.88;\n\t\t}\n\t\t.meadow-mapping-btn-secondary {\n\t\t\tbackground: var(--facto-bg-input, #fcf8f0);\n\t\t\tcolor: var(--facto-text, #3a3020);\n\t\t\tborder-color: var(--facto-border, #d6c8ae);\n\t\t}\n\t\t.meadow-mapping-btn-secondary:hover {\n\t\t\tbackground: var(--facto-border-subtle, #e8ddc8);\n\t\t}\n\t\t.meadow-mapping-btn-danger {\n\t\t\tbackground: #e74c3c;\n\t\t\tcolor: #fff;\n\t\t\tborder-color: #e74c3c;\n\t\t}\n\t\t.meadow-mapping-btn-danger:hover {\n\t\t\topacity: 0.88;\n\t\t}\n\t\t.meadow-mapping-btn-small {\n\t\t\tpadding: 0.2em 0.6em;\n\t\t\tfont-size: 0.78em;\n\t\t}\n\t\t.meadow-schema-mode-tabs {\n\t\t\tdisplay: flex;\n\t\t\tgap: 0.25em;\n\t\t}\n\t\t.meadow-schema-mode-tab {\n\t\t\tpadding: 0.25em 0.75em;\n\t\t\tfont-size: 0.8em;\n\t\t\tborder: 1px solid var(--facto-border, #d6c8ae);\n\t\t\tborder-radius: 4px;\n\t\t\tcursor: pointer;\n\t\t\tbackground: var(--facto-bg-input, #fcf8f0);\n\t\t\tcolor: var(--facto-text, #3a3020);\n\t\t}\n\t\t.meadow-schema-mode-tab.active {\n\t\t\tbackground: var(--facto-brand, #18a5a0);\n\t\t\tcolor: #fff;\n\t\t\tborder-color: var(--facto-brand, #18a5a0);\n\t\t}\n\t\t.meadow-section-title {\n\t\t\tfont-size: 0.72em;\n\t\t\tfont-weight: 600;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.5px;\n\t\t\tcolor: var(--facto-text-tertiary, #a09070);\n\t\t}\n\t`,\n\n\tTemplates:\n\t[\n\t\t{\n\t\t\tHash: \"MeadowMappingEditor-Template\",\n\t\t\tTemplate: /*html*/`\n<div>\n\t<div id=\"MeadowMap-Editor\" class=\"meadow-mapping-editor\">\n\t\t<div class=\"meadow-mapping-header\">\n\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-secondary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MeadowMappingEditor'].closeMappingEditor()\">&larr; Back</button>\n\t\t\t<h3 id=\"MeadowMap-Title\">Mapping Editor</h3>\n\t\t\t<div class=\"meadow-schema-mode-tabs\">\n\t\t\t\t<button class=\"meadow-schema-mode-tab active\" id=\"MeadowMap-Mode-Flow\" onclick=\"{~P~}.views['MeadowMappingEditor'].switchMapMode('flow')\">Visual Mapper</button>\n\t\t\t\t<button class=\"meadow-schema-mode-tab\" id=\"MeadowMap-Mode-JSON\" onclick=\"{~P~}.views['MeadowMappingEditor'].switchMapMode('json')\">JSON Config</button>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div id=\"MeadowMap-List-Wrap\">\n\t\t\t<div style=\"display:flex; align-items:center; justify-content:space-between; margin-bottom:0.75em;\">\n\t\t\t\t<div class=\"meadow-section-title\" style=\"margin:0;\">Existing Mappings</div>\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-primary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MeadowMappingEditor'].newMapping()\">+ New Mapping</button>\n\t\t\t</div>\n\t\t\t<div id=\"MeadowMap-List\"></div>\n\t\t</div>\n\n\t\t<div id=\"MeadowMap-Detail\" style=\"display:none;\">\n\t\t\t<div style=\"display:flex; gap:0.5em; align-items:center; margin-bottom:0.75em;\">\n\t\t\t\t<label style=\"font-size:0.78em; font-weight:600;\">Mapping Name</label>\n\t\t\t\t<input type=\"text\" id=\"MeadowMap-Name\" placeholder=\"Mapping name\" style=\"flex:1; padding:0.3em 0.5em; font-size:0.85em; border:1px solid var(--facto-border); border-radius:4px; background:var(--facto-bg-input); color:var(--facto-text);\">\n\t\t\t</div>\n\n\t\t\t<div style=\"display:flex; gap:0.5em; align-items:center; margin-bottom:0.75em;\">\n\t\t\t\t<label style=\"font-size:0.78em; font-weight:600;\">Source</label>\n\t\t\t\t<select id=\"MeadowMap-Source\" style=\"flex:1; padding:0.3em 0.5em; font-size:0.85em; border:1px solid var(--facto-border); border-radius:4px;\"></select>\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-secondary meadow-mapping-btn-small\" onclick=\"{~P~}.views['MeadowMappingEditor'].discoverSourceFields()\">Discover Fields</button>\n\t\t\t</div>\n\n\t\t\t<div id=\"MeadowMap-Flow-Wrap\">\n\t\t\t\t<div id=\"MeadowMap-Flow-Container\" class=\"meadow-flow-container\"></div>\n\t\t\t</div>\n\n\t\t\t<div id=\"MeadowMap-JSON-Wrap\" style=\"display:none;\">\n\t\t\t\t<textarea class=\"meadow-mapping-json-editor\" id=\"MeadowMap-JSON\" placeholder='{\"Entity\":\"MyTable\",\"GUIDTemplate\":\"{~D:Record.IDRecord~}\",\"Mappings\":{},\"Solvers\":[],\"ManyfestAddresses\":false}'></textarea>\n\t\t\t</div>\n\n\t\t\t<div style=\"margin-top:0.75em;\">\n\t\t\t\t<div style=\"font-size:0.72em; font-weight:600; text-transform:uppercase; letter-spacing:0.5px; color:var(--facto-text-tertiary); margin-bottom:0.35em;\">Target Stores</div>\n\t\t\t\t<div id=\"MeadowMap-Stores\" class=\"meadow-mapping-store-checklist\"></div>\n\t\t\t</div>\n\n\t\t\t<div style=\"margin-top:0.75em; display:flex; gap:0.5em; flex-wrap:wrap; align-items:center;\">\n\t\t\t\t<button class=\"meadow-mapping-btn meadow-mapping-btn-primary\" onclick=\"{~P~}.views['MeadowMappingEditor'].saveMapping()\">Save Mapping</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n`\n\t\t}\n\t],\n\n\tRenderables:\n\t[\n\t\t{\n\t\t\tRenderableHash: \"MeadowMappingEditor-Content\",\n\t\t\tTemplateHash: \"MeadowMappingEditor-Template\",\n\t\t\tDestinationAddress: \"#MeadowMap-Editor-Container\",\n\t\t\tRenderMethod: \"replace\"\n\t\t}\n\t]\n};\n\nclass MeadowMappingEditorView extends libPictView\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tsuper(pFable, pOptions, pServiceHash);\n\n\t\tthis._EditingContextID = 0;\n\t\tthis._EditingName = '';\n\t\tthis._CurrentMappings = [];\n\t\tthis._SelectedMappingID = 0;\n\t\tthis._DiscoveredFields = {};\n\t\tthis._FlowView = null;\n\t\tthis._MapEditorMode = 'flow';\n\t\tthis._MappingSources = [];\n\t\tthis._MappingStores = [];\n\t\tthis._CurrentTargetSchema = null;\n\t}\n\n\t// ── Overridable data methods ─────────────────────────────────────────────\n\t// Embedding apps override these to wire up their own persistence layer.\n\n\t/** Load all mappings for a context (e.g. dataset). Must return a Promise\n\t * that resolves to { Mappings: [...] }. */\n\t_doLoadMappings(pContextID)\n\t{\n\t\treturn Promise.resolve({ Mappings: [] });\n\t}\n\n\t/** Load all available sources. Must return a Promise that resolves to an\n\t * array of source objects with at least { IDSource, Name }. */\n\t_doLoadSources()\n\t{\n\t\treturn Promise.resolve([]);\n\t}\n\n\t/** Load all available target stores for a context. Must return a Promise\n\t * that resolves to { Stores: [...] }. */\n\t_doLoadStores(pContextID)\n\t{\n\t\treturn Promise.resolve({ Stores: [] });\n\t}\n\n\t/** Load the target schema for a context. Must return a Promise that\n\t * resolves to { SchemaDefinition: \"<micro-DDL string>\" }. */\n\t_doLoadTargetSchema(pContextID)\n\t{\n\t\treturn Promise.resolve({ SchemaDefinition: '' });\n\t}\n\n\t/** Load a single mapping by ID. Must return a Promise that resolves to\n\t * { Mapping: { Name, IDSource, IDProjectionStore, MappingConfiguration,\n\t * FlowDiagramState, Active, ... } }. */\n\t_doLoadMapping(pMappingID)\n\t{\n\t\treturn Promise.resolve({ Mapping: null });\n\t}\n\n\t/** Delete a mapping by ID. Must return a Promise. */\n\t_doDeleteMapping(pMappingID)\n\t{\n\t\treturn Promise.resolve({});\n\t}\n\n\t/** Discover fields from a source dataset. Must return a Promise that\n\t * resolves to { Headers: [...], SampleSize: N }. */\n\t_doDiscoverSourceFields(pContextID, pSourceID, pRecordLimit)\n\t{\n\t\treturn Promise.resolve({ Headers: [], SampleSize: 0 });\n\t}\n\n\t/** Create a new mapping. Must return a Promise that resolves to\n\t * { Mapping: { IDProjectionMapping, ... } }. */\n\t_doCreateMapping(pContextID, pData)\n\t{\n\t\treturn Promise.resolve({ Mapping: {} });\n\t}\n\n\t/** Update an existing mapping. Must return a Promise that resolves to\n\t * { Mapping: { ... } }. */\n\t_doUpdateMapping(pMappingID, pData)\n\t{\n\t\treturn Promise.resolve({ Mapping: {} });\n\t}\n\n\t/** Called when the editor is closed. Override to notify the parent view. */\n\t_onClose()\n\t{\n\t\t// Default: no-op. Override in embedding app.\n\t}\n\n\t/** Show a toast notification. */\n\t_doToast(pMessage, pOptions)\n\t{\n\t\tlet tmpModal = this.pict.views && this.pict.views['Pict-Section-Modal'];\n\t\tif (tmpModal && typeof tmpModal.toast === 'function')\n\t\t{\n\t\t\ttmpModal.toast(pMessage, pOptions);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.log.info('[MeadowMappingEditor] ' + pMessage);\n\t\t}\n\t}\n\n\t/** Show a confirmation dialog. Returns a Promise<boolean>. */\n\t_doConfirm(pMessage, pOptions)\n\t{\n\t\tlet tmpModal = this.pict.views && this.pict.views['Pict-Section-Modal'];\n\t\tif (tmpModal && typeof tmpModal.confirm === 'function')\n\t\t{\n\t\t\treturn tmpModal.confirm(pMessage, pOptions);\n\t\t}\n\t\t// Fallback to native confirm\n\t\treturn Promise.resolve(typeof window !== 'undefined' ? window.confirm(pMessage) : false);\n\t}\n\n\t// ── Public API ───────────────────────────────────────────────────────────\n\n\teditMappings(pContextID, pName)\n\t{\n\t\tthis._EditingContextID = pContextID;\n\t\tthis._EditingName = pName || '';\n\n\t\t// Render the sub-view so its DOM exists\n\t\tthis.render();\n\n\t\tlet tmpEditor = document.getElementById('MeadowMap-Editor');\n\t\tlet tmpTitle = document.getElementById('MeadowMap-Title');\n\n\t\tif (tmpEditor)\n\t\t{\n\t\t\ttmpEditor.classList.add('active');\n\t\t\ttmpEditor.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n\t\t}\n\t\tif (tmpTitle) tmpTitle.textContent = 'Mappings: ' + (pName || 'Untitled');\n\n\t\t// Show the mapping list, hide detail\n\t\tlet tmpMappingListWrap = document.getElementById('MeadowMap-List-Wrap');\n\t\tlet tmpMappingDetail = document.getElementById('MeadowMap-Detail');\n\t\tif (tmpMappingListWrap) tmpMappingListWrap.style.display = '';\n\t\tif (tmpMappingDetail) tmpMappingDetail.style.display = 'none';\n\n\t\t// Load mappings, sources, stores, and fresh schema in parallel\n\t\tPromise.all(\n\t\t[\n\t\t\tthis._doLoadMappings(pContextID),\n\t\t\tthis._doLoadSources(),\n\t\t\tthis._doLoadStores(pContextID),\n\t\t\tthis._doLoadTargetSchema(pContextID)\n\t\t]).then(\n\t\t\t(pResults) =>\n\t\t\t{\n\t\t\t\tthis._CurrentMappings = (pResults[0] && pResults[0].Mappings) ? pResults[0].Mappings : [];\n\t\t\t\tthis._MappingSources = Array.isArray(pResults[1]) ? pResults[1] : [];\n\t\t\t\tthis._MappingStores = (pResults[2] && pResults[2].Stores) ? pResults[2].Stores : [];\n\n\t\t\t\t// Pre-populate _DiscoveredFields from source Columns (config-driven).\n\t\t\t\t// Sources that include a Columns array provide field names without\n\t\t\t\t// requiring a separate \"Discover Fields\" API call.\n\t\t\t\tfor (let i = 0; i < this._MappingSources.length; i++)\n\t\t\t\t{\n\t\t\t\t\tlet tmpSrc = this._MappingSources[i];\n\t\t\t\t\tif (Array.isArray(tmpSrc.Columns) && tmpSrc.Columns.length > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._DiscoveredFields[tmpSrc.IDSource] = tmpSrc.Columns;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Store the fresh schema locally for use by flow nodes\n\t\t\t\tlet tmpSchema = pResults[3];\n\t\t\t\tif (tmpSchema && tmpSchema.SchemaDefinition)\n\t\t\t\t{\n\t\t\t\t\tthis._CurrentTargetSchema = tmpSchema.SchemaDefinition;\n\t\t\t\t}\n\n\t\t\t\tthis.refreshMappingList();\n\t\t\t});\n\t}\n\n\tcloseMappingEditor()\n\t{\n\t\t// Clean up flow view\n\t\tif (this._FlowView)\n\t\t{\n\t\t\tthis._FlowView = null;\n\t\t}\n\n\t\tthis._SelectedMappingID = 0;\n\n\t\tthis._onClose();\n\t}\n\n\trefreshMappingList()\n\t{\n\t\tlet tmpContainer = document.getElementById('MeadowMap-List');\n\t\tif (!tmpContainer) return;\n\n\t\tif (this._CurrentMappings.length === 0)\n\t\t{\n\t\t\ttmpContainer.innerHTML = '<div style=\"text-align:center; padding:1.5em; color:var(--facto-text-tertiary, #a09070);\">No mappings yet. Create one to map source fields to target columns.</div>';\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpViewID = this.options.ViewIdentifier;\n\n\t\tlet tmpHtml = '<table class=\"meadow-mapping-list-table\"><thead><tr>';\n\t\ttmpHtml += '<th>ID</th><th>Name</th><th>Source</th><th>Active</th><th>Actions</th>';\n\t\ttmpHtml += '</tr></thead><tbody>';\n\n\t\tfor (let i = 0; i < this._CurrentMappings.length; i++)\n\t\t{\n\t\t\tlet tmpMap = this._CurrentMappings[i];\n\t\t\tlet tmpSourceName = '\\u2014';\n\t\t\tfor (let j = 0; j < this._MappingSources.length; j++)\n\t\t\t{\n\t\t\t\tif (this._MappingSources[j].IDSource === tmpMap.IDSource)\n\t\t\t\t{\n\t\t\t\t\ttmpSourceName = this._MappingSources[j].Name || 'Source ' + tmpMap.IDSource;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttmpHtml += '<tr>';\n\t\t\ttmpHtml += '<td>' + tmpMap.IDProjectionMapping + '</td>';\n\t\t\ttmpHtml += '<td><strong>' + (tmpMap.Name || '\\u2014') + '</strong></td>';\n\t\t\ttmpHtml += '<td>' + tmpSourceName + '</td>';\n\t\t\ttmpHtml += '<td>' + (tmpMap.Active ? '\\u2713' : '\\u2717') + '</td>';\n\t\t\ttmpHtml += '<td>';\n\t\t\ttmpHtml += '<button class=\"meadow-mapping-btn meadow-mapping-btn-primary meadow-mapping-btn-small\" onclick=\"window._Pict.views[\\'' + tmpViewID + '\\'].openMappingDetail(' + tmpMap.IDProjectionMapping + ')\">Edit</button> ';\n\t\t\ttmpHtml += '<button class=\"meadow-mapping-btn meadow-mapping-btn-danger meadow-mapping-btn-small\" onclick=\"window._Pict.views[\\'' + tmpViewID + '\\'].deleteMapping(' + tmpMap.IDProjectionMapping + ')\">Delete</button>';\n\t\t\ttmpHtml += '</td>';\n\t\t\ttmpHtml += '</tr>';\n\t\t}\n\n\t\ttmpHtml += '</tbody></table>';\n\t\ttmpContainer.innerHTML = tmpHtml;\n\t}\n\n\tnewMapping()\n\t{\n\t\tthis._SelectedMappingID = 0;\n\n\t\tlet tmpMappingListWrap = document.getElementById('MeadowMap-List-Wrap');\n\t\tlet tmpMappingDetail = document.getElementById('MeadowMap-Detail');\n\t\tif (tmpMappingListWrap) tmpMappingListWrap.style.display = 'none';\n\t\tif (tmpMappingDetail) tmpMappingDetail.style.display = '';\n\n\t\t// Reset fields\n\t\tlet tmpNameInput = document.getElementById('MeadowMap-Name');\n\t\tif (tmpNameInput) tmpNameInput.value = '';\n\n\t\t// Populate source dropdown -- auto-select the first source if one exists.\n\t\t// _DiscoveredFields for that source is already populated from the\n\t\t// source Columns loaded in editMappings(), so _rebuildFlowNodes\n\t\t// will immediately show the source fields on the SRC node.\n\t\tlet tmpAutoSourceID = (this._MappingSources.length > 0) ? this._MappingSources[0].IDSource : undefined;\n\t\tthis._populateSourceDropdown(tmpAutoSourceID);\n\t\tthis._populateStoreChecklist();\n\n\t\t// Clear JSON editor\n\t\tlet tmpJSONTextarea = document.getElementById('MeadowMap-JSON');\n\t\tif (tmpJSONTextarea)\n\t\t{\n\t\t\tlet tmpNewEntityName = (this._EditingName || 'Record').replace(/[^a-zA-Z0-9_]/g, '');\n\t\t\tlet tmpNewGUIDCol = 'GUID' + tmpNewEntityName;\n\t\t\tlet tmpNewIDCol = 'ID' + tmpNewEntityName;\n\t\t\tlet tmpNewMappings = {};\n\t\t\ttmpNewMappings[tmpNewGUIDCol] = '{~D:Record.IDRecord~}';\n\t\t\ttmpNewMappings[tmpNewIDCol] = '{~D:Record.IDRecord~}';\n\n\t\t\ttmpJSONTextarea.value = JSON.stringify(\n\t\t\t{\n\t\t\t\tEntity: tmpNewEntityName,\n\t\t\t\tGUIDTemplate: '{~D:Record.IDRecord~}',\n\t\t\t\tGUIDName: tmpNewGUIDCol,\n\t\t\t\tMappings: tmpNewMappings,\n\t\t\t\tSolvers: [],\n\t\t\t\tManyfestAddresses: false\n\t\t\t}, null, '\\t');\n\t\t}\n\n\t\t// Clear flow container\n\t\tlet tmpFlowContainer = document.getElementById('MeadowMap-Flow-Container');\n\t\tif (tmpFlowContainer) tmpFlowContainer.innerHTML = '';\n\t\tthis._FlowView = null;\n\n\t\t// Switch to flow mode and initialize the flow editor\n\t\tthis.switchMapMode('flow');\n\t\tthis.initFlowView();\n\n\t\t// Fetch fresh schema then build TGT node ports from schema columns\n\t\tthis._doLoadTargetSchema(this._EditingContextID).then(\n\t\t\t(pSchema) =>\n\t\t\t{\n\t\t\t\tif (pSchema && pSchema.SchemaDefinition)\n\t\t\t\t{\n\t\t\t\t\tthis._CurrentTargetSchema = pSchema.SchemaDefinition;\n\t\t\t\t}\n\t\t\t\tthis._rebuildFlowNodes();\n\t\t\t});\n\t}\n\n\topenMappingDetail(pMappingID)\n\t{\n\t\tthis._SelectedMappingID = pMappingID;\n\n\t\tthis._doLoadMapping(pMappingID).then(\n\t\t\t(pResponse) =>\n\t\t\t{\n\t\t\t\tif (!pResponse || !pResponse.Mapping)\n\t\t\t\t{\n\t\t\t\t\tthis._doToast('Mapping not found.', 'error');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet tmpMapping = pResponse.Mapping;\n\n\t\t\t\tlet tmpMappingListWrap = document.getElementById('MeadowMap-List-Wrap');\n\t\t\t\tlet tmpMappingDetail = document.getElementById('MeadowMap-Detail');\n\t\t\t\tif (tmpMappingListWrap) tmpMappingListWrap.style.display = 'none';\n\t\t\t\tif (tmpMappingDetail) tmpMappingDetail.style.display = '';\n\n\t\t\t\t// Set name\n\t\t\t\tlet tmpNameInput = document.getElementById('MeadowMap-Name');\n\t\t\t\tif (tmpNameInput) tmpNameInput.value = tmpMapping.Name || '';\n\n\t\t\t\t// Populate dropdowns\n\t\t\t\tthis._populateSourceDropdown(tmpMapping.IDSource);\n\n\t\t\t\t// Parse TargetStores from config, fall back to legacy IDProjectionStore\n\t\t\t\tlet tmpTargetStores = null;\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tlet tmpParsedConfig = JSON.parse(tmpMapping.MappingConfiguration || '{}');\n\t\t\t\t\tif (Array.isArray(tmpParsedConfig.TargetStores) && tmpParsedConfig.TargetStores.length > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpTargetStores = tmpParsedConfig.TargetStores;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (e) { /* ignore */ }\n\t\t\t\tif (!tmpTargetStores && tmpMapping.IDProjectionStore)\n\t\t\t\t{\n\t\t\t\t\ttmpTargetStores = [tmpMapping.IDProjectionStore];\n\t\t\t\t}\n\t\t\t\tthis._populateStoreChecklist(tmpTargetStores);\n\n\t\t\t\t// Parse mapping config\n\t\t\t\tlet tmpConfig = {};\n\t\t\t\ttry { tmpConfig = JSON.parse(tmpMapping.MappingConfiguration || '{}'); }\n\t\t\t\tcatch (e) { /* ignore */ }\n\n\t\t\t\t// Restore discovered source fields from config (config-driven approach).\n\t\t\t\t// sourceColumns is written by saveMapping() so the SRC node shows\n\t\t\t\t// all fields immediately without an extra API call.\n\t\t\t\tif (Array.isArray(tmpConfig.sourceColumns) && tmpConfig.sourceColumns.length > 0)\n\t\t\t\t{\n\t\t\t\t\tthis._DiscoveredFields[tmpMapping.IDSource] = tmpConfig.sourceColumns;\n\t\t\t\t}\n\n\t\t\t\t// Set JSON editor\n\t\t\t\tlet tmpJSONTextarea = document.getElementById('MeadowMap-JSON');\n\t\t\t\tif (tmpJSONTextarea)\n\t\t\t\t{\n\t\t\t\t\ttmpJSONTextarea.value = JSON.stringify(tmpConfig, null, '\\t');\n\t\t\t\t}\n\n\t\t\t\t// Clear flow container and re-initialize\n\t\t\t\tlet tmpFlowContainer = document.getElementById('MeadowMap-Flow-Container');\n\t\t\t\tif (tmpFlowContainer) tmpFlowContainer.innerHTML = '';\n\t\t\t\tthis._FlowView = null;\n\n\t\t\t\t// Switch to flow mode and initialize the flow editor\n\t\t\t\tthis.switchMapMode('flow');\n\t\t\t\tthis.initFlowView();\n\n\t\t\t\t// Fetch fresh schema then build TGT node ports from schema columns\n\t\t\t\tthis._doLoadTargetSchema(this._EditingContextID).then(\n\t\t\t\t\t(pSchema) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pSchema && pSchema.SchemaDefinition)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthis._CurrentTargetSchema = pSchema.SchemaDefinition;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Restore saved flow diagram state if available,\n\t\t\t\t\t\t// then rebuild ports from current schema (schema is\n\t\t\t\t\t\t// the source of truth for ports, not saved state).\n\t\t\t\t\t\tif (this._FlowView)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlet tmpFlowState = null;\n\t\t\t\t\t\t\ttry { tmpFlowState = JSON.parse(tmpMapping.FlowDiagramState || 'null'); }\n\t\t\t\t\t\t\tcatch (pParseError) { /* ignore invalid JSON */ }\n\n\t\t\t\t\t\t\tif (tmpFlowState && tmpFlowState.Nodes && tmpFlowState.Nodes.length > 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (typeof this._FlowView.setFlowData === 'function')\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tthis._FlowView.setFlowData(tmpFlowState);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Always rebuild SRC/TGT ports from current schema\n\t\t\t\t\t\t// after restoring positions and connections\n\t\t\t\t\t\tthis._rebuildFlowNodes();\n\t\t\t\t\t});\n\t\t\t});\n\t}\n\n\tasync deleteMapping(pMappingID)\n\t{\n\t\tlet tmpConfirmed = await this._doConfirm('Delete this mapping?', { title: 'Delete Mapping', confirmLabel: 'Delete', dangerous: true });\n\t\tif (!tmpConfirmed) return;\n\n\t\tthis._doDeleteMapping(pMappingID).then(\n\t\t\t() =>\n\t\t\t{\n\t\t\t\tthis._doLoadMappings(this._EditingContextID).then(\n\t\t\t\t\t(pResult) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._CurrentMappings = (pResult && pResult.Mappings) ? pResult.Mappings : [];\n\t\t\t\t\t\tthis.refreshMappingList();\n\t\t\t\t\t});\n\t\t\t});\n\t}\n\n\tswitchMapMode(pMode)\n\t{\n\t\tthis._MapEditorMode = pMode;\n\n\t\tlet tmpFlowWrap = document.getElementById('MeadowMap-Flow-Wrap');\n\t\tlet tmpJSONWrap = document.getElementById('MeadowMap-JSON-Wrap');\n\t\tlet tmpFlowTab = document.getElementById('MeadowMap-Mode-Flow');\n\t\tlet tmpJSONTab = document.getElementById('MeadowMap-Mode-JSON');\n\n\t\tif (pMode === 'flow')\n\t\t{\n\t\t\tif (tmpFlowWrap) tmpFlowWrap.style.display = '';\n\t\t\tif (tmpJSONWrap) tmpJSONWrap.style.display = 'none';\n\t\t\tif (tmpFlowTab) tmpFlowTab.classList.add('active');\n\t\t\tif (tmpJSONTab) tmpJSONTab.classList.remove('active');\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (tmpFlowWrap) tmpFlowWrap.style.display = 'none';\n\t\t\tif (tmpJSONWrap) tmpJSONWrap.style.display = '';\n\t\t\tif (tmpFlowTab) tmpFlowTab.classList.remove('active');\n\t\t\tif (tmpJSONTab) tmpJSONTab.classList.add('active');\n\n\t\t\t// If there's a flow view, serialize flow -> JSON\n\t\t\tif (this._FlowView && typeof this._FlowView.getFlowData === 'function')\n\t\t\t{\n\t\t\t\tlet tmpConfig = this.flowToMappingConfig();\n\t\t\t\tlet tmpJSONTextarea = document.getElementById('MeadowMap-JSON');\n\t\t\t\tif (tmpJSONTextarea)\n\t\t\t\t{\n\t\t\t\t\ttmpJSONTextarea.value = JSON.stringify(tmpConfig, null, '\\t');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t_populateSourceDropdown(pSelectedIDSource)\n\t{\n\t\tlet tmpSelect = document.getElementById('MeadowMap-Source');\n\t\tif (!tmpSelect) return;\n\n\t\tlet tmpHtml = '<option value=\"0\">Select a source...</option>';\n\t\tfor (let i = 0; i < this._MappingSources.length; i++)\n\t\t{\n\t\t\tlet tmpSrc = this._MappingSources[i];\n\t\t\tlet tmpSelected = (tmpSrc.IDSource === pSelectedIDSource) ? ' selected' : '';\n\t\t\ttmpHtml += '<option value=\"' + tmpSrc.IDSource + '\"' + tmpSelected + '>' + (tmpSrc.Name || 'Source ' + tmpSrc.IDSource) + '</option>';\n\t\t}\n\t\ttmpSelect.innerHTML = tmpHtml;\n\t}\n\n\t_populateStoreChecklist(pSelectedStoreIDs)\n\t{\n\t\tlet tmpContainer = document.getElementById('MeadowMap-Stores');\n\t\tif (!tmpContainer) return;\n\n\t\tlet tmpSelectedSet = {};\n\t\tif (Array.isArray(pSelectedStoreIDs))\n\t\t{\n\t\t\tfor (let i = 0; i < pSelectedStoreIDs.length; i++)\n\t\t\t{\n\t\t\t\ttmpSelectedSet[pSelectedStoreIDs[i]] = true;\n\t\t\t}\n\t\t}\n\t\telse if (pSelectedStoreIDs)\n\t\t{\n\t\t\t// Backwards compat: single IDProjectionStore value\n\t\t\ttmpSelectedSet[pSelectedStoreIDs] = true;\n\t\t}\n\n\t\tif (this._MappingStores.length === 0)\n\t\t{\n\t\t\ttmpContainer.innerHTML = '<div style=\"font-size:0.82em; color:var(--facto-text-tertiary, #a09070);\">No stores configured yet.</div>';\n\t\t\treturn;\n\t\t}\n\n\t\tlet tmpHtml = '';\n\t\tfor (let i = 0; i < this._MappingStores.length; i++)\n\t\t{\n\t\t\tlet tmpStore = this._MappingStores[i];\n\t\t\tlet tmpChecked = tmpSelectedSet[tmpStore.IDProjectionStore] ? ' checked' : '';\n\t\t\tlet tmpLabel = (tmpStore.TargetTableName || 'Store ' + tmpStore.IDProjectionStore) + ' (' + (tmpStore.Status || 'Unknown') + ')';\n\t\t\ttmpHtml += '<label>';\n\t\t\ttmpHtml += '<input type=\"checkbox\" value=\"' + tmpStore.IDProjectionStore + '\"' + tmpChecked + '>';\n\t\t\ttmpHtml += ' ' + tmpLabel;\n\t\t\ttmpHtml += '</label>';\n\t\t}\n\t\ttmpContainer.innerHTML = tmpHtml;\n\t}\n\n\t_getCheckedStoreIDs()\n\t{\n\t\tlet tmpContainer = document.getElementById('MeadowMap-Stores');\n\t\tif (!tmpContainer) return [];\n\n\t\tlet tmpChecked = tmpContainer.querySelectorAll('input[type=\"checkbox\"]:checked');\n\t\tlet tmpIDs = [];\n\t\tfor (let i = 0; i < tmpChecked.length; i++)\n\t\t{\n\t\t\ttmpIDs.push(parseInt(tmpChecked[i].value, 10));\n\t\t}\n\t\treturn tmpIDs;\n\t}\n\n\tdiscoverSourceFields()\n\t{\n\t\tlet tmpSourceSelect = document.getElementById('MeadowMap-Source');\n\t\tlet tmpIDSource = tmpSourceSelect ? parseInt(tmpSourceSelect.value, 10) : 0;\n\n\t\tif (!tmpIDSource)\n\t\t{\n\t\t\tthis._doToast('Select a source first.', {type: 'warning'});\n\t\t\treturn;\n\t\t}\n\n\t\tthis._doDiscoverSourceFields(this._EditingContextID, tmpIDSource, 50).then(\n\t\t\t(pResponse) =>\n\t\t\t{\n\t\t\t\tif (pResponse && pResponse.Error)\n\t\t\t\t{\n\t\t\t\t\tthis._doToast('Error: ' + pResponse.Error, {type: 'error'});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet tmpHeaders = (pResponse && pResponse.Headers) ? pResponse.Headers : [];\n\t\t\t\tthis._DiscoveredFields[tmpIDSource] = tmpHeaders;\n\n\t\t\t\tthis._doToast('Discovered ' + tmpHeaders.length + ' fields from ' + (pResponse.SampleSize || 0) + ' records: ' + tmpHeaders.join(', '), {type: 'success', duration: 6000});\n\n\t\t\t\t// Rebuild the flow if it exists\n\t\t\t\tthis._rebuildFlowNodes();\n\t\t\t});\n\t}\n\n\t_rebuildFlowNodes()\n\t{\n\t\t// Get current source and schema columns\n\t\tlet tmpSourceSelect = document.getElementById('MeadowMap-Source');\n\t\tlet tmpIDSource = tmpSourceSelect ? parseInt(tmpSourceSelect.value, 10) : 0;\n\t\tlet tmpFields = this._DiscoveredFields[tmpIDSource] || [];\n\n\t\t// Get schema columns from the target\n\t\tlet tmpSchemaColumns = this._getSchemaColumns();\n\n\t\t// Initialize the flow view if needed\n\t\tthis.initFlowView();\n\n\t\tif (!this._FlowView) return;\n\n\t\tlet tmpSourceTitle = 'Source: ' + (tmpSourceSelect && tmpSourceSelect.selectedIndex >= 0 ? tmpSourceSelect.options[tmpSourceSelect.selectedIndex].text : 'Source');\n\t\tlet tmpTargetTitle = 'Target: ' + (this._EditingName || 'Target');\n\n\t\t// Build deterministic source ports (Whole Record + discovered fields)\n\t\tlet tmpSourcePorts =\n\t\t[\n\t\t\t{ Hash: 'src-whole-record', Direction: 'output', Side: 'right', Label: 'Whole Record' }\n\t\t];\n\t\tfor (let i = 0; i < tmpFields.length; i++)\n\t\t{\n\t\t\ttmpSourcePorts.push(\n\t\t\t{\n\t\t\t\tHash: 'src-field-' + tmpFields[i].replace(/[^a-zA-Z0-9_-]/g, '_'),\n\t\t\t\tDirection: 'output',\n\t\t\t\tSide: 'right',\n\t\t\t\tLabel: tmpFields[i]\n\t\t\t});\n\t\t}\n\n\t\t// Build deterministic target ports -- entity-specific GUID and ID are always present\n\t\tlet tmpEntityName = (this._EditingName || 'Record').replace(/[^a-zA-Z0-9_]/g, '');\n\t\tlet tmpGUIDColumnName = 'GUID' + tmpEntityName;\n\t\tlet tmpIDColumnName = 'ID' + tmpEntityName;\n\n\t\tlet tmpTargetPorts =\n\t\t[\n\t\t\t{ Hash: 'tgt-col-' + tmpGUIDColumnName, Direction: 'input', Side: 'left', Label: tmpGUIDColumnName },\n\t\t\t{ Hash: 'tgt-col-' + tmpIDColumnName, Direction: 'input', Side: 'left', Label: tmpIDColumnName }\n\t\t];\n\t\tfor (let i = 0; i < tmpSchemaColumns.length; i++)\n\t\t{\n\t\t\t// Skip entity GUID/ID if they appear in schema columns (already added above)\n\t\t\tif (tmpSchemaColumns[i] === tmpGUIDColumnName || tmpSchemaColumns[i] === tmpIDColumnName) continue;\n\n\t\t\ttmpTargetPorts.push(\n\t\t\t{\n\t\t\t\tHash: 'tgt-col-' + tmpSchemaColumns[i].replace(/[^a-zA-Z0-9_-]/g, '_'),\n\t\t\t\tDirection: 'input',\n\t\t\t\tSide: 'left',\n\t\t\t\tLabel: tmpSchemaColumns[i]\n\t\t\t});\n\t\t}\n\n\t\t// Find existing SRC and TGT nodes (preserve user-added TPL/SOL nodes)\n\t\tlet tmpFlowData = this._FlowView.getFlowData();\n\t\tlet tmpSrcNode = null;\n\t\tlet tmpTgtNode = null;\n\n\t\tfor (let i = 0; i < tmpFlowData.Nodes.length; i++)\n\t\t{\n\t\t\tif (tmpFlowData.Nodes[i].Type === 'SRC') tmpSrcNode = tmpFlowData.Nodes[i];\n\t\t\tif (tmpFlowData.Nodes[i].Type === 'TGT') tmpTgtNode = tmpFlowData.Nodes[i];\n\t\t}\n\n\t\tif (tmpSrcNode)\n\t\t{\n\t\t\t// Merge source ports: start with newly built ports, then preserve\n\t\t\t// any existing ports from the saved state (e.g. previously discovered\n\t\t\t// fields) that aren't already in the new set.\n\t\t\tlet tmpMergedSrcPorts = tmpSourcePorts.slice();\n\t\t\tlet tmpSrcPortHashes = {};\n\t\t\tfor (let p = 0; p < tmpMergedSrcPorts.length; p++)\n\t\t\t{\n\t\t\t\ttmpSrcPortHashes[tmpMergedSrcPorts[p].Hash] = true;\n\t\t\t}\n\t\t\tlet tmpExistingPorts = tmpSrcNode.Ports || [];\n\t\t\tfor (let p = 0; p < tmpExistingPorts.length; p++)\n\t\t\t{\n\t\t\t\tif (!tmpSrcPortHashes[tmpExistingPorts[p].Hash])\n\t\t\t\t{\n\t\t\t\t\ttmpMergedSrcPorts.push(tmpExistingPorts[p]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update existing source node in-place\n\t\t\tlet tmpInternalNodes = this._FlowView._FlowData.Nodes;\n\t\t\tfor (let i = 0; i < tmpInternalNodes.length; i++)\n\t\t\t{\n\t\t\t\tif (tmpInternalNodes[i].Hash === tmpSrcNode.Hash)\n\t\t\t\t{\n\t\t\t\t\ttmpInternalNodes[i].Ports = tmpMergedSrcPorts;\n\t\t\t\t\ttmpInternalNodes[i].Title = tmpSourceTitle;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Push directly into _FlowData.Nodes to avoid addNode() rendering with empty ports\n\t\t\tthis._FlowView._FlowData.Nodes.push(\n\t\t\t{\n\t\t\t\tHash: 'node-src-' + this.fable.getUUID(),\n\t\t\t\tType: 'SRC',\n\t\t\t\tX: 50,\n\t\t\t\tY: 50,\n\t\t\t\tWidth: 200,\n\t\t\t\tHeight: 100,\n\t\t\t\tTitle: tmpSourceTitle,\n\t\t\t\tPorts: tmpSourcePorts,\n\t\t\t\tData: {}\n\t\t\t});\n\t\t}\n\n\t\tif (tmpTgtNode)\n\t\t{\n\t\t\t// Target ports: schema is the source of truth. Start with schema-\n\t\t\t// derived ports, then preserve any extra existing ports (e.g. user-\n\t\t\t// added custom columns) that aren't already in the new set.\n\t\t\tlet tmpMergedTgtPorts = tmpTargetPorts.slice();\n\t\t\tlet tmpTgtPortHashes = {};\n\t\t\tfor (let p = 0; p < tmpMergedTgtPorts.length; p++)\n\t\t\t{\n\t\t\t\ttmpTgtPortHashes[tmpMergedTgtPorts[p].Hash] = true;\n\t\t\t}\n\t\t\tlet tmpExistingTgtPorts = tmpTgtNode.Ports || [];\n\t\t\tfor (let p = 0; p < tmpExistingTgtPorts.length; p++)\n\t\t\t{\n\t\t\t\tif (!tmpTgtPortHashes[tmpExistingTgtPorts[p].Hash])\n\t\t\t\t{\n\t\t\t\t\ttmpMergedTgtPorts.push(tmpExistingTgtPorts[p]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update existing target node in-place\n\t\t\tlet tmpInternalNodes = this._FlowView._FlowData.Nodes;\n\t\t\tfor (let i = 0; i < tmpInternalNodes.length; i++)\n\t\t\t{\n\t\t\t\tif (tmpInternalNodes[i].Hash === tmpTgtNode.Hash)\n\t\t\t\t{\n\t\t\t\t\ttmpInternalNodes[i].Ports = tmpMergedTgtPorts;\n\t\t\t\t\ttmpInternalNodes[i].Title = tmpTargetTitle;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Push directly into _FlowData.Nodes to avoid addNode() rendering with empty ports\n\t\t\tthis._FlowView._FlowData.Nodes.push(\n\t\t\t{\n\t\t\t\tHash: 'node-tgt-' + this.fable.getUUID(),\n\t\t\t\tType: 'TGT',\n\t\t\t\tX: 550,\n\t\t\t\tY: 50,\n\t\t\t\tWidth: 200,\n\t\t\t\tHeight: 100,\n\t\t\t\tTitle: tmpTargetTitle,\n\t\t\t\tPorts: tmpTargetPorts,\n\t\t\t\tData: {}\n\t\t\t});\n\t\t}\n\n\t\t// Render the flow once with all ports correctly set\n\t\tif (typeof this._FlowView.renderFlow === 'function')\n\t\t{\n\t\t\tthis._FlowView.renderFlow();\n\t\t}\n\t\telse if (typeof this._FlowView.render === 'function')\n\t\t{\n\t\t\tthis._FlowView.render();\n\t\t}\n\t}\n\n\t_getSchemaColumns()\n\t{\n\t\t// Use the locally cached schema definition\n\t\tlet tmpColumns = [];\n\t\tlet tmpDDL = this._CurrentTargetSchema || '';\n\t\tif (tmpDDL)\n\t\t{\n\t\t\tlet tmpParsedColumns = libSchemaUtils.microDDLToColumns(tmpDDL);\n\t\t\tfor (let j = 0; j < tmpParsedColumns.length; j++)\n\t\t\t{\n\t\t\t\ttmpColumns.push(tmpParsedColumns[j].Name);\n\t\t\t}\n\t\t}\n\t\treturn tmpColumns;\n\t}\n\n\tinitFlowView()\n\t{\n\t\tif (this._FlowView) return;\n\n\t\tlet tmpFlowContainer = document.getElementById('MeadowMap-Flow-Container');\n\t\tif (!tmpFlowContainer) return;\n\n\t\ttry\n\t\t{\n\t\t\tlet libPictSectionFlow = require('pict-section-flow');\n\n\t\t\tthis._FlowView = this.pict.addView('MeadowMapping-Flow',\n\t\t\t{\n\t\t\t\tViewIdentifier: 'MeadowMapping-Flow',\n\t\t\t\tDefaultDestinationAddress: '#MeadowMap-Flow-Container',\n\t\t\t\tEnableToolbar: true,\n\t\t\t\tEnablePanning: true,\n\t\t\t\tEnableZooming: true,\n\t\t\t\tEnableNodeDragging: true,\n\t\t\t\tEnableConnectionCreation: true\n\t\t\t}, libPictSectionFlow);\n\n\t\t\t// Register card types\n\t\t\tlet libFlowCardSource = require('./flow-cards/FlowCard-MappingSource.js');\n\t\t\tlet libFlowCardTarget = require('./flow-cards/FlowCard-MappingTarget.js');\n\t\t\tlet libFlowCardTemplate = require('./flow-cards/FlowCard-TemplateExpression.js');\n\t\t\tlet libFlowCardSolver = require('./flow-cards/FlowCard-SolverExpression.js');\n\n\t\t\tthis.pict.addServiceType('FlowCardMappingSource', libFlowCardSource);\n\t\t\tthis.pict.addServiceType('FlowCardMappingTarget', libFlowCardTarget);\n\t\t\tthis.pict.addServiceType('FlowCardTemplateExpression', libFlowCardTemplate);\n\t\t\tthis.pict.addServiceType('FlowCardSolverExpression', libFlowCardSolver);\n\n\t\t\t// Render the flow view first so _NodeTypeProvider is initialized\n\t\t\tif (typeof this._FlowView.render === 'function')\n\t\t\t{\n\t\t\t\tthis._FlowView.render();\n\t\t\t}\n\n\t\t\t// Register card types with the flow view (must happen after render\n\t\t\t// so _NodeTypeProvider exists)\n\t\t\tlet tmpSourceCard = this.pict.instantiateServiceProviderWithoutRegistration('FlowCardMappingSource', {});\n\t\t\tlet tmpTargetCard = this.pict.instantiateServiceProviderWithoutRegistration('FlowCardMappingTarget', {});\n\t\t\tlet tmpTemplateCard = this.pict.instantiateServiceProviderWithoutRegistration('FlowCardTemplateExpression', {});\n\t\t\tlet tmpSolverCard = this.pict.instantiateServiceProviderWithoutRegistration('FlowCardSolverExpression', {});\n\n\t\t\ttmpSourceCard.registerWithFlowView(this._FlowView);\n\t\t\ttmpTargetCard.registerWithFlowView(this._FlowView);\n\t\t\ttmpTemplateCard.registerWithFlowView(this._FlowView);\n\t\t\ttmpSolverCard.registerWithFlowView(this._FlowView);\n\t\t}\n\t\tcatch (pFlowError)\n\t\t{\n\t\t\tthis.log.error('Failed to initialize flow view: ' + pFlowError.message);\n\t\t\ttmpFlowContainer.innerHTML = '<div style=\"padding:2em; text-align:center; color:var(--facto-text-tertiary, #a09070);\">Flow editor could not be loaded. Use JSON Config mode instead.</div>';\n\t\t}\n\t}\n\n\tflowToMappingConfig()\n\t{\n\t\tlet tmpEntityName = (this._EditingName || 'Record').replace(/[^a-zA-Z0-9_]/g, '');\n\t\tlet tmpGUIDColumnName = 'GUID' + tmpEntityName;\n\t\tlet tmpIDColumnName = 'ID' + tmpEntityName;\n\n\t\tlet tmpConfig =\n\t\t{\n\t\t\tEntity: tmpEntityName,\n\t\t\tGUIDTemplate: '{~D:Record.IDRecord~}',\n\t\t\tGUIDName: tmpGUIDColumnName,\n\t\t\tMappings: {},\n\t\t\tSolvers: [],\n\t\t\tManyfestAddresses: false\n\t\t};\n\n\t\tif (!this._FlowView || typeof this._FlowView.getFlowData !== 'function')\n\t\t{\n\t\t\treturn tmpConfig;\n\t\t}\n\n\t\tlet tmpFlowData = this._FlowView.getFlowData();\n\t\tif (!tmpFlowData || !tmpFlowData.Connections) return tmpConfig;\n\n\t\t// Build node hash->node map and port hash->{Label, NodeHash, NodeType} map\n\t\tlet tmpNodeMap = {};\n\t\tlet tmpPortMap = {};\n\n\t\tif (tmpFlowData.Nodes)\n\t\t{\n\t\t\tfor (let i = 0; i < tmpFlowData.Nodes.length; i++)\n\t\t\t{\n\t\t\t\tlet tmpNode = tmpFlowData.Nodes[i];\n\t\t\t\ttmpNodeMap[tmpNode.Hash] = tmpNode;\n\n\t\t\t\tif (tmpNode.Ports)\n\t\t\t\t{\n\t\t\t\t\tfor (let j = 0; j < tmpNode.Ports.length; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpPortMap[tmpNode.Ports[j].Hash] =\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLabel: tmpNode.Ports[j].Label,\n\t\t\t\t\t\t\tNodeHash: tmpNode.Hash,\n\t\t\t\t\t\t\tNodeType: tmpNode.Type\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Track solver nodes that connect to multiple target columns\n\t\tlet tmpSolverEntries = {};\n\n\t\t// Process each connection where the target is a TGT node\n\t\tfor (let i = 0; i < tmpFlowData.Connections.length; i++)\n\t\t{\n\t\t\tlet tmpConn = tmpFlowData.Connections[i];\n\t\t\tlet tmpSourcePort = tmpPortMap[tmpConn.SourcePortHash];\n\t\t\tlet tmpTargetPort = tmpPortMap[tmpConn.TargetPortHash];\n\n\t\t\tif (!tmpSourcePort || !tmpTargetPort) continue;\n\n\t\t\t// Only process connections that end at a TGT node\n\t\t\tif (tmpTargetPort.NodeType !== 'TGT') continue;\n\n\t\t\tlet tmpTargetColumn = tmpTargetPort.Label;\n\t\t\tif (!tmpTargetColumn) continue;\n\n\t\t\tlet tmpSourceNode = tmpNodeMap[tmpSourcePort.NodeHash];\n\t\t\tif (!tmpSourceNode) continue;\n\n\t\t\tif (tmpSourceNode.Type === 'SRC')\n\t\t\t{\n\t\t\t\t// Direct mapping: SRC field -> TGT column\n\t\t\t\tlet tmpSourceField = tmpSourcePort.Label;\n\n\t\t\t\t// Skip \"Whole Record\" direct connections to TGT (need intermediate node)\n\t\t\t\tif (tmpSourceField === 'Whole Record') continue;\n\n\t\t\t\tlet tmpTemplate = (tmpConn.Data && tmpConn.Data.Template)\n\t\t\t\t\t? tmpConn.Data.Template\n\t\t\t\t\t: '{~D:Record.' + tmpSourceField + '~}';\n\n\t\t\t\t// Connection to the entity GUID port sets the GUIDTemplate for upsert uniqueness\n\t\t\t\tif (tmpTargetColumn === tmpGUIDColumnName)\n\t\t\t\t{\n\t\t\t\t\ttmpConfig.GUIDTemplate = tmpTemplate;\n\t\t\t\t}\n\n\t\t\t\ttmpConfig.Mappings[tmpTargetColumn] = tmpTemplate;\n\t\t\t}\n\t\t\telse if (tmpSourceNode.Type === 'TPL')\n\t\t\t{\n\t\t\t\t// Template expression: TPL result -> TGT column\n\t\t\t\tlet tmpExpression = (tmpSourceNode.Data && tmpSourceNode.Data.TemplateExpression)\n\t\t\t\t\t? tmpSourceNode.Data.TemplateExpression\n\t\t\t\t\t: '';\n\n\t\t\t\tif (tmpExpression)\n\t\t\t\t{\n\t\t\t\t\t// TPL connected to entity GUID sets the GUIDTemplate\n\t\t\t\t\tif (tmpTargetColumn === tmpGUIDColumnName)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpConfig.GUIDTemplate = tmpExpression;\n\t\t\t\t\t}\n\n\t\t\t\t\ttmpConfig.Mappings[tmpTargetColumn] = tmpExpression;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (tmpSourceNode.Type === 'SOL')\n\t\t\t{\n\t\t\t\t// Solver expression: SOL result -> TGT column\n\t\t\t\tlet tmpExpression = (tmpSourceNode.Data && tmpSourceNode.Data.SolverExpression)\n\t\t\t\t\t? tmpSourceNode.Data.SolverExpression\n\t\t\t\t\t: '';\n\n\t\t\t\tif (tmpExpression)\n\t\t\t\t{\n\t\t\t\t\t// Group outputs for the same solver node\n\t\t\t\t\tif (!tmpSolverEntries[tmpSourceNode.Hash])\n\t\t\t\t\t{\n\t\t\t\t\t\ttmpSolverEntries[tmpSourceNode.Hash] =\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texpression: tmpExpression,\n\t\t\t\t\t\t\toutputs: {}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\ttmpSolverEntries[tmpSourceNode.Hash].outputs[tmpTargetColumn] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Ensure entity-specific GUID and ID are always present in Mappings\n\t\tif (!tmpConfig.Mappings.hasOwnProperty(tmpGUIDColumnName))\n\t\t{\n\t\t\ttmpConfig.Mappings[tmpGUIDColumnName] = tmpConfig.GUIDTemplate;\n\t\t}\n\t\tif (!tmpConfig.Mappings.hasOwnProperty(tmpIDColumnName))\n\t\t{\n\t\t\ttmpConfig.Mappings[tmpIDColumnName] = '{~D:Record.IDRecord~}';\n\t\t}\n\n\t\t// Add grouped solver entries\n\t\tlet tmpSolverKeys = Object.keys(tmpSolverEntries);\n\t\tfor (let i = 0; i < tmpSolverKeys.length; i++)\n\t\t{\n\t\t\ttmpConfig.Solvers.push(tmpSolverEntries[tmpSolverKeys[i]]);\n\t\t}\n\n\t\treturn tmpConfig;\n\t}\n\n\tsaveMapping()\n\t{\n\t\tlet tmpNameInput = document.getElementById('MeadowMap-Name');\n\t\tlet tmpSourceSelect = document.getElementById('MeadowMap-Source');\n\n\t\tlet tmpName = tmpNameInput ? tmpNameInput.value.trim() : '';\n\t\tlet tmpIDSource = tmpSourceSelect ? parseInt(tmpSourceSelect.value, 10) : 0;\n\t\tlet tmpCheckedStoreIDs = this._getCheckedStoreIDs();\n\t\tlet tmpIDProjectionStore = tmpCheckedStoreIDs.length > 0 ? tmpCheckedStoreIDs[0] : 0;\n\n\t\tif (!tmpName)\n\t\t{\n\t\t\tthis._doToast('Enter a mapping name.', {type: 'warning'});\n\t\t\treturn;\n\t\t}\n\n\t\t// Get mapping config\n\t\tlet tmpMappingConfig;\n\t\tif (this._MapEditorMode === 'json')\n\t\t{\n\t\t\tlet tmpJSONTextarea = document.getElementById('MeadowMap-JSON');\n\t\t\tlet tmpJSON = tmpJSONTextarea ? tmpJSONTextarea.value : '{}';\n\t\t\ttry\n\t\t\t{\n\t\t\t\ttmpMappingConfig = JSON.parse(tmpJSON);\n\t\t\t}\n\t\t\tcatch (e)\n\t\t\t{\n\t\t\t\tthis._doToast('Invalid JSON: ' + e.message, {type: 'error'});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpMappingConfig = this.flowToMappingConfig();\n\t\t}\n\n\t\t// Store target stores in the mapping config\n\t\ttmpMappingConfig.TargetStores = tmpCheckedStoreIDs;\n\n\t\t// Persist discovered source columns so the SRC node loads correctly\n\t\t// on next open without requiring a separate API call.\n\t\tlet tmpSavedColumns = this._DiscoveredFields[tmpIDSource];\n\t\tif (Array.isArray(tmpSavedColumns) && tmpSavedColumns.length > 0)\n\t\t{\n\t\t\ttmpMappingConfig.sourceColumns = tmpSavedColumns;\n\t\t}\n\n\t\t// Get flow diagram state\n\t\tlet tmpFlowState = {};\n\t\tif (this._FlowView && typeof this._FlowView.getFlowData === 'function')\n\t\t{\n\t\t\ttmpFlowState = this._FlowView.getFlowData();\n\t\t}\n\n\t\tlet tmpData =\n\t\t{\n\t\t\tName: tmpName,\n\t\t\tIDSource: tmpIDSource,\n\t\t\tIDProjectionStore: tmpIDProjectionStore,\n\t\t\tMappingConfiguration: JSON.stringify(tmpMappingConfig),\n\t\t\tFlowDiagramState: JSON.stringify(tmpFlowState),\n\t\t\tActive: 1\n\t\t};\n\n\t\tlet tmpPromise;\n\t\tif (this._SelectedMappingID)\n\t\t{\n\t\t\ttmpPromise = this._doUpdateMapping(this._SelectedMappingID, tmpData);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmpPromise = this._doCreateMapping(this._EditingContextID, tmpData);\n\t\t}\n\n\t\ttmpPromise.then(\n\t\t\t(pResponse) =>\n\t\t\t{\n\t\t\t\tif (pResponse && pResponse.Error)\n\t\t\t\t{\n\t\t\t\t\tthis._doToast('Error: ' + pResponse.Error, {type: 'error'});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Update the selected mapping ID if it was a create\n\t\t\t\tif (pResponse && pResponse.Mapping && pResponse.Mapping.IDProjectionMapping)\n\t\t\t\t{\n\t\t\t\t\tthis._SelectedMappingID = pResponse.Mapping.IDProjectionMapping;\n\t\t\t\t}\n\n\t\t\t\tthis._doToast('Mapping saved.', {type: 'success'});\n\n\t\t\t\t// Refresh mapping list\n\t\t\t\tthis._doLoadMappings(this._EditingContextID).then(\n\t\t\t\t\t(pResult) =>\n\t\t\t\t\t{\n\t\t\t\t\t\tthis._CurrentMappings = (pResult && pResult.Mappings) ? pResult.Mappings : [];\n\t\t\t\t\t});\n\t\t\t});\n\t}\n\n}\n\nmodule.exports = MeadowMappingEditorView;\n\nmodule.exports.default_configuration = _ViewConfiguration;\n","const libPictFlowCard = require('pict-section-flow').PictFlowCard;\n\n/**\n * FlowCard-MappingSource\n *\n * Represents a source dataset in the mapping flow.\n * Output ports are dynamically generated from discovered fields.\n */\nclass FlowCardMappingSource extends libPictFlowCard\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({},\n\t\t\t{\n\t\t\t\tTitle: 'Mapping Source',\n\t\t\t\tName: 'Mapping Source',\n\t\t\t\tCode: 'SRC',\n\t\t\t\tCategory: 'Data Source',\n\t\t\t\tDescription: 'Source dataset with discovered record fields',\n\t\t\t\tTitleBarColor: '#2980b9',\n\t\t\t\tWidth: 200,\n\t\t\t\tHeight: 100,\n\t\t\t\tInputs: [],\n\t\t\t\tOutputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Whole Record', Side: 'right' }\n\t\t\t\t],\n\t\t\t\tShowTypeLabel: true,\n\t\t\t\tPortLabelsOnHover: false,\n\t\t\t\tPortLabelsOutside: true\n\t\t\t},\n\t\t\tpOptions);\n\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'FlowCardMappingSource';\n\t}\n}\n\nmodule.exports = FlowCardMappingSource;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Mapping Source',\n\tCode: 'SRC',\n\tCategory: 'Data Source',\n\tTitleBarColor: '#2980b9',\n\tWidth: 200,\n\tHeight: 100\n};\n","const libPictFlowCard = require('pict-section-flow').PictFlowCard;\n\n/**\n * FlowCard-MappingTarget\n *\n * Represents the mapping target table in the mapping flow.\n * Input ports are dynamically generated from schema columns.\n */\nclass FlowCardMappingTarget extends libPictFlowCard\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({},\n\t\t\t{\n\t\t\t\tTitle: 'Mapping Target',\n\t\t\t\tName: 'Mapping Target',\n\t\t\t\tCode: 'TGT',\n\t\t\t\tCategory: 'Data Target',\n\t\t\t\tDescription: 'Mapping target table with schema columns',\n\t\t\t\tTitleBarColor: '#27ae60',\n\t\t\t\tWidth: 200,\n\t\t\t\tHeight: 100,\n\t\t\t\tInputs:\n\t\t\t\t[\n\t\t\t\t],\n\t\t\t\tOutputs: [],\n\t\t\t\tShowTypeLabel: true,\n\t\t\t\tPortLabelsOnHover: false,\n\t\t\t\tPortLabelsOutside: true\n\t\t\t},\n\t\t\tpOptions);\n\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'FlowCardMappingTarget';\n\t}\n}\n\nmodule.exports = FlowCardMappingTarget;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Mapping Target',\n\tCode: 'TGT',\n\tCategory: 'Data Target',\n\tTitleBarColor: '#27ae60',\n\tWidth: 200,\n\tHeight: 100\n};\n","const libPictFlowCard = require('pict-section-flow').PictFlowCard;\n\n/**\n * FlowCard-SolverExpression\n *\n * A transform card that applies a Fable ExpressionParser solver expression\n * to compute a derived value from the whole incoming record. Connect the\n * Source \"Whole Record\" output to this card's input, then connect this\n * card's output to a target column.\n *\n * Double-click the node to edit the solver expression in the properties panel.\n *\n * @class FlowCardSolverExpression\n * @extends PictFlowCard\n */\nclass FlowCardSolverExpression extends libPictFlowCard\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({},\n\t\t\t{\n\t\t\t\tTitle: 'Solver Expression',\n\t\t\t\tName: 'Solver Expression',\n\t\t\t\tCode: 'SOL',\n\t\t\t\tCategory: 'Transform',\n\t\t\t\tDescription: 'Apply a Fable solver expression for conditional logic and computed values',\n\t\t\t\tTitleBarColor: '#d35400',\n\t\t\t\tWidth: 220,\n\t\t\t\tHeight: 90,\n\t\t\t\tInputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Whole Record', Side: 'left' }\n\t\t\t\t],\n\t\t\t\tOutputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Result', Side: 'right' }\n\t\t\t\t],\n\t\t\t\tShowTypeLabel: true,\n\t\t\t\tPortLabelsOnHover: false,\n\t\t\t\tPortLabelsOutside: true,\n\t\t\t\tLabelsInFront: true,\n\t\t\t\tEnabled: true,\n\t\t\t\tBodyContent:\n\t\t\t\t{\n\t\t\t\t\tContentType: 'html',\n\t\t\t\t\tTemplate: '<div style=\"font-size:10px; padding:2px 4px; color:#ccc; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; max-width:200px;\">{~D:Record.Data.SolverExpression~}</div>'\n\t\t\t\t},\n\t\t\t\tPropertiesPanel:\n\t\t\t\t{\n\t\t\t\t\tPanelType: 'Template',\n\t\t\t\t\tDefaultWidth: 420,\n\t\t\t\t\tDefaultHeight: 160,\n\t\t\t\t\tTitle: 'Solver Expression',\n\t\t\t\t\tConfiguration:\n\t\t\t\t\t{\n\t\t\t\t\t\tTemplate: '<div style=\"padding:8px;\"><label style=\"display:block; margin-bottom:4px; font-weight:bold; font-size:12px;\">Solver Expression</label><textarea id=\"FlowCard-SOL-{~D:Record.Hash~}\" style=\"width:100%; height:80px; font-family:monospace; font-size:12px; resize:vertical; background:#1a1a2e; color:#e0e0e0; border:1px solid #444; border-radius:4px; padding:6px;\" onchange=\"(function(el){var n=pict.views[\\'MeadowMapping-Flow\\'];if(n&&n._FlowData){for(var i=0;i<n._FlowData.Nodes.length;i++){if(n._FlowData.Nodes[i].Hash===\\'{~D:Record.Hash~}\\'){n._FlowData.Nodes[i].Data.SolverExpression=el.value;if(typeof n.renderFlow===\\'function\\')n.renderFlow();break;}}}})(this)\">{~D:Record.Data.SolverExpression~}</textarea><div style=\"font-size:10px; color:#888; margin-top:4px;\">Example: IF(IncomingRecord.Type == \\'Premium\\', \\'GOLD\\', \\'SILVER\\')</div></div>'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tpOptions);\n\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'FlowCardSolverExpression';\n\t}\n}\n\nmodule.exports = FlowCardSolverExpression;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Solver Expression',\n\tCode: 'SOL',\n\tCategory: 'Transform',\n\tTitleBarColor: '#d35400',\n\tWidth: 220,\n\tHeight: 90\n};\n","const libPictFlowCard = require('pict-section-flow').PictFlowCard;\n\n/**\n * FlowCard-TemplateExpression\n *\n * A transform card that applies a Manyfest template expression to the\n * whole incoming record. Connect the Source \"Whole Record\" output to this\n * card's input, then connect this card's output to a target column.\n *\n * Double-click the node to edit the template expression in the properties panel.\n *\n * @class FlowCardTemplateExpression\n * @extends PictFlowCard\n */\nclass FlowCardTemplateExpression extends libPictFlowCard\n{\n\tconstructor(pFable, pOptions, pServiceHash)\n\t{\n\t\tlet tmpOptions = Object.assign({},\n\t\t\t{\n\t\t\t\tTitle: 'Template Expression',\n\t\t\t\tName: 'Template Expression',\n\t\t\t\tCode: 'TPL',\n\t\t\t\tCategory: 'Transform',\n\t\t\t\tDescription: 'Apply a Manyfest template expression to map source fields to a target column',\n\t\t\t\tTitleBarColor: '#8e44ad',\n\t\t\t\tWidth: 220,\n\t\t\t\tHeight: 90,\n\t\t\t\tInputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Whole Record', Side: 'left' }\n\t\t\t\t],\n\t\t\t\tOutputs:\n\t\t\t\t[\n\t\t\t\t\t{ Name: 'Result', Side: 'right' }\n\t\t\t\t],\n\t\t\t\tShowTypeLabel: true,\n\t\t\t\tPortLabelsOnHover: false,\n\t\t\t\tPortLabelsOutside: true,\n\t\t\t\tLabelsInFront: true,\n\t\t\t\tEnabled: true,\n\t\t\t\tBodyContent:\n\t\t\t\t{\n\t\t\t\t\tContentType: 'html',\n\t\t\t\t\tTemplate: '<div style=\"font-size:10px; padding:2px 4px; color:#ccc; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; max-width:200px;\">{~D:Record.Data.TemplateExpression~}</div>'\n\t\t\t\t},\n\t\t\t\tPropertiesPanel:\n\t\t\t\t{\n\t\t\t\t\tPanelType: 'Template',\n\t\t\t\t\tDefaultWidth: 420,\n\t\t\t\t\tDefaultHeight: 160,\n\t\t\t\t\tTitle: 'Template Expression',\n\t\t\t\t\tConfiguration:\n\t\t\t\t\t{\n\t\t\t\t\t\tTemplate: '<div style=\"padding:8px;\"><label style=\"display:block; margin-bottom:4px; font-weight:bold; font-size:12px;\">Template Expression</label><textarea id=\"FlowCard-TPL-{~D:Record.Hash~}\" style=\"width:100%; height:80px; font-family:monospace; font-size:12px; resize:vertical; background:#1a1a2e; color:#e0e0e0; border:1px solid #444; border-radius:4px; padding:6px;\" onchange=\"(function(el){var n=pict.views[\\'MeadowMapping-Flow\\'];if(n&&n._FlowData){for(var i=0;i<n._FlowData.Nodes.length;i++){if(n._FlowData.Nodes[i].Hash===\\'{~D:Record.Hash~}\\'){n._FlowData.Nodes[i].Data.TemplateExpression=el.value;if(typeof n.renderFlow===\\'function\\')n.renderFlow();break;}}}})(this)\">{~D:Record.Data.TemplateExpression~}</textarea><div style=\"font-size:10px; color:#888; margin-top:4px;\">Use {~D:Record.FieldName~} syntax. Example: {~D:Record.Name~} in {~D:Record.City~}</div></div>'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tpOptions);\n\n\t\tsuper(pFable, tmpOptions, pServiceHash);\n\n\t\tthis.serviceType = 'FlowCardTemplateExpression';\n\t}\n}\n\nmodule.exports = FlowCardTemplateExpression;\n\nmodule.exports.default_configuration =\n{\n\tTitle: 'Template Expression',\n\tCode: 'TPL',\n\tCategory: 'Transform',\n\tTitleBarColor: '#8e44ad',\n\tWidth: 220,\n\tHeight: 90\n};\n"]}