strapi-plugin-magic-link-v5 5.5.9 → 6.0.0

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +733 -0
  2. package/README.md +662 -928
  3. package/dist/admin/LicenseGuard-BgLA9go3.mjs +495 -0
  4. package/dist/admin/LicenseGuard-ewCjpc17.js +513 -0
  5. package/dist/admin/de-B1FNErPH.mjs +673 -0
  6. package/dist/admin/de-e75OECdq.js +673 -0
  7. package/dist/admin/en-CABKGbxH.js +673 -0
  8. package/dist/admin/en-HqDOFBCc.mjs +673 -0
  9. package/dist/admin/es-DpWl0Fax.js +673 -0
  10. package/dist/admin/es-jb94jqPR.mjs +673 -0
  11. package/dist/admin/fr-BQhLPvYw.js +673 -0
  12. package/dist/admin/fr-BseH1wrr.mjs +673 -0
  13. package/dist/admin/index-6ot9j1cN.js +5268 -0
  14. package/dist/admin/index-B390rccl.js +622 -0
  15. package/dist/admin/index-BPGKtruf.mjs +5266 -0
  16. package/dist/admin/index-BaTIQtnA.js +3869 -0
  17. package/dist/admin/index-CmG-93yx.js +141 -0
  18. package/dist/admin/index-DXe_ge4v.mjs +620 -0
  19. package/dist/admin/index-Dv5PVT1E.mjs +3867 -0
  20. package/dist/admin/index-k240ls6p.mjs +142 -0
  21. package/dist/admin/index.js +3 -3
  22. package/dist/admin/index.mjs +1 -2
  23. package/dist/admin/pt-CPkaveti.js +673 -0
  24. package/dist/admin/pt-DhlSCJn2.mjs +673 -0
  25. package/dist/admin/useUnsavedChangesGuard-BVZ4Mg4E.js +225 -0
  26. package/dist/admin/useUnsavedChangesGuard-QnkiZTLP.mjs +224 -0
  27. package/dist/server/i18n/de.json +32 -1
  28. package/dist/server/i18n/en.json +32 -1
  29. package/dist/server/i18n/es.json +32 -1
  30. package/dist/server/i18n/fr.json +32 -1
  31. package/dist/server/i18n/pt.json +32 -1
  32. package/dist/server/index.js +7092 -5971
  33. package/dist/server/index.mjs +7115 -5972
  34. package/package.json +42 -41
  35. package/strapi-admin.js +3 -1
  36. package/strapi-server.js +3 -2
  37. package/dist/_chunks/LicenseGuard-CiyjFnrw.js +0 -54
  38. package/dist/_chunks/LicenseGuard-DJ5fq2UZ.mjs +0 -55
  39. package/dist/_chunks/de-BSOk5suQ.js +0 -454
  40. package/dist/_chunks/de-CwhYs3O3.mjs +0 -454
  41. package/dist/_chunks/en-DnvSpXvu.js +0 -454
  42. package/dist/_chunks/en-gXXgGDV1.mjs +0 -454
  43. package/dist/_chunks/es-B-tnwByt.js +0 -407
  44. package/dist/_chunks/es-DG5gW3RZ.mjs +0 -407
  45. package/dist/_chunks/fr-BKo8t7Fx.js +0 -407
  46. package/dist/_chunks/fr-BQn1OHOR.mjs +0 -407
  47. package/dist/_chunks/index-01GHNxSr.js +0 -307
  48. package/dist/_chunks/index-5jsDgD-q.mjs +0 -330
  49. package/dist/_chunks/index-BWuj-5WE.js +0 -5068
  50. package/dist/_chunks/index-Bj3H_Z4B.js +0 -142
  51. package/dist/_chunks/index-CC4Eu9Dm.js +0 -332
  52. package/dist/_chunks/index-Csyi13vd.mjs +0 -2891
  53. package/dist/_chunks/index-DJqXiiP1.js +0 -2893
  54. package/dist/_chunks/index-DQUYFsLT.mjs +0 -143
  55. package/dist/_chunks/index-Dufv-daf.mjs +0 -305
  56. package/dist/_chunks/index-szeYJKpN.mjs +0 -5066
  57. package/dist/_chunks/pt-CjEkXBAz.js +0 -407
  58. package/dist/_chunks/pt-trziAOzT.mjs +0 -407
@@ -0,0 +1,513 @@
1
+ "use strict";
2
+ const jsxRuntime = require("react/jsx-runtime");
3
+ const designSystem = require("@strapi/design-system");
4
+ const strapiAdmin = require("@strapi/strapi/admin");
5
+ const styled = require("styled-components");
6
+ const index = require("./index-CmG-93yx.js");
7
+ const react = require("react");
8
+ const reactIntl = require("react-intl");
9
+ const en = require("./en-CABKGbxH.js");
10
+ const de = require("./de-e75OECdq.js");
11
+ const fr = require("./fr-BQhLPvYw.js");
12
+ const es = require("./es-DpWl0Fax.js");
13
+ const pt = require("./pt-CPkaveti.js");
14
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
15
+ function _interopNamespace(e) {
16
+ if (e && e.__esModule) return e;
17
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
18
+ if (e) {
19
+ for (const k in e) {
20
+ if (k !== "default") {
21
+ const d = Object.getOwnPropertyDescriptor(e, k);
22
+ Object.defineProperty(n, k, d.get ? d : {
23
+ enumerable: true,
24
+ get: () => e[k]
25
+ });
26
+ }
27
+ }
28
+ }
29
+ n.default = e;
30
+ return Object.freeze(n);
31
+ }
32
+ const strapiAdmin__namespace = /* @__PURE__ */ _interopNamespace(strapiAdmin);
33
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
34
+ const useMagicLinkIntl = () => {
35
+ const intl = reactIntl.useIntl();
36
+ const t = react.useCallback(
37
+ (key, defaultMessage, values) => intl.formatMessage({ id: index.getTranslation(key), defaultMessage }, values),
38
+ [intl]
39
+ );
40
+ return { t, formatMessage: intl.formatMessage, locale: intl.locale };
41
+ };
42
+ const PluginPageTitle = ({ children }) => {
43
+ const { t } = useMagicLinkIntl();
44
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Page.Title, { children: t("common.pageTitle", "{page} | Magic Link", { page: children }) });
45
+ };
46
+ styled__default.default(designSystem.Typography).attrs((props) => ({
47
+ tag: props.tag ?? "h2",
48
+ variant: props.variant ?? "delta",
49
+ textColor: props.textColor ?? "neutral800"
50
+ }))``;
51
+ const SubsectionHeading = styled__default.default(designSystem.Typography).attrs((props) => ({
52
+ tag: props.tag ?? "h3",
53
+ variant: props.variant ?? "omega",
54
+ fontWeight: props.fontWeight ?? "bold",
55
+ textColor: props.textColor ?? "neutral800"
56
+ }))``;
57
+ const PLUGIN_CAPABILITIES = [...index.pluginPermissions.read, ...index.pluginPermissions.manage];
58
+ const SETTINGS_CAPABILITIES = [
59
+ ...index.pluginPermissions.settingsRead,
60
+ ...index.pluginPermissions.settingsUpdate
61
+ ];
62
+ const grantedActions = (permissions) => new Set(
63
+ (Array.isArray(permissions) ? permissions : []).filter(
64
+ (permission) => typeof permission?.action === "string" && (!Array.isArray(permission.conditions) || permission.conditions.length === 0)
65
+ ).map((permission) => permission.action)
66
+ );
67
+ const resolveCapabilities = (plugin = {}, settings = {}, auth = {}) => {
68
+ const isLoading = Boolean(plugin.isLoading || settings.isLoading || auth.isLoading);
69
+ const error = plugin.error ?? settings.error;
70
+ const settled = !isLoading && !error;
71
+ const granted = grantedActions(auth.permissions);
72
+ const allowed = (result, key, uid) => settled && (result.allowedActions?.[key] === true || granted.has(uid));
73
+ const canRead = allowed(plugin, "canRead", "plugin::magic-link.read");
74
+ const canManage = allowed(plugin, "canManage", "plugin::magic-link.manage");
75
+ const canReadSettings = allowed(settings, "canRead", "plugin::magic-link.settings.read");
76
+ const canUpdateSettings = allowed(settings, "canUpdate", "plugin::magic-link.settings.update");
77
+ return {
78
+ canRead,
79
+ canManage,
80
+ canReadSettings,
81
+ canUpdateSettings,
82
+ isReadOnly: canRead && !canManage,
83
+ isSettingsReadOnly: canReadSettings && !canUpdateSettings,
84
+ isLoading,
85
+ error
86
+ };
87
+ };
88
+ const useAdminPermissions = typeof strapiAdmin__namespace.useAuth === "function" ? () => {
89
+ const permissions = strapiAdmin__namespace.useAuth("MagicLinkRBAC", (state) => state.permissions);
90
+ const isLoading = strapiAdmin__namespace.useAuth("MagicLinkRBAC", (state) => state.isLoading);
91
+ return { permissions, isLoading };
92
+ } : () => EMPTY_AUTH;
93
+ const EMPTY_AUTH = Object.freeze({});
94
+ const useMagicLinkRBAC = () => {
95
+ const plugin = strapiAdmin__namespace.useRBAC(PLUGIN_CAPABILITIES) ?? {};
96
+ const settings = strapiAdmin__namespace.useRBAC(SETTINGS_CAPABILITIES) ?? {};
97
+ const auth = useAdminPermissions();
98
+ return { ...resolveCapabilities(plugin, settings, auth), permissions: index.pluginPermissions };
99
+ };
100
+ const LIGHT_FALLBACK_COLORS = Object.freeze({
101
+ alternative100: "#f6ecfc",
102
+ alternative200: "#e0c1f4",
103
+ alternative500: "#ac73e6",
104
+ alternative600: "#9736e8",
105
+ alternative700: "#8312d1",
106
+ neutral0: "#ffffff",
107
+ neutral100: "#f6f6f9",
108
+ neutral150: "#eaeaef",
109
+ neutral200: "#dcdce4",
110
+ neutral300: "#c0c0cf",
111
+ neutral400: "#a5a5ba",
112
+ neutral500: "#8e8ea9",
113
+ neutral600: "#666687",
114
+ neutral700: "#4a4a6a",
115
+ neutral800: "#32324d",
116
+ neutral900: "#212134",
117
+ neutral1000: "#181826",
118
+ primary100: "#f0f0ff",
119
+ primary200: "#d9d8ff",
120
+ primary500: "#7b79ff",
121
+ primary600: "#4945ff",
122
+ primary700: "#271fe0",
123
+ secondary100: "#eaf5ff",
124
+ secondary200: "#b8e1ff",
125
+ secondary500: "#66b7f1",
126
+ secondary600: "#0c75af",
127
+ secondary700: "#006096",
128
+ success100: "#eafbe7",
129
+ success200: "#c6f0c2",
130
+ success500: "#5cb176",
131
+ success600: "#328048",
132
+ success700: "#2f6846",
133
+ warning100: "#fdf4dc",
134
+ warning200: "#fae7b9",
135
+ warning500: "#f29d41",
136
+ warning600: "#d9822f",
137
+ warning700: "#be5d01",
138
+ danger100: "#fcecea",
139
+ danger200: "#f5c0b8",
140
+ danger500: "#ee5e52",
141
+ danger600: "#d02b20",
142
+ danger700: "#b72b1a"
143
+ });
144
+ const FONT_SIZE_FALLBACK = Object.freeze([
145
+ "1.1rem",
146
+ // 0 -> 11px (sigma, uppercase table headers only)
147
+ "1.2rem",
148
+ // 1 -> 12px (pi, the minimum for helper text)
149
+ "1.4rem",
150
+ // 2 -> 14px (omega, body)
151
+ "1.6rem",
152
+ // 3 -> 16px
153
+ "1.8rem",
154
+ // 4 -> 18px
155
+ "2rem",
156
+ // 5 -> 20px
157
+ "2.8rem",
158
+ // 6 -> 28px
159
+ "3.2rem"
160
+ // 7 -> 32px
161
+ ]);
162
+ const SPACE_FALLBACK = Object.freeze([
163
+ "0px",
164
+ "4px",
165
+ "8px",
166
+ "12px",
167
+ "16px",
168
+ "20px",
169
+ "24px",
170
+ "32px",
171
+ "40px",
172
+ "48px",
173
+ "56px",
174
+ "64px"
175
+ ]);
176
+ const color = (token) => (props) => props?.theme?.colors?.[token] ?? LIGHT_FALLBACK_COLORS[token];
177
+ const fontSize = (step) => (props) => props?.theme?.fontSizes?.[step] ?? FONT_SIZE_FALLBACK[step];
178
+ const space = (step) => (props) => props?.theme?.spaces?.[step] ?? SPACE_FALLBACK[step];
179
+ const isDarkTheme = (themeObject) => themeObject?.colorScheme === "dark";
180
+ const byScheme = (light, dark) => (props) => isDarkTheme(props?.theme) ? dark : light;
181
+ const brand = Object.freeze({
182
+ primary: "#0369A1",
183
+ primaryHover: "#075985",
184
+ secondary: "#9333EA",
185
+ secondaryHover: "#7E22CE",
186
+ success: "#15803D",
187
+ successHover: "#166534",
188
+ warning: "#B45309",
189
+ warningHover: "#92400E",
190
+ danger: "#DC2626",
191
+ dangerHover: "#B91C1C",
192
+ gradient: "linear-gradient(135deg, #0369A1 0%, #9333EA 100%)",
193
+ gradientHover: "linear-gradient(135deg, #075985 0%, #7E22CE 100%)",
194
+ successGradient: "linear-gradient(135deg, #15803D 0%, #166534 100%)",
195
+ violetGradient: "linear-gradient(135deg, #667EEA 0%, #764BA2 100%)",
196
+ violetHeroGradient: "linear-gradient(rgba(15, 23, 42, 0.25), rgba(15, 23, 42, 0.25)), linear-gradient(135deg, #667EEA 0%, #764BA2 100%)",
197
+ onFill: "#FFFFFF"
198
+ });
199
+ const ACCENT_VALUES = Object.freeze({
200
+ primary: ["#0369A1", "#7DD3FC"],
201
+ secondary: ["#7E22CE", "#D8B4FE"],
202
+ success: ["#15803D", "#86EFAC"],
203
+ warning: ["#B45309", "#FCD34D"],
204
+ danger: ["#B91C1C", "#FCA5A5"]
205
+ });
206
+ const accent = Object.freeze(
207
+ Object.fromEntries(
208
+ Object.entries(ACCENT_VALUES).map(([name, [light, dark]]) => [name, byScheme(light, dark)])
209
+ )
210
+ );
211
+ const TINT_VALUES = Object.freeze({
212
+ primary: { bg: ["#F0F9FF", "rgba(14, 165, 233, 0.12)"], border: ["#BAE6FD", "#0C4A6E"] },
213
+ success: { bg: ["#F0FDF4", "rgba(34, 197, 94, 0.12)"], border: ["#BBF7D0", "#14532D"] },
214
+ warning: { bg: ["#FFFBEB", "rgba(245, 158, 11, 0.12)"], border: ["#FDE68A", "#78350F"] },
215
+ danger: { bg: ["#FEF2F2", "rgba(239, 68, 68, 0.12)"], border: ["#FECACA", "#7F1D1D"] }
216
+ });
217
+ const tint = Object.freeze(
218
+ Object.fromEntries(
219
+ Object.entries(TINT_VALUES).map(([name, values]) => [
220
+ name,
221
+ Object.freeze({
222
+ bg: byScheme(...values.bg),
223
+ border: byScheme(...values.border),
224
+ fg: accent[name]
225
+ })
226
+ ])
227
+ )
228
+ );
229
+ const breakpoints = Object.freeze({
230
+ mobile: "768px",
231
+ tablet: "1024px",
232
+ statsThreeTwo: "960px",
233
+ tableCompact: "1199px",
234
+ compact: "520px"
235
+ });
236
+ const theme = Object.freeze({
237
+ spacing: Object.freeze({
238
+ xs: "4px",
239
+ sm: "8px",
240
+ smd: "12px",
241
+ md: "16px",
242
+ lg: "24px",
243
+ xl: "32px",
244
+ "2xl": "48px"
245
+ }),
246
+ borderRadius: Object.freeze({ sm: "4px", md: "8px", lg: "12px", xl: "16px", full: "9999px" }),
247
+ shadows: Object.freeze({
248
+ sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)",
249
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
250
+ lg: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
251
+ xl: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)"
252
+ }),
253
+ transitions: Object.freeze({
254
+ fast: "150ms cubic-bezier(0.4, 0, 0.2, 1)",
255
+ normal: "300ms cubic-bezier(0.4, 0, 0.2, 1)"
256
+ }),
257
+ /**
258
+ * DS 2.2.4 button heights at >= 768px (theme.sizes.button). Below 768px DS
259
+ * grows them to 40/44/48px. Use one size per row and match custom controls
260
+ * to it.
261
+ */
262
+ controlHeight: Object.freeze({ S: "32px", M: "36px", L: "40px" })
263
+ });
264
+ const reducedMotion = styled.css`
265
+ @media (prefers-reduced-motion: reduce) {
266
+ animation: none !important;
267
+ transition: none !important;
268
+
269
+ &::before,
270
+ &::after {
271
+ animation: none !important;
272
+ transition: none !important;
273
+ }
274
+
275
+ &:hover,
276
+ &:active,
277
+ &:focus-visible {
278
+ transform: none !important;
279
+ }
280
+ }
281
+ `;
282
+ const motionSafe = (block) => styled.css`
283
+ @media (prefers-reduced-motion: no-preference) {
284
+ ${block}
285
+ }
286
+ `;
287
+ const EMPTY_VALUE = "-";
288
+ const DATE_TIME_FORMAT = Object.freeze({ dateStyle: "medium", timeStyle: "short" });
289
+ const DATE_FORMAT = Object.freeze({ dateStyle: "medium" });
290
+ const toValidDate = (value) => {
291
+ if (value === null || value === void 0 || value === "") return null;
292
+ const date = value instanceof Date ? value : new Date(value);
293
+ return Number.isNaN(date.getTime()) ? null : date;
294
+ };
295
+ const createFormatters = (intl) => {
296
+ const formatDateTime = (value, options = DATE_TIME_FORMAT, fallback = EMPTY_VALUE) => {
297
+ const date = toValidDate(value);
298
+ return date ? intl.formatDate(date, options) : fallback;
299
+ };
300
+ const formatDateOnly = (value, options = DATE_FORMAT, fallback = EMPTY_VALUE) => {
301
+ const date = toValidDate(value);
302
+ return date ? intl.formatDate(date, options) : fallback;
303
+ };
304
+ const formatCount = (value, options, fallback = EMPTY_VALUE) => {
305
+ const number = typeof value === "number" ? value : Number(value);
306
+ return value === null || value === void 0 || value === "" || !Number.isFinite(number) ? fallback : intl.formatNumber(number, options);
307
+ };
308
+ const formatPercent = (value, fractionDigits = 1, fallback = EMPTY_VALUE) => {
309
+ if (value === null || value === void 0 || value === "") return fallback;
310
+ const number = Number(value);
311
+ return Number.isFinite(number) ? intl.formatNumber(number / 100, { style: "percent", maximumFractionDigits: fractionDigits }) : fallback;
312
+ };
313
+ const formatRelative = (value, now = /* @__PURE__ */ new Date(), fallback = EMPTY_VALUE) => {
314
+ const date = toValidDate(value);
315
+ if (!date || typeof intl.formatRelativeTime !== "function") {
316
+ return date ? formatDateTime(date) : fallback;
317
+ }
318
+ const seconds = Math.round((date.getTime() - now.getTime()) / 1e3);
319
+ const abs = Math.abs(seconds);
320
+ if (abs < 60) return intl.formatRelativeTime(seconds, "second", { numeric: "auto" });
321
+ if (abs < 3600)
322
+ return intl.formatRelativeTime(Math.round(seconds / 60), "minute", { numeric: "auto" });
323
+ if (abs < 86400)
324
+ return intl.formatRelativeTime(Math.round(seconds / 3600), "hour", { numeric: "auto" });
325
+ if (abs < 86400 * 30)
326
+ return intl.formatRelativeTime(Math.round(seconds / 86400), "day", { numeric: "auto" });
327
+ return formatDateOnly(date);
328
+ };
329
+ return {
330
+ formatDateTime,
331
+ formatDate: formatDateOnly,
332
+ formatNumber: formatCount,
333
+ formatPercent,
334
+ formatRelative
335
+ };
336
+ };
337
+ const useFormatters = () => {
338
+ const intl = reactIntl.useIntl();
339
+ return react.useMemo(() => createFormatters(intl), [intl]);
340
+ };
341
+ const copyToClipboard = async (text, env = {}) => {
342
+ if (typeof text !== "string" || text.length === 0) return false;
343
+ const win = env.window ?? (typeof window === "undefined" ? void 0 : window);
344
+ const nav = env.navigator ?? win?.navigator ?? (typeof navigator === "undefined" ? void 0 : navigator);
345
+ const doc = env.document ?? win?.document ?? (typeof document === "undefined" ? void 0 : document);
346
+ const secure = win?.isSecureContext !== false;
347
+ if (secure && typeof nav?.clipboard?.writeText === "function") {
348
+ try {
349
+ await nav.clipboard.writeText(text);
350
+ return true;
351
+ } catch {
352
+ }
353
+ }
354
+ return legacyCopy(text, doc);
355
+ };
356
+ const legacyCopy = (text, doc) => {
357
+ if (!doc?.body || typeof doc.createElement !== "function" || typeof doc.execCommand !== "function") {
358
+ return false;
359
+ }
360
+ const textarea = doc.createElement("textarea");
361
+ textarea.value = text;
362
+ textarea.setAttribute("readonly", "");
363
+ textarea.setAttribute("aria-hidden", "true");
364
+ textarea.style.position = "fixed";
365
+ textarea.style.top = "0";
366
+ textarea.style.left = "-9999px";
367
+ textarea.style.opacity = "0";
368
+ const activeElement = doc.activeElement;
369
+ doc.body.appendChild(textarea);
370
+ try {
371
+ textarea.select();
372
+ return doc.execCommand("copy") === true;
373
+ } catch {
374
+ return false;
375
+ } finally {
376
+ doc.body.removeChild(textarea);
377
+ if (activeElement && typeof activeElement.focus === "function") {
378
+ try {
379
+ activeElement.focus();
380
+ } catch {
381
+ }
382
+ }
383
+ }
384
+ };
385
+ const useCopyToClipboard = () => {
386
+ const { toggleNotification } = strapiAdmin.useNotification();
387
+ const { t } = useMagicLinkIntl();
388
+ return react.useCallback(
389
+ async (text, messages = {}) => {
390
+ const copied = await copyToClipboard(text);
391
+ toggleNotification({
392
+ type: copied ? "success" : "warning",
393
+ message: copied ? messages.success ?? t("common.clipboard.copied", "Copied to clipboard") : messages.failure ?? t("common.clipboard.failed", "Copy failed. Select the text and copy it manually.")
394
+ });
395
+ return copied;
396
+ },
397
+ [t, toggleNotification]
398
+ );
399
+ };
400
+ const STORAGE_KEY = "magic-link-language";
401
+ const SUPPORTED_LANGUAGES = Object.freeze(["en", "de", "fr", "es", "pt"]);
402
+ const translations = Object.freeze({
403
+ en: en.default,
404
+ de: de.default,
405
+ fr: fr.default,
406
+ es: es.default,
407
+ pt: pt.default
408
+ });
409
+ const toSupportedLanguage = (locale) => {
410
+ if (typeof locale !== "string" || !locale) return null;
411
+ const base = locale.toLowerCase().split(/[-_]/)[0];
412
+ return SUPPORTED_LANGUAGES.includes(base) ? base : null;
413
+ };
414
+ const readStoredLanguage = (storage = safeLocalStorage()) => {
415
+ try {
416
+ return toSupportedLanguage(storage?.getItem(STORAGE_KEY));
417
+ } catch {
418
+ return null;
419
+ }
420
+ };
421
+ const writeStoredLanguage = (language, storage = safeLocalStorage()) => {
422
+ try {
423
+ if (language) storage?.setItem(STORAGE_KEY, language);
424
+ else storage?.removeItem(STORAGE_KEY);
425
+ } catch {
426
+ }
427
+ };
428
+ function safeLocalStorage() {
429
+ try {
430
+ return typeof window === "undefined" ? void 0 : window.localStorage;
431
+ } catch {
432
+ return void 0;
433
+ }
434
+ }
435
+ const buildMessages = (adminMessages, language) => ({
436
+ ...adminMessages ?? {},
437
+ ...translations.en,
438
+ ...translations[language] ?? {}
439
+ });
440
+ const createIntlErrorHandler = (parentOnError) => (error) => {
441
+ if (error?.code === "MISSING_TRANSLATION") return;
442
+ if (typeof parentOnError === "function") parentOnError(error);
443
+ };
444
+ const LanguageContext = react.createContext(null);
445
+ const usePluginLanguage = () => {
446
+ const context = react.useContext(LanguageContext);
447
+ if (!context) {
448
+ throw new Error("usePluginLanguage must be used within LanguageProvider");
449
+ }
450
+ return context;
451
+ };
452
+ const LanguageProvider = ({ children }) => {
453
+ const parentIntl = react.useContext(reactIntl.IntlContext);
454
+ const adminLanguage = toSupportedLanguage(parentIntl?.locale) ?? "en";
455
+ const [override, setOverride] = react.useState(() => readStoredLanguage());
456
+ const language = override ?? adminLanguage;
457
+ const changeLanguage = react.useCallback((next) => {
458
+ const supported = toSupportedLanguage(next);
459
+ setOverride(supported);
460
+ writeStoredLanguage(supported);
461
+ }, []);
462
+ const messages = react.useMemo(
463
+ () => buildMessages(parentIntl?.messages, language),
464
+ [parentIntl?.messages, language]
465
+ );
466
+ const parentOnError = parentIntl?.onError;
467
+ const onError = react.useMemo(() => createIntlErrorHandler(parentOnError), [parentOnError]);
468
+ const value = react.useMemo(
469
+ () => ({
470
+ language,
471
+ adminLanguage,
472
+ isFollowingAdmin: override === null,
473
+ languages: SUPPORTED_LANGUAGES,
474
+ changeLanguage,
475
+ /** @deprecated use `useMagicLinkIntl().t(key, defaultMessage)` */
476
+ t: (key) => messages[key] ?? key
477
+ }),
478
+ [language, adminLanguage, override, changeLanguage, messages]
479
+ );
480
+ const locale = parentIntl?.locale && toSupportedLanguage(parentIntl.locale) === language ? parentIntl.locale : language;
481
+ return /* @__PURE__ */ jsxRuntime.jsx(LanguageContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(
482
+ reactIntl.IntlProvider,
483
+ {
484
+ locale,
485
+ messages,
486
+ defaultLocale: "en",
487
+ timeZone: parentIntl?.timeZone,
488
+ onError,
489
+ children
490
+ }
491
+ ) });
492
+ };
493
+ const LicenseGuard = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
494
+ exports.LanguageProvider = LanguageProvider;
495
+ exports.LicenseGuard = LicenseGuard;
496
+ exports.PluginPageTitle = PluginPageTitle;
497
+ exports.SubsectionHeading = SubsectionHeading;
498
+ exports.accent = accent;
499
+ exports.brand = brand;
500
+ exports.breakpoints = breakpoints;
501
+ exports.byScheme = byScheme;
502
+ exports.color = color;
503
+ exports.fontSize = fontSize;
504
+ exports.motionSafe = motionSafe;
505
+ exports.reducedMotion = reducedMotion;
506
+ exports.space = space;
507
+ exports.theme = theme;
508
+ exports.tint = tint;
509
+ exports.useCopyToClipboard = useCopyToClipboard;
510
+ exports.useFormatters = useFormatters;
511
+ exports.useMagicLinkIntl = useMagicLinkIntl;
512
+ exports.useMagicLinkRBAC = useMagicLinkRBAC;
513
+ exports.usePluginLanguage = usePluginLanguage;