powr-sdk-web 6.1.0 → 6.2.1

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.
@@ -52,9 +52,9 @@ function formatWhen(iso, locale) {
52
52
  }
53
53
  }
54
54
 
55
- /**
56
- * Presentational list for PowrBase activity-shaped rows (`activity.activity`, `activity.actor`, `activity.action`, `createdAt`).
57
- * Does not fetch — pass data from parent or use {@link PowrBaseActivities}.
55
+ /**
56
+ * Presentational list for PowrBase activity-shaped rows (`activity.activity`, `activity.actor`, `activity.action`, `createdAt`).
57
+ * Does not fetch — pass data from parent or use {@link PowrBaseActivities}.
58
58
  */
59
59
  function PowrActivityList(_ref) {
60
60
  var activities = _ref.activities,
@@ -36,9 +36,9 @@ var wrapperStyle = {
36
36
  fontFamily: 'Roboto, sans-serif'
37
37
  };
38
38
 
39
- /**
40
- * Fetches activities like comments: GET with contentId + projectId (via apiCall).
41
- * Date filtering is client-side: calendar picks a day, list shows items for that day.
39
+ /**
40
+ * Fetches activities like comments: GET with contentId + projectId (via apiCall).
41
+ * Date filtering is client-side: calendar picks a day, list shows items for that day.
42
42
  */
43
43
  function PowrBaseActivities(_ref) {
44
44
  var projectId = _ref.projectId,
@@ -18,10 +18,10 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
18
18
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
19
19
  function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
20
20
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
21
- /**
22
- * Full PowrBaseAdmin for host apps that already install optional peers
23
- * (Excelrs / Spriny via the package root export).
24
- * Prefer `powr-sdk-web/dist/admin` in lean apps (Ampy Ops) to avoid monaco/lucide.
21
+ /**
22
+ * Full PowrBaseAdmin for host apps that already install optional peers
23
+ * (Excelrs / Spriny via the package root export).
24
+ * Prefer `powr-sdk-web/dist/admin` in lean apps (Ampy Ops) to avoid monaco/lucide.
25
25
  */
26
26
  function PowrBaseAdminFull(props) {
27
27
  var sectionLoaders = props.sectionLoaders,
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.CORE_SECTION_LOADERS = void 0;
7
7
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
8
8
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
9
- /**
10
- * Core admin sections — no heavy optional peers (monaco / lucide / hot-toast).
11
- * Safe for apps that only need Users (e.g. Ampy Ops).
9
+ /**
10
+ * Core admin sections — no heavy optional peers (monaco / lucide / hot-toast).
11
+ * Safe for apps that only need Users (e.g. Ampy Ops).
12
12
  */
13
13
  var CORE_SECTION_LOADERS = exports.CORE_SECTION_LOADERS = {
14
14
  users: function users() {
@@ -6,14 +6,14 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports["default"] = exports.OPTIONAL_SECTION_LOADERS = void 0;
7
7
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
8
8
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
9
- /**
10
- * Heavy admin sections. Import only from apps that install optional peers:
11
- * lucide-react, @monaco-editor/react, react-hot-toast
12
- *
13
- * @example
14
- * import PowrBaseAdmin from 'powr-sdk-web/dist/admin';
15
- * import { OPTIONAL_SECTION_LOADERS } from 'powr-sdk-web/dist/admin/optionalSectionLoaders';
16
- * <PowrBaseAdmin sections={['Users','Functions']} sectionLoaders={OPTIONAL_SECTION_LOADERS} />
9
+ /**
10
+ * Heavy admin sections. Import only from apps that install optional peers:
11
+ * lucide-react, @monaco-editor/react, react-hot-toast
12
+ *
13
+ * @example
14
+ * import PowrBaseAdmin from 'powr-sdk-web/dist/admin';
15
+ * import { OPTIONAL_SECTION_LOADERS } from 'powr-sdk-web/dist/admin/optionalSectionLoaders';
16
+ * <PowrBaseAdmin sections={['Users','Functions']} sectionLoaders={OPTIONAL_SECTION_LOADERS} />
17
17
  */
18
18
  var OPTIONAL_SECTION_LOADERS = exports.OPTIONAL_SECTION_LOADERS = {
19
19
  functions: function functions() {
@@ -43,6 +43,10 @@ var PowrLogin = function PowrLogin(_ref) {
43
43
  requirePhoneVerification = _ref$requirePhoneVeri === void 0 ? true : _ref$requirePhoneVeri,
44
44
  _ref$showForgotPasswo = _ref.showForgotPassword,
45
45
  showForgotPassword = _ref$showForgotPasswo === void 0 ? true : _ref$showForgotPasswo,
46
+ _ref$passwordResetHel = _ref.passwordResetHelpUrl,
47
+ passwordResetHelpUrl = _ref$passwordResetHel === void 0 ? null : _ref$passwordResetHel,
48
+ _ref$passwordResetHel2 = _ref.passwordResetHelpLabel,
49
+ passwordResetHelpLabel = _ref$passwordResetHel2 === void 0 ? 'Forgot password? WhatsApp us' : _ref$passwordResetHel2,
46
50
  _ref$completeProfileO = _ref.completeProfileOnLogin,
47
51
  completeProfileOnLogin = _ref$completeProfileO === void 0 ? false : _ref$completeProfileO,
48
52
  _ref$requireTerms = _ref.requireTerms,
@@ -871,13 +875,18 @@ var PowrLogin = function PowrLogin(_ref) {
871
875
  style: styles.checkbox
872
876
  }), /*#__PURE__*/_react["default"].createElement("span", {
873
877
  style: styles.checkboxText
874
- }, "Remember me")), showForgotPassword && /*#__PURE__*/_react["default"].createElement("button", {
878
+ }, "Remember me")), passwordResetHelpUrl ? /*#__PURE__*/_react["default"].createElement("a", {
879
+ href: passwordResetHelpUrl,
880
+ target: "_blank",
881
+ rel: "noopener noreferrer",
882
+ style: styles.forgotPasswordLink
883
+ }, passwordResetHelpLabel) : showForgotPassword ? /*#__PURE__*/_react["default"].createElement("button", {
875
884
  type: "button",
876
885
  onClick: function onClick() {
877
886
  return setShowOtpOption(!showOtpOption);
878
887
  },
879
888
  style: styles.forgotPasswordLink
880
- }, "Forgot password?")), isLogin && showForgotPassword && showOtpOption && /*#__PURE__*/_react["default"].createElement("div", {
889
+ }, "Forgot password?") : null), isLogin && !passwordResetHelpUrl && showForgotPassword && showOtpOption && /*#__PURE__*/_react["default"].createElement("div", {
881
890
  style: styles.otpSection
882
891
  }, /*#__PURE__*/_react["default"].createElement("p", {
883
892
  style: styles.otpText
@@ -101,8 +101,8 @@ function ChevronRight() {
101
101
  }));
102
102
  }
103
103
 
104
- /**
105
- * Week strip + day picker (controlled). No data fetching — use anywhere you need a date range UI.
104
+ /**
105
+ * Week strip + day picker (controlled). No data fetching — use anywhere you need a date range UI.
106
106
  */
107
107
  function PowrCalendarStrip(_ref) {
108
108
  var selectedDate = _ref.selectedDate,
package/dist/index.js CHANGED
@@ -334,6 +334,12 @@ Object.defineProperty(exports, "evaluateSteps", {
334
334
  return _onboarding.evaluateSteps;
335
335
  }
336
336
  });
337
+ Object.defineProperty(exports, "fetchThemePreference", {
338
+ enumerable: true,
339
+ get: function get() {
340
+ return _theme.fetchThemePreference;
341
+ }
342
+ });
337
343
  Object.defineProperty(exports, "formatPhoneForSms", {
338
344
  enumerable: true,
339
345
  get: function get() {
@@ -466,6 +472,12 @@ Object.defineProperty(exports, "parsePhone", {
466
472
  return _phone.parsePhone;
467
473
  }
468
474
  });
475
+ Object.defineProperty(exports, "powrThemeInitScript", {
476
+ enumerable: true,
477
+ get: function get() {
478
+ return _theme.powrThemeInitScript;
479
+ }
480
+ });
469
481
  Object.defineProperty(exports, "powrThemeStorageKey", {
470
482
  enumerable: true,
471
483
  get: function get() {
@@ -514,6 +526,12 @@ Object.defineProperty(exports, "safeNextPath", {
514
526
  return _sso.safeNextPath;
515
527
  }
516
528
  });
529
+ Object.defineProperty(exports, "saveThemePreference", {
530
+ enumerable: true,
531
+ get: function get() {
532
+ return _theme.saveThemePreference;
533
+ }
534
+ });
517
535
  Object.defineProperty(exports, "tokensToCssVars", {
518
536
  enumerable: true,
519
537
  get: function get() {
@@ -532,6 +550,12 @@ Object.defineProperty(exports, "usePowrTheme", {
532
550
  return _theme.usePowrTheme;
533
551
  }
534
552
  });
553
+ Object.defineProperty(exports, "usePowrThemeHost", {
554
+ enumerable: true,
555
+ get: function get() {
556
+ return _theme.usePowrThemeHost;
557
+ }
558
+ });
535
559
  Object.defineProperty(exports, "writeStoredSession", {
536
560
  enumerable: true,
537
561
  get: function get() {
@@ -29,16 +29,16 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
29
29
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
30
30
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
31
31
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
32
- /**
33
- * Powr session — the file every new app used to copy (`utils/store.ts`).
34
- *
35
- * Owns `localStorage['auth-store']` in the zustand-persist shape
36
- * `{ state: { user, token }, version }` so:
37
- * - `apiCall` (utils/auth.getAuthToken) keeps reading the bearer from it
38
- * - apps that already run a zustand `auth-store` can pass `user`/`token`
39
- * as props (controlled) and nothing moves.
40
- *
41
- * No zustand dependency. Plain React context.
32
+ /**
33
+ * Powr session — the file every new app used to copy (`utils/store.ts`).
34
+ *
35
+ * Owns `localStorage['auth-store']` in the zustand-persist shape
36
+ * `{ state: { user, token }, version }` so:
37
+ * - `apiCall` (utils/auth.getAuthToken) keeps reading the bearer from it
38
+ * - apps that already run a zustand `auth-store` can pass `user`/`token`
39
+ * as props (controlled) and nothing moves.
40
+ *
41
+ * No zustand dependency. Plain React context.
42
42
  */
43
43
 
44
44
  var POWR_SESSION_STORAGE_KEY = exports.POWR_SESSION_STORAGE_KEY = "auth-store";
@@ -111,14 +111,14 @@ var PowrSessionContext = /*#__PURE__*/(0, _react.createContext)(_objectSpread(_o
111
111
  clear: function clear() {}
112
112
  }));
113
113
 
114
- /**
115
- * @param {object} props
116
- * @param {object|null} [props.user] - Controlled mode: app's own store is truth.
117
- * @param {string|null} [props.token] - Controlled mode.
118
- * @param {string|null} [props.refreshToken]
119
- * @param {(session: {user, token, refreshToken}) => void} [props.onChange] - After setSession / clear.
120
- * @param {() => void} [props.onLogout] - Controlled mode: clear your store here.
121
- * @param {boolean} [props.persist=true] - Uncontrolled mode: write `auth-store`.
114
+ /**
115
+ * @param {object} props
116
+ * @param {object|null} [props.user] - Controlled mode: app's own store is truth.
117
+ * @param {string|null} [props.token] - Controlled mode.
118
+ * @param {string|null} [props.refreshToken]
119
+ * @param {(session: {user, token, refreshToken}) => void} [props.onChange] - After setSession / clear.
120
+ * @param {() => void} [props.onLogout] - Controlled mode: clear your store here.
121
+ * @param {boolean} [props.persist=true] - Uncontrolled mode: write `auth-store`.
122
122
  */
123
123
  function PowrSessionProvider(_ref2) {
124
124
  var userProp = _ref2.user,
@@ -9,6 +9,7 @@ exports["default"] = PowrBaseAppWrapper;
9
9
  var _react = _interopRequireWildcard(require("react"));
10
10
  var _PowrThemeProvider = _interopRequireWildcard(require("../theme/PowrThemeProvider"));
11
11
  var _PowrThemeSettings = _interopRequireDefault(require("../theme/PowrThemeSettings"));
12
+ var _host = require("../theme/host");
12
13
  var _session = _interopRequireWildcard(require("../session"));
13
14
  var _auth = _interopRequireDefault(require("../auth"));
14
15
  var _PowrOnboardingProvider = _interopRequireDefault(require("../onboarding/PowrOnboardingProvider"));
@@ -35,16 +36,16 @@ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t =
35
36
  var config = require("../config");
36
37
  var POWR_SHELL_MODES = exports.POWR_SHELL_MODES = Object.freeze(["sidebar", "topbar"]);
37
38
 
38
- /**
39
- * PowrBaseAppWrapper — the one root mount for a Powr web app.
40
- *
41
- * Replaces the files every new app copied: auth store, route guards, top bar /
42
- * sidebar, login page glue, providers. Chrome + selectors live here; palette,
43
- * presets, logo, nav items, product roles and public pages stay in the product.
44
- *
45
- * Guardrails: no colours of its own (every colour is `var(--powr-*)`), more
46
- * than one shape (`sidebar` | `topbar`), no `next/*` import — `Link` and
47
- * `pathname` come in as props, like `apiUrl` does elsewhere.
39
+ /**
40
+ * PowrBaseAppWrapper — the one root mount for a Powr web app.
41
+ *
42
+ * Replaces the files every new app copied: auth store, route guards, top bar /
43
+ * sidebar, login page glue, providers. Chrome + selectors live here; palette,
44
+ * presets, logo, nav items, product roles and public pages stay in the product.
45
+ *
46
+ * Guardrails: no colours of its own (every colour is `var(--powr-*)`), more
47
+ * than one shape (`sidebar` | `topbar`), no `next/*` import — `Link` and
48
+ * `pathname` come in as props, like `apiUrl` does elsewhere.
48
49
  */
49
50
  function PowrBaseAppWrapper(props) {
50
51
  var theme = props.theme,
@@ -111,12 +112,21 @@ function Shell(_ref) {
111
112
  menuItems = _ref$menuItems === void 0 ? [] : _ref$menuItems,
112
113
  _ref$showThemeSetting = _ref.showThemeSettings,
113
114
  showThemeSettings = _ref$showThemeSetting === void 0 ? true : _ref$showThemeSetting,
115
+ _ref$syncThemeWithHos = _ref.syncThemeWithHost,
116
+ syncThemeWithHost = _ref$syncThemeWithHos === void 0 ? true : _ref$syncThemeWithHos,
114
117
  apiUrl = _ref.apiUrl,
115
118
  projectId = _ref.projectId,
116
119
  appName = _ref.appName,
117
120
  appLogo = _ref.appLogo,
118
121
  children = _ref.children;
119
122
  var session = (0, _session.usePowrSession)();
123
+ // Host is truth after login: pull once per token, Save writes back.
124
+ var themeHost = (0, _host.usePowrThemeHost)({
125
+ token: session.token,
126
+ apiUrl: apiUrl,
127
+ projectId: projectId,
128
+ enabled: syncThemeWithHost && showThemeSettings
129
+ });
120
130
  var narrow = (0, _useMediaQuery["default"])(_styles.NARROW_QUERY, false);
121
131
  var _useState = (0, _react.useState)(false),
122
132
  _useState2 = _slicedToArray(_useState, 2),
@@ -194,7 +204,8 @@ function Shell(_ref) {
194
204
  style: _styles.s.footer
195
205
  }, footer) : null, /*#__PURE__*/_react["default"].createElement(SettingsDrawer, {
196
206
  open: settingsOpen,
197
- onClose: closeSettings
207
+ onClose: closeSettings,
208
+ host: null
198
209
  }));
199
210
  }
200
211
 
@@ -268,7 +279,8 @@ function Shell(_ref) {
268
279
  onNavigate: closeNav
269
280
  })), /*#__PURE__*/_react["default"].createElement(SettingsDrawer, {
270
281
  open: settingsOpen,
271
- onClose: closeSettings
282
+ onClose: closeSettings,
283
+ host: syncThemeWithHost && session.token ? themeHost : null
272
284
  }));
273
285
  if (onboarding === false || !session.user) return content;
274
286
  return /*#__PURE__*/_react["default"].createElement(_PowrOnboardingProvider["default"], _extends({
@@ -318,13 +330,19 @@ function Brand(_ref2) {
318
330
  }
319
331
  function SettingsDrawer(_ref3) {
320
332
  var open = _ref3.open,
321
- onClose = _ref3.onClose;
333
+ onClose = _ref3.onClose,
334
+ host = _ref3.host;
322
335
  return /*#__PURE__*/_react["default"].createElement(_parts.Drawer, {
323
336
  open: open,
324
337
  side: "right",
325
338
  title: "Appearance",
326
339
  onClose: onClose
327
340
  }, /*#__PURE__*/_react["default"].createElement(_PowrThemeSettings["default"], {
328
- title: null
341
+ title: null,
342
+ onSave: host ? function () {
343
+ return host.save()["catch"](function () {});
344
+ } : undefined,
345
+ saving: host ? host.saving : false,
346
+ error: host ? host.error : null
329
347
  }));
330
348
  }
@@ -9,9 +9,9 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
9
9
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
10
10
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
11
11
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
12
- /**
13
- * Shell styles. Every colour is a `--powr-*` var with today's neutral as the
14
- * fallback — the wrapper has no palette of its own (clone-leak guardrail).
12
+ /**
13
+ * Shell styles. Every colour is a `--powr-*` var with today's neutral as the
14
+ * fallback — the wrapper has no palette of its own (clone-leak guardrail).
15
15
  */
16
16
 
17
17
  var v = exports.v = {
package/dist/sso/api.js CHANGED
@@ -27,21 +27,21 @@ function safeNextPath(next) {
27
27
  return fallback;
28
28
  }
29
29
 
30
- /**
31
- * Issue a one-time login code (caller must already be authenticated).
32
- * Host `injectProjectId` needs destination `projectId` on the body.
33
- * @param {string} apiUrl - Powr API base
34
- * @param {string} bearerToken
35
- * @param {{ projectId?: string }} [opts]
30
+ /**
31
+ * Issue a one-time login code (caller must already be authenticated).
32
+ * Host `injectProjectId` needs destination `projectId` on the body.
33
+ * @param {string} apiUrl - Powr API base
34
+ * @param {string} bearerToken
35
+ * @param {{ projectId?: string }} [opts]
36
36
  */
37
37
  function issueLoginCode(_x, _x2) {
38
38
  return _issueLoginCode.apply(this, arguments);
39
39
  }
40
- /**
41
- * Exchange loginId for session (same shape as /login).
42
- * @param {string} apiUrl
43
- * @param {string} loginId
44
- * @param {{ projectId?: string }} [opts]
40
+ /**
41
+ * Exchange loginId for session (same shape as /login).
42
+ * @param {string} apiUrl
43
+ * @param {string} loginId
44
+ * @param {{ projectId?: string }} [opts]
45
45
  */
46
46
  function _issueLoginCode() {
47
47
  _issueLoginCode = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(apiUrl, bearerToken) {
package/dist/sso/index.js CHANGED
@@ -145,22 +145,22 @@ var btnStyle = {
145
145
  cursor: "pointer"
146
146
  };
147
147
 
148
- /**
149
- * Cross-app SSO handoff UI (UrbanStall → Powr apps).
150
- * Apps keep a thin `/sso` page that renders this with `apiUrl` + `onSuccess`.
151
- *
152
- * @param {object} props
153
- * @param {string} [props.apiUrl] - Powr host. Defaults to https://powr-api.lawazia.tech
154
- * @param {string} [props.projectId] - Destination Powr id. Defaults to NEXT_PUBLIC_PROJECT_ID
155
- * @param {string} [props.appName]
156
- * @param {boolean} [props.ready=true] - Wait (e.g. auth hydrate) before exchanging code
157
- * @param {string} [props.loginHref="/login"]
158
- * @param {string} [props.loginId] - Defaults to ?loginId=
159
- * @param {string} [props.next] - Defaults to ?next=
160
- * @param {(data: object, ctx: { next: string, safeNext: string }) => void | Promise<void>} props.onSuccess
161
- * @param {string} [props.signingInTitle]
162
- * @param {string} [props.signingInMessage]
163
- * @param {string} [props.errorTitle]
148
+ /**
149
+ * Cross-app SSO handoff UI (UrbanStall → Powr apps).
150
+ * Apps keep a thin `/sso` page that renders this with `apiUrl` + `onSuccess`.
151
+ *
152
+ * @param {object} props
153
+ * @param {string} [props.apiUrl] - Powr host. Defaults to https://powr-api.lawazia.tech
154
+ * @param {string} [props.projectId] - Destination Powr id. Defaults to NEXT_PUBLIC_PROJECT_ID
155
+ * @param {string} [props.appName]
156
+ * @param {boolean} [props.ready=true] - Wait (e.g. auth hydrate) before exchanging code
157
+ * @param {string} [props.loginHref="/login"]
158
+ * @param {string} [props.loginId] - Defaults to ?loginId=
159
+ * @param {string} [props.next] - Defaults to ?next=
160
+ * @param {(data: object, ctx: { next: string, safeNext: string }) => void | Promise<void>} props.onSuccess
161
+ * @param {string} [props.signingInTitle]
162
+ * @param {string} [props.signingInMessage]
163
+ * @param {string} [props.errorTitle]
164
164
  */
165
165
  function PowrBaseSso(_ref) {
166
166
  var apiUrl = _ref.apiUrl,
@@ -79,10 +79,10 @@ function resolvePowrApiUrl(apiUrl) {
79
79
  return "";
80
80
  }
81
81
 
82
- /**
83
- * @param {string} url
84
- * @param {string[]|Array<{url?: string, projectId?: string}>} [origins] - sibling card list or origins
85
- * @returns {{ origin: string, pathname: string, search: string, hash: string, projectId: string } | null}
82
+ /**
83
+ * @param {string} url
84
+ * @param {string[]|Array<{url?: string, projectId?: string}>} [origins] - sibling card list or origins
85
+ * @returns {{ origin: string, pathname: string, search: string, hash: string, projectId: string } | null}
86
86
  */
87
87
  function matchPowrTarget(url, origins) {
88
88
  var dest = parseDest(url);
@@ -98,13 +98,13 @@ function matchPowrTarget(url, origins) {
98
98
  return dest;
99
99
  }
100
100
 
101
- /**
102
- * @deprecated Product `/powr` URLs are gone. Pass `apiUrl` + the card list as `origins`.
101
+ /**
102
+ * @deprecated Product `/powr` URLs are gone. Pass `apiUrl` + the card list as `origins`.
103
103
  */
104
104
  var DEFAULT_POWR_TARGETS = exports.DEFAULT_POWR_TARGETS = [];
105
105
 
106
- /**
107
- * Build destination `/sso?loginId=` (+ optional `next`) on the sibling app.
106
+ /**
107
+ * Build destination `/sso?loginId=` (+ optional `next`) on the sibling app.
108
108
  */
109
109
  function buildPowrSsoUrl(target, loginId) {
110
110
  var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
@@ -124,16 +124,16 @@ function openPlain(url) {
124
124
  window.open(url, "_blank", "noopener,noreferrer");
125
125
  }
126
126
 
127
- /**
128
- * Issue a login code and navigate to `/sso?loginId=`.
129
- * Host is the SDK default. Omit `apiUrl` unless this call is a real override.
130
- *
131
- * @param {string} url - Destination app URL (from the sibling card / banner)
132
- * @param {string|null|undefined} token
133
- * @param {object} [options]
134
- * @param {string} [options.apiUrl] - Override host. Do not pass product `/powr`.
135
- * @param {string} [options.projectId] - Destination Powr id (else from matched card)
136
- * @param {string[]|Array<{url?: string, projectId?: string}>} [options.origins] - The card list
127
+ /**
128
+ * Issue a login code and navigate to `/sso?loginId=`.
129
+ * Host is the SDK default. Omit `apiUrl` unless this call is a real override.
130
+ *
131
+ * @param {string} url - Destination app URL (from the sibling card / banner)
132
+ * @param {string|null|undefined} token
133
+ * @param {object} [options]
134
+ * @param {string} [options.apiUrl] - Override host. Do not pass product `/powr`.
135
+ * @param {string} [options.projectId] - Destination Powr id (else from matched card)
136
+ * @param {string[]|Array<{url?: string, projectId?: string}>} [options.origins] - The card list
137
137
  */
138
138
  function openPowrLogin(_x, _x2) {
139
139
  return _openPowrLogin.apply(this, arguments);
@@ -4,7 +4,12 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports["default"] = PowrThemeProvider;
7
- exports.powrThemeStorageKey = powrThemeStorageKey;
7
+ Object.defineProperty(exports, "powrThemeStorageKey", {
8
+ enumerable: true,
9
+ get: function get() {
10
+ return _tokens.powrThemeStorageKey;
11
+ }
12
+ });
8
13
  exports.usePowrTheme = usePowrTheme;
9
14
  var _react = _interopRequireWildcard(require("react"));
10
15
  var _tokens = require("./tokens");
@@ -24,11 +29,7 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
24
29
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
25
30
  var config = require("../config");
26
31
  var PowrThemeContext = /*#__PURE__*/(0, _react.createContext)(null);
27
- var STORAGE_PREFIX = "powr-theme";
28
32
  var DARK_QUERY = "(prefers-color-scheme: dark)";
29
- function powrThemeStorageKey(projectId) {
30
- return "".concat(STORAGE_PREFIX, ":").concat(projectId || "default");
31
- }
32
33
  function readStorage(key) {
33
34
  if (typeof window === "undefined") return null;
34
35
  try {
@@ -53,25 +54,25 @@ function systemPrefersDark() {
53
54
  return window.matchMedia(DARK_QUERY).matches;
54
55
  }
55
56
 
56
- /**
57
- * Root of the Powr theme. Mount once per app (the future `PowrBaseAppWrapper`
58
- * mounts it for you). Structure (light / dark / density) is the SDK's;
59
- * colours come from the product via `theme` and `presets`.
60
- *
61
- * With no props it writes today's neutral values, so SDK components render
62
- * exactly as before.
63
- *
64
- * @param {object} props
65
- * @param {object} [props.theme] - Product tokens. Flat `{ primary, bg, … }` (light) or `{ light: {…}, dark: {…} }`.
66
- * @param {Array<{id: string, label?: string, tokens?: object, dark?: object}>} [props.presets] - Product-named presets users may pick.
67
- * @param {"light"|"dark"|"system"} [props.scheme="light"] - Starting scheme; the user can change it (card 2 panel).
68
- * @param {"comfortable"|"compact"} [props.density="comfortable"] - Starting density.
69
- * @param {string} [props.preset] - Starting preset id.
70
- * @param {string} [props.projectId] - Cache key scope. Defaults to NEXT_PUBLIC_PROJECT_ID.
71
- * @param {boolean} [props.persist=true] - Cache choices in localStorage (cache, not truth — host save is card 3).
72
- * @param {"root"|"scoped"} [props.attach="root"] - `root` writes vars on <html>; `scoped` wraps children in one div carrying the vars.
73
- * @param {string} [props.className] - For `scoped` only.
74
- * @param {(value: {scheme: string, density: string, preset: string|null}) => void} [props.onChange] - Fires when a selector changes.
57
+ /**
58
+ * Root of the Powr theme. Mount once per app (the future `PowrBaseAppWrapper`
59
+ * mounts it for you). Structure (light / dark / density) is the SDK's;
60
+ * colours come from the product via `theme` and `presets`.
61
+ *
62
+ * With no props it writes today's neutral values, so SDK components render
63
+ * exactly as before.
64
+ *
65
+ * @param {object} props
66
+ * @param {object} [props.theme] - Product tokens. Flat `{ primary, bg, … }` (light) or `{ light: {…}, dark: {…} }`.
67
+ * @param {Array<{id: string, label?: string, tokens?: object, dark?: object}>} [props.presets] - Product-named presets users may pick.
68
+ * @param {"light"|"dark"|"system"} [props.scheme="light"] - Starting scheme; the user can change it (card 2 panel).
69
+ * @param {"comfortable"|"compact"} [props.density="comfortable"] - Starting density.
70
+ * @param {string} [props.preset] - Starting preset id.
71
+ * @param {string} [props.projectId] - Cache key scope. Defaults to NEXT_PUBLIC_PROJECT_ID.
72
+ * @param {boolean} [props.persist=true] - Cache choices in localStorage (cache, not truth — host save is card 3).
73
+ * @param {"root"|"scoped"} [props.attach="root"] - `root` writes vars on <html>; `scoped` wraps children in one div carrying the vars.
74
+ * @param {string} [props.className] - For `scoped` only.
75
+ * @param {(value: {scheme: string, density: string, preset: string|null}) => void} [props.onChange] - Fires when a selector changes.
75
76
  */
76
77
  function PowrThemeProvider(_ref) {
77
78
  var theme = _ref.theme,
@@ -91,7 +92,7 @@ function PowrThemeProvider(_ref) {
91
92
  className = _ref.className,
92
93
  onChange = _ref.onChange,
93
94
  children = _ref.children;
94
- var storageKey = powrThemeStorageKey(projectId || config.projectId);
95
+ var storageKey = (0, _tokens.powrThemeStorageKey)(projectId || config.projectId);
95
96
  var _useState = (0, _react.useState)(function () {
96
97
  return (0, _tokens.normalizeScheme)(schemeProp);
97
98
  }),
@@ -164,8 +165,10 @@ function PowrThemeProvider(_ref) {
164
165
  }, [resolved]);
165
166
 
166
167
  // attach="root": write on <html> so product CSS, portals and toasts see the same vars.
168
+ // Wait for the cache read: otherwise the first commit writes the prop defaults
169
+ // over what `powrThemeInitScript` already painted (one light frame for dark users).
167
170
  (0, _react.useEffect)(function () {
168
- if (attach !== "root" || typeof document === "undefined") return undefined;
171
+ if (attach !== "root" || typeof document === "undefined" || !hydrated) return undefined;
169
172
  var el = document.documentElement;
170
173
  for (var _i = 0, _Object$keys = Object.keys(cssVars); _i < _Object$keys.length; _i++) {
171
174
  var name = _Object$keys[_i];
@@ -193,11 +196,21 @@ function PowrThemeProvider(_ref) {
193
196
  el.removeAttribute("data-powr-preset");
194
197
  el.style.removeProperty("color-scheme");
195
198
  };
196
- }, [attach, cssVars, resolved.scheme, resolved.density, resolved.preset]);
199
+ }, [attach, hydrated, cssVars, resolved.scheme, resolved.density, resolved.preset]);
197
200
  var commit = (0, _react.useCallback)(function (next) {
198
- if (persist) writeStorage(storageKey, next);
199
201
  if (typeof onChange === "function") onChange(next);
200
- }, [persist, storageKey, onChange]);
202
+ }, [onChange]);
203
+
204
+ // Cache from state, not from setter closures — three back-to-back setX calls
205
+ // (host pull) must leave the cache equal to the final state, not the first.
206
+ (0, _react.useEffect)(function () {
207
+ if (!hydrated || !persist) return;
208
+ writeStorage(storageKey, {
209
+ scheme: scheme,
210
+ density: density,
211
+ preset: preset
212
+ });
213
+ }, [hydrated, persist, storageKey, scheme, density, preset]);
201
214
  var setScheme = (0, _react.useCallback)(function (value) {
202
215
  var s = (0, _tokens.normalizeScheme)(value);
203
216
  setSchemeState(s);
@@ -291,10 +304,10 @@ var UNMOUNTED_THEME = function () {
291
304
  });
292
305
  }();
293
306
 
294
- /**
295
- * Read the theme. Outside a provider returns the neutral defaults and no-op
296
- * setters (`mounted: false`), so SDK components work in apps that have not
297
- * adopted the theme yet.
307
+ /**
308
+ * Read the theme. Outside a provider returns the neutral defaults and no-op
309
+ * setters (`mounted: false`), so SDK components work in apps that have not
310
+ * adopted the theme yet.
298
311
  */
299
312
  function usePowrTheme() {
300
313
  var ctx = (0, _react.useContext)(PowrThemeContext);