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