@trops/dash-core 0.1.473 → 0.1.475

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
@@ -57595,6 +57595,13 @@ var DashboardConfigModal = function DashboardConfigModal(_ref) {
57595
57595
  },
57596
57596
  className: "px-3 py-1.5 text-sm font-medium -mb-px border-b-2 ".concat(activeTab === "listeners" ? "border-indigo-400" : "border-transparent opacity-60 hover:opacity-100"),
57597
57597
  children: "Listeners"
57598
+ }), /*#__PURE__*/jsx("button", {
57599
+ type: "button",
57600
+ onClick: function onClick() {
57601
+ return setActiveTab("notifications");
57602
+ },
57603
+ className: "px-3 py-1.5 text-sm font-medium -mb-px border-b-2 ".concat(activeTab === "notifications" ? "border-indigo-400" : "border-transparent opacity-60 hover:opacity-100"),
57604
+ children: "Notifications"
57598
57605
  }), /*#__PURE__*/jsx("button", {
57599
57606
  type: "button",
57600
57607
  onClick: function onClick() {
@@ -57628,6 +57635,8 @@ var DashboardConfigModal = function DashboardConfigModal(_ref) {
57628
57635
  providersByType: providersByType,
57629
57636
  onBulk: stageBulk,
57630
57637
  onPerWidget: stageBinding
57638
+ }), activeTab === "notifications" && /*#__PURE__*/jsx(NotificationsTab, {
57639
+ workspace: workspace
57631
57640
  }), activeTab === "widgets" && /*#__PURE__*/jsx(WidgetsTab, {
57632
57641
  workspace: workspace,
57633
57642
  getWidgetConfig: getWidgetConfig,
@@ -57652,25 +57661,261 @@ var DashboardConfigModal = function DashboardConfigModal(_ref) {
57652
57661
  });
57653
57662
  };
57654
57663
 
57664
+ /**
57665
+ * Notifications tab — dashboard-scoped view of every widget instance
57666
+ * in the current workspace that declares notifications. Bulk Enable
57667
+ * all / Disable all controls flip every notification toggle in the
57668
+ * filtered list at once. Per-widget toggles persist immediately via
57669
+ * `mainApi.notifications.setPreferences` — same path Settings →
57670
+ * Notifications uses, so the two views stay consistent.
57671
+ *
57672
+ * Toggles are uncontrolled-with-respect-to-the-server: we mirror them
57673
+ * locally for snappy UI but the IPC call is fire-and-forget. If a
57674
+ * write fails the user can re-toggle. No staging — the bulk modal
57675
+ * doesn't gate the user behind a Save button for boolean prefs.
57676
+ */
57677
+ function NotificationsTab(_ref4) {
57678
+ var workspace = _ref4.workspace;
57679
+ var _useState9 = useState(""),
57680
+ _useState0 = _slicedToArray(_useState9, 2),
57681
+ searchQuery = _useState0[0],
57682
+ setSearchQuery = _useState0[1];
57683
+ // Local mirror of widgetUuid -> { typeKey: bool }. Seeded from the
57684
+ // main process on mount; updated optimistically on toggle.
57685
+ var _useState1 = useState({}),
57686
+ _useState10 = _slicedToArray(_useState1, 2),
57687
+ prefs = _useState10[0],
57688
+ setPrefs = _useState10[1];
57689
+ var _useState11 = useState(false),
57690
+ _useState12 = _slicedToArray(_useState11, 2),
57691
+ loaded = _useState12[0],
57692
+ setLoaded = _useState12[1];
57693
+ React__default.useEffect(function () {
57694
+ var _window$mainApi;
57695
+ var cancelled = false;
57696
+ if (!((_window$mainApi = window.mainApi) !== null && _window$mainApi !== void 0 && (_window$mainApi = _window$mainApi.notifications) !== null && _window$mainApi !== void 0 && _window$mainApi.getPreferences)) {
57697
+ setLoaded(true);
57698
+ return function () {
57699
+ cancelled = true;
57700
+ };
57701
+ }
57702
+ window.mainApi.notifications.getPreferences().then(function (p) {
57703
+ if (cancelled) return;
57704
+ setPrefs((p === null || p === void 0 ? void 0 : p.instances) || {});
57705
+ setLoaded(true);
57706
+ });
57707
+ return function () {
57708
+ cancelled = true;
57709
+ };
57710
+ }, []);
57711
+
57712
+ // Collect every widget instance in THIS workspace that declares
57713
+ // notifications, alphabetized by title. Mirrors the Settings →
57714
+ // Notifications collection logic but scoped to one workspace.
57715
+ var widgetInstances = useMemo(function () {
57716
+ var out = [];
57717
+ var _visit = function visit(item) {
57718
+ if (!item) return;
57719
+ if (Array.isArray(item)) {
57720
+ item.forEach(_visit);
57721
+ return;
57722
+ }
57723
+ if (item.component) {
57724
+ var _config$notifications;
57725
+ var config = ComponentManager.resolve(item.component, item);
57726
+ if ((config === null || config === void 0 || (_config$notifications = config.notifications) === null || _config$notifications === void 0 ? void 0 : _config$notifications.length) > 0) {
57727
+ var _item$userPrefs;
57728
+ out.push({
57729
+ uuid: item.uuid || item.uuidString,
57730
+ title: ((_item$userPrefs = item.userPrefs) === null || _item$userPrefs === void 0 ? void 0 : _item$userPrefs.title) || config.displayName || item.component,
57731
+ "package": config["package"] || "Other",
57732
+ // Scoped component id (e.g. "trops.google.GoogleWidget")
57733
+ // — disambiguates rows when several widgets share a
57734
+ // title or display the same package label. Mirrors the
57735
+ // Listeners tab convention so the user only learns one
57736
+ // identification scheme.
57737
+ component: item.component,
57738
+ // Layout instance id — disambiguates two widgets of the
57739
+ // SAME component on the dashboard (e.g. two GitHub
57740
+ // widgets in the same workspace).
57741
+ itemId: item.id,
57742
+ notifications: config.notifications
57743
+ });
57744
+ }
57745
+ }
57746
+ if (Array.isArray(item.children)) item.children.forEach(_visit);
57747
+ if (Array.isArray(item.layout)) item.layout.forEach(_visit);
57748
+ if (Array.isArray(item.items)) item.items.forEach(_visit);
57749
+ };
57750
+ _visit(workspace === null || workspace === void 0 ? void 0 : workspace.layout);
57751
+ if (Array.isArray(workspace === null || workspace === void 0 ? void 0 : workspace.pages)) {
57752
+ workspace.pages.forEach(function (p) {
57753
+ return _visit(p === null || p === void 0 ? void 0 : p.layout);
57754
+ });
57755
+ }
57756
+ _visit(workspace === null || workspace === void 0 ? void 0 : workspace.sidebarLayout);
57757
+ return out.sort(function (a, b) {
57758
+ return String(a.title).localeCompare(String(b.title), undefined, {
57759
+ sensitivity: "base"
57760
+ });
57761
+ });
57762
+ }, [workspace]);
57763
+ var filtered = useMemo(function () {
57764
+ var q = searchQuery.trim().toLowerCase();
57765
+ if (!q) return widgetInstances;
57766
+ return widgetInstances.filter(function (wi) {
57767
+ var hay = [wi.title, wi["package"], wi.component, wi.itemId != null ? "#".concat(wi.itemId) : ""].concat(_toConsumableArray(wi.notifications.map(function (n) {
57768
+ return "".concat(n.key, " ").concat(n.displayName || "");
57769
+ }))).join(" ").toLowerCase();
57770
+ return hay.includes(q);
57771
+ });
57772
+ }, [widgetInstances, searchQuery]);
57773
+ var isEnabled = function isEnabled(uuid, typeKey, defaultEnabled) {
57774
+ var w = prefs[uuid];
57775
+ if (w && typeof w[typeKey] === "boolean") return w[typeKey];
57776
+ return !!defaultEnabled;
57777
+ };
57778
+ var setOne = function setOne(uuid, typeKey, value) {
57779
+ var _window$mainApi2;
57780
+ setPrefs(function (prev) {
57781
+ return _objectSpread$a(_objectSpread$a({}, prev), {}, _defineProperty({}, uuid, _objectSpread$a(_objectSpread$a({}, prev[uuid] || {}), {}, _defineProperty({}, typeKey, value))));
57782
+ });
57783
+ (_window$mainApi2 = window.mainApi) === null || _window$mainApi2 === void 0 || (_window$mainApi2 = _window$mainApi2.notifications) === null || _window$mainApi2 === void 0 || _window$mainApi2.setPreferences(uuid, _defineProperty({}, typeKey, value));
57784
+ };
57785
+ var setAllVisible = function setAllVisible(value) {
57786
+ // Update local state in one pass + fire one IPC per widget.
57787
+ setPrefs(function (prev) {
57788
+ var next = _objectSpread$a({}, prev);
57789
+ filtered.forEach(function (wi) {
57790
+ var w = _objectSpread$a({}, next[wi.uuid] || {});
57791
+ wi.notifications.forEach(function (n) {
57792
+ w[n.key] = value;
57793
+ });
57794
+ next[wi.uuid] = w;
57795
+ });
57796
+ return next;
57797
+ });
57798
+ filtered.forEach(function (wi) {
57799
+ var _window$mainApi3;
57800
+ var update = {};
57801
+ wi.notifications.forEach(function (n) {
57802
+ update[n.key] = value;
57803
+ });
57804
+ (_window$mainApi3 = window.mainApi) === null || _window$mainApi3 === void 0 || (_window$mainApi3 = _window$mainApi3.notifications) === null || _window$mainApi3 === void 0 || _window$mainApi3.setPreferences(wi.uuid, update);
57805
+ });
57806
+ };
57807
+ if (!loaded) {
57808
+ return /*#__PURE__*/jsx("div", {
57809
+ className: "p-4 text-sm opacity-50",
57810
+ children: "Loading\u2026"
57811
+ });
57812
+ }
57813
+ if (widgetInstances.length === 0) {
57814
+ return /*#__PURE__*/jsx("div", {
57815
+ className: "p-4 text-sm opacity-50",
57816
+ children: "No widgets in this dashboard declare notifications. Add widgets that declare notifications to see per-type controls here."
57817
+ });
57818
+ }
57819
+ return /*#__PURE__*/jsxs("div", {
57820
+ className: "flex flex-col h-full",
57821
+ children: [/*#__PURE__*/jsxs("div", {
57822
+ className: "flex flex-col gap-2 px-2 py-2 flex-shrink-0 border-b border-white/10",
57823
+ children: [/*#__PURE__*/jsx(SearchInput, {
57824
+ value: searchQuery,
57825
+ onChange: setSearchQuery,
57826
+ placeholder: "Search widgets...",
57827
+ inputClassName: "py-1.5 text-xs"
57828
+ }), /*#__PURE__*/jsxs("div", {
57829
+ className: "flex flex-row items-center justify-between text-[10px]",
57830
+ children: [/*#__PURE__*/jsxs("span", {
57831
+ className: "opacity-50",
57832
+ children: [filtered.length, " of ", widgetInstances.length, " widget", widgetInstances.length === 1 ? "" : "s"]
57833
+ }), /*#__PURE__*/jsxs("div", {
57834
+ className: "flex flex-row items-center gap-2",
57835
+ children: [/*#__PURE__*/jsx("button", {
57836
+ type: "button",
57837
+ onClick: function onClick() {
57838
+ return setAllVisible(true);
57839
+ },
57840
+ className: "px-2 py-1 rounded bg-green-700 hover:bg-green-600 text-white text-xs font-medium transition-colors",
57841
+ "data-testid": "bulk-notifications-enable-all",
57842
+ children: "Enable all"
57843
+ }), /*#__PURE__*/jsx("button", {
57844
+ type: "button",
57845
+ onClick: function onClick() {
57846
+ return setAllVisible(false);
57847
+ },
57848
+ className: "px-2 py-1 rounded bg-gray-700 hover:bg-gray-600 text-gray-200 text-xs font-medium transition-colors",
57849
+ "data-testid": "bulk-notifications-disable-all",
57850
+ children: "Disable all"
57851
+ })]
57852
+ })]
57853
+ })]
57854
+ }), /*#__PURE__*/jsx("div", {
57855
+ className: "flex-1 overflow-y-auto px-2 py-2 space-y-3",
57856
+ children: filtered.map(function (wi) {
57857
+ return /*#__PURE__*/jsxs("div", {
57858
+ className: "border border-white/10 rounded p-3 space-y-2",
57859
+ children: [/*#__PURE__*/jsxs("div", {
57860
+ className: "flex flex-col",
57861
+ children: [/*#__PURE__*/jsx("span", {
57862
+ className: "text-sm font-medium",
57863
+ children: wi.title
57864
+ }), /*#__PURE__*/jsxs("span", {
57865
+ className: "text-[10px] opacity-50 font-mono",
57866
+ children: [wi.component, wi.itemId != null ? " \xB7 #".concat(wi.itemId) : ""]
57867
+ }), /*#__PURE__*/jsx("span", {
57868
+ className: "text-[10px] opacity-40",
57869
+ children: wi["package"]
57870
+ })]
57871
+ }), /*#__PURE__*/jsx("div", {
57872
+ className: "flex flex-col gap-1.5 pl-2 border-l border-white/10",
57873
+ children: wi.notifications.map(function (notif) {
57874
+ return /*#__PURE__*/jsxs("div", {
57875
+ className: "flex flex-row items-center justify-between py-0.5",
57876
+ children: [/*#__PURE__*/jsxs("div", {
57877
+ className: "flex flex-col",
57878
+ children: [/*#__PURE__*/jsx("span", {
57879
+ className: "text-xs",
57880
+ children: notif.displayName
57881
+ }), notif.description && /*#__PURE__*/jsx("span", {
57882
+ className: "text-[10px] opacity-50",
57883
+ children: notif.description
57884
+ })]
57885
+ }), /*#__PURE__*/jsx(Switch, {
57886
+ checked: isEnabled(wi.uuid, notif.key, notif.defaultEnabled),
57887
+ onChange: function onChange(value) {
57888
+ return setOne(wi.uuid, notif.key, value);
57889
+ }
57890
+ })]
57891
+ }, notif.key);
57892
+ })
57893
+ })]
57894
+ }, wi.uuid);
57895
+ })
57896
+ })]
57897
+ });
57898
+ }
57899
+
57655
57900
  /**
57656
57901
  * Providers tab with a sidebar/detail layout mirroring the Listeners
57657
57902
  * tab. Column 1 lists provider types in this workspace (with an amber
57658
57903
  * dot per-type when any widget of that type is unresolved). Column 2
57659
57904
  * shows the selected type's bulk dropdown + per-widget dropdowns.
57660
57905
  */
57661
- function ProvidersTab(_ref4) {
57906
+ function ProvidersTab(_ref5) {
57662
57907
  var _typeEntries$, _selectedRows$;
57663
- var grouped = _ref4.grouped,
57664
- providersByType = _ref4.providersByType,
57665
- onBulk = _ref4.onBulk,
57666
- onPerWidget = _ref4.onPerWidget;
57908
+ var grouped = _ref5.grouped,
57909
+ providersByType = _ref5.providersByType,
57910
+ onBulk = _ref5.onBulk,
57911
+ onPerWidget = _ref5.onPerWidget;
57667
57912
  var typeEntries = useMemo(function () {
57668
57913
  return Array.from(grouped.entries());
57669
57914
  }, [grouped]);
57670
- var _useState9 = useState(((_typeEntries$ = typeEntries[0]) === null || _typeEntries$ === void 0 ? void 0 : _typeEntries$[0]) || null),
57671
- _useState0 = _slicedToArray(_useState9, 2),
57672
- selectedType = _useState0[0],
57673
- setSelectedType = _useState0[1];
57915
+ var _useState13 = useState(((_typeEntries$ = typeEntries[0]) === null || _typeEntries$ === void 0 ? void 0 : _typeEntries$[0]) || null),
57916
+ _useState14 = _slicedToArray(_useState13, 2),
57917
+ selectedType = _useState14[0],
57918
+ setSelectedType = _useState14[1];
57674
57919
 
57675
57920
  // If the selected type disappears (workspace changed), fall back.
57676
57921
  useMemo(function () {
@@ -57700,10 +57945,10 @@ function ProvidersTab(_ref4) {
57700
57945
  children: "Provider Types"
57701
57946
  }), /*#__PURE__*/jsx("div", {
57702
57947
  className: "overflow-y-auto flex-1",
57703
- children: typeEntries.map(function (_ref5) {
57704
- var _ref6 = _slicedToArray(_ref5, 2),
57705
- providerType = _ref6[0],
57706
- rows = _ref6[1];
57948
+ children: typeEntries.map(function (_ref6) {
57949
+ var _ref7 = _slicedToArray(_ref6, 2),
57950
+ providerType = _ref7[0],
57951
+ rows = _ref7[1];
57707
57952
  var isActive = selectedType === providerType;
57708
57953
  var unresolvedHere = rows.filter(function (r) {
57709
57954
  return r.required && !r.resolvedProviderName;
@@ -57857,8 +58102,8 @@ function ProvidersTab(_ref4) {
57857
58102
  * package (no `config.id` / `config.package` / item.workspace hint).
57858
58103
  * Usually this is a stale layout item whose widget got uninstalled.
57859
58104
  */
57860
- function DependenciesTab(_ref8) {
57861
- var dependencies = _ref8.dependencies;
58105
+ function DependenciesTab(_ref9) {
58106
+ var dependencies = _ref9.dependencies;
57862
58107
  if (!dependencies || dependencies.length === 0) {
57863
58108
  return /*#__PURE__*/jsx("div", {
57864
58109
  className: "flex items-center justify-center h-full text-sm opacity-60 text-center",
@@ -57931,21 +58176,21 @@ function sameWiringEntry(a, b) {
57931
58176
  * adjusts wiring per handler from a single dropdown of all emitters'
57932
58177
  * (widget × event) pairs.
57933
58178
  */
57934
- function ListenersTab(_ref9) {
58179
+ function ListenersTab(_ref0) {
57935
58180
  var _receivers$;
57936
- var emitters = _ref9.emitters,
57937
- receivers = _ref9.receivers,
57938
- wiring = _ref9.wiring,
57939
- onAdd = _ref9.onAdd,
57940
- onRemove = _ref9.onRemove;
57941
- var _useState11 = useState(((_receivers$ = receivers[0]) === null || _receivers$ === void 0 ? void 0 : _receivers$.key) || null),
57942
- _useState12 = _slicedToArray(_useState11, 2),
57943
- selectedReceiverKey = _useState12[0],
57944
- setSelectedReceiverKey = _useState12[1];
57945
- var _useState13 = useState(null),
57946
- _useState14 = _slicedToArray(_useState13, 2),
57947
- selectedHandler = _useState14[0],
57948
- setSelectedHandler = _useState14[1];
58181
+ var emitters = _ref0.emitters,
58182
+ receivers = _ref0.receivers,
58183
+ wiring = _ref0.wiring,
58184
+ onAdd = _ref0.onAdd,
58185
+ onRemove = _ref0.onRemove;
58186
+ var _useState17 = useState(((_receivers$ = receivers[0]) === null || _receivers$ === void 0 ? void 0 : _receivers$.key) || null),
58187
+ _useState18 = _slicedToArray(_useState17, 2),
58188
+ selectedReceiverKey = _useState18[0],
58189
+ setSelectedReceiverKey = _useState18[1];
58190
+ var _useState19 = useState(null),
58191
+ _useState20 = _slicedToArray(_useState19, 2),
58192
+ selectedHandler = _useState20[0],
58193
+ setSelectedHandler = _useState20[1];
57949
58194
 
57950
58195
  // Re-anchor selection if the previously-selected widget disappeared
57951
58196
  // (workspace switched, widget deleted, etc.).
@@ -58049,11 +58294,11 @@ function ListenersTab(_ref9) {
58049
58294
  * third column. Matches the left-column look from
58050
58295
  * PanelEditItemHandlers.
58051
58296
  */
58052
- function HandlersColumn(_ref1) {
58053
- var receiver = _ref1.receiver,
58054
- myWiring = _ref1.myWiring,
58055
- selectedHandler = _ref1.selectedHandler,
58056
- onSelectHandler = _ref1.onSelectHandler;
58297
+ function HandlersColumn(_ref10) {
58298
+ var receiver = _ref10.receiver,
58299
+ myWiring = _ref10.myWiring,
58300
+ selectedHandler = _ref10.selectedHandler,
58301
+ onSelectHandler = _ref10.onSelectHandler;
58057
58302
  var countsByHandler = useMemo(function () {
58058
58303
  var m = new Map();
58059
58304
  var _iterator9 = _createForOfIteratorHelper$6(myWiring),
@@ -58114,13 +58359,13 @@ function HandlersColumn(_ref1) {
58114
58359
  * Checked = wired; toggling commits an add/remove. Mirrors the
58115
58360
  * right-column UX of PanelEditItemHandlers.
58116
58361
  */
58117
- function EventsColumn(_ref10) {
58118
- var receiver = _ref10.receiver,
58119
- handlerName = _ref10.handlerName,
58120
- myWiring = _ref10.myWiring,
58121
- emitters = _ref10.emitters,
58122
- onAdd = _ref10.onAdd,
58123
- onRemove = _ref10.onRemove;
58362
+ function EventsColumn(_ref11) {
58363
+ var receiver = _ref11.receiver,
58364
+ handlerName = _ref11.handlerName,
58365
+ myWiring = _ref11.myWiring,
58366
+ emitters = _ref11.emitters,
58367
+ onAdd = _ref11.onAdd,
58368
+ onRemove = _ref11.onRemove;
58124
58369
  // Wired-for-this-handler: dedupe defensively (legacy workspaces
58125
58370
  // occasionally contain duplicate entries under the same handler).
58126
58371
  var wiredHere = useMemo(function () {