@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.esm.js CHANGED
@@ -3711,7 +3711,7 @@ var FolderDetail = function FolderDetail(_ref) {
3711
3711
  });
3712
3712
  };
3713
3713
 
3714
- var OptionCard$1 = function OptionCard(_ref) {
3714
+ var OptionCard$2 = function OptionCard(_ref) {
3715
3715
  var icon = _ref.icon,
3716
3716
  title = _ref.title,
3717
3717
  description = _ref.description,
@@ -3760,28 +3760,28 @@ var CreationMethodPicker = function CreationMethodPicker(_ref2) {
3760
3760
  })]
3761
3761
  }), /*#__PURE__*/jsxs("div", {
3762
3762
  className: "flex flex-col w-2/3 p-6 pt-10 space-y-3",
3763
- children: [/*#__PURE__*/jsx(OptionCard$1, {
3763
+ children: [/*#__PURE__*/jsx(OptionCard$2, {
3764
3764
  icon: "plus",
3765
3765
  title: "New Dashboard",
3766
3766
  description: "Start from a blank template and customize your layout",
3767
3767
  onClick: function onClick() {
3768
3768
  return onSelect("template");
3769
3769
  }
3770
- }), /*#__PURE__*/jsx(OptionCard$1, {
3770
+ }), /*#__PURE__*/jsx(OptionCard$2, {
3771
3771
  icon: "file-zipper",
3772
3772
  title: "Import from File",
3773
3773
  description: "Import a dashboard from a .zip file on your computer",
3774
3774
  onClick: function onClick() {
3775
3775
  return onSelect("import");
3776
3776
  }
3777
- }), /*#__PURE__*/jsx(OptionCard$1, {
3777
+ }), /*#__PURE__*/jsx(OptionCard$2, {
3778
3778
  icon: "compass",
3779
3779
  title: "Search Registry",
3780
3780
  description: "Browse and install dashboards from the online registry",
3781
3781
  onClick: function onClick() {
3782
3782
  return onSelect("registry");
3783
3783
  }
3784
- }), /*#__PURE__*/jsx(OptionCard$1, {
3784
+ }), /*#__PURE__*/jsx(OptionCard$2, {
3785
3785
  icon: "wand-magic-sparkles",
3786
3786
  title: "Dashboard Wizard",
3787
3787
  description: "Guided setup \u2014 pick categories, providers, and widgets step by step",
@@ -25443,6 +25443,7 @@ var WidgetErrorBoundary = /*#__PURE__*/function (_Component) {
25443
25443
  }, {
25444
25444
  key: "render",
25445
25445
  value: function render() {
25446
+ var _this2 = this;
25446
25447
  if (this.state.hasError) {
25447
25448
  var _this$state$error;
25448
25449
  var widgetName = this.props.widgetName;
@@ -25450,33 +25451,64 @@ var WidgetErrorBoundary = /*#__PURE__*/function (_Component) {
25450
25451
 
25451
25452
  // Check if it's a WidgetContext error
25452
25453
  var isContextError = errorMessage.includes("Widget ID not found in Context") || errorMessage.includes("WidgetContext");
25454
+
25455
+ // AI-built widgets live under @ai-built/* — they get an "Open
25456
+ // in AI Builder" recovery button that dispatches the existing
25457
+ // dash:edit-widget-with-ai event (dash-electron's Dash.js
25458
+ // listens for it and reopens the failing widget in remix mode).
25459
+ var isAiBuilt = typeof widgetName === "string" && /(^|[./])ai-built\b/i.test(widgetName);
25460
+ var openInBuilder = function openInBuilder() {
25461
+ try {
25462
+ window.dispatchEvent(new CustomEvent("dash:edit-widget-with-ai", {
25463
+ detail: {
25464
+ widgetComponentName: widgetName
25465
+ }
25466
+ }));
25467
+ } catch (err) {
25468
+ }
25469
+ };
25453
25470
  return /*#__PURE__*/jsxs("div", {
25454
- className: "flex flex-col h-full w-full bg-red-900 border-2 border-red-600 rounded p-4 text-red-100",
25471
+ className: "flex flex-col h-full w-full bg-amber-950 border border-amber-700 rounded p-4 text-amber-100",
25455
25472
  children: [/*#__PURE__*/jsx("div", {
25456
- className: "text-xl font-bold mb-2",
25457
- children: "\u26A0\uFE0F Widget Error"
25458
- }), /*#__PURE__*/jsxs("div", {
25459
- className: "text-sm mb-3",
25460
- children: [/*#__PURE__*/jsx("strong", {
25461
- children: "Widget:"
25462
- }), " ", widgetName]
25473
+ className: "text-base font-semibold mb-2 text-amber-200",
25474
+ children: "Widget couldn't render"
25463
25475
  }), /*#__PURE__*/jsxs("div", {
25464
- className: "text-sm mb-3",
25465
- children: [/*#__PURE__*/jsx("strong", {
25466
- children: "Error:"
25467
- }), " ", errorMessage]
25476
+ className: "text-xs mb-2 break-words",
25477
+ children: [/*#__PURE__*/jsx("span", {
25478
+ className: "text-amber-300",
25479
+ children: widgetName
25480
+ }), " threw", " ", /*#__PURE__*/jsx("span", {
25481
+ className: "font-mono",
25482
+ children: errorMessage
25483
+ })]
25468
25484
  }), isContextError && /*#__PURE__*/jsxs("div", {
25469
- className: "text-sm bg-red-800 border border-red-700 rounded p-3 mt-2",
25485
+ className: "text-xs bg-amber-900 border border-amber-700 rounded p-2 mt-2 mb-2",
25470
25486
  children: [/*#__PURE__*/jsx("strong", {
25471
25487
  children: "Fix:"
25472
25488
  }), " This widget uses ", /*#__PURE__*/jsx("code", {
25473
25489
  children: "WidgetContext"
25474
25490
  }), " ", "but is missing the ", /*#__PURE__*/jsx("code", {
25475
25491
  children: "<Widget>"
25476
- }), " wrapper.", /*#__PURE__*/jsx("br", {}), /*#__PURE__*/jsx("br", {}), "Add the wrapper in your widget component:", /*#__PURE__*/jsx("pre", {
25492
+ }), " wrapper.", /*#__PURE__*/jsx("br", {}), "Add the wrapper in your widget component:", /*#__PURE__*/jsx("pre", {
25477
25493
  className: "bg-gray-900 p-2 rounded mt-2 text-xs overflow-auto",
25478
25494
  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};")
25479
25495
  })]
25496
+ }), /*#__PURE__*/jsxs("div", {
25497
+ className: "flex items-center gap-2 mt-2",
25498
+ children: [/*#__PURE__*/jsx("button", {
25499
+ onClick: function onClick() {
25500
+ return _this2.setState({
25501
+ hasError: false,
25502
+ error: null
25503
+ });
25504
+ },
25505
+ className: "px-3 py-1 rounded text-xs bg-amber-700 hover:bg-amber-600 text-amber-100 transition-colors",
25506
+ children: "Retry"
25507
+ }), isAiBuilt && /*#__PURE__*/jsx("button", {
25508
+ onClick: openInBuilder,
25509
+ className: "px-3 py-1 rounded text-xs bg-indigo-600 hover:bg-indigo-500 text-white transition-colors",
25510
+ children: "Open in AI Builder"
25511
+ })]
25480
25512
  })]
25481
25513
  });
25482
25514
  }
@@ -47022,6 +47054,103 @@ var WebSocketProviderForm = function WebSocketProviderForm(_ref) {
47022
47054
  });
47023
47055
  };
47024
47056
 
47057
+ var OptionCard$1 = function OptionCard(_ref) {
47058
+ var icon = _ref.icon,
47059
+ title = _ref.title,
47060
+ description = _ref.description,
47061
+ onClick = _ref.onClick,
47062
+ currentTheme = _ref.currentTheme;
47063
+ return /*#__PURE__*/jsxs("button", {
47064
+ type: "button",
47065
+ onClick: onClick,
47066
+ 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"),
47067
+ children: [/*#__PURE__*/jsx("div", {
47068
+ className: "flex-shrink-0 h-8 w-8 flex items-center justify-center opacity-60",
47069
+ children: /*#__PURE__*/jsx(FontAwesomeIcon, {
47070
+ icon: icon,
47071
+ className: "h-5 w-5"
47072
+ })
47073
+ }), /*#__PURE__*/jsxs("div", {
47074
+ className: "flex flex-col min-w-0",
47075
+ children: [/*#__PURE__*/jsx("span", {
47076
+ className: "text-sm font-medium",
47077
+ children: title
47078
+ }), /*#__PURE__*/jsx("span", {
47079
+ className: "text-xs opacity-50 mt-0.5",
47080
+ children: description
47081
+ })]
47082
+ }), /*#__PURE__*/jsx("div", {
47083
+ className: "flex-shrink-0 ml-auto opacity-30",
47084
+ children: /*#__PURE__*/jsx(FontAwesomeIcon, {
47085
+ icon: "chevron-right",
47086
+ className: "h-3 w-3"
47087
+ })
47088
+ })]
47089
+ });
47090
+ };
47091
+
47092
+ /**
47093
+ * NewProviderPicker — the 3-option chooser shown when the user clicks
47094
+ * "+ New Provider" from the Settings → Providers header without a
47095
+ * specific class pre-selected. Mirrors InstallWidgetPicker's pattern
47096
+ * so the UI feels consistent across "+ New ..." entry points in
47097
+ * Settings.
47098
+ *
47099
+ * Calls `onSelect(class)` with the literal class string ("credential",
47100
+ * "mcp", or "websocket"). The parent (ProvidersSection) then routes
47101
+ * to the appropriate create flow:
47102
+ *
47103
+ * credential → ProviderDetail (credential create form)
47104
+ * mcp → McpCatalogDetail (catalog browser)
47105
+ * websocket → WsProviderDetail (WebSocket add form)
47106
+ *
47107
+ * The Widget Builder's existing deep-link path (which always passes
47108
+ * an explicit class) bypasses this chooser and goes straight to the
47109
+ * matching form.
47110
+ */
47111
+ var NewProviderPicker = function NewProviderPicker(_ref2) {
47112
+ var onSelect = _ref2.onSelect;
47113
+ var _useContext = useContext(ThemeContext),
47114
+ currentTheme = _useContext.currentTheme;
47115
+ var panelStyles = getStylesForItem(themeObjects.PANEL, currentTheme, {
47116
+ grow: false
47117
+ });
47118
+ return /*#__PURE__*/jsx("div", {
47119
+ className: "flex flex-col flex-1 min-h-0",
47120
+ children: /*#__PURE__*/jsxs("div", {
47121
+ className: "flex-1 overflow-y-auto p-6 space-y-3 ".concat(panelStyles.textColor || "text-gray-200"),
47122
+ children: [/*#__PURE__*/jsx("span", {
47123
+ className: "text-xs font-semibold opacity-50 block mb-4",
47124
+ children: "ADD A NEW PROVIDER"
47125
+ }), /*#__PURE__*/jsx(OptionCard$1, {
47126
+ icon: "key",
47127
+ title: "Credential",
47128
+ description: "API key or token credentials for services like Algolia, Anthropic, or any HTTP API",
47129
+ onClick: function onClick() {
47130
+ return onSelect("credential");
47131
+ },
47132
+ currentTheme: currentTheme
47133
+ }), /*#__PURE__*/jsx(OptionCard$1, {
47134
+ icon: "plug",
47135
+ title: "MCP",
47136
+ description: "Model Context Protocol server \u2014 pick from a curated catalog (Slack, Filesystem, Notion, etc.)",
47137
+ onClick: function onClick() {
47138
+ return onSelect("mcp");
47139
+ },
47140
+ currentTheme: currentTheme
47141
+ }), /*#__PURE__*/jsx(OptionCard$1, {
47142
+ icon: "diagram-project",
47143
+ title: "WebSocket",
47144
+ description: "Real-time WebSocket connection to a streaming endpoint",
47145
+ onClick: function onClick() {
47146
+ return onSelect("websocket");
47147
+ },
47148
+ currentTheme: currentTheme
47149
+ })]
47150
+ })
47151
+ });
47152
+ };
47153
+
47025
47154
  var ProvidersSection = function ProvidersSection(_ref) {
47026
47155
  var _ref$dashApi = _ref.dashApi,
47027
47156
  dashApi = _ref$dashApi === void 0 ? null : _ref$dashApi,
@@ -47062,42 +47191,51 @@ var ProvidersSection = function ProvidersSection(_ref) {
47062
47191
  _useState8 = _slicedToArray(_useState7, 2),
47063
47192
  isCreating = _useState8[0],
47064
47193
  setIsCreating = _useState8[1];
47194
+ // When the user clicks "+ New Provider" without a pre-selected
47195
+ // class (Settings header button), show the class chooser
47196
+ // (Credential / MCP / WebSocket) instead of defaulting to the
47197
+ // credential form. Widget Builder's deep-link path passes a class
47198
+ // explicitly and bypasses this chooser.
47065
47199
  var _useState9 = useState(false),
47066
47200
  _useState0 = _slicedToArray(_useState9, 2),
47067
- isEditing = _useState0[0],
47068
- setIsEditing = _useState0[1];
47069
- var _useState1 = useState(""),
47201
+ isShowingClassChooser = _useState0[0],
47202
+ setIsShowingClassChooser = _useState0[1];
47203
+ var _useState1 = useState(false),
47070
47204
  _useState10 = _slicedToArray(_useState1, 2),
47071
- formName = _useState10[0],
47072
- setFormName = _useState10[1];
47205
+ isEditing = _useState10[0],
47206
+ setIsEditing = _useState10[1];
47073
47207
  var _useState11 = useState(""),
47074
47208
  _useState12 = _slicedToArray(_useState11, 2),
47075
- formType = _useState12[0],
47076
- setFormType = _useState12[1];
47077
- var _useState13 = useState({}),
47209
+ formName = _useState12[0],
47210
+ setFormName = _useState12[1];
47211
+ var _useState13 = useState(""),
47078
47212
  _useState14 = _slicedToArray(_useState13, 2),
47079
- formCredentials = _useState14[0],
47080
- setFormCredentials = _useState14[1];
47081
- var _useState15 = useState(null),
47213
+ formType = _useState14[0],
47214
+ setFormType = _useState14[1];
47215
+ var _useState15 = useState({}),
47082
47216
  _useState16 = _slicedToArray(_useState15, 2),
47083
- deleteTarget = _useState16[0],
47084
- setDeleteTarget = _useState16[1];
47085
- var _useState17 = useState(false),
47217
+ formCredentials = _useState16[0],
47218
+ setFormCredentials = _useState16[1];
47219
+ var _useState17 = useState(null),
47086
47220
  _useState18 = _slicedToArray(_useState17, 2),
47087
- isAddingMcp = _useState18[0],
47088
- setIsAddingMcp = _useState18[1];
47221
+ deleteTarget = _useState18[0],
47222
+ setDeleteTarget = _useState18[1];
47089
47223
  var _useState19 = useState(false),
47090
47224
  _useState20 = _slicedToArray(_useState19, 2),
47091
- isEditingMcp = _useState20[0],
47092
- setIsEditingMcp = _useState20[1];
47225
+ isAddingMcp = _useState20[0],
47226
+ setIsAddingMcp = _useState20[1];
47093
47227
  var _useState21 = useState(false),
47094
47228
  _useState22 = _slicedToArray(_useState21, 2),
47095
- isAddingWs = _useState22[0],
47096
- setIsAddingWs = _useState22[1];
47229
+ isEditingMcp = _useState22[0],
47230
+ setIsEditingMcp = _useState22[1];
47097
47231
  var _useState23 = useState(false),
47098
47232
  _useState24 = _slicedToArray(_useState23, 2),
47099
- isEditingWs = _useState24[0],
47100
- setIsEditingWs = _useState24[1];
47233
+ isAddingWs = _useState24[0],
47234
+ setIsAddingWs = _useState24[1];
47235
+ var _useState25 = useState(false),
47236
+ _useState26 = _slicedToArray(_useState25, 2),
47237
+ isEditingWs = _useState26[0],
47238
+ setIsEditingWs = _useState26[1];
47101
47239
 
47102
47240
  // Row ID counter for env/header rows in MCP edit mode
47103
47241
  var nextRowIdRef = useRef(0);
@@ -47379,19 +47517,35 @@ var ProvidersSection = function ProvidersSection(_ref) {
47379
47517
  if (createRequested && !prevCreateRequested.current) {
47380
47518
  resetForm();
47381
47519
  setSelectedName(null);
47520
+ setIsShowingClassChooser(false);
47382
47521
  if (initialProviderClass === "mcp") {
47383
47522
  // MCP class: open the catalog detail. Pre-select happens in
47384
47523
  // McpCatalogDetail via the initialSelectedId prop passed below.
47385
47524
  setIsCreating(false);
47386
47525
  setIsAddingMcp(true);
47387
- } else {
47388
- // Credential class (default): open the credential create form
47389
- // and pre-fill the type field if provided.
47526
+ } else if (initialProviderClass === "websocket") {
47527
+ // WebSocket class: open the WebSocket add form. Reachable via
47528
+ // a future Widget Builder deep-link for ws-typed widgets.
47529
+ setIsCreating(false);
47530
+ setIsAddingMcp(false);
47531
+ setIsAddingWs(true);
47532
+ } else if (initialProviderClass === "credential") {
47533
+ // Credential class: open the credential create form and
47534
+ // pre-fill the type field if provided.
47390
47535
  setIsAddingMcp(false);
47391
47536
  setIsCreating(true);
47392
47537
  if (initialProviderType) {
47393
47538
  setFormType(initialProviderType);
47394
47539
  }
47540
+ } else {
47541
+ // No class specified — Settings header "+ New Provider"
47542
+ // button hits this branch. Show the chooser so the user
47543
+ // picks Credential / MCP / WebSocket explicitly instead of
47544
+ // landing on the credential form by default.
47545
+ setIsAddingMcp(false);
47546
+ setIsCreating(false);
47547
+ setIsAddingWs(false);
47548
+ setIsShowingClassChooser(true);
47395
47549
  }
47396
47550
  }
47397
47551
  prevCreateRequested.current = createRequested;
@@ -47523,6 +47677,19 @@ var ProvidersSection = function ProvidersSection(_ref) {
47523
47677
  return setIsEditingWs(false);
47524
47678
  }
47525
47679
  }, selectedName);
47680
+ } else if (isShowingClassChooser) {
47681
+ detailContent = /*#__PURE__*/jsx(NewProviderPicker, {
47682
+ onSelect: function onSelect(cls) {
47683
+ setIsShowingClassChooser(false);
47684
+ if (cls === "mcp") {
47685
+ setIsAddingMcp(true);
47686
+ } else if (cls === "websocket") {
47687
+ setIsAddingWs(true);
47688
+ } else {
47689
+ setIsCreating(true);
47690
+ }
47691
+ }
47692
+ });
47526
47693
  } else if (isAddingMcp) {
47527
47694
  detailContent = /*#__PURE__*/jsx(McpCatalogDetail, {
47528
47695
  onSave: handleMcpSave,