@trops/dash-core 0.1.249 → 0.1.252

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
@@ -23291,6 +23291,267 @@ var RegistryPackageDetail = function RegistryPackageDetail(_ref) {
23291
23291
  });
23292
23292
  };
23293
23293
 
23294
+ /**
23295
+ * useRegistryAuth — reusable hook for device-code OAuth against the Dash Registry.
23296
+ *
23297
+ * Encapsulates the full auth state machine: check status, initiate login,
23298
+ * poll for token, and cancel. Cleans up the poll interval on unmount.
23299
+ *
23300
+ * @returns {{
23301
+ * isAuthenticated: boolean,
23302
+ * isAuthenticating: boolean,
23303
+ * authFlow: { userCode: string, verificationUrlComplete: string } | null,
23304
+ * authError: string | null,
23305
+ * checkAuth: () => Promise<boolean>,
23306
+ * initiateAuth: () => Promise<void>,
23307
+ * cancelAuth: () => void,
23308
+ * }}
23309
+ */
23310
+ function useRegistryAuth() {
23311
+ var _useState = React.useState(false),
23312
+ _useState2 = _slicedToArray(_useState, 2),
23313
+ isAuthenticated = _useState2[0],
23314
+ setIsAuthenticated = _useState2[1];
23315
+ var _useState3 = React.useState(false),
23316
+ _useState4 = _slicedToArray(_useState3, 2),
23317
+ isAuthenticating = _useState4[0],
23318
+ setIsAuthenticating = _useState4[1];
23319
+ var _useState5 = React.useState(null),
23320
+ _useState6 = _slicedToArray(_useState5, 2),
23321
+ authFlow = _useState6[0],
23322
+ setAuthFlow = _useState6[1];
23323
+ var _useState7 = React.useState(null),
23324
+ _useState8 = _slicedToArray(_useState7, 2),
23325
+ authError = _useState8[0],
23326
+ setAuthError = _useState8[1];
23327
+ var pollIntervalRef = React.useRef(null);
23328
+ var onAuthorizedRef = React.useRef(null);
23329
+
23330
+ // Clean up polling on unmount
23331
+ React.useEffect(function () {
23332
+ return function () {
23333
+ if (pollIntervalRef.current) clearInterval(pollIntervalRef.current);
23334
+ };
23335
+ }, []);
23336
+ var checkAuth = React.useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
23337
+ var status, authed;
23338
+ return _regeneratorRuntime.wrap(function (_context) {
23339
+ while (1) switch (_context.prev = _context.next) {
23340
+ case 0:
23341
+ _context.prev = 0;
23342
+ _context.next = 1;
23343
+ return window.mainApi.registryAuth.getStatus();
23344
+ case 1:
23345
+ status = _context.sent;
23346
+ authed = !!(status !== null && status !== void 0 && status.authenticated);
23347
+ setIsAuthenticated(authed);
23348
+ return _context.abrupt("return", authed);
23349
+ case 2:
23350
+ _context.prev = 2;
23351
+ _context["catch"](0);
23352
+ return _context.abrupt("return", false);
23353
+ case 3:
23354
+ case "end":
23355
+ return _context.stop();
23356
+ }
23357
+ }, _callee, null, [[0, 2]]);
23358
+ })), []);
23359
+ var cancelAuth = React.useCallback(function () {
23360
+ if (pollIntervalRef.current) {
23361
+ clearInterval(pollIntervalRef.current);
23362
+ pollIntervalRef.current = null;
23363
+ }
23364
+ setIsAuthenticating(false);
23365
+ setAuthFlow(null);
23366
+ }, []);
23367
+ var initiateAuth = React.useCallback(/*#__PURE__*/function () {
23368
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3(onAuthorized) {
23369
+ var flow, interval;
23370
+ return _regeneratorRuntime.wrap(function (_context3) {
23371
+ while (1) switch (_context3.prev = _context3.next) {
23372
+ case 0:
23373
+ setAuthError(null);
23374
+ onAuthorizedRef.current = onAuthorized || null;
23375
+ _context3.prev = 1;
23376
+ _context3.next = 2;
23377
+ return window.mainApi.registryAuth.initiateLogin();
23378
+ case 2:
23379
+ flow = _context3.sent;
23380
+ setAuthFlow(flow);
23381
+ if (flow.verificationUrlComplete) {
23382
+ window.mainApi.shell.openExternal(flow.verificationUrlComplete);
23383
+ }
23384
+ setIsAuthenticating(true);
23385
+ interval = (flow.interval || 5) * 1000;
23386
+ pollIntervalRef.current = setInterval(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
23387
+ var pollResult;
23388
+ return _regeneratorRuntime.wrap(function (_context2) {
23389
+ while (1) switch (_context2.prev = _context2.next) {
23390
+ case 0:
23391
+ _context2.prev = 0;
23392
+ _context2.next = 1;
23393
+ return window.mainApi.registryAuth.pollToken(flow.deviceCode);
23394
+ case 1:
23395
+ pollResult = _context2.sent;
23396
+ if (pollResult.status === "authorized") {
23397
+ clearInterval(pollIntervalRef.current);
23398
+ pollIntervalRef.current = null;
23399
+ setIsAuthenticating(false);
23400
+ setAuthFlow(null);
23401
+ setIsAuthenticated(true);
23402
+ if (onAuthorizedRef.current) {
23403
+ onAuthorizedRef.current();
23404
+ }
23405
+ } else if (pollResult.status === "expired") {
23406
+ clearInterval(pollIntervalRef.current);
23407
+ pollIntervalRef.current = null;
23408
+ setIsAuthenticating(false);
23409
+ setAuthFlow(null);
23410
+ setAuthError("Authorization expired. Please try again.");
23411
+ }
23412
+ _context2.next = 3;
23413
+ break;
23414
+ case 2:
23415
+ _context2.prev = 2;
23416
+ _context2["catch"](0);
23417
+ clearInterval(pollIntervalRef.current);
23418
+ pollIntervalRef.current = null;
23419
+ setIsAuthenticating(false);
23420
+ case 3:
23421
+ case "end":
23422
+ return _context2.stop();
23423
+ }
23424
+ }, _callee2, null, [[0, 2]]);
23425
+ })), interval);
23426
+ _context3.next = 4;
23427
+ break;
23428
+ case 3:
23429
+ _context3.prev = 3;
23430
+ _context3["catch"](1);
23431
+ setAuthError("Could not reach the registry. Check your connection and try again.");
23432
+ case 4:
23433
+ case "end":
23434
+ return _context3.stop();
23435
+ }
23436
+ }, _callee3, null, [[1, 3]]);
23437
+ }));
23438
+ return function (_x) {
23439
+ return _ref2.apply(this, arguments);
23440
+ };
23441
+ }(), [cancelAuth]);
23442
+ return {
23443
+ isAuthenticated: isAuthenticated,
23444
+ isAuthenticating: isAuthenticating,
23445
+ authFlow: authFlow,
23446
+ authError: authError,
23447
+ checkAuth: checkAuth,
23448
+ initiateAuth: initiateAuth,
23449
+ cancelAuth: cancelAuth
23450
+ };
23451
+ }
23452
+
23453
+ var RegistryAuthPrompt = function RegistryAuthPrompt(_ref) {
23454
+ var onAuthenticated = _ref.onAuthenticated,
23455
+ _ref$onCancel = _ref.onCancel,
23456
+ onCancel = _ref$onCancel === void 0 ? null : _ref$onCancel,
23457
+ _ref$message = _ref.message,
23458
+ message = _ref$message === void 0 ? "Sign in to install from the Dash Registry." : _ref$message;
23459
+ var _useRegistryAuth = useRegistryAuth(),
23460
+ isAuthenticating = _useRegistryAuth.isAuthenticating,
23461
+ authFlow = _useRegistryAuth.authFlow,
23462
+ authError = _useRegistryAuth.authError,
23463
+ checkAuth = _useRegistryAuth.checkAuth,
23464
+ initiateAuth = _useRegistryAuth.initiateAuth,
23465
+ cancelAuth = _useRegistryAuth.cancelAuth;
23466
+ var checkedRef = React.useRef(false);
23467
+
23468
+ // Check auth on mount — if already authenticated, short-circuit
23469
+ React.useEffect(function () {
23470
+ if (checkedRef.current) return;
23471
+ checkedRef.current = true;
23472
+ checkAuth().then(function (authed) {
23473
+ if (authed && onAuthenticated) onAuthenticated();
23474
+ });
23475
+ }, [checkAuth, onAuthenticated]);
23476
+ function handleSignIn() {
23477
+ initiateAuth(function () {
23478
+ if (onAuthenticated) onAuthenticated();
23479
+ });
23480
+ }
23481
+ function handleCancel() {
23482
+ cancelAuth();
23483
+ if (onCancel) onCancel();
23484
+ }
23485
+
23486
+ // Polling state: show user code
23487
+ if (authFlow && isAuthenticating) {
23488
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
23489
+ className: "flex flex-col gap-3 p-4",
23490
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
23491
+ className: "bg-blue-500/10 border border-blue-500/20 rounded-lg p-4 space-y-3",
23492
+ children: [/*#__PURE__*/jsxRuntime.jsx("p", {
23493
+ className: "text-xs text-blue-300/90",
23494
+ children: "Enter this code in your browser:"
23495
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
23496
+ className: "text-center",
23497
+ children: /*#__PURE__*/jsxRuntime.jsx("span", {
23498
+ className: "text-2xl font-mono font-bold tracking-widest text-white",
23499
+ children: authFlow.userCode
23500
+ })
23501
+ }), /*#__PURE__*/jsxRuntime.jsx("p", {
23502
+ className: "text-xs text-blue-300/70 text-center",
23503
+ children: "Waiting for authorization \u2014 install will resume automatically..."
23504
+ })]
23505
+ }), onCancel && /*#__PURE__*/jsxRuntime.jsx("button", {
23506
+ type: "button",
23507
+ onClick: handleCancel,
23508
+ className: "self-center text-xs text-gray-500 hover:text-gray-300 transition-colors",
23509
+ children: "Cancel"
23510
+ })]
23511
+ });
23512
+ }
23513
+
23514
+ // Default: not-started / error state
23515
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
23516
+ className: "flex flex-col gap-3 p-4",
23517
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
23518
+ className: "bg-yellow-500/10 border border-yellow-500/20 rounded-lg p-3",
23519
+ children: /*#__PURE__*/jsxRuntime.jsxs("div", {
23520
+ className: "flex items-start gap-2",
23521
+ children: [/*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
23522
+ icon: "lock",
23523
+ className: "h-3.5 w-3.5 text-yellow-400 mt-0.5 flex-shrink-0"
23524
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
23525
+ className: "text-sm text-yellow-300/90",
23526
+ children: message
23527
+ })]
23528
+ })
23529
+ }), /*#__PURE__*/jsxRuntime.jsx("button", {
23530
+ type: "button",
23531
+ onClick: handleSignIn,
23532
+ className: "px-4 py-2 rounded-lg text-sm bg-blue-500/20 border border-blue-500/30 text-blue-300 hover:bg-blue-500/30 transition-colors cursor-pointer",
23533
+ children: "Sign in to Registry"
23534
+ }), authError && /*#__PURE__*/jsxRuntime.jsx("div", {
23535
+ className: "bg-red-500/10 border border-red-500/20 rounded-lg p-3",
23536
+ children: /*#__PURE__*/jsxRuntime.jsxs("div", {
23537
+ className: "flex items-start gap-2",
23538
+ children: [/*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
23539
+ icon: "circle-xmark",
23540
+ className: "h-3.5 w-3.5 text-red-400 mt-0.5 flex-shrink-0"
23541
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
23542
+ className: "text-xs text-red-300/90",
23543
+ children: authError
23544
+ })]
23545
+ })
23546
+ }), onCancel && /*#__PURE__*/jsxRuntime.jsx("button", {
23547
+ type: "button",
23548
+ onClick: handleCancel,
23549
+ className: "self-center text-xs text-gray-500 hover:text-gray-300 transition-colors",
23550
+ children: "Cancel"
23551
+ })]
23552
+ });
23553
+ };
23554
+
23294
23555
  function getWidgetSearchQuery(componentKey) {
23295
23556
  var parts = componentKey.split(".");
23296
23557
  if (parts.length >= 3) {
@@ -23340,7 +23601,8 @@ function packageToFlatWidget(pkg) {
23340
23601
  *
23341
23602
  * Shows the existing "Widget Not Found" error display and adds a
23342
23603
  * "Find in Registry" button that does an exact registry lookup and
23343
- * opens an install modal.
23604
+ * opens an install modal. When install requires auth, shows an inline
23605
+ * RegistryAuthPrompt and auto-retries after successful sign-in.
23344
23606
  */
23345
23607
  var WidgetNotFound = function WidgetNotFound(_ref) {
23346
23608
  var component = _ref.component;
@@ -23368,6 +23630,12 @@ var WidgetNotFound = function WidgetNotFound(_ref) {
23368
23630
  _useState10 = _slicedToArray(_useState1, 2),
23369
23631
  installError = _useState10[0],
23370
23632
  setInstallError = _useState10[1];
23633
+ var _useState11 = React.useState(false),
23634
+ _useState12 = _slicedToArray(_useState11, 2),
23635
+ needsAuth = _useState12[0],
23636
+ setNeedsAuth = _useState12[1];
23637
+ var _useRegistryAuth = useRegistryAuth(),
23638
+ checkAuth = _useRegistryAuth.checkAuth;
23371
23639
  var lookupWidget = React.useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
23372
23640
  var _getWidgetSearchQuery, packageName, widgetName, pkg, result;
23373
23641
  return _regeneratorRuntime.wrap(function (_context) {
@@ -23378,6 +23646,7 @@ var WidgetNotFound = function WidgetNotFound(_ref) {
23378
23646
  setNotFound(false);
23379
23647
  setRegistryWidget(null);
23380
23648
  setInstallError(null);
23649
+ setNeedsAuth(false);
23381
23650
  _getWidgetSearchQuery = getWidgetSearchQuery(component), packageName = _getWidgetSearchQuery.packageName, widgetName = _getWidgetSearchQuery.widgetName;
23382
23651
  _context.prev = 1;
23383
23652
  pkg = null; // Scoped ID — exact package lookup
@@ -23424,7 +23693,7 @@ var WidgetNotFound = function WidgetNotFound(_ref) {
23424
23693
  }, _callee, null, [[1, 6]]);
23425
23694
  })), [component]);
23426
23695
  var handleInstall = React.useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
23427
- var packageName, packageScope, downloadUrl, packageVersion, scopedId, resolvedUrl, _t2;
23696
+ var authed, packageName, packageScope, downloadUrl, packageVersion, scopedId, resolvedUrl, msg, _t2;
23428
23697
  return _regeneratorRuntime.wrap(function (_context2) {
23429
23698
  while (1) switch (_context2.prev = _context2.next) {
23430
23699
  case 0:
@@ -23436,28 +23705,53 @@ var WidgetNotFound = function WidgetNotFound(_ref) {
23436
23705
  case 1:
23437
23706
  setIsInstalling(true);
23438
23707
  setInstallError(null);
23708
+ setNeedsAuth(false);
23439
23709
  _context2.prev = 2;
23710
+ _context2.next = 3;
23711
+ return checkAuth();
23712
+ case 3:
23713
+ authed = _context2.sent;
23714
+ if (authed) {
23715
+ _context2.next = 4;
23716
+ break;
23717
+ }
23718
+ setNeedsAuth(true);
23719
+ setIsInstalling(false);
23720
+ return _context2.abrupt("return");
23721
+ case 4:
23440
23722
  packageName = registryWidget.packageName, packageScope = registryWidget.packageScope, downloadUrl = registryWidget.downloadUrl, packageVersion = registryWidget.packageVersion;
23441
23723
  scopedId = packageScope ? "@".concat(packageScope.replace(/^@/, ""), "/").concat(packageName) : packageName;
23442
23724
  resolvedUrl = downloadUrl.replace(/\{version\}/g, packageVersion).replace(/\{name\}/g, packageName);
23443
- _context2.next = 3;
23725
+ _context2.next = 5;
23444
23726
  return window.mainApi.widgets.install(scopedId, resolvedUrl);
23445
- case 3:
23727
+ case 5:
23446
23728
  setShowModal(false);
23447
- _context2.next = 5;
23729
+ _context2.next = 7;
23448
23730
  break;
23449
- case 4:
23450
- _context2.prev = 4;
23731
+ case 6:
23732
+ _context2.prev = 6;
23451
23733
  _t2 = _context2["catch"](2);
23452
- setInstallError(_t2.message || "Failed to install package");
23453
- case 5:
23734
+ msg = _t2.message || "Failed to install package";
23735
+ if (msg.toLowerCase().includes("unauthorized")) {
23736
+ setNeedsAuth(true);
23737
+ } else {
23738
+ setInstallError(msg);
23739
+ }
23740
+ case 7:
23454
23741
  setIsInstalling(false);
23455
- case 6:
23742
+ case 8:
23456
23743
  case "end":
23457
23744
  return _context2.stop();
23458
23745
  }
23459
- }, _callee2, null, [[2, 4]]);
23460
- })), [registryWidget]);
23746
+ }, _callee2, null, [[2, 6]]);
23747
+ })), [registryWidget, checkAuth]);
23748
+ var handleAuthSuccess = React.useCallback(function () {
23749
+ setNeedsAuth(false);
23750
+ handleInstall();
23751
+ }, [handleInstall]);
23752
+ var handleClose = React.useCallback(function () {
23753
+ setShowModal(false);
23754
+ }, []);
23461
23755
  return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
23462
23756
  children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
23463
23757
  className: "flex flex-col h-full justify-center items-center w-full z-10 gap-2 p-4 text-center",
@@ -23482,43 +23776,56 @@ var WidgetNotFound = function WidgetNotFound(_ref) {
23482
23776
  className: "h-3 w-3"
23483
23777
  }), "Find in Registry"]
23484
23778
  })]
23485
- }), showModal && /*#__PURE__*/jsxRuntime.jsxs(DashReact.Modal, {
23486
- title: "Registry Package",
23779
+ }), /*#__PURE__*/jsxRuntime.jsx(DashReact.Modal, {
23780
+ isOpen: showModal,
23781
+ setIsOpen: setShowModal,
23487
23782
  width: "w-1/3",
23488
- height: "h-auto",
23489
- onClose: function onClose() {
23490
- return setShowModal(false);
23491
- },
23492
- children: [isLoading && /*#__PURE__*/jsxRuntime.jsx("div", {
23493
- className: "flex items-center justify-center p-12",
23494
- children: /*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
23495
- icon: "spinner",
23496
- className: "h-5 w-5 text-gray-400 animate-spin"
23497
- })
23498
- }), !isLoading && registryWidget && /*#__PURE__*/jsxRuntime.jsx(RegistryPackageDetail, {
23499
- widget: registryWidget,
23500
- onInstall: handleInstall,
23501
- isInstalling: isInstalling,
23502
- installError: installError
23503
- }), !isLoading && notFound && /*#__PURE__*/jsxRuntime.jsxs("div", {
23504
- className: "flex flex-col items-center justify-center gap-3 p-12 text-center",
23505
- children: [/*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
23506
- icon: "triangle-exclamation",
23507
- className: "h-6 w-6 text-amber-500"
23508
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
23509
- className: "text-sm text-gray-400",
23510
- children: "This widget is not available in the registry."
23511
- }), /*#__PURE__*/jsxRuntime.jsx(DashReact.Button, {
23512
- title: "Close",
23513
- bgColor: "bg-gray-600",
23514
- hoverBackgroundColor: "hover:bg-gray-700",
23515
- textSize: "text-sm",
23516
- padding: "py-1.5 px-4",
23517
- onClick: function onClick() {
23518
- return setShowModal(false);
23519
- }
23783
+ height: "auto",
23784
+ children: /*#__PURE__*/jsxRuntime.jsxs("div", {
23785
+ className: "relative",
23786
+ children: [/*#__PURE__*/jsxRuntime.jsx("button", {
23787
+ type: "button",
23788
+ className: "absolute top-3 right-3 z-10 text-gray-500 hover:text-gray-300 transition-colors",
23789
+ onClick: handleClose,
23790
+ children: /*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
23791
+ icon: "xmark",
23792
+ className: "h-4 w-4"
23793
+ })
23794
+ }), isLoading && /*#__PURE__*/jsxRuntime.jsx("div", {
23795
+ className: "flex items-center justify-center p-12",
23796
+ children: /*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
23797
+ icon: "spinner",
23798
+ className: "h-5 w-5 text-gray-400 animate-spin"
23799
+ })
23800
+ }), !isLoading && needsAuth && registryWidget && /*#__PURE__*/jsxRuntime.jsx(RegistryAuthPrompt, {
23801
+ onAuthenticated: handleAuthSuccess,
23802
+ onCancel: function onCancel() {
23803
+ return setNeedsAuth(false);
23804
+ },
23805
+ message: "Sign in to install this widget from the Dash Registry."
23806
+ }), !isLoading && !needsAuth && registryWidget && /*#__PURE__*/jsxRuntime.jsx(RegistryPackageDetail, {
23807
+ widget: registryWidget,
23808
+ onInstall: handleInstall,
23809
+ isInstalling: isInstalling,
23810
+ installError: installError
23811
+ }), !isLoading && notFound && /*#__PURE__*/jsxRuntime.jsxs("div", {
23812
+ className: "flex flex-col items-center justify-center gap-3 p-12 text-center",
23813
+ children: [/*#__PURE__*/jsxRuntime.jsx(DashReact.FontAwesomeIcon, {
23814
+ icon: "triangle-exclamation",
23815
+ className: "h-6 w-6 text-amber-500"
23816
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
23817
+ className: "text-sm text-gray-400",
23818
+ children: "This widget is not available in the registry."
23819
+ }), /*#__PURE__*/jsxRuntime.jsx(DashReact.Button, {
23820
+ title: "Close",
23821
+ bgColor: "bg-gray-600",
23822
+ hoverBackgroundColor: "hover:bg-gray-700",
23823
+ textSize: "text-sm",
23824
+ padding: "py-1.5 px-4",
23825
+ onClick: handleClose
23826
+ })]
23520
23827
  })]
23521
- })]
23828
+ })
23522
23829
  })]
23523
23830
  });
23524
23831
  };