@trops/dash-core 0.1.158 → 0.1.160

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.
package/dist/index.esm.js CHANGED
@@ -26695,7 +26695,7 @@ var useWidgetProviders = function useWidgetProviders() {
26695
26695
  var serverStates = new Map();
26696
26696
  // Map<serverName, { status, tools, resources, consumerCount }>
26697
26697
 
26698
- var pendingConnects = new Map();
26698
+ var pendingConnects$1 = new Map();
26699
26699
  // Map<serverName, Promise<result>>
26700
26700
 
26701
26701
  /**
@@ -26910,13 +26910,13 @@ var useMcpProvider = function useMcpProvider(providerType) {
26910
26910
  setError(null);
26911
26911
 
26912
26912
  // 2. Another hook instance already connecting? Piggyback on its promise
26913
- if (!pendingConnects.has(selectedProviderName)) {
26913
+ if (!pendingConnects$1.has(selectedProviderName)) {
26914
26914
  _context.next = 17;
26915
26915
  break;
26916
26916
  }
26917
26917
  _context.prev = 10;
26918
26918
  _context.next = 11;
26919
- return pendingConnects.get(selectedProviderName);
26919
+ return pendingConnects$1.get(selectedProviderName);
26920
26920
  case 11:
26921
26921
  result = _context.sent;
26922
26922
  if (mountedRef.current) {
@@ -26958,7 +26958,7 @@ var useMcpProvider = function useMcpProvider(providerType) {
26958
26958
  // 3. First caller — fire the IPC call and share the promise
26959
26959
  connectPromise = new Promise(function (resolve, reject) {
26960
26960
  dashApi.mcpStartServer(selectedProviderName, provider.mcpConfig, provider.credentials, function (event, result) {
26961
- pendingConnects["delete"](selectedProviderName);
26961
+ pendingConnects$1["delete"](selectedProviderName);
26962
26962
  if (result.error) {
26963
26963
  serverStates.set(selectedProviderName, {
26964
26964
  status: "error",
@@ -26979,7 +26979,7 @@ var useMcpProvider = function useMcpProvider(providerType) {
26979
26979
  });
26980
26980
  resolve(result);
26981
26981
  }, function (event, err) {
26982
- pendingConnects["delete"](selectedProviderName);
26982
+ pendingConnects$1["delete"](selectedProviderName);
26983
26983
  serverStates.set(selectedProviderName, {
26984
26984
  status: "error",
26985
26985
  tools: [],
@@ -26989,7 +26989,7 @@ var useMcpProvider = function useMcpProvider(providerType) {
26989
26989
  reject(err);
26990
26990
  });
26991
26991
  });
26992
- pendingConnects.set(selectedProviderName, connectPromise);
26992
+ pendingConnects$1.set(selectedProviderName, connectPromise);
26993
26993
  _context.prev = 18;
26994
26994
  _context.next = 19;
26995
26995
  return connectPromise;
@@ -27075,7 +27075,7 @@ var useMcpProvider = function useMcpProvider(providerType) {
27075
27075
  setResources([]);
27076
27076
  setStatus("disconnected");
27077
27077
  connectedRef.current = false;
27078
- pendingConnects["delete"](selectedProviderName);
27078
+ pendingConnects$1["delete"](selectedProviderName);
27079
27079
  return _context2.abrupt("return", new Promise(function (resolve) {
27080
27080
  dashApi.mcpStopServer(selectedProviderName, function () {
27081
27081
  return resolve();
@@ -27517,6 +27517,528 @@ var useScheduler = function useScheduler() {
27517
27517
  };
27518
27518
  };
27519
27519
 
27520
+ /**
27521
+ * Module-level shared state for WebSocket connections.
27522
+ * Prevents multiple hook instances (e.g., 4 widgets using the same WS provider)
27523
+ * from each firing their own IPC connect call.
27524
+ *
27525
+ * connectionStates: tracks connection status + consumer reference count per provider
27526
+ * pendingConnects: deduplicates in-flight IPC calls so only 1 fires per provider
27527
+ */
27528
+ var connectionStates = new Map();
27529
+ // Map<providerName, { status, consumerCount }>
27530
+
27531
+ var pendingConnects = new Map();
27532
+ // Map<providerName, Promise<result>>
27533
+
27534
+ var STATUS = {
27535
+ DISCONNECTED: "disconnected",
27536
+ CONNECTING: "connecting",
27537
+ CONNECTED: "connected",
27538
+ ERROR: "error"
27539
+ };
27540
+
27541
+ /**
27542
+ * useWebSocketProvider Hook
27543
+ *
27544
+ * Provides access to a WebSocket connection for a widget.
27545
+ * Handles connection lifecycle, shared-connection ref counting,
27546
+ * message buffering, and bidirectional communication.
27547
+ *
27548
+ * Mirrors useMcpProvider.js patterns exactly:
27549
+ * - Module-level Maps (not component state) for connection sharing
27550
+ * - consumerCount determines socket lifecycle
27551
+ * - pendingConnects prevents duplicate connect calls during mount storms
27552
+ *
27553
+ * @param {string} providerType - The WebSocket provider type (e.g., "crypto-ws")
27554
+ * @param {Object} options - Optional configuration
27555
+ * @param {boolean} options.autoConnect - Whether to auto-connect on mount (default: true)
27556
+ * @param {number} options.maxMessages - Max messages in buffer (default: 100)
27557
+ *
27558
+ * @returns {Object} WebSocket provider interface
27559
+ */
27560
+ var useWebSocketProvider = function useWebSocketProvider(providerType) {
27561
+ var _app$providers;
27562
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
27563
+ var _options$autoConnect = options.autoConnect,
27564
+ autoConnect = _options$autoConnect === void 0 ? true : _options$autoConnect,
27565
+ _options$maxMessages = options.maxMessages,
27566
+ maxMessages = _options$maxMessages === void 0 ? 100 : _options$maxMessages;
27567
+ var app = useContext(AppContext);
27568
+ var workspace = useContext(WorkspaceContext);
27569
+ var widgetContext = useContext(WidgetContext);
27570
+ var _useState = useState(false),
27571
+ _useState2 = _slicedToArray(_useState, 2),
27572
+ isConnected = _useState2[0],
27573
+ setIsConnected = _useState2[1];
27574
+ var _useState3 = useState(false),
27575
+ _useState4 = _slicedToArray(_useState3, 2),
27576
+ isConnecting = _useState4[0],
27577
+ setIsConnecting = _useState4[1];
27578
+ var _useState5 = useState(null),
27579
+ _useState6 = _slicedToArray(_useState5, 2),
27580
+ error = _useState6[0],
27581
+ setError = _useState6[1];
27582
+ var _useState7 = useState(null),
27583
+ _useState8 = _slicedToArray(_useState7, 2),
27584
+ lastMessage = _useState8[0],
27585
+ setLastMessage = _useState8[1];
27586
+ var _useState9 = useState([]),
27587
+ _useState0 = _slicedToArray(_useState9, 2),
27588
+ messages = _useState0[0],
27589
+ setMessages = _useState0[1];
27590
+ var _useState1 = useState(STATUS.DISCONNECTED),
27591
+ _useState10 = _slicedToArray(_useState1, 2),
27592
+ status = _useState10[0],
27593
+ setStatus = _useState10[1];
27594
+ var connectedRef = useRef(false);
27595
+ var mountedRef = useRef(true);
27596
+ var messagesRef = useRef([]);
27597
+ var maxMessagesRef = useRef(maxMessages);
27598
+ maxMessagesRef.current = maxMessages;
27599
+ var dashApi = app === null || app === void 0 ? void 0 : app.dashApi;
27600
+
27601
+ // Get the widget data
27602
+ var widgetData = widgetContext === null || widgetContext === void 0 ? void 0 : widgetContext.widgetData;
27603
+
27604
+ // Get the selected WebSocket provider for this widget
27605
+ // Same two-layer lookup as useMcpProvider:
27606
+ // 1. Widget-level: stored directly on the layout item
27607
+ // 2. Workspace-level: stored as workspace.selectedProviders[widgetId][providerType]
27608
+ var widgetId = widgetData === null || widgetData === void 0 ? void 0 : widgetData.uuidString;
27609
+ var selectedProviderName = function (_widgetData$selectedP, _workspace$workspaceD) {
27610
+ if (widgetData !== null && widgetData !== void 0 && (_widgetData$selectedP = widgetData.selectedProviders) !== null && _widgetData$selectedP !== void 0 && _widgetData$selectedP[providerType]) {
27611
+ return widgetData.selectedProviders[providerType];
27612
+ }
27613
+ if (widgetId && workspace !== null && workspace !== void 0 && (_workspace$workspaceD = workspace.workspaceData) !== null && _workspace$workspaceD !== void 0 && (_workspace$workspaceD = _workspace$workspaceD.selectedProviders) !== null && _workspace$workspaceD !== void 0 && (_workspace$workspaceD = _workspace$workspaceD[widgetId]) !== null && _workspace$workspaceD !== void 0 && _workspace$workspaceD[providerType]) {
27614
+ return workspace.workspaceData.selectedProviders[widgetId][providerType];
27615
+ }
27616
+ return null;
27617
+ }();
27618
+
27619
+ // Get the provider data (including credentials)
27620
+ // Read from AppContext.providers (not DashboardContext)
27621
+ var provider = selectedProviderName ? app === null || app === void 0 || (_app$providers = app.providers) === null || _app$providers === void 0 ? void 0 : _app$providers[selectedProviderName] : null;
27622
+
27623
+ /**
27624
+ * Apply connection result to this hook instance's local state.
27625
+ */
27626
+ var applyConnected = useCallback(function () {
27627
+ if (!mountedRef.current) return;
27628
+ setIsConnected(true);
27629
+ setIsConnecting(false);
27630
+ setStatus(STATUS.CONNECTED);
27631
+ connectedRef.current = true;
27632
+ }, []);
27633
+
27634
+ /**
27635
+ * Connect to the WebSocket server.
27636
+ * Uses module-level deduplication so only one IPC call fires per provider,
27637
+ * even when multiple hook instances call connect() simultaneously.
27638
+ */
27639
+ var connect = useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
27640
+ var _provider$wsConfig;
27641
+ var cached, statusResult, result, state, connectPromise, _result2, _t3, _t4;
27642
+ return _regeneratorRuntime.wrap(function (_context2) {
27643
+ while (1) switch (_context2.prev = _context2.next) {
27644
+ case 0:
27645
+ if (!connectedRef.current) {
27646
+ _context2.next = 1;
27647
+ break;
27648
+ }
27649
+ return _context2.abrupt("return");
27650
+ case 1:
27651
+ if (!(!(dashApi !== null && dashApi !== void 0 && dashApi.webSocket) || !provider)) {
27652
+ _context2.next = 2;
27653
+ break;
27654
+ }
27655
+ setError(!provider ? "No ".concat(providerType, " WebSocket provider selected for this widget") : "Dashboard API not available");
27656
+ return _context2.abrupt("return");
27657
+ case 2:
27658
+ if (!(provider.providerClass !== "websocket")) {
27659
+ _context2.next = 3;
27660
+ break;
27661
+ }
27662
+ setError("Provider \"".concat(selectedProviderName, "\" is not a WebSocket provider"));
27663
+ return _context2.abrupt("return");
27664
+ case 3:
27665
+ if ((_provider$wsConfig = provider.wsConfig) !== null && _provider$wsConfig !== void 0 && _provider$wsConfig.url) {
27666
+ _context2.next = 4;
27667
+ break;
27668
+ }
27669
+ setError("Provider \"".concat(selectedProviderName, "\" has no WebSocket URL configured"));
27670
+ return _context2.abrupt("return");
27671
+ case 4:
27672
+ // 1. Already connected at module level? Verify with main process.
27673
+ cached = connectionStates.get(selectedProviderName);
27674
+ if (!(cached && cached.status === STATUS.CONNECTED)) {
27675
+ _context2.next = 9;
27676
+ break;
27677
+ }
27678
+ _context2.prev = 5;
27679
+ _context2.next = 6;
27680
+ return dashApi.webSocket.getStatus(selectedProviderName);
27681
+ case 6:
27682
+ statusResult = _context2.sent;
27683
+ if (!((statusResult === null || statusResult === void 0 ? void 0 : statusResult.status) === "connected")) {
27684
+ _context2.next = 7;
27685
+ break;
27686
+ }
27687
+ cached.consumerCount++;
27688
+ applyConnected();
27689
+ return _context2.abrupt("return");
27690
+ case 7:
27691
+ // Server was stopped externally — clear stale cache and reconnect
27692
+ connectionStates["delete"](selectedProviderName);
27693
+ _context2.next = 9;
27694
+ break;
27695
+ case 8:
27696
+ _context2.prev = 8;
27697
+ _context2["catch"](5);
27698
+ connectionStates["delete"](selectedProviderName);
27699
+ case 9:
27700
+ setIsConnecting(true);
27701
+ setError(null);
27702
+ setStatus(STATUS.CONNECTING);
27703
+
27704
+ // 2. Another hook instance already connecting? Piggyback on its promise
27705
+ if (!pendingConnects.has(selectedProviderName)) {
27706
+ _context2.next = 17;
27707
+ break;
27708
+ }
27709
+ _context2.prev = 10;
27710
+ _context2.next = 11;
27711
+ return pendingConnects.get(selectedProviderName);
27712
+ case 11:
27713
+ result = _context2.sent;
27714
+ if (mountedRef.current) {
27715
+ _context2.next = 12;
27716
+ break;
27717
+ }
27718
+ return _context2.abrupt("return");
27719
+ case 12:
27720
+ if (!result.error) {
27721
+ _context2.next = 13;
27722
+ break;
27723
+ }
27724
+ setError(result.message);
27725
+ setIsConnecting(false);
27726
+ setStatus(STATUS.ERROR);
27727
+ return _context2.abrupt("return");
27728
+ case 13:
27729
+ state = connectionStates.get(selectedProviderName);
27730
+ if (state) state.consumerCount++;
27731
+ applyConnected();
27732
+ _context2.next = 16;
27733
+ break;
27734
+ case 14:
27735
+ _context2.prev = 14;
27736
+ _t3 = _context2["catch"](10);
27737
+ if (mountedRef.current) {
27738
+ _context2.next = 15;
27739
+ break;
27740
+ }
27741
+ return _context2.abrupt("return");
27742
+ case 15:
27743
+ setError((_t3 === null || _t3 === void 0 ? void 0 : _t3.message) || "Failed to connect to WebSocket server");
27744
+ setIsConnecting(false);
27745
+ setStatus(STATUS.ERROR);
27746
+ case 16:
27747
+ return _context2.abrupt("return");
27748
+ case 17:
27749
+ // 3. First caller — fire the IPC call and share the promise
27750
+ connectPromise = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
27751
+ var _result, _t;
27752
+ return _regeneratorRuntime.wrap(function (_context) {
27753
+ while (1) switch (_context.prev = _context.next) {
27754
+ case 0:
27755
+ _context.prev = 0;
27756
+ _context.next = 1;
27757
+ return dashApi.webSocket.connect(selectedProviderName, {
27758
+ url: provider.wsConfig.url,
27759
+ headers: provider.wsConfig.headers || null,
27760
+ subprotocols: provider.wsConfig.subprotocols || null,
27761
+ credentials: provider.credentials || null
27762
+ });
27763
+ case 1:
27764
+ _result = _context.sent;
27765
+ pendingConnects["delete"](selectedProviderName);
27766
+ if (!_result.error) {
27767
+ _context.next = 2;
27768
+ break;
27769
+ }
27770
+ connectionStates.set(selectedProviderName, {
27771
+ status: STATUS.ERROR,
27772
+ consumerCount: 0
27773
+ });
27774
+ return _context.abrupt("return", _result);
27775
+ case 2:
27776
+ connectionStates.set(selectedProviderName, {
27777
+ status: STATUS.CONNECTED,
27778
+ consumerCount: 1
27779
+ });
27780
+ return _context.abrupt("return", _result);
27781
+ case 3:
27782
+ _context.prev = 3;
27783
+ _t = _context["catch"](0);
27784
+ pendingConnects["delete"](selectedProviderName);
27785
+ connectionStates.set(selectedProviderName, {
27786
+ status: STATUS.ERROR,
27787
+ consumerCount: 0
27788
+ });
27789
+ throw _t;
27790
+ case 4:
27791
+ case "end":
27792
+ return _context.stop();
27793
+ }
27794
+ }, _callee, null, [[0, 3]]);
27795
+ }))();
27796
+ pendingConnects.set(selectedProviderName, connectPromise);
27797
+ _context2.prev = 18;
27798
+ _context2.next = 19;
27799
+ return connectPromise;
27800
+ case 19:
27801
+ _result2 = _context2.sent;
27802
+ if (mountedRef.current) {
27803
+ _context2.next = 20;
27804
+ break;
27805
+ }
27806
+ return _context2.abrupt("return");
27807
+ case 20:
27808
+ if (!_result2.error) {
27809
+ _context2.next = 21;
27810
+ break;
27811
+ }
27812
+ setError(_result2.message);
27813
+ setIsConnecting(false);
27814
+ setStatus(STATUS.ERROR);
27815
+ return _context2.abrupt("return");
27816
+ case 21:
27817
+ applyConnected();
27818
+ _context2.next = 24;
27819
+ break;
27820
+ case 22:
27821
+ _context2.prev = 22;
27822
+ _t4 = _context2["catch"](18);
27823
+ if (mountedRef.current) {
27824
+ _context2.next = 23;
27825
+ break;
27826
+ }
27827
+ return _context2.abrupt("return");
27828
+ case 23:
27829
+ setError((_t4 === null || _t4 === void 0 ? void 0 : _t4.message) || "Failed to connect to WebSocket server");
27830
+ setIsConnecting(false);
27831
+ setStatus(STATUS.ERROR);
27832
+ case 24:
27833
+ case "end":
27834
+ return _context2.stop();
27835
+ }
27836
+ }, _callee2, null, [[5, 8], [10, 14], [18, 22]]);
27837
+ })), [dashApi, provider, providerType, selectedProviderName, applyConnected]);
27838
+
27839
+ /**
27840
+ * Disconnect from the WebSocket server.
27841
+ * Only sends the IPC disconnect call when this is the last consumer.
27842
+ */
27843
+ var disconnect = useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
27844
+ var state;
27845
+ return _regeneratorRuntime.wrap(function (_context3) {
27846
+ while (1) switch (_context3.prev = _context3.next) {
27847
+ case 0:
27848
+ if (!(!(dashApi !== null && dashApi !== void 0 && dashApi.webSocket) || !selectedProviderName)) {
27849
+ _context3.next = 1;
27850
+ break;
27851
+ }
27852
+ return _context3.abrupt("return");
27853
+ case 1:
27854
+ state = connectionStates.get(selectedProviderName);
27855
+ if (!state) {
27856
+ _context3.next = 3;
27857
+ break;
27858
+ }
27859
+ state.consumerCount = Math.max(0, state.consumerCount - 1);
27860
+ if (!(state.consumerCount > 0)) {
27861
+ _context3.next = 2;
27862
+ break;
27863
+ }
27864
+ // Other widgets still using this connection — just update local state
27865
+ setIsConnected(false);
27866
+ setLastMessage(null);
27867
+ setMessages([]);
27868
+ messagesRef.current = [];
27869
+ setStatus(STATUS.DISCONNECTED);
27870
+ connectedRef.current = false;
27871
+ return _context3.abrupt("return");
27872
+ case 2:
27873
+ // Last consumer — actually disconnect
27874
+ connectionStates["delete"](selectedProviderName);
27875
+ case 3:
27876
+ // Clear state synchronously before the IPC call
27877
+ setIsConnected(false);
27878
+ setLastMessage(null);
27879
+ setMessages([]);
27880
+ messagesRef.current = [];
27881
+ setStatus(STATUS.DISCONNECTED);
27882
+ connectedRef.current = false;
27883
+ pendingConnects["delete"](selectedProviderName);
27884
+ _context3.prev = 4;
27885
+ _context3.next = 5;
27886
+ return dashApi.webSocket.disconnect(selectedProviderName);
27887
+ case 5:
27888
+ _context3.next = 7;
27889
+ break;
27890
+ case 6:
27891
+ _context3.prev = 6;
27892
+ _context3["catch"](4);
27893
+ case 7:
27894
+ case "end":
27895
+ return _context3.stop();
27896
+ }
27897
+ }, _callee3, null, [[4, 6]]);
27898
+ })), [dashApi, selectedProviderName]);
27899
+
27900
+ /**
27901
+ * Send data through the WebSocket connection
27902
+ */
27903
+ var send = useCallback(/*#__PURE__*/function () {
27904
+ var _ref4 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee4(data) {
27905
+ var result;
27906
+ return _regeneratorRuntime.wrap(function (_context4) {
27907
+ while (1) switch (_context4.prev = _context4.next) {
27908
+ case 0:
27909
+ if (!(!(dashApi !== null && dashApi !== void 0 && dashApi.webSocket) || !selectedProviderName)) {
27910
+ _context4.next = 1;
27911
+ break;
27912
+ }
27913
+ throw new Error("WebSocket not connected");
27914
+ case 1:
27915
+ _context4.next = 2;
27916
+ return dashApi.webSocket.send(selectedProviderName, data);
27917
+ case 2:
27918
+ result = _context4.sent;
27919
+ if (!result.error) {
27920
+ _context4.next = 3;
27921
+ break;
27922
+ }
27923
+ throw new Error(result.message);
27924
+ case 3:
27925
+ return _context4.abrupt("return", result);
27926
+ case 4:
27927
+ case "end":
27928
+ return _context4.stop();
27929
+ }
27930
+ }, _callee4);
27931
+ }));
27932
+ return function (_x) {
27933
+ return _ref4.apply(this, arguments);
27934
+ };
27935
+ }(), [dashApi, selectedProviderName]);
27936
+
27937
+ // Keep a ref to connect so the auto-connect effect doesn't depend on it
27938
+ var connectRef = useRef(connect);
27939
+ connectRef.current = connect;
27940
+
27941
+ // Listen for incoming messages from main process
27942
+ useEffect(function () {
27943
+ if (!(dashApi !== null && dashApi !== void 0 && dashApi.webSocket) || !selectedProviderName) return;
27944
+ var handleMessage = function handleMessage(_event, payload) {
27945
+ if (payload.provider !== selectedProviderName) return;
27946
+ if (!mountedRef.current) return;
27947
+ var msg = payload.data;
27948
+
27949
+ // Update circular buffer
27950
+ var next = [].concat(_toConsumableArray(messagesRef.current), [msg]);
27951
+ if (next.length > maxMessagesRef.current) {
27952
+ next.splice(0, next.length - maxMessagesRef.current);
27953
+ }
27954
+ messagesRef.current = next;
27955
+ setMessages(next);
27956
+ setLastMessage(msg);
27957
+ };
27958
+ dashApi.webSocket.onMessage(handleMessage);
27959
+ return function () {
27960
+ return dashApi.webSocket.offMessage(handleMessage);
27961
+ };
27962
+ }, [dashApi, selectedProviderName]);
27963
+
27964
+ // Listen for status changes from main process
27965
+ useEffect(function () {
27966
+ if (!(dashApi !== null && dashApi !== void 0 && dashApi.webSocket) || !selectedProviderName) return;
27967
+ var handleStatusChange = function handleStatusChange(_event, payload) {
27968
+ if (payload.provider !== selectedProviderName) return;
27969
+ if (!mountedRef.current) return;
27970
+ var newStatus = payload.status;
27971
+ setStatus(newStatus);
27972
+ if (newStatus === STATUS.CONNECTED) {
27973
+ setIsConnected(true);
27974
+ setIsConnecting(false);
27975
+ setError(null);
27976
+ connectedRef.current = true;
27977
+ } else if (newStatus === STATUS.DISCONNECTED) {
27978
+ setIsConnected(false);
27979
+ setIsConnecting(false);
27980
+ connectedRef.current = false;
27981
+ } else if (newStatus === STATUS.ERROR) {
27982
+ setIsConnected(false);
27983
+ setIsConnecting(false);
27984
+ setError(payload.error || "WebSocket error");
27985
+ connectedRef.current = false;
27986
+ } else if (newStatus === STATUS.CONNECTING) {
27987
+ setIsConnecting(true);
27988
+ }
27989
+ };
27990
+ dashApi.webSocket.onStatusChange(handleStatusChange);
27991
+ return function () {
27992
+ return dashApi.webSocket.offStatusChange(handleStatusChange);
27993
+ };
27994
+ }, [dashApi, selectedProviderName]);
27995
+
27996
+ // Auto-connect on mount or when provider selection changes
27997
+ useEffect(function () {
27998
+ if (autoConnect && selectedProviderName && !connectedRef.current) {
27999
+ connectRef.current();
28000
+ }
28001
+ // eslint-disable-next-line react-hooks/exhaustive-deps
28002
+ }, [autoConnect, selectedProviderName]);
28003
+
28004
+ // Track mounted state and cleanup on unmount
28005
+ useEffect(function () {
28006
+ mountedRef.current = true;
28007
+ return function () {
28008
+ mountedRef.current = false;
28009
+
28010
+ // Decrement consumer count; only disconnect if last consumer
28011
+ if (connectedRef.current && dashApi !== null && dashApi !== void 0 && dashApi.webSocket && selectedProviderName) {
28012
+ var state = connectionStates.get(selectedProviderName);
28013
+ if (state) {
28014
+ state.consumerCount = Math.max(0, state.consumerCount - 1);
28015
+ if (state.consumerCount > 0) {
28016
+ // Other widgets still using this connection — don't disconnect
28017
+ return;
28018
+ }
28019
+
28020
+ // Last consumer — disconnect
28021
+ connectionStates["delete"](selectedProviderName);
28022
+ }
28023
+ dashApi.webSocket.disconnect(selectedProviderName)["catch"](function () {});
28024
+ }
28025
+ };
28026
+ }, [dashApi, selectedProviderName]);
28027
+ return {
28028
+ isConnected: isConnected,
28029
+ isConnecting: isConnecting,
28030
+ error: error,
28031
+ lastMessage: lastMessage,
28032
+ messages: messages,
28033
+ send: send,
28034
+ connect: connect,
28035
+ disconnect: disconnect,
28036
+ status: status,
28037
+ provider: provider,
28038
+ serverName: selectedProviderName
28039
+ };
28040
+ };
28041
+
27520
28042
  function ownKeys$h(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
27521
28043
  function _objectSpread$h(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$h(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$h(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
27522
28044
  var PreviewComponentsPane = function PreviewComponentsPane(_ref) {
@@ -46274,5 +46796,5 @@ var WorkspaceMenu = function WorkspaceMenu(_ref) {
46274
46796
 
46275
46797
  ComponentManager.registerContainerTypes(LayoutContainer, LayoutGridContainer);
46276
46798
 
46277
- export { ALGOLIA_ANALYTICS_FOR_QUERY, ALGOLIA_ANALYTICS_FOR_QUERY_COMPLETE, ALGOLIA_ANALYTICS_FOR_QUERY_ERROR, ALGOLIA_LIST_INDICES, ALGOLIA_LIST_INDICES_COMPLETE, ALGOLIA_LIST_INDICES_ERROR, AVAILABLE_COLORS, AddMenuItemModal, AdvancedMcpConfig, AppContext, AppSettingsModal, AppThemeScope, AppWrapper, CHOOSE_FILE, CHOOSE_FILE_COMPLETE, CHOOSE_FILE_ERROR, ColorModel, ComponentConfigModel, ComponentManager, ContextModel, DATA_JSON_TO_CSV_FILE, DATA_JSON_TO_CSV_FILE_COMPLETE, DATA_JSON_TO_CSV_FILE_ERROR, DATA_JSON_TO_CSV_STRING, DATA_JSON_TO_CSV_STRING_COMPLETE, DATA_JSON_TO_CSV_STRING_ERROR, DATA_READ_FROM_FILE, DATA_READ_FROM_FILE_COMPLETE, DATA_READ_FROM_FILE_ERROR, DATA_SAVE_TO_FILE, DATA_SAVE_TO_FILE_COMPLETE, DATA_SAVE_TO_FILE_ERROR, DashCommandPalette, DashNavbar, DashSidebar, DashTabBar, DashboardStage as Dashboard, DashboardApi, DashboardContext, DashboardFooter, DashboardHeader, DashboardMenuItem, DashboardModel, DashboardMonitor, DashboardPublisher, DashboardStage, DashboardThemeProvider, DashboardWrapper, ElectronDashboardApi, ErrorBoundary, ExternalWidget, GRID_CELL_WIDGET_TYPE, HARMONY_STRATEGIES, LAYOUT_LIST, LAYOUT_LIST_COMPLETE, LAYOUT_LIST_ERROR, LAYOUT_SAVE, LAYOUT_SAVE_COMPLETE, LAYOUT_SAVE_ERROR, Layout, LayoutBuilder, LayoutBuilderAddItemModal, LayoutBuilderConfigContainerMenuItem, LayoutBuilderConfigMenuItem, LayoutBuilderConfigModal, LayoutBuilderEditItemModal, LayoutBuilderEventModal, LayoutBuilderGridItem, LayoutContainer, LayoutDragBuilder, LayoutDragBuilderEdit, LayoutGridContainer, LayoutManagerModal, LayoutModel, LayoutQuickAddMenu, MCP_CALL_TOOL_COMPLETE, MCP_CALL_TOOL_ERROR, MCP_GET_CATALOG_COMPLETE, MCP_GET_CATALOG_ERROR, MCP_LIST_RESOURCES_COMPLETE, MCP_LIST_RESOURCES_ERROR, MCP_LIST_TOOLS_COMPLETE, MCP_LIST_TOOLS_ERROR, MCP_READ_RESOURCE_COMPLETE, MCP_READ_RESOURCE_ERROR, MCP_RUN_AUTH_COMPLETE, MCP_RUN_AUTH_ERROR, MCP_SERVER_STATUS_COMPLETE, MCP_SERVER_STATUS_ERROR, MCP_START_SERVER_COMPLETE, MCP_START_SERVER_ERROR, MCP_STOP_SERVER_COMPLETE, MCP_STOP_SERVER_ERROR, MENU_ITEMS_DELETE, MENU_ITEMS_DELETE_COMPLETE, MENU_ITEMS_DELETE_ERROR, MENU_ITEMS_LIST, MENU_ITEMS_LIST_COMPLETE, MENU_ITEMS_LIST_ERROR, MENU_ITEMS_SAVE, MENU_ITEMS_SAVE_COMPLETE, MENU_ITEMS_SAVE_ERROR, MainMenu, MainMenuItem, MainMenuSection, McpServerPicker, MenuItemModel, MenuSlideOverlay, MergeCellsModal, MissingProviderPrompt, MockDashboardApi, PROVIDER_DELETE_COMPLETE, PROVIDER_DELETE_ERROR, PROVIDER_GET_COMPLETE, PROVIDER_GET_ERROR, PROVIDER_LIST_COMPLETE, PROVIDER_LIST_ERROR, PROVIDER_SAVE_COMPLETE, PROVIDER_SAVE_ERROR, PanelCode, PanelEditItem, PanelEditItemHandlers, PanelEditItemNotifications, ProviderContext, ProviderErrorBoundary, ProviderForm, ProviderSelector, SECURE_STORAGE_ENCRYPT_STRING, SECURE_STORAGE_ENCRYPT_STRING_COMPLETE, SECURE_STORAGE_ENCRYPT_STRING_ERROR, SECURE_STORE_ENCRYPTION_CHECK, SECURE_STORE_ENCRYPTION_CHECK_COMPLETE, SECURE_STORE_ENCRYPTION_CHECK_ERROR, SECURE_STORE_GET_DATA, SECURE_STORE_GET_DATA_COMPLETE, SECURE_STORE_GET_DATA_ERROR, SECURE_STORE_SET_DATA, SECURE_STORE_SET_DATA_COMPLETE, SECURE_STORE_SET_DATA_ERROR, SETTINGS_GET, SETTINGS_GET_COMPLETE, SETTINGS_GET_ERROR, SETTINGS_SAVE, SETTINGS_SAVE_COMPLETE, SETTINGS_SAVE_ERROR, SIDEBAR_WIDGET_TYPE, SettingsModel, SideMenu, SplitCellModal, THEME_DELETE, THEME_DELETE_COMPLETE, THEME_DELETE_ERROR, THEME_EXTRACT_FROM_URL, THEME_EXTRACT_FROM_URL_COMPLETE, THEME_EXTRACT_FROM_URL_ERROR, THEME_LIST, THEME_LIST_COMPLETE, THEME_LIST_ERROR, THEME_MAP_PALETTE, THEME_MAP_PALETTE_COMPLETE, THEME_MAP_PALETTE_ERROR, THEME_SAVE, THEME_SAVE_COMPLETE, THEME_SAVE_ERROR, ThemeApi, ThemeColorDots, ThemeManagerModal, ThemeModel, ThemeWrapper, WORKSPACE_DELETE, WORKSPACE_DELETE_COMPLETE, WORKSPACE_DELETE_ERROR, WORKSPACE_LIST, WORKSPACE_LIST_COMPLETE, WORKSPACE_LIST_ERROR, WORKSPACE_SAVE, WORKSPACE_SAVE_COMPLETE, WORKSPACE_SAVE_ERROR, WebDashboardApi, Widget, WidgetApi, WidgetConfigPanel, WidgetContext, WidgetFactory, WidgetPopoutStage, WidgetProviderWrapper, WidgetSidebar, Workspace, WorkspaceContext, WorkspaceFooter, WorkspaceMenu, WorkspaceModel, addChildToLayoutItem, addItemToItemLayout, buildMcpConfigFromOverrides, canHaveChildren, changeDirectionForLayoutItem, createProviderRegistry, deriveFormFields, envMappingToRows, evaluateBundle, extractWidgetConfigs, formStateToMcpJson, formatFieldName, generateCustomTheme, generateHarmonyTheme, generateRandomTheme, generateThemeName, getBorderStyle, getChildrenForLayoutItem, getComponentInLayout, getContainerBorderColor, getContainerColor, getIndexOfLayoutChildrenForItem, getIndexOfLayoutItem, getLayoutItemById, getLayoutItemForWorkspace, getNearestParentWorkspace, getNextHighestId, getNextHighestItemInLayout, getNextHighestOrder, getNextHighestParentId, getNextLowestItemInLayout, getParentForLayoutItem, getParentWorkspaceForItem, getThemePresets, getUserConfigurableProviders, getWidgetsForWorkspace, getWorkspacesForWorkspace, headerTemplateToRows, isContainer, isLikelySecret, isMaxOrderForItem, isMinOrderForItem, isWidget, isWidgetResolvable, isWorkspace, layoutItemHasWorkspaceAsChild, loadWidgetBundle, mcpJsonToFormState, numChildrenForLayout, removeItemFromLayout, renderComponent, renderGridLayout, renderGridLayoutFlow, _renderLayout as renderLayout, renderLayoutMenu, replaceItemInLayout, resolveIcon, setHostModules, traverseParentTree, updateLayoutItem, updateParentForItem, useDashboard, useMcpProvider, useNotifications, useProvider, useProviderClient, useScheduler, useWidgetEvents, useWidgetProviders, useWidgetSchedulerStatus, validateCellMerge, validateGridCell, validateGridPlacement, validateWidgetPlacement, withProviderDetection };
46799
+ export { ALGOLIA_ANALYTICS_FOR_QUERY, ALGOLIA_ANALYTICS_FOR_QUERY_COMPLETE, ALGOLIA_ANALYTICS_FOR_QUERY_ERROR, ALGOLIA_LIST_INDICES, ALGOLIA_LIST_INDICES_COMPLETE, ALGOLIA_LIST_INDICES_ERROR, AVAILABLE_COLORS, AddMenuItemModal, AdvancedMcpConfig, AppContext, AppSettingsModal, AppThemeScope, AppWrapper, CHOOSE_FILE, CHOOSE_FILE_COMPLETE, CHOOSE_FILE_ERROR, ColorModel, ComponentConfigModel, ComponentManager, ContextModel, DATA_JSON_TO_CSV_FILE, DATA_JSON_TO_CSV_FILE_COMPLETE, DATA_JSON_TO_CSV_FILE_ERROR, DATA_JSON_TO_CSV_STRING, DATA_JSON_TO_CSV_STRING_COMPLETE, DATA_JSON_TO_CSV_STRING_ERROR, DATA_READ_FROM_FILE, DATA_READ_FROM_FILE_COMPLETE, DATA_READ_FROM_FILE_ERROR, DATA_SAVE_TO_FILE, DATA_SAVE_TO_FILE_COMPLETE, DATA_SAVE_TO_FILE_ERROR, DashCommandPalette, DashNavbar, DashSidebar, DashTabBar, DashboardStage as Dashboard, DashboardApi, DashboardContext, DashboardFooter, DashboardHeader, DashboardMenuItem, DashboardModel, DashboardMonitor, DashboardPublisher, DashboardStage, DashboardThemeProvider, DashboardWrapper, ElectronDashboardApi, ErrorBoundary, ExternalWidget, GRID_CELL_WIDGET_TYPE, HARMONY_STRATEGIES, LAYOUT_LIST, LAYOUT_LIST_COMPLETE, LAYOUT_LIST_ERROR, LAYOUT_SAVE, LAYOUT_SAVE_COMPLETE, LAYOUT_SAVE_ERROR, Layout, LayoutBuilder, LayoutBuilderAddItemModal, LayoutBuilderConfigContainerMenuItem, LayoutBuilderConfigMenuItem, LayoutBuilderConfigModal, LayoutBuilderEditItemModal, LayoutBuilderEventModal, LayoutBuilderGridItem, LayoutContainer, LayoutDragBuilder, LayoutDragBuilderEdit, LayoutGridContainer, LayoutManagerModal, LayoutModel, LayoutQuickAddMenu, MCP_CALL_TOOL_COMPLETE, MCP_CALL_TOOL_ERROR, MCP_GET_CATALOG_COMPLETE, MCP_GET_CATALOG_ERROR, MCP_LIST_RESOURCES_COMPLETE, MCP_LIST_RESOURCES_ERROR, MCP_LIST_TOOLS_COMPLETE, MCP_LIST_TOOLS_ERROR, MCP_READ_RESOURCE_COMPLETE, MCP_READ_RESOURCE_ERROR, MCP_RUN_AUTH_COMPLETE, MCP_RUN_AUTH_ERROR, MCP_SERVER_STATUS_COMPLETE, MCP_SERVER_STATUS_ERROR, MCP_START_SERVER_COMPLETE, MCP_START_SERVER_ERROR, MCP_STOP_SERVER_COMPLETE, MCP_STOP_SERVER_ERROR, MENU_ITEMS_DELETE, MENU_ITEMS_DELETE_COMPLETE, MENU_ITEMS_DELETE_ERROR, MENU_ITEMS_LIST, MENU_ITEMS_LIST_COMPLETE, MENU_ITEMS_LIST_ERROR, MENU_ITEMS_SAVE, MENU_ITEMS_SAVE_COMPLETE, MENU_ITEMS_SAVE_ERROR, MainMenu, MainMenuItem, MainMenuSection, McpServerPicker, MenuItemModel, MenuSlideOverlay, MergeCellsModal, MissingProviderPrompt, MockDashboardApi, PROVIDER_DELETE_COMPLETE, PROVIDER_DELETE_ERROR, PROVIDER_GET_COMPLETE, PROVIDER_GET_ERROR, PROVIDER_LIST_COMPLETE, PROVIDER_LIST_ERROR, PROVIDER_SAVE_COMPLETE, PROVIDER_SAVE_ERROR, PanelCode, PanelEditItem, PanelEditItemHandlers, PanelEditItemNotifications, ProviderContext, ProviderErrorBoundary, ProviderForm, ProviderSelector, SECURE_STORAGE_ENCRYPT_STRING, SECURE_STORAGE_ENCRYPT_STRING_COMPLETE, SECURE_STORAGE_ENCRYPT_STRING_ERROR, SECURE_STORE_ENCRYPTION_CHECK, SECURE_STORE_ENCRYPTION_CHECK_COMPLETE, SECURE_STORE_ENCRYPTION_CHECK_ERROR, SECURE_STORE_GET_DATA, SECURE_STORE_GET_DATA_COMPLETE, SECURE_STORE_GET_DATA_ERROR, SECURE_STORE_SET_DATA, SECURE_STORE_SET_DATA_COMPLETE, SECURE_STORE_SET_DATA_ERROR, SETTINGS_GET, SETTINGS_GET_COMPLETE, SETTINGS_GET_ERROR, SETTINGS_SAVE, SETTINGS_SAVE_COMPLETE, SETTINGS_SAVE_ERROR, SIDEBAR_WIDGET_TYPE, SettingsModel, SideMenu, SplitCellModal, THEME_DELETE, THEME_DELETE_COMPLETE, THEME_DELETE_ERROR, THEME_EXTRACT_FROM_URL, THEME_EXTRACT_FROM_URL_COMPLETE, THEME_EXTRACT_FROM_URL_ERROR, THEME_LIST, THEME_LIST_COMPLETE, THEME_LIST_ERROR, THEME_MAP_PALETTE, THEME_MAP_PALETTE_COMPLETE, THEME_MAP_PALETTE_ERROR, THEME_SAVE, THEME_SAVE_COMPLETE, THEME_SAVE_ERROR, ThemeApi, ThemeColorDots, ThemeManagerModal, ThemeModel, ThemeWrapper, WORKSPACE_DELETE, WORKSPACE_DELETE_COMPLETE, WORKSPACE_DELETE_ERROR, WORKSPACE_LIST, WORKSPACE_LIST_COMPLETE, WORKSPACE_LIST_ERROR, WORKSPACE_SAVE, WORKSPACE_SAVE_COMPLETE, WORKSPACE_SAVE_ERROR, WebDashboardApi, Widget, WidgetApi, WidgetConfigPanel, WidgetContext, WidgetFactory, WidgetPopoutStage, WidgetProviderWrapper, WidgetSidebar, Workspace, WorkspaceContext, WorkspaceFooter, WorkspaceMenu, WorkspaceModel, addChildToLayoutItem, addItemToItemLayout, buildMcpConfigFromOverrides, canHaveChildren, changeDirectionForLayoutItem, createProviderRegistry, deriveFormFields, envMappingToRows, evaluateBundle, extractWidgetConfigs, formStateToMcpJson, formatFieldName, generateCustomTheme, generateHarmonyTheme, generateRandomTheme, generateThemeName, getBorderStyle, getChildrenForLayoutItem, getComponentInLayout, getContainerBorderColor, getContainerColor, getIndexOfLayoutChildrenForItem, getIndexOfLayoutItem, getLayoutItemById, getLayoutItemForWorkspace, getNearestParentWorkspace, getNextHighestId, getNextHighestItemInLayout, getNextHighestOrder, getNextHighestParentId, getNextLowestItemInLayout, getParentForLayoutItem, getParentWorkspaceForItem, getThemePresets, getUserConfigurableProviders, getWidgetsForWorkspace, getWorkspacesForWorkspace, headerTemplateToRows, isContainer, isLikelySecret, isMaxOrderForItem, isMinOrderForItem, isWidget, isWidgetResolvable, isWorkspace, layoutItemHasWorkspaceAsChild, loadWidgetBundle, mcpJsonToFormState, numChildrenForLayout, removeItemFromLayout, renderComponent, renderGridLayout, renderGridLayoutFlow, _renderLayout as renderLayout, renderLayoutMenu, replaceItemInLayout, resolveIcon, setHostModules, traverseParentTree, updateLayoutItem, updateParentForItem, useDashboard, useMcpProvider, useNotifications, useProvider, useProviderClient, useScheduler, useWebSocketProvider, useWidgetEvents, useWidgetProviders, useWidgetSchedulerStatus, validateCellMerge, validateGridCell, validateGridPlacement, validateWidgetPlacement, withProviderDetection };
46278
46800
  //# sourceMappingURL=index.esm.js.map