@trops/dash-core 0.1.452 → 0.1.454

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.js CHANGED
@@ -3729,7 +3729,7 @@ var FolderDetail = function FolderDetail(_ref) {
3729
3729
  });
3730
3730
  };
3731
3731
 
3732
- var OptionCard$1 = function OptionCard(_ref) {
3732
+ var OptionCard$2 = function OptionCard(_ref) {
3733
3733
  var icon = _ref.icon,
3734
3734
  title = _ref.title,
3735
3735
  description = _ref.description,
@@ -3778,28 +3778,28 @@ var CreationMethodPicker = function CreationMethodPicker(_ref2) {
3778
3778
  })]
3779
3779
  }), /*#__PURE__*/jsxRuntime.jsxs("div", {
3780
3780
  className: "flex flex-col w-2/3 p-6 pt-10 space-y-3",
3781
- children: [/*#__PURE__*/jsxRuntime.jsx(OptionCard$1, {
3781
+ children: [/*#__PURE__*/jsxRuntime.jsx(OptionCard$2, {
3782
3782
  icon: "plus",
3783
3783
  title: "New Dashboard",
3784
3784
  description: "Start from a blank template and customize your layout",
3785
3785
  onClick: function onClick() {
3786
3786
  return onSelect("template");
3787
3787
  }
3788
- }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$1, {
3788
+ }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$2, {
3789
3789
  icon: "file-zipper",
3790
3790
  title: "Import from File",
3791
3791
  description: "Import a dashboard from a .zip file on your computer",
3792
3792
  onClick: function onClick() {
3793
3793
  return onSelect("import");
3794
3794
  }
3795
- }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$1, {
3795
+ }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$2, {
3796
3796
  icon: "compass",
3797
3797
  title: "Search Registry",
3798
3798
  description: "Browse and install dashboards from the online registry",
3799
3799
  onClick: function onClick() {
3800
3800
  return onSelect("registry");
3801
3801
  }
3802
- }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$1, {
3802
+ }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$2, {
3803
3803
  icon: "wand-magic-sparkles",
3804
3804
  title: "Dashboard Wizard",
3805
3805
  description: "Guided setup \u2014 pick categories, providers, and widgets step by step",
@@ -25461,6 +25461,7 @@ var WidgetErrorBoundary = /*#__PURE__*/function (_Component) {
25461
25461
  }, {
25462
25462
  key: "render",
25463
25463
  value: function render() {
25464
+ var _this2 = this;
25464
25465
  if (this.state.hasError) {
25465
25466
  var _this$state$error;
25466
25467
  var widgetName = this.props.widgetName;
@@ -25468,33 +25469,64 @@ var WidgetErrorBoundary = /*#__PURE__*/function (_Component) {
25468
25469
 
25469
25470
  // Check if it's a WidgetContext error
25470
25471
  var isContextError = errorMessage.includes("Widget ID not found in Context") || errorMessage.includes("WidgetContext");
25472
+
25473
+ // AI-built widgets live under @ai-built/* — they get an "Open
25474
+ // in AI Builder" recovery button that dispatches the existing
25475
+ // dash:edit-widget-with-ai event (dash-electron's Dash.js
25476
+ // listens for it and reopens the failing widget in remix mode).
25477
+ var isAiBuilt = typeof widgetName === "string" && /(^|[./])ai-built\b/i.test(widgetName);
25478
+ var openInBuilder = function openInBuilder() {
25479
+ try {
25480
+ window.dispatchEvent(new CustomEvent("dash:edit-widget-with-ai", {
25481
+ detail: {
25482
+ widgetComponentName: widgetName
25483
+ }
25484
+ }));
25485
+ } catch (err) {
25486
+ }
25487
+ };
25471
25488
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
25472
- className: "flex flex-col h-full w-full bg-red-900 border-2 border-red-600 rounded p-4 text-red-100",
25489
+ className: "flex flex-col h-full w-full bg-amber-950 border border-amber-700 rounded p-4 text-amber-100",
25473
25490
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
25474
- className: "text-xl font-bold mb-2",
25475
- children: "\u26A0\uFE0F Widget Error"
25476
- }), /*#__PURE__*/jsxRuntime.jsxs("div", {
25477
- className: "text-sm mb-3",
25478
- children: [/*#__PURE__*/jsxRuntime.jsx("strong", {
25479
- children: "Widget:"
25480
- }), " ", widgetName]
25491
+ className: "text-base font-semibold mb-2 text-amber-200",
25492
+ children: "Widget couldn't render"
25481
25493
  }), /*#__PURE__*/jsxRuntime.jsxs("div", {
25482
- className: "text-sm mb-3",
25483
- children: [/*#__PURE__*/jsxRuntime.jsx("strong", {
25484
- children: "Error:"
25485
- }), " ", errorMessage]
25494
+ className: "text-xs mb-2 break-words",
25495
+ children: [/*#__PURE__*/jsxRuntime.jsx("span", {
25496
+ className: "text-amber-300",
25497
+ children: widgetName
25498
+ }), " threw", " ", /*#__PURE__*/jsxRuntime.jsx("span", {
25499
+ className: "font-mono",
25500
+ children: errorMessage
25501
+ })]
25486
25502
  }), isContextError && /*#__PURE__*/jsxRuntime.jsxs("div", {
25487
- className: "text-sm bg-red-800 border border-red-700 rounded p-3 mt-2",
25503
+ className: "text-xs bg-amber-900 border border-amber-700 rounded p-2 mt-2 mb-2",
25488
25504
  children: [/*#__PURE__*/jsxRuntime.jsx("strong", {
25489
25505
  children: "Fix:"
25490
25506
  }), " This widget uses ", /*#__PURE__*/jsxRuntime.jsx("code", {
25491
25507
  children: "WidgetContext"
25492
25508
  }), " ", "but is missing the ", /*#__PURE__*/jsxRuntime.jsx("code", {
25493
25509
  children: "<Widget>"
25494
- }), " wrapper.", /*#__PURE__*/jsxRuntime.jsx("br", {}), /*#__PURE__*/jsxRuntime.jsx("br", {}), "Add the wrapper in your widget component:", /*#__PURE__*/jsxRuntime.jsx("pre", {
25510
+ }), " wrapper.", /*#__PURE__*/jsxRuntime.jsx("br", {}), "Add the wrapper in your widget component:", /*#__PURE__*/jsxRuntime.jsx("pre", {
25495
25511
  className: "bg-gray-900 p-2 rounded mt-2 text-xs overflow-auto",
25496
25512
  children: "import { Widget } from \"@trops/dash-react\";\n\nexport const ".concat(widgetName, " = (props) => {\n return (\n <Widget {...props}>\n {/* Your widget content */}\n </Widget>\n );\n};")
25497
25513
  })]
25514
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
25515
+ className: "flex items-center gap-2 mt-2",
25516
+ children: [/*#__PURE__*/jsxRuntime.jsx("button", {
25517
+ onClick: function onClick() {
25518
+ return _this2.setState({
25519
+ hasError: false,
25520
+ error: null
25521
+ });
25522
+ },
25523
+ className: "px-3 py-1 rounded text-xs bg-amber-700 hover:bg-amber-600 text-amber-100 transition-colors",
25524
+ children: "Retry"
25525
+ }), isAiBuilt && /*#__PURE__*/jsxRuntime.jsx("button", {
25526
+ onClick: openInBuilder,
25527
+ className: "px-3 py-1 rounded text-xs bg-indigo-600 hover:bg-indigo-500 text-white transition-colors",
25528
+ children: "Open in AI Builder"
25529
+ })]
25498
25530
  })]
25499
25531
  });
25500
25532
  }
@@ -47040,6 +47072,103 @@ var WebSocketProviderForm = function WebSocketProviderForm(_ref) {
47040
47072
  });
47041
47073
  };
47042
47074
 
47075
+ var OptionCard$1 = function OptionCard(_ref) {
47076
+ var icon = _ref.icon,
47077
+ title = _ref.title,
47078
+ description = _ref.description,
47079
+ onClick = _ref.onClick,
47080
+ currentTheme = _ref.currentTheme;
47081
+ return /*#__PURE__*/jsxRuntime.jsxs("button", {
47082
+ type: "button",
47083
+ onClick: onClick,
47084
+ className: "w-full flex flex-row items-center gap-4 p-4 rounded-lg text-left transition-opacity ".concat(currentTheme["bg-primary-medium"] || "bg-white/5", " hover:opacity-80"),
47085
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
47086
+ className: "flex-shrink-0 h-8 w-8 flex items-center justify-center opacity-60",
47087
+ children: /*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
47088
+ icon: icon,
47089
+ className: "h-5 w-5"
47090
+ })
47091
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
47092
+ className: "flex flex-col min-w-0",
47093
+ children: [/*#__PURE__*/jsxRuntime.jsx("span", {
47094
+ className: "text-sm font-medium",
47095
+ children: title
47096
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
47097
+ className: "text-xs opacity-50 mt-0.5",
47098
+ children: description
47099
+ })]
47100
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
47101
+ className: "flex-shrink-0 ml-auto opacity-30",
47102
+ children: /*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
47103
+ icon: "chevron-right",
47104
+ className: "h-3 w-3"
47105
+ })
47106
+ })]
47107
+ });
47108
+ };
47109
+
47110
+ /**
47111
+ * NewProviderPicker — the 3-option chooser shown when the user clicks
47112
+ * "+ New Provider" from the Settings → Providers header without a
47113
+ * specific class pre-selected. Mirrors InstallWidgetPicker's pattern
47114
+ * so the UI feels consistent across "+ New ..." entry points in
47115
+ * Settings.
47116
+ *
47117
+ * Calls `onSelect(class)` with the literal class string ("credential",
47118
+ * "mcp", or "websocket"). The parent (ProvidersSection) then routes
47119
+ * to the appropriate create flow:
47120
+ *
47121
+ * credential → ProviderDetail (credential create form)
47122
+ * mcp → McpCatalogDetail (catalog browser)
47123
+ * websocket → WsProviderDetail (WebSocket add form)
47124
+ *
47125
+ * The Widget Builder's existing deep-link path (which always passes
47126
+ * an explicit class) bypasses this chooser and goes straight to the
47127
+ * matching form.
47128
+ */
47129
+ var NewProviderPicker = function NewProviderPicker(_ref2) {
47130
+ var onSelect = _ref2.onSelect;
47131
+ var _useContext = React.useContext(DashReact.ThemeContext),
47132
+ currentTheme = _useContext.currentTheme;
47133
+ var panelStyles = DashReact.getStylesForItem(DashReact.themeObjects.PANEL, currentTheme, {
47134
+ grow: false
47135
+ });
47136
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
47137
+ className: "flex flex-col flex-1 min-h-0",
47138
+ children: /*#__PURE__*/jsxRuntime.jsxs("div", {
47139
+ className: "flex-1 overflow-y-auto p-6 space-y-3 ".concat(panelStyles.textColor || "text-gray-200"),
47140
+ children: [/*#__PURE__*/jsxRuntime.jsx("span", {
47141
+ className: "text-xs font-semibold opacity-50 block mb-4",
47142
+ children: "ADD A NEW PROVIDER"
47143
+ }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$1, {
47144
+ icon: "key",
47145
+ title: "Credential",
47146
+ description: "API key or token credentials for services like Algolia, Anthropic, or any HTTP API",
47147
+ onClick: function onClick() {
47148
+ return onSelect("credential");
47149
+ },
47150
+ currentTheme: currentTheme
47151
+ }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$1, {
47152
+ icon: "plug",
47153
+ title: "MCP",
47154
+ description: "Model Context Protocol server \u2014 pick from a curated catalog (Slack, Filesystem, Notion, etc.)",
47155
+ onClick: function onClick() {
47156
+ return onSelect("mcp");
47157
+ },
47158
+ currentTheme: currentTheme
47159
+ }), /*#__PURE__*/jsxRuntime.jsx(OptionCard$1, {
47160
+ icon: "diagram-project",
47161
+ title: "WebSocket",
47162
+ description: "Real-time WebSocket connection to a streaming endpoint",
47163
+ onClick: function onClick() {
47164
+ return onSelect("websocket");
47165
+ },
47166
+ currentTheme: currentTheme
47167
+ })]
47168
+ })
47169
+ });
47170
+ };
47171
+
47043
47172
  var ProvidersSection = function ProvidersSection(_ref) {
47044
47173
  var _ref$dashApi = _ref.dashApi,
47045
47174
  dashApi = _ref$dashApi === void 0 ? null : _ref$dashApi,
@@ -47080,42 +47209,51 @@ var ProvidersSection = function ProvidersSection(_ref) {
47080
47209
  _useState8 = _slicedToArray(_useState7, 2),
47081
47210
  isCreating = _useState8[0],
47082
47211
  setIsCreating = _useState8[1];
47212
+ // When the user clicks "+ New Provider" without a pre-selected
47213
+ // class (Settings header button), show the class chooser
47214
+ // (Credential / MCP / WebSocket) instead of defaulting to the
47215
+ // credential form. Widget Builder's deep-link path passes a class
47216
+ // explicitly and bypasses this chooser.
47083
47217
  var _useState9 = React.useState(false),
47084
47218
  _useState0 = _slicedToArray(_useState9, 2),
47085
- isEditing = _useState0[0],
47086
- setIsEditing = _useState0[1];
47087
- var _useState1 = React.useState(""),
47219
+ isShowingClassChooser = _useState0[0],
47220
+ setIsShowingClassChooser = _useState0[1];
47221
+ var _useState1 = React.useState(false),
47088
47222
  _useState10 = _slicedToArray(_useState1, 2),
47089
- formName = _useState10[0],
47090
- setFormName = _useState10[1];
47223
+ isEditing = _useState10[0],
47224
+ setIsEditing = _useState10[1];
47091
47225
  var _useState11 = React.useState(""),
47092
47226
  _useState12 = _slicedToArray(_useState11, 2),
47093
- formType = _useState12[0],
47094
- setFormType = _useState12[1];
47095
- var _useState13 = React.useState({}),
47227
+ formName = _useState12[0],
47228
+ setFormName = _useState12[1];
47229
+ var _useState13 = React.useState(""),
47096
47230
  _useState14 = _slicedToArray(_useState13, 2),
47097
- formCredentials = _useState14[0],
47098
- setFormCredentials = _useState14[1];
47099
- var _useState15 = React.useState(null),
47231
+ formType = _useState14[0],
47232
+ setFormType = _useState14[1];
47233
+ var _useState15 = React.useState({}),
47100
47234
  _useState16 = _slicedToArray(_useState15, 2),
47101
- deleteTarget = _useState16[0],
47102
- setDeleteTarget = _useState16[1];
47103
- var _useState17 = React.useState(false),
47235
+ formCredentials = _useState16[0],
47236
+ setFormCredentials = _useState16[1];
47237
+ var _useState17 = React.useState(null),
47104
47238
  _useState18 = _slicedToArray(_useState17, 2),
47105
- isAddingMcp = _useState18[0],
47106
- setIsAddingMcp = _useState18[1];
47239
+ deleteTarget = _useState18[0],
47240
+ setDeleteTarget = _useState18[1];
47107
47241
  var _useState19 = React.useState(false),
47108
47242
  _useState20 = _slicedToArray(_useState19, 2),
47109
- isEditingMcp = _useState20[0],
47110
- setIsEditingMcp = _useState20[1];
47243
+ isAddingMcp = _useState20[0],
47244
+ setIsAddingMcp = _useState20[1];
47111
47245
  var _useState21 = React.useState(false),
47112
47246
  _useState22 = _slicedToArray(_useState21, 2),
47113
- isAddingWs = _useState22[0],
47114
- setIsAddingWs = _useState22[1];
47247
+ isEditingMcp = _useState22[0],
47248
+ setIsEditingMcp = _useState22[1];
47115
47249
  var _useState23 = React.useState(false),
47116
47250
  _useState24 = _slicedToArray(_useState23, 2),
47117
- isEditingWs = _useState24[0],
47118
- setIsEditingWs = _useState24[1];
47251
+ isAddingWs = _useState24[0],
47252
+ setIsAddingWs = _useState24[1];
47253
+ var _useState25 = React.useState(false),
47254
+ _useState26 = _slicedToArray(_useState25, 2),
47255
+ isEditingWs = _useState26[0],
47256
+ setIsEditingWs = _useState26[1];
47119
47257
 
47120
47258
  // Row ID counter for env/header rows in MCP edit mode
47121
47259
  var nextRowIdRef = React.useRef(0);
@@ -47397,19 +47535,35 @@ var ProvidersSection = function ProvidersSection(_ref) {
47397
47535
  if (createRequested && !prevCreateRequested.current) {
47398
47536
  resetForm();
47399
47537
  setSelectedName(null);
47538
+ setIsShowingClassChooser(false);
47400
47539
  if (initialProviderClass === "mcp") {
47401
47540
  // MCP class: open the catalog detail. Pre-select happens in
47402
47541
  // McpCatalogDetail via the initialSelectedId prop passed below.
47403
47542
  setIsCreating(false);
47404
47543
  setIsAddingMcp(true);
47405
- } else {
47406
- // Credential class (default): open the credential create form
47407
- // and pre-fill the type field if provided.
47544
+ } else if (initialProviderClass === "websocket") {
47545
+ // WebSocket class: open the WebSocket add form. Reachable via
47546
+ // a future Widget Builder deep-link for ws-typed widgets.
47547
+ setIsCreating(false);
47548
+ setIsAddingMcp(false);
47549
+ setIsAddingWs(true);
47550
+ } else if (initialProviderClass === "credential") {
47551
+ // Credential class: open the credential create form and
47552
+ // pre-fill the type field if provided.
47408
47553
  setIsAddingMcp(false);
47409
47554
  setIsCreating(true);
47410
47555
  if (initialProviderType) {
47411
47556
  setFormType(initialProviderType);
47412
47557
  }
47558
+ } else {
47559
+ // No class specified — Settings header "+ New Provider"
47560
+ // button hits this branch. Show the chooser so the user
47561
+ // picks Credential / MCP / WebSocket explicitly instead of
47562
+ // landing on the credential form by default.
47563
+ setIsAddingMcp(false);
47564
+ setIsCreating(false);
47565
+ setIsAddingWs(false);
47566
+ setIsShowingClassChooser(true);
47413
47567
  }
47414
47568
  }
47415
47569
  prevCreateRequested.current = createRequested;
@@ -47541,6 +47695,19 @@ var ProvidersSection = function ProvidersSection(_ref) {
47541
47695
  return setIsEditingWs(false);
47542
47696
  }
47543
47697
  }, selectedName);
47698
+ } else if (isShowingClassChooser) {
47699
+ detailContent = /*#__PURE__*/jsxRuntime.jsx(NewProviderPicker, {
47700
+ onSelect: function onSelect(cls) {
47701
+ setIsShowingClassChooser(false);
47702
+ if (cls === "mcp") {
47703
+ setIsAddingMcp(true);
47704
+ } else if (cls === "websocket") {
47705
+ setIsAddingWs(true);
47706
+ } else {
47707
+ setIsCreating(true);
47708
+ }
47709
+ }
47710
+ });
47544
47711
  } else if (isAddingMcp) {
47545
47712
  detailContent = /*#__PURE__*/jsxRuntime.jsx(McpCatalogDetail, {
47546
47713
  onSave: handleMcpSave,